From 64d4d49bdb507ecaba5048f227751171f125b6c7 Mon Sep 17 00:00:00 2001 From: Hykilpikonna Date: Wed, 9 Nov 2022 23:49:19 -0500 Subject: [PATCH] [O] Reformat code --- src/main/java/Mars.java | 6 +- src/main/java/mars/ErrorList.java | 368 +- src/main/java/mars/ErrorMessage.java | 524 +- src/main/java/mars/Globals.java | 214 +- src/main/java/mars/MIPSprogram.java | 810 +-- src/main/java/mars/MarsLaunch.java | 1606 ++--- src/main/java/mars/MarsSplashScreen.java | 172 +- src/main/java/mars/ProcessingException.java | 205 +- src/main/java/mars/ProgramStatement.java | 490 +- src/main/java/mars/Settings.java | 844 ++- src/main/java/mars/assembler/Assembler.java | 2813 ++++---- src/main/java/mars/assembler/DataTypes.java | 219 +- src/main/java/mars/assembler/Directives.java | 335 +- src/main/java/mars/assembler/Macro.java | 434 +- src/main/java/mars/assembler/MacroPool.java | 353 +- .../java/mars/assembler/OperandFormat.java | 312 +- src/main/java/mars/assembler/SourceLine.java | 142 +- src/main/java/mars/assembler/Symbol.java | 142 +- src/main/java/mars/assembler/SymbolTable.java | 496 +- src/main/java/mars/assembler/Token.java | 297 +- src/main/java/mars/assembler/TokenList.java | 250 +- src/main/java/mars/assembler/TokenTypes.java | 543 +- src/main/java/mars/assembler/Tokenizer.java | 1094 ++-- .../java/mars/assembler/TranslationCode.java | 23 +- .../mars/mips/dump/AbstractDumpFormat.java | 181 +- .../mars/mips/dump/AsciiTextDumpFormat.java | 122 +- .../java/mars/mips/dump/BinaryDumpFormat.java | 111 +- .../mars/mips/dump/BinaryTextDumpFormat.java | 118 +- src/main/java/mars/mips/dump/DumpFormat.java | 111 +- .../java/mars/mips/dump/DumpFormatLoader.java | 143 +- .../mars/mips/dump/HexTextDumpFormat.java | 118 +- .../mars/mips/dump/IntelHexDumpFormat.java | 147 +- .../java/mars/mips/dump/MIFDumpFormat.java | 69 +- .../mips/dump/SegmentWindowDumpFormat.java | 241 +- .../java/mars/mips/hardware/AccessNotice.java | 123 +- .../mips/hardware/AddressErrorException.java | 78 +- .../java/mars/mips/hardware/Coprocessor0.java | 392 +- .../java/mars/mips/hardware/Coprocessor1.java | 1028 +-- .../InvalidRegisterAccessException.java | 28 +- src/main/java/mars/mips/hardware/Memory.java | 2567 ++++---- .../mips/hardware/MemoryAccessNotice.java | 104 +- .../mips/hardware/MemoryConfiguration.java | 275 +- .../mips/hardware/MemoryConfigurations.java | 575 +- .../java/mars/mips/hardware/Register.java | 264 +- .../mips/hardware/RegisterAccessNotice.java | 54 +- .../java/mars/mips/hardware/RegisterFile.java | 628 +- .../mips/instructions/BasicInstruction.java | 200 +- .../instructions/BasicInstructionFormat.java | 25 +- .../instructions/ExtendedInstruction.java | 1233 ++-- .../mars/mips/instructions/Instruction.java | 125 +- .../mips/instructions/InstructionSet.java | 5687 +++++++++-------- .../mips/instructions/SimulationCode.java | 25 +- .../mars/mips/instructions/SyscallLoader.java | 288 +- .../syscalls/AbstractSyscall.java | 136 +- .../instructions/syscalls/RandomStreams.java | 27 +- .../mips/instructions/syscalls/Syscall.java | 92 +- .../instructions/syscalls/SyscallClose.java | 49 +- .../syscalls/SyscallConfirmDialog.java | 93 +- .../instructions/syscalls/SyscallExit.java | 46 +- .../instructions/syscalls/SyscallExit2.java | 57 +- .../syscalls/SyscallInputDialogDouble.java | 166 +- .../syscalls/SyscallInputDialogFloat.java | 156 +- .../syscalls/SyscallInputDialogInt.java | 153 +- .../syscalls/SyscallInputDialogString.java | 185 +- .../syscalls/SyscallMessageDialog.java | 102 +- .../syscalls/SyscallMessageDialogDouble.java | 112 +- .../syscalls/SyscallMessageDialogFloat.java | 92 +- .../syscalls/SyscallMessageDialogInt.java | 91 +- .../syscalls/SyscallMessageDialogString.java | 115 +- .../instructions/syscalls/SyscallMidiOut.java | 103 +- .../syscalls/SyscallMidiOutSync.java | 112 +- .../syscalls/SyscallNumberOverride.java | 104 +- .../instructions/syscalls/SyscallOpen.java | 134 +- .../syscalls/SyscallPrintChar.java | 57 +- .../syscalls/SyscallPrintDouble.java | 59 +- .../syscalls/SyscallPrintFloat.java | 26 +- .../syscalls/SyscallPrintInt.java | 41 +- .../syscalls/SyscallPrintIntBinary.java | 50 +- .../syscalls/SyscallPrintIntHex.java | 50 +- .../syscalls/SyscallPrintIntUnsigned.java | 54 +- .../syscalls/SyscallPrintString.java | 76 +- .../syscalls/SyscallRandDouble.java | 86 +- .../syscalls/SyscallRandFloat.java | 66 +- .../instructions/syscalls/SyscallRandInt.java | 66 +- .../syscalls/SyscallRandIntRange.java | 93 +- .../syscalls/SyscallRandSeed.java | 70 +- .../instructions/syscalls/SyscallRead.java | 108 +- .../syscalls/SyscallReadChar.java | 75 +- .../syscalls/SyscallReadDouble.java | 80 +- .../syscalls/SyscallReadFloat.java | 70 +- .../instructions/syscalls/SyscallReadInt.java | 73 +- .../syscalls/SyscallReadString.java | 104 +- .../instructions/syscalls/SyscallSbrk.java | 69 +- .../instructions/syscalls/SyscallSleep.java | 64 +- .../instructions/syscalls/SyscallTime.java | 57 +- .../instructions/syscalls/SyscallWrite.java | 121 +- .../instructions/syscalls/ToneGenerator.java | 464 +- src/main/java/mars/simulator/BackStepper.java | 852 +-- .../java/mars/simulator/DelayedBranch.java | 235 +- src/main/java/mars/simulator/Exceptions.java | 130 +- .../mars/simulator/ProgramArgumentList.java | 314 +- src/main/java/mars/simulator/Simulator.java | 1010 +-- .../java/mars/simulator/SimulatorNotice.java | 108 +- src/main/java/mars/simulator/SwingWorker.java | 202 +- .../tools/AbstractMarsToolAndApplication.java | 1576 ++--- src/main/java/mars/tools/BHTEntry.java | 322 +- src/main/java/mars/tools/BHTSimGUI.java | 584 +- src/main/java/mars/tools/BHTSimulator.java | 709 +- src/main/java/mars/tools/BHTableModel.java | 400 +- src/main/java/mars/tools/BitmapDisplay.java | 1113 ++-- src/main/java/mars/tools/CacheSimulator.java | 1766 ++--- src/main/java/mars/tools/DigitalLabSim.java | 750 ++- .../java/mars/tools/FloatRepresentation.java | 1751 ++--- .../mars/tools/FunctionUnitVisualization.java | 125 +- .../java/mars/tools/InstructionCounter.java | 427 +- .../mars/tools/InstructionStatistics.java | 588 +- src/main/java/mars/tools/IntroToTools.java | 263 +- .../tools/KeyboardAndDisplaySimulator.java | 1965 +++--- src/main/java/mars/tools/MarsBot.java | 532 +- src/main/java/mars/tools/MarsTool.java | 55 +- .../tools/MemoryReferenceVisualization.java | 1575 ++--- src/main/java/mars/tools/MipsXray.java | 3109 +++++---- src/main/java/mars/tools/ScavengerHunt.java | 1754 ++--- src/main/java/mars/tools/ScreenMagnifier.java | 1792 +++--- src/main/java/mars/tools/UnitAnimation.java | 1795 +++--- src/main/java/mars/util/Binary.java | 1126 ++-- src/main/java/mars/util/EditorFont.java | 411 +- src/main/java/mars/util/FilenameFinder.java | 896 +-- src/main/java/mars/util/MemoryDump.java | 217 +- src/main/java/mars/util/PropertiesFile.java | 68 +- src/main/java/mars/util/SystemIO.java | 1003 +-- .../mars/venus/AbstractFontSettingDialog.java | 525 +- .../java/mars/venus/Coprocessor0Window.java | 729 ++- .../java/mars/venus/Coprocessor1Window.java | 1004 +-- .../java/mars/venus/DataSegmentWindow.java | 2135 ++++--- src/main/java/mars/venus/EditCopyAction.java | 42 +- src/main/java/mars/venus/EditCutAction.java | 42 +- .../mars/venus/EditFindReplaceAction.java | 468 +- src/main/java/mars/venus/EditPane.java | 1192 ++-- src/main/java/mars/venus/EditPasteAction.java | 42 +- src/main/java/mars/venus/EditRedoAction.java | 65 +- .../java/mars/venus/EditSelectAllAction.java | 42 +- src/main/java/mars/venus/EditTabbedPane.java | 1370 ++-- src/main/java/mars/venus/EditUndoAction.java | 67 +- src/main/java/mars/venus/Editor.java | 451 +- src/main/java/mars/venus/ExecutePane.java | 457 +- src/main/java/mars/venus/FileCloseAction.java | 42 +- .../java/mars/venus/FileCloseAllAction.java | 42 +- .../java/mars/venus/FileDumpMemoryAction.java | 534 +- src/main/java/mars/venus/FileExitAction.java | 51 +- src/main/java/mars/venus/FileNewAction.java | 50 +- src/main/java/mars/venus/FileOpenAction.java | 76 +- src/main/java/mars/venus/FilePrintAction.java | 141 +- src/main/java/mars/venus/FileSaveAction.java | 51 +- .../java/mars/venus/FileSaveAllAction.java | 42 +- .../java/mars/venus/FileSaveAsAction.java | 45 +- src/main/java/mars/venus/FileStatus.java | 642 +- src/main/java/mars/venus/GuiAction.java | 58 +- src/main/java/mars/venus/HardcopyWriter.java | 663 +- src/main/java/mars/venus/HelpAboutAction.java | 70 +- src/main/java/mars/venus/HelpHelpAction.java | 838 +-- src/main/java/mars/venus/LabelsWindow.java | 1065 +-- src/main/java/mars/venus/MainPane.java | 231 +- src/main/java/mars/venus/MessagesPane.java | 948 +-- .../mars/venus/MonoRightCellRenderer.java | 35 +- .../mars/venus/NumberDisplayBaseChooser.java | 472 +- src/main/java/mars/venus/PopupListener.java | 56 +- src/main/java/mars/venus/RegistersPane.java | 139 +- src/main/java/mars/venus/RegistersWindow.java | 771 ++- src/main/java/mars/venus/RepeatButton.java | 668 +- .../java/mars/venus/RunAssembleAction.java | 313 +- .../java/mars/venus/RunBackstepAction.java | 106 +- .../mars/venus/RunClearBreakpointsAction.java | 92 +- src/main/java/mars/venus/RunGoAction.java | 364 +- src/main/java/mars/venus/RunPauseAction.java | 50 +- src/main/java/mars/venus/RunResetAction.java | 150 +- src/main/java/mars/venus/RunSpeedPanel.java | 258 +- src/main/java/mars/venus/RunStepAction.java | 184 +- src/main/java/mars/venus/RunStopAction.java | 52 +- .../venus/RunToggleBreakpointsAction.java | 66 +- .../SettingsAddressDisplayBaseAction.java | 55 +- .../mars/venus/SettingsAssembleAllAction.java | 55 +- .../venus/SettingsAssembleOnOpenAction.java | 53 +- .../venus/SettingsDelayedBranchingAction.java | 90 +- .../java/mars/venus/SettingsEditorAction.java | 943 +-- .../venus/SettingsExceptionHandlerAction.java | 302 +- .../mars/venus/SettingsExtendedAction.java | 51 +- .../venus/SettingsHighlightingAction.java | 1046 +-- .../java/mars/venus/SettingsLabelAction.java | 55 +- .../SettingsMemoryConfigurationAction.java | 429 +- .../mars/venus/SettingsPopupInputAction.java | 49 +- .../venus/SettingsProgramArgumentsAction.java | 69 +- .../SettingsSelfModifyingCodeAction.java | 60 +- .../mars/venus/SettingsStartAtMainAction.java | 54 +- .../venus/SettingsValueDisplayBaseAction.java | 55 +- .../SettingsWarningsAreErrorsAction.java | 54 +- .../java/mars/venus/TextSegmentWindow.java | 2140 ++++--- src/main/java/mars/venus/ToolAction.java | 90 +- src/main/java/mars/venus/ToolLoader.java | 241 +- src/main/java/mars/venus/VenusUI.java | 165 +- .../venus/editors/MARSTextEditingArea.java | 158 +- .../editors/generic/GenericTextArea.java | 713 ++- .../jeditsyntax/DefaultInputHandler.java | 758 +-- .../editors/jeditsyntax/InputHandler.java | 1946 +++--- .../jeditsyntax/JEditBasedTextArea.java | 777 +-- .../editors/jeditsyntax/JEditTextArea.java | 4953 +++++++------- .../venus/editors/jeditsyntax/KeywordMap.java | 237 +- .../editors/jeditsyntax/PopupHelpItem.java | 229 +- .../editors/jeditsyntax/SyntaxDocument.java | 271 +- .../editors/jeditsyntax/SyntaxStyle.java | 262 +- .../editors/jeditsyntax/SyntaxUtilities.java | 435 +- .../editors/jeditsyntax/TextAreaDefaults.java | 163 +- .../editors/jeditsyntax/TextAreaPainter.java | 1433 +++-- .../editors/jeditsyntax/TextUtilities.java | 322 +- .../tokenmarker/MIPSTokenMarker.java | 1016 +-- .../jeditsyntax/tokenmarker/Token.java | 234 +- .../jeditsyntax/tokenmarker/TokenMarker.java | 644 +- 217 files changed, 53970 insertions(+), 46112 deletions(-) diff --git a/src/main/java/Mars.java b/src/main/java/Mars.java index 3b2d1fb..0ce1121 100644 --- a/src/main/java/Mars.java +++ b/src/main/java/Mars.java @@ -33,8 +33,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @version March 2006 **/ -public class Mars { - public static void main(String[] args) { +public class Mars +{ + public static void main(String[] args) + { new mars.MarsLaunch(args); } } diff --git a/src/main/java/mars/ErrorList.java b/src/main/java/mars/ErrorList.java index 5d61e9b..bfbdb4f 100644 --- a/src/main/java/mars/ErrorList.java +++ b/src/main/java/mars/ErrorList.java @@ -1,6 +1,7 @@ - package mars; - import java.util.*; - import java.io.*; +package mars; + +import java.io.File; +import java.util.ArrayList; /* Copyright (c) 2003-2012, Pete Sanderson and Kenneth Vollmar @@ -28,171 +29,218 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + /** - * Maintains list of generated error messages, regardless of source (tokenizing, parsing, - * assembly, execution). - * + * Maintains list of generated error messages, regardless of source (tokenizing, parsing, assembly, execution). + * * @author Pete Sanderson * @version August 2003 **/ - public class ErrorList { - private ArrayList messages; - private int errorCount; - private int warningCount; - public static final String ERROR_MESSAGE_PREFIX = "Error"; - public static final String WARNING_MESSAGE_PREFIX = "Warning"; - public static final String FILENAME_PREFIX = " in "; - public static final String LINE_PREFIX = " line "; - public static final String POSITION_PREFIX = " column "; - public static final String MESSAGE_SEPARATOR = ": "; - - - /** - * Constructor for ErrorList - **/ - - public ErrorList() { - messages = new ArrayList(); - errorCount = 0; - warningCount = 0; - } - - /** - * Get ArrayList of error messages. - * @return ArrayList of ErrorMessage objects - */ - public ArrayList getErrorMessages() { - return messages; - } - - /** - * Determine whether error has occured or not. - * @return true if an error has occurred (does not include warnings), false otherwise. - **/ - public boolean errorsOccurred() { - return (errorCount != 0 ); - } - - /** - * Determine whether warning has occured or not. - * @return true if an warning has occurred, false otherwise. - **/ - public boolean warningsOccurred() { - return (warningCount != 0 ); - } - - /** Add new error message to end of list. - * @param mess ErrorMessage object to be added to end of error list. - **/ - public void add(ErrorMessage mess){ - add(mess, messages.size()); - } - - /** Add new error message at specified index position. - * @param mess ErrorMessage object to be added to end of error list. - * @param index position in error list - **/ - public void add(ErrorMessage mess, int index) { - if (errorCount > getErrorLimit()) { +public class ErrorList +{ + public static final String ERROR_MESSAGE_PREFIX = "Error"; + + public static final String WARNING_MESSAGE_PREFIX = "Warning"; + + public static final String FILENAME_PREFIX = " in "; + + public static final String LINE_PREFIX = " line "; + + public static final String POSITION_PREFIX = " column "; + + public static final String MESSAGE_SEPARATOR = ": "; + + private final ArrayList messages; + + private int errorCount; + + private int warningCount; + + + /** + * Constructor for ErrorList + **/ + + public ErrorList() + { + messages = new ArrayList(); + errorCount = 0; + warningCount = 0; + } + + /** + * Get ArrayList of error messages. + * + * @return ArrayList of ErrorMessage objects + */ + public ArrayList getErrorMessages() + { + return messages; + } + + /** + * Determine whether error has occured or not. + * + * @return true if an error has occurred (does not include warnings), false otherwise. + **/ + public boolean errorsOccurred() + { + return (errorCount != 0); + } + + /** + * Determine whether warning has occured or not. + * + * @return true if an warning has occurred, false otherwise. + **/ + public boolean warningsOccurred() + { + return (warningCount != 0); + } + + /** + * Add new error message to end of list. + * + * @param mess ErrorMessage object to be added to end of error list. + **/ + public void add(ErrorMessage mess) + { + add(mess, messages.size()); + } + + /** + * Add new error message at specified index position. + * + * @param mess ErrorMessage object to be added to end of error list. + * @param index position in error list + **/ + public void add(ErrorMessage mess, int index) + { + if (errorCount > getErrorLimit()) + { return; - } - if (errorCount == getErrorLimit()) { - messages.add(new ErrorMessage((MIPSprogram)null, mess.getLine(), mess.getPosition(),"Error Limit of "+getErrorLimit()+" exceeded.")); + } + if (errorCount == getErrorLimit()) + { + messages.add(new ErrorMessage((MIPSprogram) null, mess.getLine(), mess.getPosition(), "Error Limit of " + getErrorLimit() + " exceeded.")); errorCount++; // subsequent errors will not be added; see if statement above return; - } - messages.add(index, mess); - if (mess.isWarning()) { + } + messages.add(index, mess); + if (mess.isWarning()) + { warningCount++; - } - else { + } + else + { errorCount++; - } - } - - - /** - * Count of number of error messages in list. - * @return Number of error messages in list. - **/ - - public int errorCount() { - return this.errorCount; - } - - /** - * Count of number of warning messages in list. - * @return Number of warning messages in list. - **/ - - public int warningCount() { - return this.warningCount; - } - - /** - * Check to see if error limit has been exceeded. - * @return True if error limit exceeded, false otherwise. - **/ - - public boolean errorLimitExceeded() { - return this.errorCount > getErrorLimit(); - } - - /** - * Get limit on number of error messages to be generated - * by one assemble operation. - * @return error limit. - **/ - - public int getErrorLimit() { - return Globals.maximumErrorMessages; - } - - /** - * Produce error report. - * @return String containing report. - **/ - public String generateErrorReport() { - return generateReport(ErrorMessage.ERROR); - } - - /** - * Produce warning report. - * @return String containing report. - **/ - public String generateWarningReport() { - return generateReport(ErrorMessage.WARNING); - } - - /** - * Produce report containing both warnings and errors, warnings first. - * @return String containing report. - **/ - public String generateErrorAndWarningReport() { - return generateWarningReport()+generateErrorReport(); - } - - // Produces either error or warning report. - private String generateReport(boolean isWarning) { - StringBuffer report = new StringBuffer(""); - String reportLine; - for (int i = 0; i < messages.size(); i++) { - ErrorMessage m = (ErrorMessage) messages.get(i); - if ((isWarning && m.isWarning()) || (!isWarning && !m.isWarning())) { - reportLine = ((isWarning) ? WARNING_MESSAGE_PREFIX : ERROR_MESSAGE_PREFIX) + FILENAME_PREFIX; - if (m.getFilename().length() > 0) - reportLine = reportLine + (new File(m.getFilename()).getPath()); //.getName()); - if (m.getLine() > 0) - reportLine = reportLine + LINE_PREFIX +m.getMacroExpansionHistory()+ m.getLine(); - if (m.getPosition() > 0) - reportLine = reportLine + POSITION_PREFIX + m.getPosition(); - reportLine = reportLine + MESSAGE_SEPARATOR + m.getMessage() + "\n"; - report.append(reportLine); - } - } - return report.toString(); - } - } // ErrorList + } + } + + + /** + * Count of number of error messages in list. + * + * @return Number of error messages in list. + **/ + + public int errorCount() + { + return this.errorCount; + } + + /** + * Count of number of warning messages in list. + * + * @return Number of warning messages in list. + **/ + + public int warningCount() + { + return this.warningCount; + } + + /** + * Check to see if error limit has been exceeded. + * + * @return True if error limit exceeded, false otherwise. + **/ + + public boolean errorLimitExceeded() + { + return this.errorCount > getErrorLimit(); + } + + /** + * Get limit on number of error messages to be generated by one assemble operation. + * + * @return error limit. + **/ + + public int getErrorLimit() + { + return Globals.maximumErrorMessages; + } + + /** + * Produce error report. + * + * @return String containing report. + **/ + public String generateErrorReport() + { + return generateReport(ErrorMessage.ERROR); + } + + /** + * Produce warning report. + * + * @return String containing report. + **/ + public String generateWarningReport() + { + return generateReport(ErrorMessage.WARNING); + } + + /** + * Produce report containing both warnings and errors, warnings first. + * + * @return String containing report. + **/ + public String generateErrorAndWarningReport() + { + return generateWarningReport() + generateErrorReport(); + } + + // Produces either error or warning report. + private String generateReport(boolean isWarning) + { + StringBuffer report = new StringBuffer(); + String reportLine; + for (int i = 0; i < messages.size(); i++) + { + ErrorMessage m = (ErrorMessage) messages.get(i); + if ((isWarning && m.isWarning()) || (!isWarning && !m.isWarning())) + { + reportLine = ((isWarning) ? WARNING_MESSAGE_PREFIX : ERROR_MESSAGE_PREFIX) + FILENAME_PREFIX; + if (m.getFilename().length() > 0) + { + reportLine = reportLine + (new File(m.getFilename()).getPath()); //.getName()); + } + if (m.getLine() > 0) + { + reportLine = reportLine + LINE_PREFIX + m.getMacroExpansionHistory() + m.getLine(); + } + if (m.getPosition() > 0) + { + reportLine = reportLine + POSITION_PREFIX + m.getPosition(); + } + reportLine = reportLine + MESSAGE_SEPARATOR + m.getMessage() + "\n"; + report.append(reportLine); + } + } + return report.toString(); + } +} // ErrorList diff --git a/src/main/java/mars/ErrorMessage.java b/src/main/java/mars/ErrorMessage.java index 926a731..227dd22 100644 --- a/src/main/java/mars/ErrorMessage.java +++ b/src/main/java/mars/ErrorMessage.java @@ -1,8 +1,8 @@ - package mars; - - import java.util.regex.Pattern; - import java.util.regex.Matcher; - import java.util.ArrayList; +package mars; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /* Copyright (c) 2003-2012, Pete Sanderson and Kenneth Vollmar @@ -33,253 +33,301 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Represents occurrance of an error detected during tokenizing, assembly or simulation. + * * @author Pete Sanderson * @version August 2003 **/ - public class ErrorMessage { - private boolean isWarning; // allow for warnings too (added Nov 2006) - private String filename; // name of source file (added Oct 2006) - private int line; // line in source code where error detected - private int position; // position in source line where error detected - private String message; - private String macroExpansionHistory; - - /** - * Constant to indicate this message is warning not error - */ - public static final boolean WARNING = true; - - /** - * Constant to indicate this message is error not warning - */ - public static final boolean ERROR = false; - - /** - * Constructor for ErrorMessage. - * @param filename String containing name of source file in which this error appears. - * @param line Line number in source program being processed when error occurred. - * @param position Position within line being processed when error occurred. Normally is starting - * position of source token. - * @param message String containing appropriate error message. - * @deprecated Newer constructors replace the String filename parameter with a MIPSprogram parameter to provide more information. - **/ +public class ErrorMessage +{ + /** + * Constant to indicate this message is warning not error + */ + public static final boolean WARNING = true; + + /** + * Constant to indicate this message is error not warning + */ + public static final boolean ERROR = false; + + private final boolean isWarning; // allow for warnings too (added Nov 2006) + + private final String filename; // name of source file (added Oct 2006) + + private final int line; // line in source code where error detected + + private final int position; // position in source line where error detected + + private final String message; + + private final String macroExpansionHistory; + + /** + * Constructor for ErrorMessage. + * + * @param filename String containing name of source file in which this error appears. + * @param line Line number in source program being processed when error occurred. + * @param position Position within line being processed when error occurred. Normally is starting position of + * source token. + * @param message String containing appropriate error message. + * @deprecated Newer constructors replace the String filename parameter with a MIPSprogram parameter to provide more + * information. + **/ // Added filename October 2006 - @Deprecated - public ErrorMessage(String filename, int line, int position, String message) { - this(ERROR, filename, line, position, message, ""); - } - - /** - * Constructor for ErrorMessage. - * @param filename String containing name of source file in which this error appears. - * @param line Line number in source program being processed when error occurred. - * @param position Position within line being processed when error occurred. Normally is starting - * position of source token. - * @param message String containing appropriate error message. - * @param macroExpansionHistory - * @deprecated Newer constructors replace the String filename parameter with a MIPSprogram parameter to provide more information. - **/ + @Deprecated + public ErrorMessage(String filename, int line, int position, String message) + { + this(ERROR, filename, line, position, message, ""); + } + + /** + * Constructor for ErrorMessage. + * + * @param filename String containing name of source file in which this error appears. + * @param line Line number in source program being processed when error occurred. + * @param position Position within line being processed when error occurred. Normally is starting position of + * source token. + * @param message String containing appropriate error message. + * @param macroExpansionHistory + * @deprecated Newer constructors replace the String filename parameter with a MIPSprogram parameter to provide more + * information. + **/ // Added macroExpansionHistory Dec 2012 - - @Deprecated - public ErrorMessage(String filename, int line, int position, String message, String macroExpansionHistory) { - this(ERROR, filename, line, position, message, macroExpansionHistory); - } - - /** - * Constructor for ErrorMessage. - * @param isWarning set to WARNING if message is a warning not error, else set to ERROR or omit. - * @param filename String containing name of source file in which this error appears. - * @param line Line number in source program being processed when error occurred. - * @param position Position within line being processed when error occurred. Normally is starting - * position of source token. - * @param message String containing appropriate error message. - * @param macroExpansionHistory provided so message for macro can include both definition and usage line numbers - * @deprecated Newer constructors replace the String filename parameter with a MIPSprogram parameter to provide more information. - **/ - @Deprecated - public ErrorMessage(boolean isWarning, String filename, int line, int position, String message, String macroExpansionHistory) { - this.isWarning = isWarning; - this.filename = filename; - this.line = line; - this.position = position; - this.message = message; - this.macroExpansionHistory=macroExpansionHistory; - } - - - /** - * Constructor for ErrorMessage. Assumes line number is calculated after any .include files expanded, and - * if there were, it will adjust filename and line number so message reflects original file and line number. - * @param sourceMIPSprogram MIPSprogram object of source file in which this error appears. - * @param line Line number in source program being processed when error occurred. - * @param position Position within line being processed when error occurred. Normally is starting - * position of source token. - * @param message String containing appropriate error message. - **/ - - public ErrorMessage(MIPSprogram sourceMIPSprogram, int line, int position, String message) { - this(ERROR, sourceMIPSprogram, line, position, message); - } - - - /** - * Constructor for ErrorMessage. Assumes line number is calculated after any .include files expanded, and - * if there were, it will adjust filename and line number so message reflects original file and line number. - * @param isWarning set to WARNING if message is a warning not error, else set to ERROR or omit. - * @param sourceMIPSprogram MIPSprogram object of source file in which this error appears. - * @param line Line number in source program being processed when error occurred. - * @param position Position within line being processed when error occurred. Normally is starting - * position of source token. - * @param message String containing appropriate error message. - **/ - - public ErrorMessage(boolean isWarning, MIPSprogram sourceMIPSprogram, int line, int position, String message) { - this.isWarning = isWarning; - if (sourceMIPSprogram == null) { + @Deprecated + public ErrorMessage(String filename, int line, int position, String message, String macroExpansionHistory) + { + this(ERROR, filename, line, position, message, macroExpansionHistory); + } + + /** + * Constructor for ErrorMessage. + * + * @param isWarning set to WARNING if message is a warning not error, else set to ERROR or omit. + * @param filename String containing name of source file in which this error appears. + * @param line Line number in source program being processed when error occurred. + * @param position Position within line being processed when error occurred. Normally is starting position of + * source token. + * @param message String containing appropriate error message. + * @param macroExpansionHistory provided so message for macro can include both definition and usage line + * numbers + * @deprecated Newer constructors replace the String filename parameter with a MIPSprogram parameter to provide more + * information. + **/ + @Deprecated + public ErrorMessage(boolean isWarning, String filename, int line, int position, String message, String macroExpansionHistory) + { + this.isWarning = isWarning; + this.filename = filename; + this.line = line; + this.position = position; + this.message = message; + this.macroExpansionHistory = macroExpansionHistory; + } + + + /** + * Constructor for ErrorMessage. Assumes line number is calculated after any .include files expanded, and if there + * were, it will adjust filename and line number so message reflects original file and line number. + * + * @param sourceMIPSprogram MIPSprogram object of source file in which this error appears. + * @param line Line number in source program being processed when error occurred. + * @param position Position within line being processed when error occurred. Normally is starting position of + * source token. + * @param message String containing appropriate error message. + **/ + + public ErrorMessage(MIPSprogram sourceMIPSprogram, int line, int position, String message) + { + this(ERROR, sourceMIPSprogram, line, position, message); + } + + + /** + * Constructor for ErrorMessage. Assumes line number is calculated after any .include files expanded, and if there + * were, it will adjust filename and line number so message reflects original file and line number. + * + * @param isWarning set to WARNING if message is a warning not error, else set to ERROR or omit. + * @param sourceMIPSprogram MIPSprogram object of source file in which this error appears. + * @param line Line number in source program being processed when error occurred. + * @param position Position within line being processed when error occurred. Normally is starting position of + * source token. + * @param message String containing appropriate error message. + **/ + + public ErrorMessage(boolean isWarning, MIPSprogram sourceMIPSprogram, int line, int position, String message) + { + this.isWarning = isWarning; + if (sourceMIPSprogram == null) + { this.filename = ""; this.line = line; - } - else { - if (sourceMIPSprogram.getSourceLineList() == null) { - this.filename = sourceMIPSprogram.getFilename(); - this.line = line; - } - else { - mars.assembler.SourceLine sourceLine = sourceMIPSprogram.getSourceLineList().get(line-1); - this.filename = sourceLine.getFilename(); - this.line = sourceLine.getLineNumber(); + } + else + { + if (sourceMIPSprogram.getSourceLineList() == null) + { + this.filename = sourceMIPSprogram.getFilename(); + this.line = line; } - } - this.position = position; - this.message = message; - this.macroExpansionHistory = getExpansionHistory(sourceMIPSprogram); - } - - /** - * Constructor for ErrorMessage, to be used for runtime exceptions. - * @param statement The ProgramStatement object for the instruction causing the runtime error - * @param message String containing appropriate error message. - **/ - // Added January 2013 - - public ErrorMessage(ProgramStatement statement, String message) { - this.isWarning = ERROR; - this.filename = (statement.getSourceMIPSprogram() == null) - ? "" : statement.getSourceMIPSprogram().getFilename(); - this.position = 0; - this.message = message; - // Somewhere along the way we lose the macro history, but can - // normally recreate it here. The line number for macro use (in the - // expansion) comes with the ProgramStatement.getSourceLine(). - // The line number for the macro definition comes embedded in - // the source code from ProgramStatement.getSource(), which is - // displayed in the Text Segment display. It would previously - // have had the macro definition line prepended in brackets, - // e.g. "<13> syscall # finished". So I'll extract that - // bracketed number here and include it in the error message. - // Looks bass-ackwards, but to get the line numbers to display correctly - // for runtime error occurring in macro expansion (expansion->definition), need - // to assign to the opposite variables. - ArrayList defineLine = parseMacroHistory(statement.getSource()); - if (defineLine.size() == 0) { + else + { + mars.assembler.SourceLine sourceLine = sourceMIPSprogram.getSourceLineList().get(line - 1); + this.filename = sourceLine.getFilename(); + this.line = sourceLine.getLineNumber(); + } + } + this.position = position; + this.message = message; + this.macroExpansionHistory = getExpansionHistory(sourceMIPSprogram); + } + + /** + * Constructor for ErrorMessage, to be used for runtime exceptions. + * + * @param statement The ProgramStatement object for the instruction causing the runtime error + * @param message String containing appropriate error message. + **/ + // Added January 2013 + public ErrorMessage(ProgramStatement statement, String message) + { + this.isWarning = ERROR; + this.filename = (statement.getSourceMIPSprogram() == null) + ? "" : statement.getSourceMIPSprogram().getFilename(); + this.position = 0; + this.message = message; + // Somewhere along the way we lose the macro history, but can + // normally recreate it here. The line number for macro use (in the + // expansion) comes with the ProgramStatement.getSourceLine(). + // The line number for the macro definition comes embedded in + // the source code from ProgramStatement.getSource(), which is + // displayed in the Text Segment display. It would previously + // have had the macro definition line prepended in brackets, + // e.g. "<13> syscall # finished". So I'll extract that + // bracketed number here and include it in the error message. + // Looks bass-ackwards, but to get the line numbers to display correctly + // for runtime error occurring in macro expansion (expansion->definition), need + // to assign to the opposite variables. + ArrayList defineLine = parseMacroHistory(statement.getSource()); + if (defineLine.size() == 0) + { this.line = statement.getSourceLine(); this.macroExpansionHistory = ""; - } - else { + } + else + { this.line = defineLine.get(0); - this.macroExpansionHistory = ""+statement.getSourceLine(); - } - } - - private ArrayList parseMacroHistory(String string) { - Pattern pattern = Pattern.compile("<\\d+>"); - Matcher matcher = pattern.matcher(string); - String verify = new String(string).trim(); - ArrayList macroHistory = new ArrayList(); - while (matcher.find()) { + this.macroExpansionHistory = "" + statement.getSourceLine(); + } + } + + // Added by Mohammad Sekavat Dec 2012 + private static String getExpansionHistory(MIPSprogram sourceMIPSprogram) + { + if (sourceMIPSprogram == null || sourceMIPSprogram.getLocalMacroPool() == null) + { + return ""; + } + return sourceMIPSprogram.getLocalMacroPool().getExpansionHistory(); + } + + private ArrayList parseMacroHistory(String string) + { + Pattern pattern = Pattern.compile("<\\d+>"); + Matcher matcher = pattern.matcher(string); + String verify = string.trim(); + ArrayList macroHistory = new ArrayList(); + while (matcher.find()) + { String match = matcher.group(); - if (verify.indexOf(match)==0) { - try { - int line = Integer.parseInt(match.substring(1,match.length()-1)); - macroHistory.add(line); - } - catch (NumberFormatException e) { - break; - } - verify = verify.substring(match.length()).trim(); - } - else { - break; + if (verify.indexOf(match) == 0) + { + try + { + int line = Integer.parseInt(match.substring(1, match.length() - 1)); + macroHistory.add(line); + } + catch (NumberFormatException e) + { + break; + } + verify = verify.substring(match.length()).trim(); } - } - return macroHistory; - } - - /** - * Produce name of file containing error. - * @return Returns String containing name of source file containing the error. - */ - // Added October 2006 - - public String getFilename() { - return filename; - } - - /** - * Produce line number of error. - * @return Returns line number in source program where error occurred. - */ - - public int getLine() { - return line; - } - - /** - * Produce position within erroneous line. - * @return Returns position within line of source program where error occurred. - */ - - public int getPosition() { - return position; - } - - /** - * Produce error message. - * @return Returns String containing textual error message. - */ - - public String getMessage() { - return message; - } - - /** - * Determine whether this message represents error or warning. - * @return Returns true if this message reflects warning, false if error. - */ + else + { + break; + } + } + return macroHistory; + } + + /** + * Produce name of file containing error. + * + * @return Returns String containing name of source file containing the error. + */ + // Added October 2006 + public String getFilename() + { + return filename; + } + + /** + * Produce line number of error. + * + * @return Returns line number in source program where error occurred. + */ + + public int getLine() + { + return line; + } + + /** + * Produce position within erroneous line. + * + * @return Returns position within line of source program where error occurred. + */ + + public int getPosition() + { + return position; + } + + /** + * Produce error message. + * + * @return Returns String containing textual error message. + */ + + public String getMessage() + { + return message; + } + + /** + * Determine whether this message represents error or warning. + * + * @return Returns true if this message reflects warning, false if error. + */ // Method added 28 Nov 2006 - public boolean isWarning() { - return this.isWarning; - } - - /** - * Returns string describing macro expansion. Empty string if none. - * @return string describing macro expansion - */ + public boolean isWarning() + { + return this.isWarning; + } + + /** + * Returns string describing macro expansion. Empty string if none. + * + * @return string describing macro expansion + */ // Method added by Mohammad Sekavat Dec 2012 - - public String getMacroExpansionHistory() { - if (macroExpansionHistory==null || macroExpansionHistory.length()==0) + public String getMacroExpansionHistory() + { + if (macroExpansionHistory == null || macroExpansionHistory.length() == 0) + { return ""; - return macroExpansionHistory+"->"; - } - - // Added by Mohammad Sekavat Dec 2012 - private static String getExpansionHistory(MIPSprogram sourceMIPSprogram) { - if (sourceMIPSprogram==null || sourceMIPSprogram.getLocalMacroPool()==null) - return ""; - return sourceMIPSprogram.getLocalMacroPool().getExpansionHistory(); - } - - } // ErrorMessage \ No newline at end of file + } + return macroExpansionHistory + "->"; + } + +} // ErrorMessage diff --git a/src/main/java/mars/Globals.java b/src/main/java/mars/Globals.java index 00099cd..a80be05 100644 --- a/src/main/java/mars/Globals.java +++ b/src/main/java/mars/Globals.java @@ -46,114 +46,144 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @author Pete Sanderson * @version August 2003 */ -public class Globals { +public class Globals +{ /** * Path to folder that contains images */ // The leading "/" in filepath prevents package name from being pre-pended. public static final String imagesPath = "/images/"; + /** * Path to folder that contains help text */ public static final String helpPath = "/help/"; + /** * The current MARS version number. Can't wait for "initialize()" call to get it. */ public static final String version = "4.5"; + /** * MARS copyright years */ public static final String copyrightYears = getCopyrightYears(); + /** * MARS copyright holders */ public static final String copyrightHolders = getCopyrightHolders(); - /** - * The set of implemented MIPS instructions. - **/ - public static InstructionSet instructionSet; - /** - * the program currently being worked with. Used by GUI only, not command line. - **/ - public static MIPSprogram program; - /** - * Symbol table for file currently being assembled. - **/ - public static SymbolTable symbolTable; - /** - * Simulated MIPS memory component. - **/ - public static Memory memory; - /** - * Lock variable used at head of synchronized block to guard MIPS memory and registers - **/ - public static Object memoryAndRegistersLock = new Object(); - /** - * Flag to determine whether or not to produce internal debugging information. - **/ - public static boolean debug = false; - /** - * String to GUI's RunI/O text area when echoing user input from pop-up dialog. - */ - public static String userInputAlert = "**** user input : "; - /** - * MARS exit code -- useful with SYSCALL 17 when running from command line (not GUI) - */ - public static int exitCode = 0; - public static boolean runSpeedPanelExists = false; - /** - * Object that contains various settings that can be accessed modified internally. - **/ - static Settings settings; - /* The GUI being used (if any) with this simulator. */ - static VenusUI gui = null; + // List these first because they are referenced by methods called at initialization. private static final String configPropertiesFile = "Config"; + /** * List of accepted file extensions for MIPS assembly source files. */ public static final ArrayList fileExtensions = getFileExtensions(); + /** * Maximum length of scrolled message window (MARS Messages and Run I/O) */ public static final int maximumMessageCharacters = getMessageLimit(); + /** * Maximum number of assembler errors produced by one assemble operation */ public static final int maximumErrorMessages = getErrorLimit(); + /** * Maximum number of back-step operations to buffer */ public static final int maximumBacksteps = getBackstepLimit(); + /** * Placeholder for non-printable ASCII codes */ public static final String ASCII_NON_PRINT = getAsciiNonPrint(); + /** * Array of strings to display for ASCII codes in ASCII display of data segment. ASCII code 0-255 is array index. */ public static final String[] ASCII_TABLE = getAsciiStrings(); + private static final String syscallPropertiesFile = "Syscall"; + + /** + * The set of implemented MIPS instructions. + **/ + public static InstructionSet instructionSet; + + /** + * the program currently being worked with. Used by GUI only, not command line. + **/ + public static MIPSprogram program; + + /** + * Symbol table for file currently being assembled. + **/ + public static SymbolTable symbolTable; + + /** + * Simulated MIPS memory component. + **/ + public static Memory memory; + + /** + * Lock variable used at head of synchronized block to guard MIPS memory and registers + **/ + public static Object memoryAndRegistersLock = new Object(); + + /** + * Flag to determine whether or not to produce internal debugging information. + **/ + public static boolean debug = false; + + /** + * String to GUI's RunI/O text area when echoing user input from pop-up dialog. + */ + public static String userInputAlert = "**** user input : "; + + /** + * MARS exit code -- useful with SYSCALL 17 when running from command line (not GUI) + */ + public static int exitCode = 0; + + public static boolean runSpeedPanelExists = false; + + /** + * Object that contains various settings that can be accessed modified internally. + **/ + static Settings settings; + + /* The GUI being used (if any) with this simulator. */ + static VenusUI gui = null; + /* Flag that indicates whether or not instructionSet has been initialized. */ private static boolean initialized = false; - private static String getCopyrightYears() { + private static String getCopyrightYears() + { return "2003-2014"; } - private static String getCopyrightHolders() { + private static String getCopyrightHolders() + { return "Pete Sanderson and Kenneth Vollmar"; } - public static VenusUI getGui() { + public static VenusUI getGui() + { return gui; } - public static void setGui(VenusUI g) { + public static void setGui(VenusUI g) + { gui = g; } - public static Settings getSettings() { + public static Settings getSettings() + { return settings; } @@ -161,8 +191,10 @@ public class Globals { * Method called once upon system initialization to create the global data structures. **/ - public static void initialize(boolean gui) { - if (!initialized) { + public static void initialize(boolean gui) + { + if (!initialized) + { memory = Memory.getInstance(); //clients can use Memory.getInstance instead of Globals.memory instructionSet = new InstructionSet(); instructionSet.populate(); @@ -175,22 +207,26 @@ public class Globals { } // Read byte limit of Run I/O or MARS Messages text to buffer. - private static int getMessageLimit() { + private static int getMessageLimit() + { return getIntegerProperty(configPropertiesFile, "MessageLimit", 1000000); } // Read limit on number of error messages produced by one assemble operation. - private static int getErrorLimit() { + private static int getErrorLimit() + { return getIntegerProperty(configPropertiesFile, "ErrorLimit", 200); } // Read backstep limit (number of operations to buffer) from properties file. - private static int getBackstepLimit() { + private static int getBackstepLimit() + { return getIntegerProperty(configPropertiesFile, "BackstepLimit", 1000); } // Read ASCII default display character for non-printing characters, from properties file. - public static String getAsciiNonPrint() { + public static String getAsciiNonPrint() + { String anp = getPropertyEntry(configPropertiesFile, "AsciiNonPrint"); return (anp == null) ? "." : ((anp.equals("space")) ? " " : anp); } @@ -198,19 +234,31 @@ public class Globals { // Read ASCII strings for codes 0-255, from properties file. If string // value is "null", substitute value of ASCII_NON_PRINT. If string is // "space", substitute string containing one space character. - public static String[] getAsciiStrings() { + public static String[] getAsciiStrings() + { String let = getPropertyEntry(configPropertiesFile, "AsciiTable"); String placeHolder = getAsciiNonPrint(); String[] lets = let.split(" +"); int maxLength = 0; - for (int i = 0; i < lets.length; i++) { - if (lets[i].equals("null")) lets[i] = placeHolder; - if (lets[i].equals("space")) lets[i] = " "; - if (lets[i].length() > maxLength) maxLength = lets[i].length(); + for (int i = 0; i < lets.length; i++) + { + if (lets[i].equals("null")) + { + lets[i] = placeHolder; + } + if (lets[i].equals("space")) + { + lets[i] = " "; + } + if (lets[i].length() > maxLength) + { + maxLength = lets[i].length(); + } } String padding = " "; maxLength++; - for (int i = 0; i < lets.length; i++) { + for (int i = 0; i < lets.length; i++) + { lets[i] = padding.substring(0, maxLength - lets[i].length()) + lets[i]; } return lets; @@ -218,12 +266,16 @@ public class Globals { // Read and return integer property value for given file and property name. // Default value is returned if property file or name not found. - private static int getIntegerProperty(String propertiesFile, String propertyName, int defaultValue) { + private static int getIntegerProperty(String propertiesFile, String propertyName, int defaultValue) + { int limit = defaultValue; // just in case no entry is found Properties properties = PropertiesFile.loadPropertiesFromFile(propertiesFile); - try { + try + { limit = Integer.parseInt(properties.getProperty(propertyName, Integer.toString(defaultValue))); - } catch (NumberFormatException nfe) { + } + catch (NumberFormatException nfe) + { } // do nothing, I already have a default return limit; } @@ -231,12 +283,15 @@ public class Globals { // Read assembly language file extensions from properties file. Resulting // string is tokenized into array list (assume StringTokenizer default delimiters). - private static ArrayList getFileExtensions() { + private static ArrayList getFileExtensions() + { ArrayList extensionsList = new ArrayList(); String extensions = getPropertyEntry(configPropertiesFile, "Extensions"); - if (extensions != null) { + if (extensions != null) + { StringTokenizer st = new StringTokenizer(extensions); - while (st.hasMoreTokens()) { + while (st.hasMoreTokens()) + { extensionsList.add(st.nextToken()); } } @@ -244,20 +299,22 @@ public class Globals { } /** - * Get list of MarsTools that reside outside the MARS distribution. - * Currently this is done by adding the tool's path name to the list - * of values for the external_tools property. Use ";" as delimiter! + * Get list of MarsTools that reside outside the MARS distribution. Currently this is done by adding the tool's path + * name to the list of values for the external_tools property. Use ";" as delimiter! * - * @return ArrayList. Each item is file path to .class file - * of a class that implements MarsTool. If none, returns empty list. + * @return ArrayList. Each item is file path to .class file of a class that implements MarsTool. If none, returns + * empty list. */ - public static ArrayList getExternalTools() { + public static ArrayList getExternalTools() + { ArrayList toolsList = new ArrayList(); String delimiter = ";"; String tools = getPropertyEntry(configPropertiesFile, "ExternalTools"); - if (tools != null) { + if (tools != null) + { StringTokenizer st = new StringTokenizer(tools, delimiter); - while (st.hasMoreTokens()) { + while (st.hasMoreTokens()) + { toolsList.add(st.nextToken()); } } @@ -267,12 +324,13 @@ public class Globals { /** * Read and return property file value (if any) for requested property. * - * @param propertiesFile name of properties file (do NOT include filename extension, - * which is assumed to be ".properties") - * @param propertyName String containing desired property name + * @param propertiesFile name of properties file (do NOT include filename extension, which is assumed to be + * ".properties") + * @param propertyName String containing desired property name * @return String containing associated value; null if property not found */ - public static String getPropertyEntry(String propertiesFile, String propertyName) { + public static String getPropertyEntry(String propertiesFile, String propertyName) + { return PropertiesFile.loadPropertiesFromFile(propertiesFile).getProperty(propertyName); } @@ -281,11 +339,13 @@ public class Globals { * * @return ArrayList of SyscallNumberOverride objects */ - public ArrayList getSyscallOverrides() { + public ArrayList getSyscallOverrides() + { ArrayList overrides = new ArrayList(); Properties properties = PropertiesFile.loadPropertiesFromFile(syscallPropertiesFile); Enumeration keys = properties.keys(); - while (keys.hasMoreElements()) { + while (keys.hasMoreElements()) + { String key = (String) keys.nextElement(); overrides.add(new SyscallNumberOverride(key, properties.getProperty(key))); } diff --git a/src/main/java/mars/MIPSprogram.java b/src/main/java/mars/MIPSprogram.java index 1d5f0ee..4d9e7ef 100644 --- a/src/main/java/mars/MIPSprogram.java +++ b/src/main/java/mars/MIPSprogram.java @@ -1,14 +1,14 @@ - package mars; - - import mars.venus.*; - import mars.assembler.*; - import mars.simulator.*; - import mars.mips.hardware.*; - - import java.util.*; - import java.io.*; - import java.awt.event.*; - import javax.swing.*; +package mars; + +import mars.assembler.*; +import mars.mips.hardware.RegisterFile; +import mars.simulator.BackStepper; +import mars.simulator.Simulator; + +import javax.swing.*; +import java.io.BufferedReader; +import java.io.FileReader; +import java.util.ArrayList; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -39,384 +39,440 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Internal representations of MIPS program. Connects source, tokens and machine code. Having - * all these structures available facilitates construction of good messages, - * debugging, and easy simulation. - * + * Internal representations of MIPS program. Connects source, tokens and machine code. Having all these structures + * available facilitates construction of good messages, debugging, and easy simulation. + * * @author Pete Sanderson * @version August 2003 **/ - public class MIPSprogram { - - // See explanation of method inSteppedExecution() below. - private boolean steppedExecution = false; - - private String filename; - private ArrayList sourceList; - private ArrayList tokenList; - private ArrayList parsedList; - private ArrayList machineList; - private BackStepper backStepper; - private SymbolTable localSymbolTable; - private MacroPool macroPool; - private ArrayList sourceLineList; - private Tokenizer tokenizer; - - /** - * Produces list of source statements that comprise the program. - * @return ArrayList of String. Each String is one line of MIPS source code. - **/ - - public ArrayList getSourceList() { - return sourceList; - } - - /** - * Set list of source statements that comprise the program. - * @param sourceLineList ArrayList of SourceLine. - * Each SourceLine represents one line of MIPS source code. - **/ - - public void setSourceLineList(ArrayList sourceLineList) { - this.sourceLineList = sourceLineList; - sourceList = new ArrayList(); - for (SourceLine sl : sourceLineList) { +public class MIPSprogram +{ + + // See explanation of method inSteppedExecution() below. + private boolean steppedExecution = false; + + private String filename; + + private ArrayList sourceList; + + private ArrayList tokenList; + + private ArrayList parsedList; + + private ArrayList machineList; + + private BackStepper backStepper; + + private SymbolTable localSymbolTable; + + private MacroPool macroPool; + + private ArrayList sourceLineList; + + private Tokenizer tokenizer; + + /** + * Produces list of source statements that comprise the program. + * + * @return ArrayList of String. Each String is one line of MIPS source code. + **/ + + public ArrayList getSourceList() + { + return sourceList; + } + + /** + * Retrieve list of source statements that comprise the program. + * + * @return ArrayList of SourceLine. Each SourceLine represents one line of MIPS source cod + **/ + + public ArrayList getSourceLineList() + { + return this.sourceLineList; + } + + /** + * Set list of source statements that comprise the program. + * + * @param sourceLineList ArrayList of SourceLine. Each SourceLine represents one line of MIPS source code. + **/ + + public void setSourceLineList(ArrayList sourceLineList) + { + this.sourceLineList = sourceLineList; + sourceList = new ArrayList(); + for (SourceLine sl : sourceLineList) + { sourceList.add(sl.getSource()); - } - } - - /** - * Retrieve list of source statements that comprise the program. - * @return ArrayList of SourceLine. - * Each SourceLine represents one line of MIPS source cod - **/ - - public ArrayList getSourceLineList() { - return this.sourceLineList; - } - - /** - * Produces name of associated source code file. - * @return File name as String. - **/ - - public String getFilename() { - return filename; - } - - /** - * Produces list of tokens that comprise the program. - * @return ArrayList of TokenList. Each TokenList is list of tokens generated by - * corresponding line of MIPS source code. - * @see TokenList - **/ - - public ArrayList getTokenList() { - return tokenList; - } - - /** - * Retrieves Tokenizer for this program - * @return Tokenizer - **/ - - public Tokenizer getTokenizer() { - return tokenizer; - } - - /** - * Produces new empty list to hold parsed source code statements. - * @return ArrayList of ProgramStatement. Each ProgramStatement represents a parsed - * MIPS statement. - * @see ProgramStatement - **/ - - public ArrayList createParsedList() { - parsedList = new ArrayList(); - return parsedList; - } - - /** - * Produces existing list of parsed source code statements. - * @return ArrayList of ProgramStatement. Each ProgramStatement represents a parsed - * MIPS statement. - * @see ProgramStatement - **/ - - public ArrayList getParsedList() { - return parsedList; - } - - /** - * Produces list of machine statements that are assembled from the program. - * @return ArrayList of ProgramStatement. Each ProgramStatement represents an assembled - * basic MIPS instruction. - * @see ProgramStatement - **/ - - public ArrayList getMachineList() { - return machineList; - } - - - /** - * Returns BackStepper associated with this program. It is created upon successful assembly. - * @return BackStepper object, null if there is none. - **/ - - public BackStepper getBackStepper() { - return backStepper; - } - - /** - * Returns SymbolTable associated with this program. It is created at assembly time, - * and stores local labels (those not declared using .globl directive). - **/ - - public SymbolTable getLocalSymbolTable() { - return localSymbolTable; - } - - /** - * Returns status of BackStepper associated with this program. - * @return true if enabled, false if disabled or non-existant. - **/ - - public boolean backSteppingEnabled() { - return (backStepper!=null && backStepper.enabled()); - } - - /** - * Produces specified line of MIPS source program. - * @param i Line number of MIPS source program to get. Line 1 is first line. - * @return Returns specified line of MIPS source. If outside the line range, - * it returns null. Line 1 is first line. - **/ - - public String getSourceLine(int i) { - if ( (i >= 1) && (i <= sourceList.size()) ) - return (String) sourceList.get(i-1); - else + } + } + + /** + * Produces name of associated source code file. + * + * @return File name as String. + **/ + + public String getFilename() + { + return filename; + } + + /** + * Produces list of tokens that comprise the program. + * + * @return ArrayList of TokenList. Each TokenList is list of tokens generated by corresponding line of MIPS source + * code. + * @see TokenList + **/ + + public ArrayList getTokenList() + { + return tokenList; + } + + /** + * Retrieves Tokenizer for this program + * + * @return Tokenizer + **/ + + public Tokenizer getTokenizer() + { + return tokenizer; + } + + /** + * Produces new empty list to hold parsed source code statements. + * + * @return ArrayList of ProgramStatement. Each ProgramStatement represents a parsed MIPS statement. + * @see ProgramStatement + **/ + + public ArrayList createParsedList() + { + parsedList = new ArrayList(); + return parsedList; + } + + /** + * Produces existing list of parsed source code statements. + * + * @return ArrayList of ProgramStatement. Each ProgramStatement represents a parsed MIPS statement. + * @see ProgramStatement + **/ + + public ArrayList getParsedList() + { + return parsedList; + } + + /** + * Produces list of machine statements that are assembled from the program. + * + * @return ArrayList of ProgramStatement. Each ProgramStatement represents an assembled basic MIPS instruction. + * @see ProgramStatement + **/ + + public ArrayList getMachineList() + { + return machineList; + } + + + /** + * Returns BackStepper associated with this program. It is created upon successful assembly. + * + * @return BackStepper object, null if there is none. + **/ + + public BackStepper getBackStepper() + { + return backStepper; + } + + /** + * Returns SymbolTable associated with this program. It is created at assembly time, and stores local labels (those + * not declared using .globl directive). + **/ + + public SymbolTable getLocalSymbolTable() + { + return localSymbolTable; + } + + /** + * Returns status of BackStepper associated with this program. + * + * @return true if enabled, false if disabled or non-existant. + **/ + + public boolean backSteppingEnabled() + { + return (backStepper != null && backStepper.enabled()); + } + + /** + * Produces specified line of MIPS source program. + * + * @param i Line number of MIPS source program to get. Line 1 is first line. + * @return Returns specified line of MIPS source. If outside the line range, it returns null. Line 1 is first + * line. + **/ + + public String getSourceLine(int i) + { + if ((i >= 1) && (i <= sourceList.size())) + { + return (String) sourceList.get(i - 1); + } + else + { return null; - } - - - /** - * Reads MIPS source code from file into structure. Will always read from file. - * It is GUI responsibility to assure that source edits are written to file - * when user selects compile or run/step options. - * - * @param file String containing name of MIPS source code file. - * @throws ProcessingException Will throw exception if there is any problem reading the file. - **/ - - public void readSource(String file) throws ProcessingException { - this.filename = file; - this.sourceList = new ArrayList(); - ErrorList errors = null; - BufferedReader inputFile; - String line; - int lengthSoFar = 0; - try { + } + } + + + /** + * Reads MIPS source code from file into structure. Will always read from file. It is GUI responsibility to assure + * that source edits are written to file when user selects compile or run/step options. + * + * @param file String containing name of MIPS source code file. + * @throws ProcessingException Will throw exception if there is any problem reading the file. + **/ + + public void readSource(String file) throws ProcessingException + { + this.filename = file; + this.sourceList = new ArrayList(); + ErrorList errors = null; + BufferedReader inputFile; + String line; + int lengthSoFar = 0; + try + { inputFile = new BufferedReader(new FileReader(file)); line = inputFile.readLine(); - while (line != null) { - sourceList.add(line); - line = inputFile.readLine(); + while (line != null) + { + sourceList.add(line); + line = inputFile.readLine(); } - } - catch (Exception e) { - errors = new ErrorList(); - errors.add(new ErrorMessage((MIPSprogram)null,0,0,e.toString())); - throw new ProcessingException(errors); - } - return; - } - - /** - * Tokenizes the MIPS source program. Program must have already been read from file. - * @throws ProcessingException Will throw exception if errors occured while tokenizing. - **/ - - public void tokenize() throws ProcessingException { - this.tokenizer = new Tokenizer(); - this.tokenList = tokenizer.tokenize(this); - this.localSymbolTable = new SymbolTable(this.filename); // prepare for assembly - return; - } - - /** - * Prepares the given list of files for assembly. This involves - * reading and tokenizing all the source files. There may be only one. - * @param filenames ArrayList containing the source file name(s) in no particular order - * @param leadFilename String containing name of source file that needs to go first and - * will be represented by "this" MIPSprogram object. - * @param exceptionHandler String containing name of source file containing exception - * handler. This will be assembled first, even ahead of leadFilename, to allow it to - * include "startup" instructions loaded beginning at 0x00400000. Specify null or - * empty String to indicate there is no such designated exception handler. - * @return ArrayList containing one MIPSprogram object for each file to assemble. - * objects for any additional files (send ArrayList to assembler) - * @throws ProcessingException Will throw exception if errors occured while reading or tokenizing. - **/ - - public ArrayList prepareFilesForAssembly(ArrayList filenames, String leadFilename, String exceptionHandler) throws ProcessingException { - ArrayList MIPSprogramsToAssemble = new ArrayList(); - int leadFilePosition = 0; - if (exceptionHandler != null && exceptionHandler.length() > 0) { + } + catch (Exception e) + { + errors = new ErrorList(); + errors.add(new ErrorMessage((MIPSprogram) null, 0, 0, e.toString())); + throw new ProcessingException(errors); + } + } + + /** + * Tokenizes the MIPS source program. Program must have already been read from file. + * + * @throws ProcessingException Will throw exception if errors occured while tokenizing. + **/ + + public void tokenize() throws ProcessingException + { + this.tokenizer = new Tokenizer(); + this.tokenList = tokenizer.tokenize(this); + this.localSymbolTable = new SymbolTable(this.filename); // prepare for assembly + } + + /** + * Prepares the given list of files for assembly. This involves reading and tokenizing all the source files. There + * may be only one. + * + * @param filenames ArrayList containing the source file name(s) in no particular order + * @param leadFilename String containing name of source file that needs to go first and will be represented by + * "this" MIPSprogram object. + * @param exceptionHandler String containing name of source file containing exception handler. This will be + * assembled first, even ahead of leadFilename, to allow it to include "startup" instructions loaded beginning + * at 0x00400000. Specify null or empty String to indicate there is no such designated exception handler. + * @return ArrayList containing one MIPSprogram object for each file to assemble. objects for any additional files + * (send ArrayList to assembler) + * @throws ProcessingException Will throw exception if errors occured while reading or tokenizing. + **/ + + public ArrayList prepareFilesForAssembly(ArrayList filenames, String leadFilename, String exceptionHandler) throws ProcessingException + { + ArrayList MIPSprogramsToAssemble = new ArrayList(); + int leadFilePosition = 0; + if (exceptionHandler != null && exceptionHandler.length() > 0) + { filenames.add(0, exceptionHandler); leadFilePosition = 1; - } - for (int i=0; i0) { - MIPSprogramsToAssemble.add(leadFilePosition,preparee); - } - else { - MIPSprogramsToAssemble.add(preparee); + // I want "this" MIPSprogram to be the first in the list...except for exception handler + if (preparee == this && MIPSprogramsToAssemble.size() > 0) + { + MIPSprogramsToAssemble.add(leadFilePosition, preparee); } - } - return MIPSprogramsToAssemble; - } - - /** - * Assembles the MIPS source program. All files comprising the program must have - * already been tokenized. Assembler warnings are not considered errors. - * @param MIPSprogramsToAssemble ArrayList of MIPSprogram objects, each representing a tokenized source file. - * @param extendedAssemblerEnabled A boolean value - true means extended (pseudo) instructions - * are permitted in source code and false means they are to be flagged as errors. - * @throws ProcessingException Will throw exception if errors occured while assembling. - * @return ErrorList containing nothing or only warnings (otherwise would have thrown exception). - **/ - - public ErrorList assemble(ArrayList MIPSprogramsToAssemble, boolean extendedAssemblerEnabled) - throws ProcessingException { - return assemble(MIPSprogramsToAssemble, extendedAssemblerEnabled, false); - } - - /** - * Assembles the MIPS source program. All files comprising the program must have - * already been tokenized. - * @param MIPSprogramsToAssemble ArrayList of MIPSprogram objects, each representing a tokenized source file. - * @param extendedAssemblerEnabled A boolean value - true means extended (pseudo) instructions - * are permitted in source code and false means they are to be flagged as errors - * @param warningsAreErrors A boolean value - true means assembler warnings will be considered errors and terminate - the assemble; false means the assembler will produce warning message but otherwise ignore warnings. - * @throws ProcessingException Will throw exception if errors occured while assembling. - * @return ErrorList containing nothing or only warnings (otherwise would have thrown exception). - **/ - - public ErrorList assemble(ArrayList MIPSprogramsToAssemble, boolean extendedAssemblerEnabled, - boolean warningsAreErrors) throws ProcessingException { - this.backStepper = null; - Assembler asm = new Assembler(); - this.machineList = asm.assemble(MIPSprogramsToAssemble, extendedAssemblerEnabled, warningsAreErrors); - this.backStepper = new BackStepper(); - return asm.getErrorList(); - } - - - /** - * Simulates execution of the MIPS program. Program must have already been assembled. - * Begins simulation at beginning of text segment and continues to completion. - * @param breakPoints int array of breakpoints (PC addresses). Can be null. - * @return true if execution completed and false otherwise - * @throws ProcessingException Will throw exception if errors occured while simulating. - **/ - - public boolean simulate(int[] breakPoints) throws ProcessingException { - return this.simulateFromPC(breakPoints, -1, null); - } - - - /** - * Simulates execution of the MIPS program. Program must have already been assembled. - * Begins simulation at beginning of text segment and continues to completion or - * until the specified maximum number of steps are simulated. - * @param maxSteps maximum number of steps to simulate. - * @return true if execution completed and false otherwise - * @throws ProcessingException Will throw exception if errors occured while simulating. - **/ - - public boolean simulate(int maxSteps) throws ProcessingException { - return this.simulateFromPC(null, maxSteps, null); - } - - /** - * Simulates execution of the MIPS program. Program must have already been assembled. - * Begins simulation at current program counter address and continues until stopped, - * paused, maximum steps exceeded, or exception occurs. - * @param breakPoints int array of breakpoints (PC addresses). Can be null. - * @param maxSteps maximum number of instruction executions. Default -1 means no maximum. - * @param a the GUI component responsible for this call (GO normally). set to null if none. - * @return true if execution completed and false otherwise - * @throws ProcessingException Will throw exception if errors occured while simulating. - **/ - public boolean simulateFromPC(int[] breakPoints, int maxSteps, AbstractAction a) throws ProcessingException { - steppedExecution = false; - Simulator sim = Simulator.getInstance(); - return sim.simulate(this, RegisterFile.getProgramCounter(), maxSteps, breakPoints, a); - } - - - - /** - * Simulates execution of the MIPS program. Program must have already been assembled. - * Begins simulation at current program counter address and executes one step. - * @param a the GUI component responsible for this call (STEP normally). Set to null if none. - * @return true if execution completed and false otherwise - * @throws ProcessingException Will throw exception if errors occured while simulating. - **/ - public boolean simulateStepAtPC(AbstractAction a) throws ProcessingException { - steppedExecution = true; - Simulator sim = Simulator.getInstance(); - boolean done = sim.simulate(this, RegisterFile.getProgramCounter(), 1, null,a); - return done; - } - - /** Will be true only while in process of simulating a program statement - * in step mode (e.g. returning to GUI after each step). This is used to - * prevent spurious AccessNotices from being sent from Memory and Register - * to observers at other times (e.g. while updating the data and register - * displays, while assembling program's data segment, etc). - */ - public boolean inSteppedExecution() { - return steppedExecution; - } - - /** - * Instantiates a new {@link MacroPool} and sends reference of this - * {@link MIPSprogram} to it - * - * @return instatiated MacroPool - * @author M.H.Sekhavat - */ - public MacroPool createMacroPool() { - macroPool = new MacroPool(this); - return macroPool; - } - - /** - * Gets local macro pool {@link MacroPool} for this program - * @return MacroPool - * @author M.H.Sekhavat - */ - public MacroPool getLocalMacroPool() { - return macroPool; - } - - /** - * Sets local macro pool {@link MacroPool} for this program - * @param macroPool reference to MacroPool - * @author M.H.Sekhavat - */ - public void setLocalMacroPool(MacroPool macroPool) { - this.macroPool = macroPool; - } - - } // MIPSprogram + else + { + MIPSprogramsToAssemble.add(preparee); + } + } + return MIPSprogramsToAssemble; + } + + /** + * Assembles the MIPS source program. All files comprising the program must have already been tokenized. Assembler + * warnings are not considered errors. + * + * @param MIPSprogramsToAssemble ArrayList of MIPSprogram objects, each representing a tokenized source file. + * @param extendedAssemblerEnabled A boolean value - true means extended (pseudo) instructions are permitted in + * source code and false means they are to be flagged as errors. + * @return ErrorList containing nothing or only warnings (otherwise would have thrown exception). + * @throws ProcessingException Will throw exception if errors occured while assembling. + **/ + + public ErrorList assemble(ArrayList MIPSprogramsToAssemble, boolean extendedAssemblerEnabled) + throws ProcessingException + { + return assemble(MIPSprogramsToAssemble, extendedAssemblerEnabled, false); + } + + /** + * Assembles the MIPS source program. All files comprising the program must have already been tokenized. + * + * @param MIPSprogramsToAssemble ArrayList of MIPSprogram objects, each representing a tokenized source file. + * @param extendedAssemblerEnabled A boolean value - true means extended (pseudo) instructions are permitted in + * source code and false means they are to be flagged as errors + * @param warningsAreErrors A boolean value - true means assembler warnings will be considered errors and + * terminate the assemble; false means the assembler will produce warning message but otherwise ignore + * warnings. + * @return ErrorList containing nothing or only warnings (otherwise would have thrown exception). + * @throws ProcessingException Will throw exception if errors occured while assembling. + **/ + + public ErrorList assemble(ArrayList MIPSprogramsToAssemble, boolean extendedAssemblerEnabled, + boolean warningsAreErrors) throws ProcessingException + { + this.backStepper = null; + Assembler asm = new Assembler(); + this.machineList = asm.assemble(MIPSprogramsToAssemble, extendedAssemblerEnabled, warningsAreErrors); + this.backStepper = new BackStepper(); + return asm.getErrorList(); + } + + + /** + * Simulates execution of the MIPS program. Program must have already been assembled. Begins simulation at beginning + * of text segment and continues to completion. + * + * @param breakPoints int array of breakpoints (PC addresses). Can be null. + * @return true if execution completed and false otherwise + * @throws ProcessingException Will throw exception if errors occured while simulating. + **/ + + public boolean simulate(int[] breakPoints) throws ProcessingException + { + return this.simulateFromPC(breakPoints, -1, null); + } + + + /** + * Simulates execution of the MIPS program. Program must have already been assembled. Begins simulation at beginning + * of text segment and continues to completion or until the specified maximum number of steps are simulated. + * + * @param maxSteps maximum number of steps to simulate. + * @return true if execution completed and false otherwise + * @throws ProcessingException Will throw exception if errors occured while simulating. + **/ + + public boolean simulate(int maxSteps) throws ProcessingException + { + return this.simulateFromPC(null, maxSteps, null); + } + + /** + * Simulates execution of the MIPS program. Program must have already been assembled. Begins simulation at current + * program counter address and continues until stopped, paused, maximum steps exceeded, or exception occurs. + * + * @param breakPoints int array of breakpoints (PC addresses). Can be null. + * @param maxSteps maximum number of instruction executions. Default -1 means no maximum. + * @param a the GUI component responsible for this call (GO normally). set to null if none. + * @return true if execution completed and false otherwise + * @throws ProcessingException Will throw exception if errors occured while simulating. + **/ + public boolean simulateFromPC(int[] breakPoints, int maxSteps, AbstractAction a) throws ProcessingException + { + steppedExecution = false; + Simulator sim = Simulator.getInstance(); + return sim.simulate(this, RegisterFile.getProgramCounter(), maxSteps, breakPoints, a); + } + + + /** + * Simulates execution of the MIPS program. Program must have already been assembled. Begins simulation at current + * program counter address and executes one step. + * + * @param a the GUI component responsible for this call (STEP normally). Set to null if none. + * @return true if execution completed and false otherwise + * @throws ProcessingException Will throw exception if errors occured while simulating. + **/ + public boolean simulateStepAtPC(AbstractAction a) throws ProcessingException + { + steppedExecution = true; + Simulator sim = Simulator.getInstance(); + boolean done = sim.simulate(this, RegisterFile.getProgramCounter(), 1, null, a); + return done; + } + + /** + * Will be true only while in process of simulating a program statement in step mode (e.g. returning to GUI after + * each step). This is used to prevent spurious AccessNotices from being sent from Memory and Register to observers + * at other times (e.g. while updating the data and register displays, while assembling program's data segment, + * etc). + */ + public boolean inSteppedExecution() + { + return steppedExecution; + } + + /** + * Instantiates a new {@link MacroPool} and sends reference of this {@link MIPSprogram} to it + * + * @return instatiated MacroPool + * @author M.H.Sekhavat + */ + public MacroPool createMacroPool() + { + macroPool = new MacroPool(this); + return macroPool; + } + + /** + * Gets local macro pool {@link MacroPool} for this program + * + * @return MacroPool + * @author M.H.Sekhavat + */ + public MacroPool getLocalMacroPool() + { + return macroPool; + } + + /** + * Sets local macro pool {@link MacroPool} for this program + * + * @param macroPool reference to MacroPool + * @author M.H.Sekhavat + */ + public void setLocalMacroPool(MacroPool macroPool) + { + this.macroPool = macroPool; + } + +} // MIPSprogram diff --git a/src/main/java/mars/MarsLaunch.java b/src/main/java/mars/MarsLaunch.java index 63dfd49..f6c9c8d 100644 --- a/src/main/java/mars/MarsLaunch.java +++ b/src/main/java/mars/MarsLaunch.java @@ -1,14 +1,23 @@ - package mars; - import mars.venus.*; - import mars.util.*; - import mars.mips.dump.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import java.io.*; - import java.util.*; - import java.awt.*; - import javax.swing.*; - import javax.swing.JOptionPane; // KENV 9/8/2004 +package mars; + +import mars.mips.dump.DumpFormat; +import mars.mips.dump.DumpFormatLoader; +import mars.mips.hardware.*; +import mars.simulator.ProgramArgumentList; +import mars.util.Binary; +import mars.util.FilenameFinder; +import mars.util.MemoryDump; +import mars.venus.VenusUI; + +import javax.swing.*; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Observable; +import java.util.Observer; /* Copyright (c) 2003-2012, Pete Sanderson and Kenneth Vollmar @@ -40,111 +49,128 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Launch the Mars application - * + * * @author Pete Sanderson * @version December 2009 **/ - public class MarsLaunch { - - /** - * Main takes a number of command line arguments.
- Usage: Mars [options] filename
- Valid options (not case sensitive, separate by spaces) are:
- a -- assemble only, do not simulate
- ad -- both a and d
- ae -- terminate MARS with integer exit code if an assemble error occurs.
- ascii -- display memory or register contents interpreted as ASCII - b -- brief - do not display register/memory address along with contents
- d -- print debugging statements
- da -- both a and d
- db -- MIPS delayed branching is enabled.
- dec -- display memory or register contents in decimal.
- dump -- dump memory contents to file. Option has 3 arguments, e.g.
- dump <segment> <format> <file>. Also supports
- an address range (see m-n below). Current supported
- segments are .text and .data. Current supported dump formats
- are Binary, HexText, BinaryText.
- h -- display help. Use by itself and with no filename
- hex -- display memory or register contents in hexadecimal (default)
- ic -- display count of MIPS basic instructions 'executed'"); - mc -- set memory configuration. Option has 1 argument, e.g.
- mc <config$gt;, where <config$gt; is Default
- for the MARS default 32-bit address space, CompactDataAtZero for
- a 32KB address space with data segment at address 0, or CompactTextAtZero
- for a 32KB address space with text segment at address 0.
- me -- display MARS messages to standard err instead of standard out. Can separate via redirection.
- nc -- do not display copyright notice (for cleaner redirected/piped output).
- np -- No Pseudo-instructions allowed ("ne" will work also).
- p -- Project mode - assemble all files in the same directory as given file.
- se -- terminate MARS with integer exit code if a simulation (run) error occurs.
- sm -- Start execution at Main - Execution will start at program statement globally labeled main.
- smc -- Self Modifying Code - Program can write and branch to either text or data segment
- we -- assembler Warnings will be considered Errors
- -- where is an integer maximum count of steps to simulate.
- If 0, negative or not specified, there is no maximum.
- $ -- where is number or name (e.g. 5, t3, f10) of register whose
- content to display at end of run. Option may be repeated.
- -- where is name (e.g. t3, f10) of register whose
- content to display at end of run. Option may be repeated. $ not required.
- - -- memory address range from to whose contents to
- display at end of run. and may be hex or decimal,
- <= , both must be on word boundary. Option may be repeated.
- pa -- Program Arguments follow in a space-separated list. This
- option must be placed AFTER ALL FILE NAMES, because everything
- that follows it is interpreted as a program argument to be
- made available to the MIPS program at runtime.
- **/ - - - - private boolean simulate; - private int displayFormat; - private boolean verbose; // display register name or address along with contents - private boolean assembleProject; // assemble only the given file or all files in its directory - private boolean pseudo; // pseudo instructions allowed in source code or not. - private boolean delayedBranching; // MIPS delayed branching is enabled. - private boolean warningsAreErrors; // Whether assembler warnings should be considered errors. - private boolean startAtMain; // Whether to start execution at statement labeled 'main' - private boolean countInstructions; // Whether to count and report number of instructions executed - private boolean selfModifyingCode; // Whether to allow self-modifying code (e.g. write to text segment) - private static final String rangeSeparator = "-"; - private static final int splashDuration = 2000; // time in MS to show splash screen - private static final int memoryWordsPerLine = 4; // display 4 memory words, tab separated, per line - private static final int DECIMAL = 0; // memory and register display format - private static final int HEXADECIMAL = 1;// memory and register display format - private static final int ASCII = 2;// memory and register display format - private ArrayList registerDisplayList; - private ArrayList memoryDisplayList; - private ArrayList filenameList; - private MIPSprogram code; - private int maxSteps; - private int instructionCount; - private PrintStream out; // stream for display of command line output - private ArrayList dumpTriples = null; // each element holds 3 arguments for dump option - private ArrayList programArgumentList; // optional program args for MIPS program (becomes argc, argv) - private int assembleErrorExitCode; // MARS command exit code to return if assemble error occurs - private int simulateErrorExitCode;// MARS command exit code to return if simulation error occurs - - public MarsLaunch(String[] args) { - boolean gui = (args.length == 0); - Globals.initialize(gui); - if (gui) { - launchIDE(); - } - else { // running from command line. +public class MarsLaunch +{ + + private static final String rangeSeparator = "-"; + + private static final int splashDuration = 2000; // time in MS to show splash screen + + private static final int memoryWordsPerLine = 4; // display 4 memory words, tab separated, per line + + private static final int DECIMAL = 0; // memory and register display format + + private static final int HEXADECIMAL = 1;// memory and register display format + + private static final int ASCII = 2;// memory and register display format + + /** + * Main takes a number of command line arguments.
Usage: Mars [options] filename
Valid options (not case + * sensitive, separate by spaces) are:
a -- assemble only, do not simulate
ad -- both a and d
ae + * -- terminate MARS with integer exit code if an assemble error occurs.
ascii -- display memory or + * register contents interpreted as ASCII b -- brief - do not display register/memory address along with + * contents
d -- print debugging statements
da -- both a and d
db -- MIPS delayed branching is + * enabled.
dec -- display memory or register contents in decimal.
dump -- dump memory contents to file. + * Option has 3 arguments, e.g.
+ * dump <segment> <format> <file>. Also supports
+ * an address range (see m-n below). Current supported
segments are .text and .data. + * Current supported dump formats
are Binary, HexText, BinaryText.
h -- display + * help. Use by itself and with no filename
hex -- display memory or register contents in hexadecimal + * (default)
ic -- display count of MIPS basic instructions 'executed'"); mc -- set memory configuration. + * Option has 1 argument, e.g.
+ * mc <config$gt;, where <config$gt; is Default
+ * for the MARS default 32-bit address space, CompactDataAtZero for
a 32KB address space with data + * segment at address 0, or CompactTextAtZero
for a 32KB address space with text segment at address + * 0.
me -- display MARS messages to standard err instead of standard out. Can separate via redirection.
+ * nc -- do not display copyright notice (for cleaner redirected/piped output).
np -- No Pseudo-instructions + * allowed ("ne" will work also).
p -- Project mode - assemble all files in the same directory as given + * file.
se -- terminate MARS with integer exit code if a simulation (run) error occurs.
sm -- + * Start execution at Main - Execution will start at program statement globally labeled main.
smc -- Self + * Modifying Code - Program can write and branch to either text or data segment
we -- assembler Warnings will + * be considered Errors
+ * -- where is an integer maximum count of steps to simulate.
+ * If 0, negative or not specified, there is no maximum.
$ -- where is number or name (e.g. 5, t3, + * f10) of register whose
content to display at end of run. Option may be repeated.
+ * -- where is name (e.g. t3, f10) of register whose
+ * content to display at end of run. Option may be repeated. $ not required.
+ * - -- memory address range from to whose contents to
+ * display at end of run. and may be hex or decimal,
+ * <= , both must be on word boundary. Option may be repeated.
+ * pa -- Program Arguments follow in a space-separated list. This
option must be placed AFTER ALL FILE NAMES, + * because everything
that follows it is interpreted as a program argument to be
made available to the MIPS + * program at runtime.
+ **/ + + + private boolean simulate; + + private int displayFormat; + + private boolean verbose; // display register name or address along with contents + + private boolean assembleProject; // assemble only the given file or all files in its directory + + private boolean pseudo; // pseudo instructions allowed in source code or not. + + private boolean delayedBranching; // MIPS delayed branching is enabled. + + private boolean warningsAreErrors; // Whether assembler warnings should be considered errors. + + private boolean startAtMain; // Whether to start execution at statement labeled 'main' + + private boolean countInstructions; // Whether to count and report number of instructions executed + + private boolean selfModifyingCode; // Whether to allow self-modifying code (e.g. write to text segment) + + private ArrayList registerDisplayList; + + private ArrayList memoryDisplayList; + + private ArrayList filenameList; + + private MIPSprogram code; + + private int maxSteps; + + private int instructionCount; + + private PrintStream out; // stream for display of command line output + + private ArrayList dumpTriples = null; // each element holds 3 arguments for dump option + + private ArrayList programArgumentList; // optional program args for MIPS program (becomes argc, argv) + + private int assembleErrorExitCode; // MARS command exit code to return if assemble error occurs + + private int simulateErrorExitCode;// MARS command exit code to return if simulation error occurs + + public MarsLaunch(String[] args) + { + boolean gui = (args.length == 0); + Globals.initialize(gui); + if (gui) + { + launchIDE(); + } + else + { // running from command line. // assure command mode works in headless environment (generates exception if not) - System.setProperty("java.awt.headless", "true"); + System.setProperty("java.awt.headless", "true"); simulate = true; displayFormat = HEXADECIMAL; - verbose = true; + verbose = true; assembleProject = false; pseudo = true; delayedBranching = false; warningsAreErrors = false; startAtMain = false; countInstructions = false; - selfModifyingCode = false; + selfModifyingCode = false; instructionCount = 0; assembleErrorExitCode = 0; simulateErrorExitCode = 0; @@ -152,691 +178,839 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. memoryDisplayList = new ArrayList(); filenameList = new ArrayList(); MemoryConfigurations.setCurrentConfiguration(MemoryConfigurations.getDefaultConfiguration()); - // do NOT use Globals.program for command line MARS -- it triggers 'backstep' log. - code = new MIPSprogram(); + // do NOT use Globals.program for command line MARS -- it triggers 'backstep' log. + code = new MIPSprogram(); maxSteps = -1; - out = System.out; - if (parseCommandArgs(args)) { - if (runCommand()) { - displayMiscellaneousPostMortem(); - displayRegistersPostMortem(); - displayMemoryPostMortem(); - } - dumpSegments(); + out = System.out; + if (parseCommandArgs(args)) + { + if (runCommand()) + { + displayMiscellaneousPostMortem(); + displayRegistersPostMortem(); + displayMemoryPostMortem(); + } + dumpSegments(); } System.exit(Globals.exitCode); - } - } - - ///////////////////////////////////////////////////////////// - // Perform any specified dump operations. See "dump" option. - // - - private void dumpSegments() { - - if (dumpTriples == null) + } + } + + ///////////////////////////////////////////////////////////// + // Perform any specified dump operations. See "dump" option. + // + + private void dumpSegments() + { + + if (dumpTriples == null) + { return; - - for (int i=0; i 1) { - // Using "p" project option PLUS listing more than one filename on command line. - // Add the additional files, avoiding duplicates. - filenameList.remove(0); // first one has already been processed - ArrayList moreFilesToAssemble = FilenameFinder.getFilenameList(filenameList, FilenameFinder.MATCH_ALL_EXTENSIONS); - // Remove any duplicates then merge the two lists. - for (int index2 = 0; index2 1) + { + // Using "p" project option PLUS listing more than one filename on command line. + // Add the additional files, avoiding duplicates. + filenameList.remove(0); // first one has already been processed + ArrayList moreFilesToAssemble = FilenameFinder.getFilenameList(filenameList, FilenameFinder.MATCH_ALL_EXTENSIONS); + // Remove any duplicates then merge the two lists. + for (int index2 = 0; index2 < moreFilesToAssemble.size(); index2++) + { + for (int index1 = 0; index1 < filesToAssemble.size(); index1++) + { + if (filesToAssemble.get(index1).equals(moreFilesToAssemble.get(index2))) + { + moreFilesToAssemble.remove(index2); + index2--; // adjust for left shift in moreFilesToAssemble... + break; // break out of inner loop... + } } - } - } - filesToAssemble.addAll(moreFilesToAssemble); - } - } - else { - filesToAssemble = FilenameFinder.getFilenameList(filenameList, FilenameFinder.MATCH_ALL_EXTENSIONS); + } + filesToAssemble.addAll(moreFilesToAssemble); + } } - if (Globals.debug) { - out.println("-------- TOKENIZING BEGINS -----------"); + else + { + filesToAssemble = FilenameFinder.getFilenameList(filenameList, FilenameFinder.MATCH_ALL_EXTENSIONS); } - ArrayList MIPSprogramsToAssemble = - code.prepareFilesForAssembly(filesToAssemble, mainFile.getAbsolutePath(), null); - if (Globals.debug) { - out.println("-------- ASSEMBLY BEGINS -----------"); + if (Globals.debug) + { + out.println("-------- TOKENIZING BEGINS -----------"); } - // Added logic to check for warnings and print if any. DPS 11/28/06 + ArrayList MIPSprogramsToAssemble = + code.prepareFilesForAssembly(filesToAssemble, mainFile.getAbsolutePath(), null); + if (Globals.debug) + { + out.println("-------- ASSEMBLY BEGINS -----------"); + } + // Added logic to check for warnings and print if any. DPS 11/28/06 ErrorList warnings = code.assemble(MIPSprogramsToAssemble, pseudo, warningsAreErrors); - if (warnings != null && warnings.warningsOccurred()) { - out.println(warnings.generateWarningReport()); + if (warnings != null && warnings.warningsOccurred()) + { + out.println(warnings.generateWarningReport()); } RegisterFile.initializeProgramCounter(startAtMain); // DPS 3/9/09 - if (simulate) { - // store program args (if any) in MIPS memory - new ProgramArgumentList(programArgumentList).storeProgramArguments(); - // establish observer if specified - establishObserver(); - if (Globals.debug) { - out.println("-------- SIMULATION BEGINS -----------"); - } - programRan = true; - boolean done = code.simulate(maxSteps); - if (!done) { - out.println("\nProgram terminated when maximum step limit "+maxSteps+" reached."); - } + if (simulate) + { + // store program args (if any) in MIPS memory + new ProgramArgumentList(programArgumentList).storeProgramArguments(); + // establish observer if specified + establishObserver(); + if (Globals.debug) + { + out.println("-------- SIMULATION BEGINS -----------"); + } + programRan = true; + boolean done = code.simulate(maxSteps); + if (!done) + { + out.println("\nProgram terminated when maximum step limit " + maxSteps + " reached."); + } } - if (Globals.debug) { - out.println("\n-------- ALL PROCESSING COMPLETE -----------"); + if (Globals.debug) + { + out.println("\n-------- ALL PROCESSING COMPLETE -----------"); } - } - catch (ProcessingException e) { - Globals.exitCode = (programRan) ? simulateErrorExitCode : assembleErrorExitCode; - out.println(e.errors().generateErrorAndWarningReport()); - out.println("Processing terminated due to errors."); - } - return programRan; - } - - - ////////////////////////////////////////////////////////////////////// - // Check for memory address subrange. Has to be two integers separated - // by "-"; no embedded spaces. e.g. 0x00400000-0x00400010 - // If number is not multiple of 4, will be rounded up to next higher. - - private String[] checkMemoryAddressRange(String arg) throws NumberFormatException { - String[] memoryRange = null; - if (arg.indexOf(rangeSeparator) > 0 && - arg.indexOf(rangeSeparator) < arg.length()-1) { + } + catch (ProcessingException e) + { + Globals.exitCode = (programRan) ? simulateErrorExitCode : assembleErrorExitCode; + out.println(e.errors().generateErrorAndWarningReport()); + out.println("Processing terminated due to errors."); + } + return programRan; + } + + + ////////////////////////////////////////////////////////////////////// + // Check for memory address subrange. Has to be two integers separated + // by "-"; no embedded spaces. e.g. 0x00400000-0x00400010 + // If number is not multiple of 4, will be rounded up to next higher. + + private String[] checkMemoryAddressRange(String arg) throws NumberFormatException + { + String[] memoryRange = null; + if (arg.indexOf(rangeSeparator) > 0 && + arg.indexOf(rangeSeparator) < arg.length() - 1) + { // assume correct format, two numbers separated by -, no embedded spaces. // If that doesn't work it is invalid. memoryRange = new String[2]; - memoryRange[0] = arg.substring(0,arg.indexOf(rangeSeparator)); - memoryRange[1] = arg.substring(arg.indexOf(rangeSeparator)+1); + memoryRange[0] = arg.substring(0, arg.indexOf(rangeSeparator)); + memoryRange[1] = arg.substring(arg.indexOf(rangeSeparator) + 1); // NOTE: I will use homegrown decoder, because Integer.decode will throw // exception on address higher than 0x7FFFFFFF (e.g. sign bit is 1). if (Binary.stringToInt(memoryRange[0]) > Binary.stringToInt(memoryRange[1]) || - !Memory.wordAligned(Binary.stringToInt(memoryRange[0])) || - !Memory.wordAligned(Binary.stringToInt(memoryRange[1]))) { - throw new NumberFormatException(); + !Memory.wordAligned(Binary.stringToInt(memoryRange[0])) || + !Memory.wordAligned(Binary.stringToInt(memoryRange[1]))) + { + throw new NumberFormatException(); } - } - return memoryRange; - } - - ///////////////////////////////////////////////////////////////// - // Required for counting instructions executed, if that option is specified. - // DPS 19 July 2012 - private void establishObserver() { - if (countInstructions) { - Observer instructionCounter = - new Observer() { - private int lastAddress = 0; - public void update(Observable o, Object obj) { - if (obj instanceof AccessNotice) { - AccessNotice notice = (AccessNotice) obj; - if (!notice.accessIsFromMIPS()) - return; - if (notice.getAccessType() != AccessNotice.READ) - return; - MemoryAccessNotice m = (MemoryAccessNotice) notice; - int a = m.getAddress(); - if (a == lastAddress) - return; - lastAddress = a; - instructionCount++; - } - } - }; - try { - Globals.memory.addObserver(instructionCounter, Memory.textBaseAddress, Memory.textLimitAddress); - } - catch (AddressErrorException aee) { - out.println("Internal error: MarsLaunch uses incorrect text segment address for instruction observer"); - } - } - } - - ////////////////////////////////////////////////////////////////////// - // Displays any specified runtime properties. Initially just instruction count - // DPS 19 July 2012 - private void displayMiscellaneousPostMortem() { - if (countInstructions) { - out.println("\n"+instructionCount); - } - } - - - ////////////////////////////////////////////////////////////////////// - // Displays requested register or registers - - private void displayRegistersPostMortem() { - int value; // handy local to use throughout the next couple loops - String strValue; - // Display requested register contents - out.println(); - Iterator regIter = registerDisplayList.iterator(); - while (regIter.hasNext()) { + } + return memoryRange; + } + + ///////////////////////////////////////////////////////////////// + // Required for counting instructions executed, if that option is specified. + // DPS 19 July 2012 + private void establishObserver() + { + if (countInstructions) + { + Observer instructionCounter = + new Observer() + { + private int lastAddress = 0; + + public void update(Observable o, Object obj) + { + if (obj instanceof AccessNotice) + { + AccessNotice notice = (AccessNotice) obj; + if (!notice.accessIsFromMIPS()) + { + return; + } + if (notice.getAccessType() != AccessNotice.READ) + { + return; + } + MemoryAccessNotice m = (MemoryAccessNotice) notice; + int a = m.getAddress(); + if (a == lastAddress) + { + return; + } + lastAddress = a; + instructionCount++; + } + } + }; + try + { + Globals.memory.addObserver(instructionCounter, Memory.textBaseAddress, Memory.textLimitAddress); + } + catch (AddressErrorException aee) + { + out.println("Internal error: MarsLaunch uses incorrect text segment address for instruction observer"); + } + } + } + + ////////////////////////////////////////////////////////////////////// + // Displays any specified runtime properties. Initially just instruction count + // DPS 19 July 2012 + private void displayMiscellaneousPostMortem() + { + if (countInstructions) + { + out.println("\n" + instructionCount); + } + } + + + ////////////////////////////////////////////////////////////////////// + // Displays requested register or registers + + private void displayRegistersPostMortem() + { + int value; // handy local to use throughout the next couple loops + String strValue; + // Display requested register contents + out.println(); + Iterator regIter = registerDisplayList.iterator(); + while (regIter.hasNext()) + { String reg = regIter.next().toString(); - if (RegisterFile.getUserRegister(reg)!=null) { - // integer register - if (verbose) - out.print(reg+"\t"); - value = RegisterFile.getUserRegister(reg).getValue(); - out.println( formatIntForDisplay(value) ); - } - else { - // floating point register - float fvalue = Coprocessor1.getFloatFromRegister(reg); - int ivalue = Coprocessor1.getIntFromRegister(reg); - double dvalue = Double.NaN; - long lvalue = 0; - boolean hasDouble = false; - try { - dvalue = Coprocessor1.getDoubleFromRegisterPair(reg); - lvalue = Coprocessor1.getLongFromRegisterPair(reg); - hasDouble = true; - } - catch (InvalidRegisterAccessException irae) { } - if (verbose) { - out.print(reg+"\t"); - } - if (displayFormat == HEXADECIMAL) { - // display float (and double, if applicable) in hex - out.print( - Binary.binaryStringToHexString( - Binary.intToBinaryString(ivalue))); - if (hasDouble) { - out.println("\t"+ - Binary.binaryStringToHexString( - Binary.longToBinaryString(lvalue))); - } - else { - out.println(""); - } - } - else if (displayFormat == DECIMAL) { - // display float (and double, if applicable) in decimal - out.print(fvalue); - if (hasDouble) { - out.println("\t"+dvalue); - } - else { - out.println(""); - } - } - else { // displayFormat == ASCII - out.print(Binary.intToAscii(ivalue)); - if (hasDouble) { - out.println("\t"+ - Binary.intToAscii(Binary.highOrderLongToInt(lvalue))+ - Binary.intToAscii(Binary.lowOrderLongToInt(lvalue))); - } - else { - out.println(""); - } - } + if (RegisterFile.getUserRegister(reg) != null) + { + // integer register + if (verbose) + { + out.print(reg + "\t"); + } + value = RegisterFile.getUserRegister(reg).getValue(); + out.println(formatIntForDisplay(value)); + } + else + { + // floating point register + float fvalue = Coprocessor1.getFloatFromRegister(reg); + int ivalue = Coprocessor1.getIntFromRegister(reg); + double dvalue = Double.NaN; + long lvalue = 0; + boolean hasDouble = false; + try + { + dvalue = Coprocessor1.getDoubleFromRegisterPair(reg); + lvalue = Coprocessor1.getLongFromRegisterPair(reg); + hasDouble = true; + } + catch (InvalidRegisterAccessException irae) + { + } + if (verbose) + { + out.print(reg + "\t"); + } + if (displayFormat == HEXADECIMAL) + { + // display float (and double, if applicable) in hex + out.print( + Binary.binaryStringToHexString( + Binary.intToBinaryString(ivalue))); + if (hasDouble) + { + out.println("\t" + + Binary.binaryStringToHexString( + Binary.longToBinaryString(lvalue))); + } + else + { + out.println(); + } + } + else if (displayFormat == DECIMAL) + { + // display float (and double, if applicable) in decimal + out.print(fvalue); + if (hasDouble) + { + out.println("\t" + dvalue); + } + else + { + out.println(); + } + } + else + { // displayFormat == ASCII + out.print(Binary.intToAscii(ivalue)); + if (hasDouble) + { + out.println("\t" + + Binary.intToAscii(Binary.highOrderLongToInt(lvalue)) + + Binary.intToAscii(Binary.lowOrderLongToInt(lvalue))); + } + else + { + out.println(); + } + } + } + } + } + + ////////////////////////////////////////////////////////////////////// + // Formats int value for display: decimal, hex, ascii + private String formatIntForDisplay(int value) + { + String strValue; + switch (displayFormat) + { + case DECIMAL: + strValue = "" + value; + break; + case HEXADECIMAL: + strValue = Binary.intToHexString(value); + break; + case ASCII: + strValue = Binary.intToAscii(value); + break; + default: + strValue = Binary.intToHexString(value); + } + return strValue; + } + + ////////////////////////////////////////////////////////////////////// + // Displays requested memory range or ranges + + private void displayMemoryPostMortem() + { + int value; + // Display requested memory range contents + Iterator memIter = memoryDisplayList.iterator(); + int addressStart = 0, addressEnd = 0; + while (memIter.hasNext()) + { + try + { // This will succeed; error would have been caught during command arg parse + addressStart = Binary.stringToInt(memIter.next().toString()); + addressEnd = Binary.stringToInt(memIter.next().toString()); + } + catch (NumberFormatException nfe) + { } - } - } - - ////////////////////////////////////////////////////////////////////// - // Formats int value for display: decimal, hex, ascii - private String formatIntForDisplay(int value) { - String strValue; - switch (displayFormat) { - case DECIMAL : - strValue = ""+value; - break; - case HEXADECIMAL : - strValue = Binary.intToHexString(value); - break; - case ASCII : - strValue = Binary.intToAscii(value); - break; - default : - strValue = Binary.intToHexString(value); - } - return strValue; - } - - ////////////////////////////////////////////////////////////////////// - // Displays requested memory range or ranges - - private void displayMemoryPostMortem() { - int value; - // Display requested memory range contents - Iterator memIter = memoryDisplayList.iterator(); - int addressStart=0, addressEnd=0; - while (memIter.hasNext()) { - try { // This will succeed; error would have been caught during command arg parse - addressStart = Binary.stringToInt(memIter.next().toString()); - addressEnd = Binary.stringToInt(memIter.next().toString()); - } - catch (NumberFormatException nfe) { - } int valuesDisplayed = 0; - for (int addr=addressStart; addr<=addressEnd; addr+=Memory.WORD_LENGTH_BYTES) { - if (addr < 0 && addressEnd > 0) - break; // happens only if addressEnd is 0x7ffffffc - if (valuesDisplayed % memoryWordsPerLine == 0) { - out.print( (valuesDisplayed > 0) ? "\n" : ""); - if (verbose) { - out.print("Mem["+Binary.intToHexString(addr)+"]\t"); - } - } - try { - // Allow display of binary text segment (machine code) DPS 14-July-2008 - if (Memory.inTextSegment(addr) || Memory.inKernelTextSegment(addr)) { - Integer iValue = Globals.memory.getRawWordOrNull(addr); - value = (iValue==null) ? 0 : iValue.intValue(); - } - else { - value = Globals.memory.getWord(addr); - } - out.print( formatIntForDisplay(value)+"\t"); - } - catch (AddressErrorException aee) { - out.print("Invalid address: "+addr+"\t"); - } - valuesDisplayed++; + for (int addr = addressStart; addr <= addressEnd; addr += Memory.WORD_LENGTH_BYTES) + { + if (addr < 0 && addressEnd > 0) + { + break; // happens only if addressEnd is 0x7ffffffc + } + if (valuesDisplayed % memoryWordsPerLine == 0) + { + out.print((valuesDisplayed > 0) ? "\n" : ""); + if (verbose) + { + out.print("Mem[" + Binary.intToHexString(addr) + "]\t"); + } + } + try + { + // Allow display of binary text segment (machine code) DPS 14-July-2008 + if (Memory.inTextSegment(addr) || Memory.inKernelTextSegment(addr)) + { + Integer iValue = Globals.memory.getRawWordOrNull(addr); + value = (iValue == null) ? 0 : iValue.intValue(); + } + else + { + value = Globals.memory.getWord(addr); + } + out.print(formatIntForDisplay(value) + "\t"); + } + catch (AddressErrorException aee) + { + out.print("Invalid address: " + addr + "\t"); + } + valuesDisplayed++; } out.println(); - } - } - - /////////////////////////////////////////////////////////////////////// - // If option to display MARS messages to standard err (System.err) is - // present, it must be processed before all others. Since messages may - // be output as early as during the command parse. - private void processDisplayMessagesToErrSwitch(String[] args, String displayMessagesToErrSwitch) { - for (int i=0; i -- terminate MARS with integer exit code if an assemble error occurs."); - out.println(" ascii -- display memory or register contents interpreted as ASCII codes."); - out.println(" b -- brief - do not display register/memory address along with contents"); - out.println(" d -- display MARS debugging statements"); - out.println(" db -- MIPS delayed branching is enabled"); - out.println(" dec -- display memory or register contents in decimal."); - out.println(" dump -- memory dump of specified memory segment"); - out.println(" in specified format to specified file. Option may be repeated."); - out.println(" Dump occurs at the end of simulation unless 'a' option is used."); - out.println(" Segment and format are case-sensitive and possible values are:"); - out.println(" = "+segments); - out.println(" = "+formats); - out.println(" h -- display this help. Use by itself with no filename."); - out.println(" hex -- display memory or register contents in hexadecimal (default)"); - out.println(" ic -- display count of MIPS basic instructions 'executed'"); - out.println(" mc -- set memory configuration. Argument is"); - out.println(" case-sensitive and possible values are: Default for the default"); - out.println(" 32-bit address space, CompactDataAtZero for a 32KB memory with"); - out.println(" data segment at address 0, or CompactTextAtZero for a 32KB"); - out.println(" memory with text segment at address 0."); - out.println(" me -- display MARS messages to standard err instead of standard out. "); - out.println(" Can separate messages from program output using redirection"); - out.println(" nc -- do not display copyright notice (for cleaner redirected/piped output)."); - out.println(" np -- use of pseudo instructions and formats not permitted"); - out.println(" p -- Project mode - assemble all files in the same directory as given file."); - out.println(" se -- terminate MARS with integer exit code if a simulation (run) error occurs."); - out.println(" sm -- start execution at statement with global label main, if defined"); - out.println(" smc -- Self Modifying Code - Program can write and branch to either text or data segment"); - out.println(" -- where is an integer maximum count of steps to simulate."); - out.println(" If 0, negative or not specified, there is no maximum."); - out.println(" $ -- where is number or name (e.g. 5, t3, f10) of register whose "); - out.println(" content to display at end of run. Option may be repeated."); - out.println(" -- where is name (e.g. t3, f10) of register whose"); - out.println(" content to display at end of run. Option may be repeated. "); - out.println(" The $ is not required."); - out.println("- -- memory address range from to whose contents to"); - out.println(" display at end of run. and may be hex or decimal,"); - out.println(" must be on word boundary, <= . Option may be repeated."); - out.println(" pa -- Program Arguments follow in a space-separated list. This"); - out.println(" option must be placed AFTER ALL FILE NAMES, because everything"); - out.println(" that follows it is interpreted as a program argument to be"); - out.println(" made available to the MIPS program at runtime."); - out.println("If more than one filename is listed, the first is assumed to be the main"); - out.println("unless the global statement label 'main' is defined in one of the files."); - out.println("Exception handler not automatically assembled. Add it to the file list."); - out.println("Options used here do not affect MARS Settings menu values and vice versa."); - } - - } + } + out.println("Usage: Mars [options] filename [additional filenames]"); + out.println(" Valid options (not case sensitive, separate by spaces) are:"); + out.println(" a -- assemble only, do not simulate"); + out.println(" ae -- terminate MARS with integer exit code if an assemble error occurs."); + out.println(" ascii -- display memory or register contents interpreted as ASCII codes."); + out.println(" b -- brief - do not display register/memory address along with contents"); + out.println(" d -- display MARS debugging statements"); + out.println(" db -- MIPS delayed branching is enabled"); + out.println(" dec -- display memory or register contents in decimal."); + out.println(" dump -- memory dump of specified memory segment"); + out.println(" in specified format to specified file. Option may be repeated."); + out.println(" Dump occurs at the end of simulation unless 'a' option is used."); + out.println(" Segment and format are case-sensitive and possible values are:"); + out.println(" = " + segments); + out.println(" = " + formats); + out.println(" h -- display this help. Use by itself with no filename."); + out.println(" hex -- display memory or register contents in hexadecimal (default)"); + out.println(" ic -- display count of MIPS basic instructions 'executed'"); + out.println(" mc -- set memory configuration. Argument is"); + out.println(" case-sensitive and possible values are: Default for the default"); + out.println(" 32-bit address space, CompactDataAtZero for a 32KB memory with"); + out.println(" data segment at address 0, or CompactTextAtZero for a 32KB"); + out.println(" memory with text segment at address 0."); + out.println(" me -- display MARS messages to standard err instead of standard out. "); + out.println(" Can separate messages from program output using redirection"); + out.println(" nc -- do not display copyright notice (for cleaner redirected/piped output)."); + out.println(" np -- use of pseudo instructions and formats not permitted"); + out.println(" p -- Project mode - assemble all files in the same directory as given file."); + out.println(" se -- terminate MARS with integer exit code if a simulation (run) error occurs."); + out.println(" sm -- start execution at statement with global label main, if defined"); + out.println(" smc -- Self Modifying Code - Program can write and branch to either text or data segment"); + out.println(" -- where is an integer maximum count of steps to simulate."); + out.println(" If 0, negative or not specified, there is no maximum."); + out.println(" $ -- where is number or name (e.g. 5, t3, f10) of register whose "); + out.println(" content to display at end of run. Option may be repeated."); + out.println(" -- where is name (e.g. t3, f10) of register whose"); + out.println(" content to display at end of run. Option may be repeated. "); + out.println(" The $ is not required."); + out.println("- -- memory address range from to whose contents to"); + out.println(" display at end of run. and may be hex or decimal,"); + out.println(" must be on word boundary, <= . Option may be repeated."); + out.println(" pa -- Program Arguments follow in a space-separated list. This"); + out.println(" option must be placed AFTER ALL FILE NAMES, because everything"); + out.println(" that follows it is interpreted as a program argument to be"); + out.println(" made available to the MIPS program at runtime."); + out.println("If more than one filename is listed, the first is assumed to be the main"); + out.println("unless the global statement label 'main' is defined in one of the files."); + out.println("Exception handler not automatically assembled. Add it to the file list."); + out.println("Options used here do not affect MARS Settings menu values and vice versa."); + } + +} diff --git a/src/main/java/mars/MarsSplashScreen.java b/src/main/java/mars/MarsSplashScreen.java index fbf7611..66af44b 100644 --- a/src/main/java/mars/MarsSplashScreen.java +++ b/src/main/java/mars/MarsSplashScreen.java @@ -1,6 +1,7 @@ - package mars; - import java.awt.*; - import javax.swing.*; +package mars; + +import javax.swing.*; +import java.awt.*; /* Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar @@ -31,88 +32,99 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Produces MARS splash screen.
- * Adapted from http://www.java-tips.org/content/view/1267/2/
+ * Produces MARS splash screen.
Adapted from http://www.java-tips.org/content/view/1267/2/
*/ - public class MarsSplashScreen extends JWindow { - - private int duration; - - public MarsSplashScreen(int d) { - duration = d; - } - - /** - * A simple little method to show a title screen in the center - * of the screen for the amount of time given in the constructor - */ - public void showSplash() { - ImageBackgroundPanel content = new ImageBackgroundPanel(); - this.setContentPane(content); - - // Set the window's bounds, centering the window - // Wee bit of a hack. I've hardcoded the image dimensions of - // MarsSurfacePathfinder.jpg, because obtaining them via - // getHeight() and getWidth() is not trival -- it is possible - // that at the time of the call the image has not completed - // loading so the Image object doesn't know how big it is. - // So observers are involved -- see the API. - int width = 390; - int height =215; - Toolkit tk = Toolkit.getDefaultToolkit(); - Dimension screen = tk.getScreenSize(); - int x = (screen.width-width)/2; - int y = (screen.height-height)/2; - setBounds(x,y,width,height); - - // Build the splash screen - JLabel title = new JLabel("MARS: Mips Assembler and Runtime Simulator", JLabel.CENTER); - JLabel copyrt1 = new JLabel - ("

Version "+Globals.version+" Copyright (c) "+Globals.copyrightYears+"", JLabel.CENTER); - JLabel copyrt2 = new JLabel - ("

"+Globals.copyrightHolders+"", JLabel.CENTER); - title.setFont(new Font("Sans-Serif", Font.BOLD, 16)); - title.setForeground(Color.black); - copyrt1.setFont(new Font("Sans-Serif", Font.BOLD, 14)); - copyrt2.setFont(new Font("Sans-Serif", Font.BOLD, 14)); - copyrt1.setForeground(Color.white); - copyrt2.setForeground(Color.white); +public class MarsSplashScreen extends JWindow +{ - content.add(title,BorderLayout.NORTH); - content.add(copyrt1,BorderLayout.CENTER); - content.add(copyrt2,BorderLayout.SOUTH); + private final int duration; - // Display it - setVisible(true); - // Wait a little while, maybe while loading resources - try { Thread.sleep(duration); } - catch (Exception e) {} - setVisible(false); - } - - class ImageBackgroundPanel extends JPanel - { - Image image; - public ImageBackgroundPanel() - { + public MarsSplashScreen(int d) + { + duration = d; + } + + /** + * A simple little method to show a title screen in the center of the screen for the amount of time given in the + * constructor + */ + public void showSplash() + { + ImageBackgroundPanel content = new ImageBackgroundPanel(); + this.setContentPane(content); + + // Set the window's bounds, centering the window + // Wee bit of a hack. I've hardcoded the image dimensions of + // MarsSurfacePathfinder.jpg, because obtaining them via + // getHeight() and getWidth() is not trival -- it is possible + // that at the time of the call the image has not completed + // loading so the Image object doesn't know how big it is. + // So observers are involved -- see the API. + int width = 390; + int height = 215; + Toolkit tk = Toolkit.getDefaultToolkit(); + Dimension screen = tk.getScreenSize(); + int x = (screen.width - width) / 2; + int y = (screen.height - height) / 2; + setBounds(x, y, width, height); + + // Build the splash screen + JLabel title = new JLabel("MARS: Mips Assembler and Runtime Simulator", JLabel.CENTER); + JLabel copyrt1 = new JLabel + ("

Version " + Globals.version + " Copyright (c) " + Globals.copyrightYears + "", JLabel.CENTER); + JLabel copyrt2 = new JLabel + ("

" + Globals.copyrightHolders + "", JLabel.CENTER); + title.setFont(new Font("Sans-Serif", Font.BOLD, 16)); + title.setForeground(Color.black); + copyrt1.setFont(new Font("Sans-Serif", Font.BOLD, 14)); + copyrt2.setFont(new Font("Sans-Serif", Font.BOLD, 14)); + copyrt1.setForeground(Color.white); + copyrt2.setForeground(Color.white); + + content.add(title, BorderLayout.NORTH); + content.add(copyrt1, BorderLayout.CENTER); + content.add(copyrt2, BorderLayout.SOUTH); + + // Display it + setVisible(true); + // Wait a little while, maybe while loading resources + try + { + Thread.sleep(duration); + } + catch (Exception e) + { + } + setVisible(false); + } + + class ImageBackgroundPanel extends JPanel + { + Image image; + + public ImageBackgroundPanel() + { try { - image = new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(Globals.imagesPath+"MarsSurfacePathfinder.jpg"))).getImage(); + image = new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(Globals.imagesPath + "MarsSurfacePathfinder.jpg"))).getImage(); } - catch (Exception e) {System.out.println(e); /*handled in paintComponent()*/ } - } - - @Override - protected void paintComponent(Graphics g) - { - super.paintComponent(g); + catch (Exception e) + { + System.out.println(e); /*handled in paintComponent()*/ + } + } + + @Override + protected void paintComponent(Graphics g) + { + super.paintComponent(g); if (image != null) - g.drawImage(image, 0,0,this.getWidth(),this.getHeight(),this); - } - } - - - - - } + { + g.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), this); + } + } + } + + +} diff --git a/src/main/java/mars/ProcessingException.java b/src/main/java/mars/ProcessingException.java index 53684cc..2ad8628 100644 --- a/src/main/java/mars/ProcessingException.java +++ b/src/main/java/mars/ProcessingException.java @@ -1,8 +1,10 @@ - package mars; - import mars.util.*; - import mars.mips.hardware.*; - import mars.mips.instructions.Instruction; - import mars.simulator.*; +package mars; + +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; +import mars.mips.instructions.Instruction; +import mars.simulator.Exceptions; +import mars.util.Binary; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -34,100 +36,107 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Class to represent error that occurs while assembling or running a MIPS program. - * + * * @author Pete Sanderson * @version August 2003 **/ - public class ProcessingException extends Exception { - private ErrorList errs; - - /** - * Constructor for ProcessingException. - * - * @param e An ErrorList which is an ArrayList of ErrorMessage objects. Each ErrorMessage - * represents one processing error. - **/ - public ProcessingException(ErrorList e) { - errs = e; - } - - /** - * Constructor for ProcessingException. - * - * @param e An ErrorList which is an ArrayList of ErrorMessage objects. Each ErrorMessage - * represents one processing error. - * @param aee AddressErrorException object containing specialized error message, cause, address - **/ - public ProcessingException(ErrorList e, AddressErrorException aee) { - errs = e; - Exceptions.setRegisters(aee.getType(), aee.getAddress()); - } - - /** - * Constructor for ProcessingException to handle runtime exceptions - * - * @param ps a ProgramStatement of statement causing runtime exception - * @param m a String containing specialized error message - **/ - public ProcessingException(ProgramStatement ps, String m) { - errs = new ErrorList(); - errs.add(new ErrorMessage(ps, "Runtime exception at "+ - Binary.intToHexString(RegisterFile.getProgramCounter()-Instruction.INSTRUCTION_LENGTH)+ - ": "+m)); - // Stopped using ps.getAddress() because of pseudo-instructions. All instructions in - // the macro expansion point to the same ProgramStatement, and thus all will return the - // same value for getAddress(). But only the first such expanded instruction will - // be stored at that address. So now I use the program counter (which has already - // been incremented). - } - - - /** - * Constructor for ProcessingException to handle runtime exceptions - * - * @param ps a ProgramStatement of statement causing runtime exception - * @param m a String containing specialized error message - * @param cause exception cause (see Exceptions class for list) - **/ - public ProcessingException(ProgramStatement ps, String m, int cause) { - this(ps,m); - Exceptions.setRegisters(cause); - } - - - /** - * Constructor for ProcessingException to handle address runtime exceptions - * - * @param ps a ProgramStatement of statement causing runtime exception - * @param aee AddressErrorException object containing specialized error message, cause, address - **/ - - public ProcessingException(ProgramStatement ps, AddressErrorException aee) { - this(ps, aee.getMessage()); - Exceptions.setRegisters(aee.getType(), aee.getAddress()); - } - - /** - * Constructor for ProcessingException. - * - * No parameter and thus no error list. Use this for normal MIPS - * program termination (e.g. syscall 10 for exit). - **/ - public ProcessingException() { - errs = null; - } - - /** - * Produce the list of error messages. - * - * @return Returns ErrorList of error messages. - * @see ErrorList - * @see ErrorMessage - **/ - - public ErrorList errors() { - return errs; - } - - } +public class ProcessingException extends Exception +{ + private final ErrorList errs; + + /** + * Constructor for ProcessingException. + * + * @param e An ErrorList which is an ArrayList of ErrorMessage objects. Each ErrorMessage represents one + * processing error. + **/ + public ProcessingException(ErrorList e) + { + errs = e; + } + + /** + * Constructor for ProcessingException. + * + * @param e An ErrorList which is an ArrayList of ErrorMessage objects. Each ErrorMessage represents one + * processing error. + * @param aee AddressErrorException object containing specialized error message, cause, address + **/ + public ProcessingException(ErrorList e, AddressErrorException aee) + { + errs = e; + Exceptions.setRegisters(aee.getType(), aee.getAddress()); + } + + /** + * Constructor for ProcessingException to handle runtime exceptions + * + * @param ps a ProgramStatement of statement causing runtime exception + * @param m a String containing specialized error message + **/ + public ProcessingException(ProgramStatement ps, String m) + { + errs = new ErrorList(); + errs.add(new ErrorMessage(ps, "Runtime exception at " + + Binary.intToHexString(RegisterFile.getProgramCounter() - Instruction.INSTRUCTION_LENGTH) + + ": " + m)); + // Stopped using ps.getAddress() because of pseudo-instructions. All instructions in + // the macro expansion point to the same ProgramStatement, and thus all will return the + // same value for getAddress(). But only the first such expanded instruction will + // be stored at that address. So now I use the program counter (which has already + // been incremented). + } + + + /** + * Constructor for ProcessingException to handle runtime exceptions + * + * @param ps a ProgramStatement of statement causing runtime exception + * @param m a String containing specialized error message + * @param cause exception cause (see Exceptions class for list) + **/ + public ProcessingException(ProgramStatement ps, String m, int cause) + { + this(ps, m); + Exceptions.setRegisters(cause); + } + + + /** + * Constructor for ProcessingException to handle address runtime exceptions + * + * @param ps a ProgramStatement of statement causing runtime exception + * @param aee AddressErrorException object containing specialized error message, cause, address + **/ + + public ProcessingException(ProgramStatement ps, AddressErrorException aee) + { + this(ps, aee.getMessage()); + Exceptions.setRegisters(aee.getType(), aee.getAddress()); + } + + /** + * Constructor for ProcessingException. + *

+ * No parameter and thus no error list. Use this for normal MIPS program termination (e.g. syscall 10 for exit). + **/ + public ProcessingException() + { + errs = null; + } + + /** + * Produce the list of error messages. + * + * @return Returns ErrorList of error messages. + * @see ErrorList + * @see ErrorMessage + **/ + + public ErrorList errors() + { + return errs; + } + +} diff --git a/src/main/java/mars/ProgramStatement.java b/src/main/java/mars/ProgramStatement.java index 58325ac..ef536a0 100644 --- a/src/main/java/mars/ProgramStatement.java +++ b/src/main/java/mars/ProgramStatement.java @@ -1,11 +1,17 @@ package mars; -import mars.assembler.*; -import mars.mips.instructions.*; -import mars.mips.hardware.*; -import mars.util.*; +import mars.assembler.SymbolTable; +import mars.assembler.Token; +import mars.assembler.TokenList; +import mars.assembler.TokenTypes; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.RegisterFile; +import mars.mips.instructions.BasicInstruction; +import mars.mips.instructions.BasicInstructionFormat; +import mars.mips.instructions.Instruction; +import mars.util.Binary; -import java.util.*; +import java.util.ArrayList; /* Copyright (c) 2003-2013, Pete Sanderson and Kenneth Vollmar @@ -36,44 +42,58 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Represents one assembly/machine statement. This represents the "bare machine" level. - * Pseudo-instructions have already been processed at this point and each assembly - * statement generated by them is one of these. + * Represents one assembly/machine statement. This represents the "bare machine" level. Pseudo-instructions have + * already been processed at this point and each assembly statement generated by them is one of these. * * @author Pete Sanderson and Jason Bumgarner * @version August 2003 */ -public class ProgramStatement { - private MIPSprogram sourceMIPSprogram; - private String source, basicAssemblyStatement, machineStatement; - private TokenList originalTokenList, strippedTokenList; - private BasicStatementList basicStatementList; - private int[] operands; - private int numOperands; - private Instruction instruction; - private int textAddress; - private int sourceLine; - private int binaryStatement; - private boolean altered; +public class ProgramStatement +{ private static final String invalidOperator = ""; + private final MIPSprogram sourceMIPSprogram; + + private String source, basicAssemblyStatement, machineStatement; + + private final TokenList originalTokenList; + + private final TokenList strippedTokenList; + + private final BasicStatementList basicStatementList; + + private final int[] operands; + + private int numOperands; + + private final Instruction instruction; + + private final int textAddress; + + private int sourceLine; + + private int binaryStatement; + + private final boolean altered; + ////////////////////////////////////////////////////////////////////////////////// /** - * Constructor for ProgramStatement when there are links back to all source and token - * information. These can be used by a debugger later on. + * Constructor for ProgramStatement when there are links back to all source and token information. These can be + * used by a debugger later on. * * @param sourceMIPSprogram The MIPSprogram object that contains this statement - * @param source The corresponding MIPS source statement. - * @param origTokenList Complete list of Token objects (includes labels, comments, parentheses, etc) + * @param source The corresponding MIPS source statement. + * @param origTokenList Complete list of Token objects (includes labels, comments, parentheses, etc) * @param strippedTokenList List of Token objects with all but operators and operands removed. - * @param inst The Instruction object for this statement's operator. - * @param textAddress The Text Segment address in memory where the binary machine code for this statement - * is stored. + * @param inst The Instruction object for this statement's operator. + * @param textAddress The Text Segment address in memory where the binary machine code for this statement is + * stored. **/ - public ProgramStatement(MIPSprogram sourceMIPSprogram, String source, TokenList origTokenList, TokenList strippedTokenList, Instruction inst, int textAddress, int sourceLine) { + public ProgramStatement(MIPSprogram sourceMIPSprogram, String source, TokenList origTokenList, TokenList strippedTokenList, Instruction inst, int textAddress, int sourceLine) + { this.sourceMIPSprogram = sourceMIPSprogram; this.source = source; this.originalTokenList = origTokenList; @@ -94,17 +114,16 @@ public class ProgramStatement { ////////////////////////////////////////////////////////////////////////////////// /** - * Constructor for ProgramStatement used only for writing a binary machine - * instruction with no source code to refer back to. Originally supported - * only NOP instruction (all zeroes), but extended in release 4.4 to support - * all basic instructions. This was required for the self-modifying code - * feature. + * Constructor for ProgramStatement used only for writing a binary machine instruction with no source code to refer + * back to. Originally supported only NOP instruction (all zeroes), but extended in release 4.4 to support all + * basic instructions. This was required for the self-modifying code feature. * * @param binaryStatement The 32-bit machine code. - * @param textAddress The Text Segment address in memory where the binary machine code for this statement - * is stored. + * @param textAddress The Text Segment address in memory where the binary machine code for this statement is + * stored. **/ - public ProgramStatement(int binaryStatement, int textAddress) { + public ProgramStatement(int binaryStatement, int textAddress) + { this.sourceMIPSprogram = null; this.binaryStatement = binaryStatement; this.textAddress = textAddress; @@ -112,12 +131,15 @@ public class ProgramStatement { this.source = ""; this.machineStatement = this.basicAssemblyStatement = null; BasicInstruction instr = Globals.instructionSet.findByBinaryCode(binaryStatement); - if (instr == null) { + if (instr == null) + { this.operands = null; this.numOperands = 0; this.instruction = (binaryStatement == 0) // this is a "nop" statement - ? (Instruction) Globals.instructionSet.matchOperator("nop").get(0) : null; - } else { + ? (Instruction) Globals.instructionSet.matchOperator("nop").get(0) : null; + } + else + { this.operands = new int[4]; this.numOperands = 0; this.instruction = instr; @@ -126,16 +148,21 @@ public class ProgramStatement { String fmt = instr.getOperationMask(); BasicInstructionFormat instrFormat = instr.getInstructionFormat(); int numOps = 0; - for (int i = 0; i < opandCodes.length(); i++) { + for (int i = 0; i < opandCodes.length(); i++) + { int code = opandCodes.charAt(i); int j = fmt.indexOf(code); - if (j >= 0) { + if (j >= 0) + { int k0 = 31 - fmt.lastIndexOf(code); int k1 = 31 - j; int opand = (binaryStatement >> k0) & ((1 << (k1 - k0 + 1)) - 1); - if (instrFormat.equals(BasicInstructionFormat.I_BRANCH_FORMAT) && numOps == 2) { + if (instrFormat.equals(BasicInstructionFormat.I_BRANCH_FORMAT) && numOps == 2) + { opand = opand << 16 >> 16; - } else if (instrFormat.equals(BasicInstructionFormat.J_FORMAT) && numOps == 0) { + } + else if (instrFormat.equals(BasicInstructionFormat.J_FORMAT) && numOps == 0) + { opand |= (textAddress >> 2) & 0x3C000000; } this.operands[numOps] = opand; @@ -152,63 +179,76 @@ public class ProgramStatement { ///////////////////////////////////////////////////////////////////////////// /** - * Given specification of BasicInstruction for this operator, build the - * corresponding assembly statement in basic assembly format (e.g. substituting - * register numbers for register names, replacing labels by values). + * Given specification of BasicInstruction for this operator, build the corresponding assembly statement in basic + * assembly format (e.g. substituting register numbers for register names, replacing labels by values). * * @param errors The list of assembly errors encountered so far. May add to it here. **/ - public void buildBasicStatementFromBasicInstruction(ErrorList errors) { + public void buildBasicStatementFromBasicInstruction(ErrorList errors) + { Token token = strippedTokenList.get(0); String basicStatementElement = token.getValue() + " "; - ; String basic = basicStatementElement; basicStatementList.addString(basicStatementElement); // the operator TokenTypes tokenType, nextTokenType; String tokenValue; int registerNumber; this.numOperands = 0; - for (int i = 1; i < strippedTokenList.size(); i++) { + for (int i = 1; i < strippedTokenList.size(); i++) + { token = strippedTokenList.get(i); tokenType = token.getType(); tokenValue = token.getValue(); - if (tokenType == TokenTypes.REGISTER_NUMBER) { + if (tokenType == TokenTypes.REGISTER_NUMBER) + { basicStatementElement = tokenValue; basic += basicStatementElement; basicStatementList.addString(basicStatementElement); - try { + try + { registerNumber = RegisterFile.getUserRegister(tokenValue).getNumber(); - } catch (Exception e) { + } + catch (Exception e) + { // should never happen; should be caught before now... errors.add(new ErrorMessage(this.sourceMIPSprogram, token.getSourceLine(), token.getStartPos(), "invalid register name")); return; } this.operands[this.numOperands++] = registerNumber; - } else if (tokenType == TokenTypes.REGISTER_NAME) { + } + else if (tokenType == TokenTypes.REGISTER_NAME) + { registerNumber = RegisterFile.getNumber(tokenValue); basicStatementElement = "$" + registerNumber; basic += basicStatementElement; basicStatementList.addString(basicStatementElement); - if (registerNumber < 0) { + if (registerNumber < 0) + { // should never happen; should be caught before now... errors.add(new ErrorMessage(this.sourceMIPSprogram, token.getSourceLine(), token.getStartPos(), "invalid register name")); return; } this.operands[this.numOperands++] = registerNumber; - } else if (tokenType == TokenTypes.FP_REGISTER_NAME) { + } + else if (tokenType == TokenTypes.FP_REGISTER_NAME) + { registerNumber = Coprocessor1.getRegisterNumber(tokenValue); basicStatementElement = "$f" + registerNumber; basic += basicStatementElement; basicStatementList.addString(basicStatementElement); - if (registerNumber < 0) { + if (registerNumber < 0) + { // should never happen; should be caught before now... errors.add(new ErrorMessage(this.sourceMIPSprogram, token.getSourceLine(), token.getStartPos(), "invalid FPU register name")); return; } this.operands[this.numOperands++] = registerNumber; - } else if (tokenType == TokenTypes.IDENTIFIER) { + } + else if (tokenType == TokenTypes.IDENTIFIER) + { int address = this.sourceMIPSprogram.getLocalSymbolTable().getAddressLocalOrGlobal(tokenValue); - if (address == SymbolTable.NOT_FOUND) { // symbol used without being defined + if (address == SymbolTable.NOT_FOUND) + { // symbol used without being defined errors.add(new ErrorMessage(this.sourceMIPSprogram, token.getSourceLine(), token.getStartPos(), "Symbol \"" + tokenValue + "\" not found in symbol table.")); return; } @@ -231,9 +271,11 @@ public class ProgramStatement { // This mod must be made in conjunction with InstructionSet.java's processBranch() // method. There are some comments there as well. - if (instruction instanceof BasicInstruction) { + if (instruction instanceof BasicInstruction) + { BasicInstructionFormat format = ((BasicInstruction) instruction).getInstructionFormat(); - if (format == BasicInstructionFormat.I_BRANCH_FORMAT) { + if (format == BasicInstructionFormat.I_BRANCH_FORMAT) + { //address = (address - (this.textAddress+((Globals.getSettings().getDelayedBranchingEnabled())? Instruction.INSTRUCTION_LENGTH : 0))) >> 2; address = (address - (this.textAddress + Instruction.INSTRUCTION_LENGTH)) >> 2; absoluteAddress = false; @@ -241,13 +283,18 @@ public class ProgramStatement { } ////////////////////////////////////////////////////////////////////// basic += address; - if (absoluteAddress) { // record as address if absolute, value if relative + if (absoluteAddress) + { // record as address if absolute, value if relative basicStatementList.addAddress(address); - } else { + } + else + { basicStatementList.addValue(address); } this.operands[this.numOperands++] = address; - } else if (tokenType == TokenTypes.INTEGER_5 || tokenType == TokenTypes.INTEGER_16 || tokenType == TokenTypes.INTEGER_16U || tokenType == TokenTypes.INTEGER_32) { + } + else if (tokenType == TokenTypes.INTEGER_5 || tokenType == TokenTypes.INTEGER_16 || tokenType == TokenTypes.INTEGER_16U || tokenType == TokenTypes.INTEGER_32) + { int tempNumeric = Binary.stringToInt(tokenValue); @@ -296,16 +343,20 @@ public class ProgramStatement { basicStatementList.addValue(tempNumeric); this.operands[this.numOperands++] = tempNumeric; ///// End modification 1/7/05 KENV /////////////////////////////////////////// - } else { + } + else + { basicStatementElement = tokenValue; basic += basicStatementElement; basicStatementList.addString(basicStatementElement); } // add separator if not at end of token list AND neither current nor // next token is a parenthesis - if ((i < strippedTokenList.size() - 1)) { + if ((i < strippedTokenList.size() - 1)) + { nextTokenType = strippedTokenList.get(i + 1).getType(); - if (tokenType != TokenTypes.LEFT_PAREN && tokenType != TokenTypes.RIGHT_PAREN && nextTokenType != TokenTypes.LEFT_PAREN && nextTokenType != TokenTypes.RIGHT_PAREN) { + if (tokenType != TokenTypes.LEFT_PAREN && tokenType != TokenTypes.RIGHT_PAREN && nextTokenType != TokenTypes.LEFT_PAREN && nextTokenType != TokenTypes.RIGHT_PAREN) + { basicStatementElement = ","; basic += basicStatementElement; basicStatementList.addString(basicStatementElement); @@ -319,27 +370,32 @@ public class ProgramStatement { ///////////////////////////////////////////////////////////////////////////// /** - * Given the current statement in Basic Assembly format (see above), build the - * 32-bit binary machine code statement. + * Given the current statement in Basic Assembly format (see above), build the 32-bit binary machine code + * statement. * * @param errors The list of assembly errors encountered so far. May add to it here. **/ - public void buildMachineStatementFromBasicStatement(ErrorList errors) { + public void buildMachineStatementFromBasicStatement(ErrorList errors) + { - try { + try + { //mask indicates bit positions for 'f'irst, 's'econd, 't'hird operand this.machineStatement = ((BasicInstruction) instruction).getOperationMask(); } // This means the pseudo-instruction expansion generated another // pseudo-instruction (expansion must be to all basic instructions). // This is an error on the part of the pseudo-instruction author. - catch (ClassCastException cce) { + catch (ClassCastException cce) + { errors.add(new ErrorMessage(this.sourceMIPSprogram, this.sourceLine, 0, "INTERNAL ERROR: pseudo-instruction expansion contained a pseudo-instruction")); return; } BasicInstructionFormat format = ((BasicInstruction) instruction).getInstructionFormat(); - if (format == BasicInstructionFormat.J_FORMAT) { - if ((this.textAddress & 0xF0000000) != (this.operands[0] & 0xF0000000)) { + if (format == BasicInstructionFormat.J_FORMAT) + { + if ((this.textAddress & 0xF0000000) != (this.operands[0] & 0xF0000000)) + { // attempt to jump beyond 28-bit byte (26-bit word) address range. // SPIM flags as warning, I'll flag as error b/c MARS text segment not long enough for it to be OK. errors.add(new ErrorMessage(this.sourceMIPSprogram, this.sourceLine, 0, "Jump target word address beyond 26-bit range")); @@ -348,17 +404,23 @@ public class ProgramStatement { // Note the bit shift to make this a word address. this.operands[0] = this.operands[0] >>> 2; this.insertBinaryCode(this.operands[0], Instruction.operandMask[0], errors); - } else if (format == BasicInstructionFormat.I_BRANCH_FORMAT) { - for (int i = 0; i < this.numOperands - 1; i++) { + } + else if (format == BasicInstructionFormat.I_BRANCH_FORMAT) + { + for (int i = 0; i < this.numOperands - 1; i++) + { this.insertBinaryCode(this.operands[i], Instruction.operandMask[i], errors); } this.insertBinaryCode(operands[this.numOperands - 1], Instruction.operandMask[this.numOperands - 1], errors); - } else { // R_FORMAT or I_FORMAT + } + else + { // R_FORMAT or I_FORMAT for (int i = 0; i < this.numOperands; i++) + { this.insertBinaryCode(this.operands[i], Instruction.operandMask[i], errors); + } } this.binaryStatement = Binary.binaryStringToInt(this.machineStatement); - return; } // buildMachineStatementFromBasicStatement( @@ -370,81 +432,45 @@ public class ProgramStatement { * @return A String representing the ProgramStatement. **/ - public String toString() { + public String toString() + { // a crude attempt at string formatting. Where's C when you need it? String blanks = " "; String result = "[" + this.textAddress + "]"; - if (this.basicAssemblyStatement != null) { + if (this.basicAssemblyStatement != null) + { int firstSpace = this.basicAssemblyStatement.indexOf(" "); result += blanks.substring(0, 16 - result.length()) + this.basicAssemblyStatement.substring(0, firstSpace); result += blanks.substring(0, 24 - result.length()) + this.basicAssemblyStatement.substring(firstSpace + 1); - ; - } else { + } + else + { result += blanks.substring(0, 16 - result.length()) + "0x" + Integer.toString(this.binaryStatement, 16); } result += blanks.substring(0, 40 - result.length()) + "; "; // this.source; - if (operands != null) { + if (operands != null) + { for (int i = 0; i < this.numOperands; i++) - // result += operands[i] + " "; + // result += operands[i] + " "; + { result += Integer.toString(operands[i], 16) + " "; + } } - if (this.machineStatement != null) { + if (this.machineStatement != null) + { result += "[" + Binary.binaryStringToHexString(this.machineStatement) + "]"; result += " " + this.machineStatement.substring(0, 6) + "|" + this.machineStatement.substring(6, 11) + "|" + this.machineStatement.substring(11, 16) + "|" + this.machineStatement.substring(16, 21) + "|" + this.machineStatement.substring(21, 26) + "|" + this.machineStatement.substring(26, 32); } return result; } // toString() - /** - * Assigns given String to be Basic Assembly statement equivalent to this source line. - * - * @param statement A String containing equivalent Basic Assembly statement. - **/ - - public void setBasicAssemblyStatement(String statement) { - basicAssemblyStatement = statement; - } - - /** - * Assigns given String to be binary machine code (32 characters, all of them 0 or 1) - * equivalent to this source line. - * - * @param statement A String containing equivalent machine code. - **/ - - public void setMachineStatement(String statement) { - machineStatement = statement; - } - - /** - * Assigns given int to be binary machine code equivalent to this source line. - * - * @param binaryCode An int containing equivalent binary machine code. - **/ - - public void setBinaryStatement(int binaryCode) { - binaryStatement = binaryCode; - } - - - /** - * associates MIPS source statement. Used by assembler when generating basic - * statements during macro expansion of extended statement. - * - * @param src a MIPS source statement. - **/ - - public void setSource(String src) { - source = src; - } - - /** * Produces MIPSprogram object representing the source file containing this statement. * * @return The MIPSprogram object. May be null... **/ - public MIPSprogram getSourceMIPSprogram() { + public MIPSprogram getSourceMIPSprogram() + { return sourceMIPSprogram; } @@ -453,51 +479,75 @@ public class ProgramStatement { * * @return The file name. **/ - public String getSourceFile() { + public String getSourceFile() + { return (sourceMIPSprogram == null) ? "" : sourceMIPSprogram.getFilename(); } - /** * Produces MIPS source statement. * * @return The MIPS source statement. **/ - public String getSource() { + public String getSource() + { return source; } + /** + * associates MIPS source statement. Used by assembler when generating basic statements during macro expansion of + * extended statement. + * + * @param src a MIPS source statement. + **/ + + public void setSource(String src) + { + source = src; + } + /** * Produces line number of MIPS source statement. * * @return The MIPS source statement line number. **/ - public int getSourceLine() { + public int getSourceLine() + { return sourceLine; } /** - * Produces Basic Assembly statement for this MIPS source statement. - * All numeric values are in decimal. + * Produces Basic Assembly statement for this MIPS source statement. All numeric values are in decimal. * * @return The Basic Assembly statement. **/ - public String getBasicAssemblyStatement() { + public String getBasicAssemblyStatement() + { return basicAssemblyStatement; } /** - * Produces printable Basic Assembly statement for this MIPS source - * statement. This is generated dynamically and any addresses and - * values will be rendered in hex or decimal depending on the current - * setting. + * Assigns given String to be Basic Assembly statement equivalent to this source line. + * + * @param statement A String containing equivalent Basic Assembly statement. + **/ + + public void setBasicAssemblyStatement(String statement) + { + basicAssemblyStatement = statement; + } + + /** + * Produces printable Basic Assembly statement for this MIPS source statement. This is generated dynamically and + * any addresses and values will be rendered in hex or decimal depending on the current setting. * * @return The Basic Assembly statement. **/ - public String getPrintableBasicAssemblyStatement() { + public String getPrintableBasicAssemblyStatement() + { return basicStatementList.toString(); } @@ -507,35 +557,62 @@ public class ProgramStatement { * @return The String version of 32-bit binary machine code. **/ - public String getMachineStatement() { + public String getMachineStatement() + { return machineStatement; } + /** + * Assigns given String to be binary machine code (32 characters, all of them 0 or 1) equivalent to this source + * line. + * + * @param statement A String containing equivalent machine code. + **/ + + public void setMachineStatement(String statement) + { + machineStatement = statement; + } + /** * Produces 32-bit binary machine statement as int. * * @return The int version of 32-bit binary machine code. **/ - public int getBinaryStatement() { + public int getBinaryStatement() + { return binaryStatement; } + /** + * Assigns given int to be binary machine code equivalent to this source line. + * + * @param binaryCode An int containing equivalent binary machine code. + **/ + + public void setBinaryStatement(int binaryCode) + { + binaryStatement = binaryCode; + } + /** * Produces token list generated from original source statement. * * @return The TokenList of Token objects generated from original source. **/ - public TokenList getOriginalTokenList() { + public TokenList getOriginalTokenList() + { return originalTokenList; } /** * Produces token list stripped of all but operator and operand tokens. * - * @return The TokenList of Token objects generated by stripping original list of all - * except operator and operand tokens. + * @return The TokenList of Token objects generated by stripping original list of all except operator and operand + * tokens. **/ - public TokenList getStrippedTokenList() { + public TokenList getStrippedTokenList() + { return strippedTokenList; } @@ -544,7 +621,8 @@ public class ProgramStatement { * * @return The Instruction that matches the operator used in this statement. **/ - public Instruction getInstruction() { + public Instruction getInstruction() + { return instruction; } @@ -553,7 +631,8 @@ public class ProgramStatement { * * @return address in Text Segment of this binary machine statement. **/ - public int getAddress() { + public int getAddress() + { return textAddress; } @@ -562,7 +641,8 @@ public class ProgramStatement { * * @return int array of operand values (if any) required by this statement's operator. **/ - public int[] getOperands() { + public int[] getOperands() + { return operands; } @@ -572,10 +652,14 @@ public class ProgramStatement { * @param i Operand position in array (first operand is position 0). * @return Operand value at given operand array position. If < 0 or >= numOperands, it returns -1. **/ - public int getOperand(int i) { - if (i >= 0 && i < this.numOperands) { + public int getOperand(int i) + { + if (i >= 0 && i < this.numOperands) + { return operands[i]; - } else { + } + else + { return -1; } } @@ -584,18 +668,22 @@ public class ProgramStatement { ////////////////////////////////////////////////////////////////////////////// // Given operand (register or integer) and mask character ('f', 's', or 't'), // generate the correct sequence of bits and replace the mask with them. - private void insertBinaryCode(int value, char mask, ErrorList errors) { + private void insertBinaryCode(int value, char mask, ErrorList errors) + { int startPos = this.machineStatement.indexOf(mask); int endPos = this.machineStatement.lastIndexOf(mask); - if (startPos == -1 || endPos == -1) { // should NEVER occur + if (startPos == -1 || endPos == -1) + { // should NEVER occur errors.add(new ErrorMessage(this.sourceMIPSprogram, this.sourceLine, 0, "INTERNAL ERROR: mismatch in number of operands in statement vs mask")); return; } String bitString = Binary.intToBinaryString(value, endPos - startPos + 1); String state = this.machineStatement.substring(0, startPos) + bitString; - if (endPos < this.machineStatement.length() - 1) state = state + this.machineStatement.substring(endPos + 1); + if (endPos < this.machineStatement.length() - 1) + { + state = state + this.machineStatement.substring(endPos + 1); + } this.machineStatement = state; - return; } // insertBinaryCode() @@ -606,47 +694,66 @@ public class ProgramStatement { * used by the constructor that is given only the int address and binary code. It is not * intended to be used when source code is available. DPS 11-July-2013 */ - private BasicStatementList buildBasicStatementListFromBinaryCode(int binary, BasicInstruction instr, int[] operands, int numOperands) { + private BasicStatementList buildBasicStatementListFromBinaryCode(int binary, BasicInstruction instr, int[] operands, int numOperands) + { BasicStatementList statementList = new BasicStatementList(); int tokenListCounter = 1; // index 0 is operator; operands start at index 1 - if (instr == null) { + if (instr == null) + { statementList.addString(invalidOperator); return statementList; - } else { + } + else + { statementList.addString(instr.getName() + " "); } - for (int i = 0; i < numOperands; i++) { + for (int i = 0; i < numOperands; i++) + { // add separator if not at end of token list AND neither current nor // next token is a parenthesis - if (tokenListCounter > 1 && tokenListCounter < instr.getTokenList().size()) { + if (tokenListCounter > 1 && tokenListCounter < instr.getTokenList().size()) + { TokenTypes thisTokenType = instr.getTokenList().get(tokenListCounter).getType(); - if (thisTokenType != TokenTypes.LEFT_PAREN && thisTokenType != TokenTypes.RIGHT_PAREN) { + if (thisTokenType != TokenTypes.LEFT_PAREN && thisTokenType != TokenTypes.RIGHT_PAREN) + { statementList.addString(","); } } boolean notOperand = true; - while (notOperand && tokenListCounter < instr.getTokenList().size()) { + while (notOperand && tokenListCounter < instr.getTokenList().size()) + { TokenTypes tokenType = instr.getTokenList().get(tokenListCounter).getType(); - if (tokenType.equals(TokenTypes.LEFT_PAREN)) { + if (tokenType.equals(TokenTypes.LEFT_PAREN)) + { statementList.addString("("); - } else if (tokenType.equals(TokenTypes.RIGHT_PAREN)) { + } + else if (tokenType.equals(TokenTypes.RIGHT_PAREN)) + { statementList.addString(")"); - } else if (tokenType.toString().contains("REGISTER")) { + } + else if (tokenType.toString().contains("REGISTER")) + { String marker = (tokenType.toString().contains("FP_REGISTER")) ? "$f" : "$"; statementList.addString(marker + operands[i]); notOperand = false; - } else { + } + else + { statementList.addValue(operands[i]); notOperand = false; } tokenListCounter++; } } - while (tokenListCounter < instr.getTokenList().size()) { + while (tokenListCounter < instr.getTokenList().size()) + { TokenTypes tokenType = instr.getTokenList().get(tokenListCounter).getType(); - if (tokenType.equals(TokenTypes.LEFT_PAREN)) { + if (tokenType.equals(TokenTypes.LEFT_PAREN)) + { statementList.addString("("); - } else if (tokenType.equals(TokenTypes.RIGHT_PAREN)) { + } + else if (tokenType.equals(TokenTypes.RIGHT_PAREN)) + { statementList.addString(")"); } tokenListCounter++; @@ -670,34 +777,42 @@ public class ProgramStatement { // // DPS 29-July-2010 - private class BasicStatementList { + private class BasicStatementList + { - private ArrayList list; + private final ArrayList list; - BasicStatementList() { + BasicStatementList() + { list = new ArrayList(); } - void addString(String string) { + void addString(String string) + { list.add(new ListElement(0, string, 0)); } - void addAddress(int address) { + void addAddress(int address) + { list.add(new ListElement(1, null, address)); } - void addValue(int value) { + void addValue(int value) + { list.add(new ListElement(2, null, value)); } - public String toString() { + public String toString() + { int addressBase = (Globals.getSettings().getBooleanSetting(Settings.DISPLAY_ADDRESSES_IN_HEX)) ? mars.venus.NumberDisplayBaseChooser.HEXADECIMAL : mars.venus.NumberDisplayBaseChooser.DECIMAL; int valueBase = (Globals.getSettings().getBooleanSetting(Settings.DISPLAY_VALUES_IN_HEX)) ? mars.venus.NumberDisplayBaseChooser.HEXADECIMAL : mars.venus.NumberDisplayBaseChooser.DECIMAL; StringBuffer result = new StringBuffer(); - for (int i = 0; i < list.size(); i++) { + for (int i = 0; i < list.size(); i++) + { ListElement e = (ListElement) list.get(i); - switch (e.type) { + switch (e.type) + { case 0: result.append(e.sValue); break; @@ -705,9 +820,12 @@ public class ProgramStatement { result.append(mars.venus.NumberDisplayBaseChooser.formatNumber(e.iValue, addressBase)); break; case 2: - if (valueBase == mars.venus.NumberDisplayBaseChooser.HEXADECIMAL) { + if (valueBase == mars.venus.NumberDisplayBaseChooser.HEXADECIMAL) + { result.append(mars.util.Binary.intToHexString(e.iValue)); // 13-July-2011, was: intToHalfHexString() - } else { + } + else + { result.append(mars.venus.NumberDisplayBaseChooser.formatNumber(e.iValue, valueBase)); } default: @@ -717,12 +835,16 @@ public class ProgramStatement { return result.toString(); } - private class ListElement { + private class ListElement + { int type; + String sValue; + int iValue; - ListElement(int type, String sValue, int iValue) { + ListElement(int type, String sValue, int iValue) + { this.type = type; this.sValue = sValue; this.iValue = iValue; diff --git a/src/main/java/mars/Settings.java b/src/main/java/mars/Settings.java index 13d6c42..0b72fdb 100644 --- a/src/main/java/mars/Settings.java +++ b/src/main/java/mars/Settings.java @@ -53,10 +53,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * @author Pete Sanderson **/ -public class Settings extends Observable { +public class Settings extends Observable +{ /** - * Flag to determine whether a program can write binary code to the text or data segment and - * execute that code. + * Flag to determine whether a program can write binary code to the text or data segment and execute that code. */ public static final int SELF_MODIFYING_CODE_ENABLED = 20; ///////////////////////////// PROPERTY ARRAY INDEXES ///////////////////////////// @@ -65,73 +65,82 @@ public class Settings extends Observable { // BOOLEAN SETTINGS... /** - * Flag to determine whether or not program being assembled is limited to - * basic MIPS instructions and formats. + * Flag to determine whether or not program being assembled is limited to basic MIPS instructions and formats. */ public static final int EXTENDED_ASSEMBLER_ENABLED = 0; /** - * Flag to determine whether or not program being assembled is limited to - * using register numbers instead of names. NOTE: Its default value is - * false and the IDE provides no means to change it! + * Flag to determine whether or not program being assembled is limited to using register numbers instead of names. + * NOTE: Its default value is false and the IDE provides no means to change it! */ public static final int BARE_MACHINE_ENABLED = 1; + /** - * Flag to determine whether or not a file is immediately and automatically assembled - * upon opening. Handy when using externa editor like mipster. + * Flag to determine whether or not a file is immediately and automatically assembled upon opening. Handy when using + * externa editor like mipster. */ public static final int ASSEMBLE_ON_OPEN_ENABLED = 2; + /** - * Flag to determine whether only the current editor source file (enabled false) or - * all files in its directory (enabled true) will be assembled when assembly is selected. + * Flag to determine whether only the current editor source file (enabled false) or all files in its directory + * (enabled true) will be assembled when assembly is selected. */ public static final int ASSEMBLE_ALL_ENABLED = 3; + /** - * Default visibilty of label window (symbol table). Default only, dynamic status - * maintained by ExecutePane + * Default visibilty of label window (symbol table). Default only, dynamic status maintained by ExecutePane */ public static final int LABEL_WINDOW_VISIBILITY = 4; + /** - * Default setting for displaying addresses and values in hexidecimal in the Execute - * pane. + * Default setting for displaying addresses and values in hexidecimal in the Execute pane. */ public static final int DISPLAY_ADDRESSES_IN_HEX = 5; + public static final int DISPLAY_VALUES_IN_HEX = 6; + /** - * Flag to determine whether the currently selected exception handler source file will - * be included in each assembly operation. + * Flag to determine whether the currently selected exception handler source file will be included in each assembly + * operation. */ public static final int EXCEPTION_HANDLER_ENABLED = 7; + /** - * Flag to determine whether or not delayed branching is in effect at MIPS execution. - * This means we simulate the pipeline and statement FOLLOWING a successful branch - * is executed before branch is taken. DPS 14 June 2007. + * Flag to determine whether or not delayed branching is in effect at MIPS execution. This means we simulate the + * pipeline and statement FOLLOWING a successful branch is executed before branch is taken. DPS 14 June 2007. */ public static final int DELAYED_BRANCHING_ENABLED = 8; + /** * Flag to determine whether or not the editor will display line numbers. */ public static final int EDITOR_LINE_NUMBERS_DISPLAYED = 9; + /** * Flag to determine whether or not assembler warnings are considered errors. */ public static final int WARNINGS_ARE_ERRORS = 10; + /** * Flag to determine whether or not to display and use program arguments */ public static final int PROGRAM_ARGUMENTS = 11; + /** * Flag to control whether or not highlighting is applied to data segment window */ public static final int DATA_SEGMENT_HIGHLIGHTING = 12; + /** * Flag to control whether or not highlighting is applied to register windows */ public static final int REGISTERS_HIGHLIGHTING = 13; + /** * Flag to control whether or not assembler automatically initializes program counter to 'main's address */ public static final int START_AT_MAIN = 14; + /** * Flag to control whether or not editor will highlight the line currently being edited */ @@ -156,52 +165,64 @@ public class Settings extends Observable { * Flag to control whether or not language-aware editor will use auto-indent feature */ public static final int AUTO_INDENT = 19; + /** * Current specified exception handler file (a MIPS assembly source file) */ public static final int EXCEPTION_HANDLER = 0; + /** * Order of text segment table columns */ public static final int TEXT_COLUMN_ORDER = 1; + /** * State for sorting label window display */ public static final int LABEL_SORT_STATE = 2; // STRING SETTINGS. Each array position has associated name. + /** * Identifier of current memory configuration */ public static final int MEMORY_CONFIGURATION = 3; + /** * Caret blink rate in milliseconds, 0 means don't blink. */ public static final int CARET_BLINK_RATE = 4; + /** * Editor tab size in characters. */ public static final int EDITOR_TAB_SIZE = 5; + /** * Number of letters to be matched by editor's instruction guide before popup generated (if popup enabled) */ public static final int EDITOR_POPUP_PREFIX_LENGTH = 6; + /** * Font for the text editor */ public static final int EDITOR_FONT = 0; + /** * Font for table even row background (text, data, register displays) */ public static final int EVEN_ROW_FONT = 1; + /** * Font for table odd row background (text, data, register displays) */ public static final int ODD_ROW_FONT = 2; + /** * Font for table odd row foreground (text, data, register displays) */ public static final int TEXTSEGMENT_HIGHLIGHT_FONT = 3; + /** * Font for text segment delay slot highlighted background */ @@ -209,54 +230,67 @@ public class Settings extends Observable { // FONT SETTINGS. Each array position has associated name. + /** * Font for text segment highlighted background */ public static final int DATASEGMENT_HIGHLIGHT_FONT = 5; + /** * Font for register highlighted background */ public static final int REGISTER_HIGHLIGHT_FONT = 6; + /** * RGB color for table even row background (text, data, register displays) */ public static final int EVEN_ROW_BACKGROUND = 0; + /** * RGB color for table even row foreground (text, data, register displays) */ public static final int EVEN_ROW_FOREGROUND = 1; + /** * RGB color for table odd row background (text, data, register displays) */ public static final int ODD_ROW_BACKGROUND = 2; + /** * RGB color for table odd row foreground (text, data, register displays) */ public static final int ODD_ROW_FOREGROUND = 3; + /** * RGB color for text segment highlighted background */ public static final int TEXTSEGMENT_HIGHLIGHT_BACKGROUND = 4; + /** * RGB color for text segment highlighted foreground */ public static final int TEXTSEGMENT_HIGHLIGHT_FOREGROUND = 5; + /** * RGB color for text segment delay slot highlighted background */ public static final int TEXTSEGMENT_DELAYSLOT_HIGHLIGHT_BACKGROUND = 6; + /** * RGB color for text segment delay slot highlighted foreground */ public static final int TEXTSEGMENT_DELAYSLOT_HIGHLIGHT_FOREGROUND = 7; + /** * RGB color for text segment highlighted background */ public static final int DATASEGMENT_HIGHLIGHT_BACKGROUND = 8; + /** * RGB color for text segment highlighted foreground */ public static final int DATASEGMENT_HIGHLIGHT_FOREGROUND = 9; + /** * RGB color for register highlighted background */ @@ -264,29 +298,37 @@ public class Settings extends Observable { // COLOR SETTINGS. Each array position has associated name. + /** * RGB color for register highlighted foreground */ public static final int REGISTER_HIGHLIGHT_FOREGROUND = 11; + /* Properties file used to hold default settings. */ private static final String settingsFile = "Settings"; + // NOTE: key sequence must match up with labels above which are used for array indexes! private static final String[] booleanSettingsKeys = {"ExtendedAssembler", "BareMachine", "AssembleOnOpen", "AssembleAll", "LabelWindowVisibility", "DisplayAddressesInHex", "DisplayValuesInHex", "LoadExceptionHandler", "DelayedBranching", "EditorLineNumbersDisplayed", "WarningsAreErrors", "ProgramArguments", "DataSegmentHighlighting", "RegistersHighlighting", "StartAtMain", "EditorCurrentLineHighlighting", "PopupInstructionGuidance", "PopupSyscallInput", "GenericTextEditor", "AutoIndent", "SelfModifyingCode"}; + // Match the above by position. private static final String[] stringSettingsKeys = {"ExceptionHandler", "TextColumnOrder", "LabelSortState", "MemoryConfiguration", "CaretBlinkRate", "EditorTabSize", "EditorPopupPrefixLength"}; + /** * Last resort default values for String settings; will use only if neither the Preferences nor the properties file * work. If you wish to change, do so before instantiating the Settings object. Must match key by list position. */ private static final String[] defaultStringSettingsValues = {"", "0 1 2 3 4", "0", "", "500", "8", "2"}; + private static final String[] fontFamilySettingsKeys = {"EditorFontFamily", "EvenRowFontFamily", "OddRowFontFamily", " TextSegmentHighlightFontFamily", "TextSegmentDelayslotHighightFontFamily", "DataSegmentHighlightFontFamily", "RegisterHighlightFontFamily"}; + private static final String[] fontStyleSettingsKeys = {"EditorFontStyle", "EvenRowFontStyle", "OddRowFontStyle", " TextSegmentHighlightFontStyle", "TextSegmentDelayslotHighightFontStyle", "DataSegmentHighlightFontStyle", "RegisterHighlightFontStyle"}; + private static final String[] fontSizeSettingsKeys = {"EditorFontSize", "EvenRowFontSize", "OddRowFontSize", " TextSegmentHighlightFontSize", "TextSegmentDelayslotHighightFontSize", "DataSegmentHighlightFontSize", "RegisterHighlightFontSize"}; + /** - * Last resort default values for Font settings; - * will use only if neither the Preferences nor the properties file work. - * If you wish to change, do so before instantiating the Settings object. - * Must match key by list position shown above. + * Last resort default values for Font settings; will use only if neither the Preferences nor the properties file + * work. If you wish to change, do so before instantiating the Settings object. Must match key by list position + * shown above. */ // DPS 3-Oct-2012 @@ -294,36 +336,56 @@ public class Settings extends Observable { // correctly rendering the left parenthesis character in the editor or text segment display. // See http://www.mirthcorp.com/community/issues/browse/MIRTH-1921?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel private static final String[] defaultFontFamilySettingsValues = {"Monospaced", "Monospaced", "Monospaced", "Monospaced", "Monospaced", "Monospaced", "Monospaced"}; + private static final String[] defaultFontStyleSettingsValues = {"Plain", "Plain", "Plain", "Plain", "Plain", "Plain", "Plain"}; + private static final String[] defaultFontSizeSettingsValues = {"12", "12", "12", "12", "12", "12", "12",}; + // Match the above by position. private static final String[] colorSettingsKeys = {"EvenRowBackground", "EvenRowForeground", "OddRowBackground", "OddRowForeground", "TextSegmentHighlightBackground", "TextSegmentHighlightForeground", "TextSegmentDelaySlotHighlightBackground", "TextSegmentDelaySlotHighlightForeground", "DataSegmentHighlightBackground", "DataSegmentHighlightForeground", "RegisterHighlightBackground", "RegisterHighlightForeground"}; + /** * Last resort default values for color settings; will use only if neither the Preferences nor the properties file * work. If you wish to change, do so before instantiating the Settings object. Must match key by list position. */ private static final String[] defaultColorSettingsValues = {"0x00e0e0e0", "0", "0x00ffffff", "0", "0x00ffff99", "0", "0x0033ff00", "0", "0x0099ccff", "0", "0x0099cc55", "0"}; + private static final String SYNTAX_STYLE_COLOR_PREFIX = "SyntaxStyleColor_"; + private static final String SYNTAX_STYLE_BOLD_PREFIX = "SyntaxStyleBold_"; + private static final String SYNTAX_STYLE_ITALIC_PREFIX = "SyntaxStyleItalic_"; + /** * Last resort default values for boolean settings; will use only if neither the Preferences nor the properties * file work. If you wish to change them, do so before instantiating the Settings object. Values are matched to keys * by list position. */ public static boolean[] defaultBooleanSettingsValues = { // match the above list by position - true, false, false, false, false, true, true, false, false, true, false, false, true, true, false, true, true, false, false, true, false}; + true, false, false, false, false, true, true, false, false, true, false, false, true, true, false, true, true, false, false, true, false}; + private static String[] syntaxStyleColorSettingsKeys, syntaxStyleBoldSettingsKeys, syntaxStyleItalicSettingsKeys; + private static String[] defaultSyntaxStyleColorSettingsValues; + private static boolean[] defaultSyntaxStyleBoldSettingsValues; + private static boolean[] defaultSyntaxStyleItalicSettingsValues; + private final boolean[] booleanSettingsValues; + private final String[] stringSettingsValues; + private final String[] fontFamilySettingsValues; + private final String[] fontStyleSettingsValues; + private final String[] fontSizeSettingsValues; + private final String[] colorSettingsValues; + private final Preferences preferences; + /* ************************************************************************** This section contains all code related to syntax highlighting styles settings. A style includes 3 components: color, bold (t/f), italic (t/f) @@ -335,24 +397,29 @@ public class Settings extends Observable { */ private String[] syntaxStyleColorSettingsValues; + private boolean[] syntaxStyleBoldSettingsValues; + private boolean[] syntaxStyleItalicSettingsValues; /** * Create Settings object and set to saved values. If saved values not found, will set based on defaults stored in * Settings.properties file. If file problems, will set based on defaults stored in this class. */ - public Settings() { + public Settings() + { this(true); } + /** * Create Settings object and set to saved values. If saved values not found, will set based on defaults stored in * Settings.properties file. If file problems, will set based on defaults stored in this class. * * @param gui true if running the graphical IDE, false if running from command line. Ignored as of release 3.6 - * but retained for compatability. + * but retained for compatability. */ - public Settings(boolean gui) { + public Settings(boolean gui) + { booleanSettingsValues = new boolean[booleanSettingsKeys.length]; stringSettingsValues = new String[stringSettingsKeys.length]; fontFamilySettingsValues = new String[fontFamilySettingsKeys.length]; @@ -374,50 +441,60 @@ public class Settings extends Observable { } /** - * Return whether backstepping is permitted at this time. Backstepping is ability to undo execution - * steps one at a time. Available only in the IDE. This is not a persistent setting and is not under - * MARS user control. + * Return whether backstepping is permitted at this time. Backstepping is ability to undo execution steps one at a + * time. Available only in the IDE. This is not a persistent setting and is not under MARS user control. * * @return true if backstepping is permitted, false otherwise. */ - public boolean getBackSteppingEnabled() { + public boolean getBackSteppingEnabled() + { return (Globals.program != null && Globals.program.getBackStepper() != null && Globals.program.getBackStepper().enabled()); } /** * Reset settings to default values, as described in the constructor comments. * - * @param gui true if running from GUI IDE and false if running from command mode. - * Ignored as of release 3.6 but retained for compatibility. + * @param gui true if running from GUI IDE and false if running from command mode. Ignored as of release 3.6 but + * retained for compatibility. */ - public void reset(boolean gui) { + public void reset(boolean gui) + { initialize(); } - public void setEditorSyntaxStyleByPosition(int index, SyntaxStyle syntaxStyle) { + public void setEditorSyntaxStyleByPosition(int index, SyntaxStyle syntaxStyle) + { syntaxStyleColorSettingsValues[index] = syntaxStyle.getColorAsHexString(); syntaxStyleItalicSettingsValues[index] = syntaxStyle.isItalic(); syntaxStyleBoldSettingsValues[index] = syntaxStyle.isBold(); saveEditorSyntaxStyle(index); } - public SyntaxStyle getEditorSyntaxStyleByPosition(int index) { + public SyntaxStyle getEditorSyntaxStyleByPosition(int index) + { return new SyntaxStyle(getColorValueByPosition(index, syntaxStyleColorSettingsValues), syntaxStyleItalicSettingsValues[index], syntaxStyleBoldSettingsValues[index]); } - public SyntaxStyle getDefaultEditorSyntaxStyleByPosition(int index) { + public SyntaxStyle getDefaultEditorSyntaxStyleByPosition(int index) + { return new SyntaxStyle(getColorValueByPosition(index, defaultSyntaxStyleColorSettingsValues), defaultSyntaxStyleItalicSettingsValues[index], defaultSyntaxStyleBoldSettingsValues[index]); } - private void saveEditorSyntaxStyle(int index) { - try { + private void saveEditorSyntaxStyle(int index) + { + try + { preferences.put(syntaxStyleColorSettingsKeys[index], syntaxStyleColorSettingsValues[index]); preferences.putBoolean(syntaxStyleBoldSettingsKeys[index], syntaxStyleBoldSettingsValues[index]); preferences.putBoolean(syntaxStyleItalicSettingsKeys[index], syntaxStyleItalicSettingsValues[index]); preferences.flush(); - } catch (SecurityException se) { + } + catch (SecurityException se) + { // cannot write to persistent storage for security reasons - } catch (BackingStoreException bse) { + } + catch (BackingStoreException bse) + { // unable to communicate with persistent storage (strange days) } } @@ -430,7 +507,8 @@ public class Settings extends Observable { // On othe other hand, the first statement of this method causes Color objects // to be created! It is possible but a real pain in the rear to avoid using // Color objects totally. Requires new methods for the SyntaxUtilities class. - private void initializeEditorSyntaxStyles() { + private void initializeEditorSyntaxStyles() + { SyntaxStyle[] syntaxStyle = SyntaxUtilities.getDefaultSyntaxStyles(); int tokens = syntaxStyle.length; syntaxStyleColorSettingsKeys = new String[tokens]; @@ -442,7 +520,8 @@ public class Settings extends Observable { syntaxStyleColorSettingsValues = new String[tokens]; syntaxStyleBoldSettingsValues = new boolean[tokens]; syntaxStyleItalicSettingsValues = new boolean[tokens]; - for (int i = 0; i < tokens; i++) { + for (int i = 0; i < tokens; i++) + { syntaxStyleColorSettingsKeys[i] = SYNTAX_STYLE_COLOR_PREFIX + i; syntaxStyleBoldSettingsKeys[i] = SYNTAX_STYLE_BOLD_PREFIX + i; syntaxStyleItalicSettingsKeys[i] = SYNTAX_STYLE_ITALIC_PREFIX + i; @@ -452,8 +531,10 @@ public class Settings extends Observable { } } - private void getEditorSyntaxStyleSettingsFromPreferences() { - for (int i = 0; i < syntaxStyleColorSettingsKeys.length; i++) { + private void getEditorSyntaxStyleSettingsFromPreferences() + { + for (int i = 0; i < syntaxStyleColorSettingsKeys.length; i++) + { syntaxStyleColorSettingsValues[i] = preferences.get(syntaxStyleColorSettingsKeys[i], syntaxStyleColorSettingsValues[i]); syntaxStyleBoldSettingsValues[i] = preferences.getBoolean(syntaxStyleBoldSettingsKeys[i], syntaxStyleBoldSettingsValues[i]); syntaxStyleItalicSettingsValues[i] = preferences.getBoolean(syntaxStyleItalicSettingsKeys[i], syntaxStyleItalicSettingsValues[i]); @@ -474,10 +555,14 @@ public class Settings extends Observable { * @return corresponding boolean setting. * @throws IllegalArgumentException if identifier is invalid. */ - public boolean getBooleanSetting(int id) { - if (id >= 0 && id < booleanSettingsValues.length) { + public boolean getBooleanSetting(int id) + { + if (id >= 0 && id < booleanSettingsValues.length) + { return booleanSettingsValues[id]; - } else { + } + else + { throw new IllegalArgumentException("Invalid boolean setting ID"); } } @@ -488,10 +573,11 @@ public class Settings extends Observable { * * @return true if only bare machine instructions allowed, false otherwise. * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. - * Settings.BARE_MACHINE_ENABLED) + * Settings.BARE_MACHINE_ENABLED) */ @Deprecated - public boolean getBareMachineEnabled() { + public boolean getBareMachineEnabled() + { return booleanSettingsValues[BARE_MACHINE_ENABLED]; } @@ -501,24 +587,25 @@ public class Settings extends Observable { * * @return true if pseudo-instructions and formats permitted, false otherwise. * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. - * Settings.EXTENDED_ASSEMBLER_ENABLED) + * Settings.EXTENDED_ASSEMBLER_ENABLED) */ @Deprecated - public boolean getExtendedAssemblerEnabled() { + public boolean getExtendedAssemblerEnabled() + { return booleanSettingsValues[EXTENDED_ASSEMBLER_ENABLED]; } /** - * Establish setting for whether or not pseudo-instructions and formats are permitted - * in user programs. User can change this setting via the IDE. If setting changes, - * new setting will be written to properties file. + * Establish setting for whether or not pseudo-instructions and formats are permitted in user programs. User can + * change this setting via the IDE. If setting changes, new setting will be written to properties file. * * @param value True to permit, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.EXTENDED_ASSEMBLER_ENABLED) + * (e.g. Settings.EXTENDED_ASSEMBLER_ENABLED) */ @Deprecated - public void setExtendedAssemblerEnabled(boolean value) { + public void setExtendedAssemblerEnabled(boolean value) + { internalSetBooleanSetting(EXTENDED_ASSEMBLER_ENABLED, value); } @@ -528,24 +615,26 @@ public class Settings extends Observable { * * @return true if file is to be automatically assembled upon opening and false otherwise. * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. - * Settings.ASSEMBLE_ON_OPEN_ENABLED) + * Settings.ASSEMBLE_ON_OPEN_ENABLED) */ @Deprecated - public boolean getAssembleOnOpenEnabled() { + public boolean getAssembleOnOpenEnabled() + { return booleanSettingsValues[ASSEMBLE_ON_OPEN_ENABLED]; } /** - * Establish setting for whether a file will be automatically assembled as soon as it - * is opened. This is handy for those using an external text editor such as Mipster. - * If setting changes, new setting will be written to properties file. + * Establish setting for whether a file will be automatically assembled as soon as it is opened. This is handy for + * those using an external text editor such as Mipster. If setting changes, new setting will be written to + * properties file. * * @param value True to automatically assemble, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.ASSEMBLE_ON_OPEN_ENABLED) + * (e.g. Settings.ASSEMBLE_ON_OPEN_ENABLED) */ @Deprecated - public void setAssembleOnOpenEnabled(boolean value) { + public void setAssembleOnOpenEnabled(boolean value) + { internalSetBooleanSetting(ASSEMBLE_ON_OPEN_ENABLED, value); } @@ -554,23 +643,24 @@ public class Settings extends Observable { * * @return true if addresses are displayed in hexadecimal and false otherwise (decimal). * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. - * Settings.DISPLAY_ADDRESSES_IN_HEX) + * Settings.DISPLAY_ADDRESSES_IN_HEX) */ @Deprecated - public boolean getDisplayAddressesInHex() { + public boolean getDisplayAddressesInHex() + { return booleanSettingsValues[DISPLAY_ADDRESSES_IN_HEX]; } /** - * Establish setting for whether addresses in the Execute pane will be displayed - * in hexadecimal format. + * Establish setting for whether addresses in the Execute pane will be displayed in hexadecimal format. * * @param value True to display addresses in hexadecimal, false for decimal. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.DISPLAY_ADDRESSES_IN_HEX) + * (e.g. Settings.DISPLAY_ADDRESSES_IN_HEX) */ @Deprecated - public void setDisplayAddressesInHex(boolean value) { + public void setDisplayAddressesInHex(boolean value) + { internalSetBooleanSetting(DISPLAY_ADDRESSES_IN_HEX, value); } @@ -579,114 +669,114 @@ public class Settings extends Observable { * * @return true if values are displayed in hexadecimal and false otherwise (decimal). * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. - * Settings.DISPLAY_VALUES_IN_HEX) + * Settings.DISPLAY_VALUES_IN_HEX) */ @Deprecated - public boolean getDisplayValuesInHex() { + public boolean getDisplayValuesInHex() + { return booleanSettingsValues[DISPLAY_VALUES_IN_HEX]; } /** - * Establish setting for whether values in the Execute pane will be displayed - * in hexadecimal format. + * Establish setting for whether values in the Execute pane will be displayed in hexadecimal format. * * @param value True to display values in hexadecimal, false for decimal. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.DISPLAY_VALUES_IN_HEX) + * (e.g. Settings.DISPLAY_VALUES_IN_HEX) */ @Deprecated - public void setDisplayValuesInHex(boolean value) { + public void setDisplayValuesInHex(boolean value) + { internalSetBooleanSetting(DISPLAY_VALUES_IN_HEX, value); } /** - * Setting for whether the assemble operation applies only to the file currently open in - * the editor or whether it applies to all files in that file's directory (primitive project - * capability). If the "assemble on open" setting is set, this "assemble all" setting will - * be applied as soon as the file is opened. + * Setting for whether the assemble operation applies only to the file currently open in the editor or whether it + * applies to all files in that file's directory (primitive project capability). If the "assemble on open" setting + * is set, this "assemble all" setting will be applied as soon as the file is opened. * * @return true if all files are to be assembled, false if only the file open in editor. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.ASSEMBLE_ALL_ENABLED) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.ASSEMBLE_ALL_ENABLED) */ @Deprecated - public boolean getAssembleAllEnabled() { + public boolean getAssembleAllEnabled() + { return booleanSettingsValues[ASSEMBLE_ALL_ENABLED]; } /** - * Establish setting for whether a file will be assembled by itself (false) or along - * with all other files in its directory (true). This permits multi-file programs - * and a primitive "project" capability. If setting changes, + * Establish setting for whether a file will be assembled by itself (false) or along with all other files in its + * directory (true). This permits multi-file programs and a primitive "project" capability. If setting changes, * new setting will be written to properties file. * * @param value True to assemble all, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.ASSEMBLE_ALL_ENABLED) + * (e.g. Settings.ASSEMBLE_ALL_ENABLED) */ @Deprecated - public void setAssembleAllEnabled(boolean value) { + public void setAssembleAllEnabled(boolean value) + { internalSetBooleanSetting(ASSEMBLE_ALL_ENABLED, value); } /** - * Setting for whether the currently selected exception handler - * (a MIPS source file) will be automatically included in each - * assemble operation. + * Setting for whether the currently selected exception handler (a MIPS source file) will be automatically included + * in each assemble operation. * * @return true if exception handler is to be included in assemble, false otherwise. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.EXCEPTION_HANDLER_ENABLED) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.EXCEPTION_HANDLER_ENABLED) */ @Deprecated - public boolean getExceptionHandlerEnabled() { + public boolean getExceptionHandlerEnabled() + { return booleanSettingsValues[EXCEPTION_HANDLER_ENABLED]; } /** - * Establish setting for whether the currently selected exception handler - * (a MIPS source file) will be automatically included in each - * assemble operation. If setting changes, new setting will be written - * to properties file. + * Establish setting for whether the currently selected exception handler (a MIPS source file) will be automatically + * included in each assemble operation. If setting changes, new setting will be written to properties file. * * @param value True to assemble exception handler, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.EXCEPTION_HANDLER_ENABLED) + * (e.g. Settings.EXCEPTION_HANDLER_ENABLED) */ @Deprecated - public void setExceptionHandlerEnabled(boolean value) { + public void setExceptionHandlerEnabled(boolean value) + { internalSetBooleanSetting(EXCEPTION_HANDLER_ENABLED, value); } /** - * Setting for whether delayed branching will be applied during MIPS - * program execution. If enabled, the statement following a successful - * branch will be executed and then the branch is taken! This simulates - * pipelining and all MIPS processors do it. However it is confusing to - * assembly language students so is disabled by default. SPIM does same thing. + * Setting for whether delayed branching will be applied during MIPS program execution. If enabled, the statement + * following a successful branch will be executed and then the branch is taken! This simulates pipelining and all + * MIPS processors do it. However it is confusing to assembly language students so is disabled by default. SPIM + * does same thing. * * @return true if delayed branching is enabled, false otherwise. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.DELAYED_BRANCHING_ENABLED) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.DELAYED_BRANCHING_ENABLED) */ @Deprecated - public boolean getDelayedBranchingEnabled() { + public boolean getDelayedBranchingEnabled() + { return booleanSettingsValues[DELAYED_BRANCHING_ENABLED]; } /** - * Establish setting for whether delayed branching will be applied during - * MIPS program execution. If enabled, the statement following a successful - * branch will be executed and then the branch is taken! This simulates - * pipelining and all MIPS processors do it. However it is confusing to - * assembly language students so is disabled by default. SPIM does same thing. + * Establish setting for whether delayed branching will be applied during MIPS program execution. If enabled, the + * statement following a successful branch will be executed and then the branch is taken! This simulates pipelining + * and all MIPS processors do it. However it is confusing to assembly language students so is disabled by default. + * SPIM does same thing. * * @param value True to enable delayed branching, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.DELAYED_BRANCHING_ENABLED) + * (e.g. Settings.DELAYED_BRANCHING_ENABLED) */ @Deprecated - public void setDelayedBranchingEnabled(boolean value) { + public void setDelayedBranchingEnabled(boolean value) + { internalSetBooleanSetting(DELAYED_BRANCHING_ENABLED, value); } @@ -694,25 +784,26 @@ public class Settings extends Observable { * Setting concerning whether or not to display the Labels Window -- symbol table. * * @return true if label window is to be displayed, false otherwise. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.LABEL_WINDOW_VISIBILITY) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.LABEL_WINDOW_VISIBILITY) */ @Deprecated - public boolean getLabelWindowVisibility() { + public boolean getLabelWindowVisibility() + { return booleanSettingsValues[LABEL_WINDOW_VISIBILITY]; } /** - * Establish setting for whether the labels window (i.e. symbol table) will - * be displayed as part of the Text Segment display. If setting changes, - * new setting will be written to properties file. + * Establish setting for whether the labels window (i.e. symbol table) will be displayed as part of the Text Segment + * display. If setting changes, new setting will be written to properties file. * * @param value True to dispay labels window, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.LABEL_WINDOW_VISIBILITY) + * (e.g. Settings.LABEL_WINDOW_VISIBILITY) */ @Deprecated - public void setLabelWindowVisibility(boolean value) { + public void setLabelWindowVisibility(boolean value) + { internalSetBooleanSetting(LABEL_WINDOW_VISIBILITY, value); } @@ -720,24 +811,25 @@ public class Settings extends Observable { * Setting concerning whether or not the editor will display line numbers. * * @return true if line numbers are to be displayed, false otherwise. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.EDITOR_LINE_NUMBERS_DISPLAYED) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.EDITOR_LINE_NUMBERS_DISPLAYED) */ @Deprecated - public boolean getEditorLineNumbersDisplayed() { + public boolean getEditorLineNumbersDisplayed() + { return booleanSettingsValues[EDITOR_LINE_NUMBERS_DISPLAYED]; } /** - * Establish setting for whether line numbers will be displayed by the - * text editor. + * Establish setting for whether line numbers will be displayed by the text editor. * * @param value True to display line numbers, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.EDITOR_LINE_NUMBERS_DISPLAYED) + * (e.g. Settings.EDITOR_LINE_NUMBERS_DISPLAYED) */ @Deprecated - public void setEditorLineNumbersDisplayed(boolean value) { + public void setEditorLineNumbersDisplayed(boolean value) + { internalSetBooleanSetting(EDITOR_LINE_NUMBERS_DISPLAYED, value); } @@ -745,11 +837,12 @@ public class Settings extends Observable { * Setting concerning whether or not assembler will consider warnings to be errors. * * @return true if warnings are considered errors, false otherwise. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.WARNINGS_ARE_ERRORS) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.WARNINGS_ARE_ERRORS) */ @Deprecated - public boolean getWarningsAreErrors() { + public boolean getWarningsAreErrors() + { return booleanSettingsValues[WARNINGS_ARE_ERRORS]; } @@ -758,10 +851,11 @@ public class Settings extends Observable { * * @param value True to consider warnings to be errors, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.WARNINGS_ARE_ERRORS) + * (e.g. Settings.WARNINGS_ARE_ERRORS) */ @Deprecated - public void setWarningsAreErrors(boolean value) { + public void setWarningsAreErrors(boolean value) + { internalSetBooleanSetting(WARNINGS_ARE_ERRORS, value); } @@ -769,11 +863,12 @@ public class Settings extends Observable { * Setting concerning whether or not program arguments can be entered and used. * * @return true if program arguments can be entered/used, false otherwise. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.PROGRAM_ARGUMENTS) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.PROGRAM_ARGUMENTS) */ @Deprecated - public boolean getProgramArguments() { + public boolean getProgramArguments() + { return booleanSettingsValues[PROGRAM_ARGUMENTS]; } @@ -782,10 +877,11 @@ public class Settings extends Observable { * * @param value True if program arguments can be entered/used, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.PROGRAM_ARGUMENTS) + * (e.g. Settings.PROGRAM_ARGUMENTS) */ @Deprecated - public void setProgramArguments(boolean value) { + public void setProgramArguments(boolean value) + { internalSetBooleanSetting(PROGRAM_ARGUMENTS, value); } @@ -793,76 +889,79 @@ public class Settings extends Observable { * Setting concerning whether or not highlighting is applied to Data Segment window. * * @return true if highlighting is to be applied, false otherwise. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.DATA_SEGMENT_HIGHLIGHTING) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.DATA_SEGMENT_HIGHLIGHTING) */ @Deprecated - public boolean getDataSegmentHighlighting() { + public boolean getDataSegmentHighlighting() + { return booleanSettingsValues[DATA_SEGMENT_HIGHLIGHTING]; } /** - * Establish setting for whether highlighting is to be applied to - * Data Segment window. + * Establish setting for whether highlighting is to be applied to Data Segment window. * * @param value True if highlighting is to be applied, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.DATA_SEGMENT_HIGHLIGHTING) + * (e.g. Settings.DATA_SEGMENT_HIGHLIGHTING) */ @Deprecated - public void setDataSegmentHighlighting(boolean value) { + public void setDataSegmentHighlighting(boolean value) + { internalSetBooleanSetting(DATA_SEGMENT_HIGHLIGHTING, value); } /** - * Setting concerning whether or not highlighting is applied to Registers, - * Coprocessor0, and Coprocessor1 windows. + * Setting concerning whether or not highlighting is applied to Registers, Coprocessor0, and Coprocessor1 windows. * * @return true if highlighting is to be applied, false otherwise. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.REGISTERS_HIGHLIGHTING) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.REGISTERS_HIGHLIGHTING) */ @Deprecated - public boolean getRegistersHighlighting() { + public boolean getRegistersHighlighting() + { return booleanSettingsValues[REGISTERS_HIGHLIGHTING]; } /** - * Establish setting for whether highlighting is to be applied to - * Registers, Coprocessor0 and Coprocessor1 windows. + * Establish setting for whether highlighting is to be applied to Registers, Coprocessor0 and Coprocessor1 windows. * * @param value True if highlighting is to be applied, false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.REGISTERS_HIGHLIGHTING) + * (e.g. Settings.REGISTERS_HIGHLIGHTING) */ @Deprecated - public void setRegistersHighlighting(boolean value) { + public void setRegistersHighlighting(boolean value) + { internalSetBooleanSetting(REGISTERS_HIGHLIGHTING, value); } /** - * Setting concerning whether or not assembler will automatically initialize - * the program counter to address of statement labeled 'main' if defined. + * Setting concerning whether or not assembler will automatically initialize the program counter to address of + * statement labeled 'main' if defined. * * @return true if it initializes to 'main', false otherwise. - * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID - * (e.g. Settings.START_AT_MAIN) + * @deprecated Use getBooleanSetting(int id) with the appropriate boolean setting ID (e.g. + * Settings.START_AT_MAIN) */ @Deprecated - public boolean getStartAtMain() { + public boolean getStartAtMain() + { return booleanSettingsValues[START_AT_MAIN]; } /** - * Establish setting for whether assembler will automatically initialize - * program counter to address of statement labeled 'main' if defined. + * Establish setting for whether assembler will automatically initialize program counter to address of statement + * labeled 'main' if defined. * * @param value True if PC set to address of 'main', false otherwise. * @deprecated Use setBooleanSetting(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.START_AT_MAIN) + * (e.g. Settings.START_AT_MAIN) */ @Deprecated - public void setStartAtMain(boolean value) { + public void setStartAtMain(boolean value) + { internalSetBooleanSetting(START_AT_MAIN, value); } @@ -871,7 +970,8 @@ public class Settings extends Observable { * * @return String pathname of current exception handler file, empty if none. */ - public String getExceptionHandler() { + public String getExceptionHandler() + { return stringSettingsValues[EXCEPTION_HANDLER]; } @@ -885,7 +985,8 @@ public class Settings extends Observable { * * @param newFilename name of exception handler file */ - public void setExceptionHandler(String newFilename) { + public void setExceptionHandler(String newFilename) + { setStringSetting(EXCEPTION_HANDLER, newFilename); } @@ -894,7 +995,8 @@ public class Settings extends Observable { * * @return String identifier of current built-in memory configuration, empty if none. */ - public String getMemoryConfiguration() { + public String getMemoryConfiguration() + { return stringSettingsValues[MEMORY_CONFIGURATION]; } @@ -904,28 +1006,29 @@ public class Settings extends Observable { * @param config A string that identifies the current built-in memory configuration */ - public void setMemoryConfiguration(String config) { + public void setMemoryConfiguration(String config) + { setStringSetting(MEMORY_CONFIGURATION, config); } /** - * Current editor font. Retained for compatibility but replaced - * by: getFontByPosition(Settings.EDITOR_FONT) + * Current editor font. Retained for compatibility but replaced by: getFontByPosition(Settings.EDITOR_FONT) * * @return Font object for current editor font. */ - public Font getEditorFont() { + public Font getEditorFont() + { return getFontByPosition(EDITOR_FONT); } /** - * Set editor font to the specified Font object and write it to persistent storage. - * This method retained for compatibility but replaced by: - * setFontByPosition(Settings.EDITOR_FONT, font) + * Set editor font to the specified Font object and write it to persistent storage. This method retained for + * compatibility but replaced by: setFontByPosition(Settings.EDITOR_FONT, font) * * @param font Font object to be used by text editor. */ - public void setEditorFont(Font font) { + public void setEditorFont(Font font) + { setFontByPosition(EDITOR_FONT, font); } @@ -935,10 +1038,14 @@ public class Settings extends Observable { * @param fontSettingPosition constant that identifies which item * @return Font object for given item */ - public Font getFontByPosition(int fontSettingPosition) { - if (fontSettingPosition >= 0 && fontSettingPosition < fontFamilySettingsValues.length) { + public Font getFontByPosition(int fontSettingPosition) + { + if (fontSettingPosition >= 0 && fontSettingPosition < fontFamilySettingsValues.length) + { return EditorFont.createFontFromStringValues(fontFamilySettingsValues[fontSettingPosition], fontStyleSettingsValues[fontSettingPosition], fontSizeSettingsValues[fontSettingPosition]); - } else { + } + else + { return null; } } @@ -949,10 +1056,14 @@ public class Settings extends Observable { * @param fontSettingPosition constant that identifies which item * @return Font object for given item */ - public Font getDefaultFontByPosition(int fontSettingPosition) { - if (fontSettingPosition >= 0 && fontSettingPosition < defaultFontFamilySettingsValues.length) { + public Font getDefaultFontByPosition(int fontSettingPosition) + { + if (fontSettingPosition >= 0 && fontSettingPosition < defaultFontFamilySettingsValues.length) + { return EditorFont.createFontFromStringValues(defaultFontFamilySettingsValues[fontSettingPosition], defaultFontStyleSettingsValues[fontSettingPosition], defaultFontSizeSettingsValues[fontSettingPosition]); - } else { + } + else + { return null; } } @@ -962,37 +1073,42 @@ public class Settings extends Observable { * * @return Array of int indicating the order. Original order is 0 1 2 3 4. */ - public int[] getTextColumnOrder() { + public int[] getTextColumnOrder() + { return getTextSegmentColumnOrder(stringSettingsValues[TEXT_COLUMN_ORDER]); } /** - * Store the current order of Text Segment window table columns, so the ordering - * can be preserved and restored. + * Store the current order of Text Segment window table columns, so the ordering can be preserved and restored. * * @param columnOrder An array of int indicating column order. */ - public void setTextColumnOrder(int[] columnOrder) { + public void setTextColumnOrder(int[] columnOrder) + { String stringifiedOrder = ""; - for (int j : columnOrder) { + for (int j : columnOrder) + { stringifiedOrder += j + " "; } setStringSetting(TEXT_COLUMN_ORDER, stringifiedOrder); } /** - * Retrieve the caret blink rate in milliseconds. Blink rate of 0 means - * do not blink. + * Retrieve the caret blink rate in milliseconds. Blink rate of 0 means do not blink. * * @return int blink rate in milliseconds */ - public int getCaretBlinkRate() { + public int getCaretBlinkRate() + { int rate = 500; - try { + try + { rate = Integer.parseInt(stringSettingsValues[CARET_BLINK_RATE]); - } catch (NumberFormatException nfe) { + } + catch (NumberFormatException nfe) + { rate = Integer.parseInt(defaultStringSettingsValues[CARET_BLINK_RATE]); } return rate; @@ -1003,7 +1119,8 @@ public class Settings extends Observable { * * @param rate blink rate in milliseconds */ - public void setCaretBlinkRate(int rate) { + public void setCaretBlinkRate(int rate) + { setStringSetting(CARET_BLINK_RATE, "" + rate); } @@ -1012,11 +1129,15 @@ public class Settings extends Observable { * * @return tab size in characters. */ - public int getEditorTabSize() { + public int getEditorTabSize() + { int size = 8; - try { + try + { size = Integer.parseInt(stringSettingsValues[EDITOR_TAB_SIZE]); - } catch (NumberFormatException nfe) { + } + catch (NumberFormatException nfe) + { size = getDefaultEditorTabSize(); } return size; @@ -1027,22 +1148,27 @@ public class Settings extends Observable { * * @param size tab size in characters. */ - public void setEditorTabSize(int size) { + public void setEditorTabSize(int size) + { setStringSetting(EDITOR_TAB_SIZE, "" + size); } /** * Get number of letters to be matched by editor's instruction guide before popup generated (if popup enabled). - * Should be 1 or 2. If 1, the popup will be generated after first letter typed, based on all matches; if 2, - * the popup will be generated after second letter typed. + * Should be 1 or 2. If 1, the popup will be generated after first letter typed, based on all matches; if 2, the + * popup will be generated after second letter typed. * * @return number of letters (should be 1 or 2). */ - public int getEditorPopupPrefixLength() { + public int getEditorPopupPrefixLength() + { int length = 2; - try { + try + { length = Integer.parseInt(stringSettingsValues[EDITOR_POPUP_PREFIX_LENGTH]); - } catch (NumberFormatException ignored) { + } + catch (NumberFormatException ignored) + { } return length; @@ -1050,12 +1176,13 @@ public class Settings extends Observable { /** * Set number of letters to be matched by editor's instruction guide before popup generated (if popup enabled). - * Should be 1 or 2. If 1, the popup will be generated after first letter typed, based on all matches; if 2, - * the popup will be generated after second letter typed. + * Should be 1 or 2. If 1, the popup will be generated after first letter typed, based on all matches; if 2, the + * popup will be generated after second letter typed. * * @param length number of letters (should be 1 or 2). */ - public void setEditorPopupPrefixLength(int length) { + public void setEditorPopupPrefixLength(int length) + { setStringSetting(EDITOR_POPUP_PREFIX_LENGTH, "" + length); } @@ -1064,18 +1191,19 @@ public class Settings extends Observable { * * @return tab size in characters */ - public int getDefaultEditorTabSize() { + public int getDefaultEditorTabSize() + { return Integer.parseInt(defaultStringSettingsValues[EDITOR_TAB_SIZE]); } /** - * Get the saved state of the Labels Window sorting (can sort by either - * label or address and either ascending or descending order). - * Default state is 0, by ascending addresses. + * Get the saved state of the Labels Window sorting (can sort by either label or address and either ascending or + * descending order). Default state is 0, by ascending addresses. * * @return State value 0-7, as a String. */ - public String getLabelSortState() { + public String getLabelSortState() + { return stringSettingsValues[LABEL_SORT_STATE]; } @@ -1086,97 +1214,109 @@ public class Settings extends Observable { * @param state The current labels window sorting state, as a String. */ - public void setLabelSortState(String state) { + public void setLabelSortState(String state) + { setStringSetting(LABEL_SORT_STATE, state); } /** - * Get Color object for specified settings key. - * Returns null if key is not found or its value is not a valid color encoding. + * Get Color object for specified settings key. Returns null if key is not found or its value is not a valid color + * encoding. * * @param key the Setting key * @return corresponding Color, or null if key not found or value not valid color */ - public Color getColorSettingByKey(String key) { + public Color getColorSettingByKey(String key) + { return getColorValueByKey(key, colorSettingsValues); } /** - * Get default Color value for specified settings key. - * Returns null if key is not found or its value is not a valid color encoding. + * Get default Color value for specified settings key. Returns null if key is not found or its value is not a valid + * color encoding. * * @param key the Setting key * @return corresponding default Color, or null if key not found or value not valid color */ - public Color getDefaultColorSettingByKey(String key) { + public Color getDefaultColorSettingByKey(String key) + { return getColorValueByKey(key, defaultColorSettingsValues); } /** - * Get Color object for specified settings name (a static constant). - * Returns null if argument invalid or its value is not a valid color encoding. + * Get Color object for specified settings name (a static constant). Returns null if argument invalid or its value + * is not a valid color encoding. * * @param position the Setting name (see list of static constants) * @return corresponding Color, or null if argument invalid or value not valid color */ - public Color getColorSettingByPosition(int position) { + public Color getColorSettingByPosition(int position) + { return getColorValueByPosition(position, colorSettingsValues); } /** - * Get default Color object for specified settings name (a static constant). - * Returns null if argument invalid or its value is not a valid color encoding. + * Get default Color object for specified settings name (a static constant). Returns null if argument invalid or its + * value is not a valid color encoding. * * @param position the Setting name (see list of static constants) * @return corresponding default Color, or null if argument invalid or value not valid color */ - public Color getDefaultColorSettingByPosition(int position) { + public Color getDefaultColorSettingByPosition(int position) + { return getColorValueByPosition(position, defaultColorSettingsValues); } /** * Set value of a boolean setting given its id and the value. * - * @param id int containing the setting's identifier (constants listed above) + * @param id int containing the setting's identifier (constants listed above) * @param value boolean value to store * @throws IllegalArgumentException if identifier is not valid. */ - public void setBooleanSetting(int id, boolean value) { - if (id >= 0 && id < booleanSettingsValues.length) { + public void setBooleanSetting(int id, boolean value) + { + if (id >= 0 && id < booleanSettingsValues.length) + { internalSetBooleanSetting(id, value); - } else { + } + else + { throw new IllegalArgumentException("Invalid boolean setting ID"); } } /** - * Temporarily establish boolean setting. This setting will NOT be written to persisent - * store! Currently this is used only when running MARS from the command line + * Temporarily establish boolean setting. This setting will NOT be written to persisent store! Currently this is + * used only when running MARS from the command line * - * @param id setting identifier. These are defined for this class as static final int. + * @param id setting identifier. These are defined for this class as static final int. * @param value True to enable the setting, false otherwise. */ - public void setBooleanSettingNonPersistent(int id, boolean value) { - if (id >= 0 && id < booleanSettingsValues.length) { + public void setBooleanSettingNonPersistent(int id, boolean value) + { + if (id >= 0 && id < booleanSettingsValues.length) + { booleanSettingsValues[id] = value; - } else { + } + else + { throw new IllegalArgumentException("Invalid boolean setting ID"); } } /** - * Establish setting for whether delayed branching will be applied during - * MIPS program execution. This setting will NOT be written to persisent - * store! This method should be called only to temporarily set this - * setting -- currently this is needed only when running MARS from the - * command line. + * Establish setting for whether delayed branching will be applied during MIPS program execution. This setting will + * NOT be written to persisent store! This method should be called only to temporarily set this setting -- + * currently this is needed only when running MARS from the command line. * * @param value True to enabled delayed branching, false otherwise. - * @deprecated Use setBooleanSettingNonPersistent(int id, boolean value) with the appropriate boolean setting ID - * (e.g. Settings.DELAYED_BRANCHING_ENABLED) + * @deprecated Use setBooleanSettingNonPersistent(int id, boolean value) with the appropriate boolean + * setting ID (e.g. Settings.DELAYED_BRANCHING_ENABLED) */ @Deprecated - public void setDelayedBranchingEnabledNonPersistent(boolean value) { + public void setDelayedBranchingEnabledNonPersistent(boolean value) + { // Note: Doing assignment to array results in non-persistent // setting (lost when MARS terminates). For persistent, use // the internalSetBooleanSetting() method instead. @@ -1187,10 +1327,12 @@ public class Settings extends Observable { * Store a Font setting * * @param fontSettingPosition Constant that identifies the item the font goes with - * @param font The font to set that item to + * @param font The font to set that item to */ - public void setFontByPosition(int fontSettingPosition, Font font) { - if (fontSettingPosition >= 0 && fontSettingPosition < fontFamilySettingsValues.length) { + public void setFontByPosition(int fontSettingPosition, Font font) + { + if (fontSettingPosition >= 0 && fontSettingPosition < fontFamilySettingsValues.length) + { fontFamilySettingsValues[fontSettingPosition] = font.getFamily(); fontStyleSettingsValues[fontSettingPosition] = EditorFont.styleIntToStyleString(font.getStyle()); fontSizeSettingsValues[fontSettingPosition] = EditorFont.sizeIntToSizeString(font.getSize()); @@ -1198,7 +1340,8 @@ public class Settings extends Observable { saveFontSetting(fontSettingPosition, fontStyleSettingsKeys, fontStyleSettingsValues); saveFontSetting(fontSettingPosition, fontSizeSettingsKeys, fontSizeSettingsValues); } - if (fontSettingPosition == EDITOR_FONT) { + if (fontSettingPosition == EDITOR_FONT) + { setChanged(); notifyObservers(); } @@ -1207,12 +1350,15 @@ public class Settings extends Observable { /** * Set Color object for specified settings key. Has no effect if key is invalid. * - * @param key the Setting key + * @param key the Setting key * @param color the Color to save */ - public void setColorSettingByKey(String key, Color color) { - for (int i = 0; i < colorSettingsKeys.length; i++) { - if (key.equals(colorSettingsKeys[i])) { + public void setColorSettingByKey(String key, Color color) + { + for (int i = 0; i < colorSettingsKeys.length; i++) + { + if (key.equals(colorSettingsKeys[i])) + { setColorSettingByPosition(i, color); return; } @@ -1223,10 +1369,12 @@ public class Settings extends Observable { * Set Color object for specified settings name (a static constant). Has no effect if invalid. * * @param position the Setting name (see list of static constants) - * @param color the Color to save + * @param color the Color to save */ - public void setColorSettingByPosition(int position, Color color) { - if (position >= 0 && position < colorSettingsKeys.length) { + public void setColorSettingByPosition(int position, Color color) + { + if (position >= 0 && position < colorSettingsKeys.length) + { setColorSetting(position, color); } } @@ -1242,19 +1390,23 @@ public class Settings extends Observable { // If that fails, set from array. // In either case, use these values as defaults in call to Preferences. - private void initialize() { + private void initialize() + { applyDefaultSettings(); - if (!readSettingsFromPropertiesFile(settingsFile)) { + if (!readSettingsFromPropertiesFile(settingsFile)) + { System.out.println("MARS System error: unable to read Settings.properties defaults. Using built-in defaults."); } getSettingsFromPreferences(); } // Default values. Will be replaced if available from property file or Preferences object. - private void applyDefaultSettings() { + private void applyDefaultSettings() + { System.arraycopy(defaultBooleanSettingsValues, 0, booleanSettingsValues, 0, booleanSettingsValues.length); System.arraycopy(defaultStringSettingsValues, 0, stringSettingsValues, 0, stringSettingsValues.length); - for (int i = 0; i < fontFamilySettingsValues.length; i++) { + for (int i = 0; i < fontFamilySettingsValues.length; i++) + { fontFamilySettingsValues[i] = defaultFontFamilySettingsValues[i]; fontStyleSettingsValues[i] = defaultFontStyleSettingsValues[i]; fontSizeSettingsValues[i] = defaultFontSizeSettingsValues[i]; @@ -1264,8 +1416,10 @@ public class Settings extends Observable { } // Used by all the boolean setting "setter" methods. - private void internalSetBooleanSetting(int settingIndex, boolean value) { - if (value != booleanSettingsValues[settingIndex]) { + private void internalSetBooleanSetting(int settingIndex, boolean value) + { + if (value != booleanSettingsValues[settingIndex]) + { booleanSettingsValues[settingIndex] = value; saveBooleanSetting(settingIndex); setChanged(); @@ -1274,23 +1428,28 @@ public class Settings extends Observable { } // Used by setter method(s) for string-based settings (initially, only exception handler name) - private void setStringSetting(int settingIndex, String value) { + private void setStringSetting(int settingIndex, String value) + { stringSettingsValues[settingIndex] = value; saveStringSetting(settingIndex); } // Used by setter methods for color-based settings - private void setColorSetting(int settingIndex, Color color) { + private void setColorSetting(int settingIndex, Color color) + { colorSettingsValues[settingIndex] = Binary.intToHexString(color.getRed() << 16 | color.getGreen() << 8 | color.getBlue()); saveColorSetting(settingIndex); } // Get Color object for this key value. Get it from values array provided as argument (could be either // the current or the default settings array). - private Color getColorValueByKey(String key, String[] values) { + private Color getColorValueByKey(String key, String[] values) + { Color color = null; - for (int i = 0; i < colorSettingsKeys.length; i++) { - if (key.equals(colorSettingsKeys[i])) { + for (int i = 0; i < colorSettingsKeys.length; i++) + { + if (key.equals(colorSettingsKeys[i])) + { return getColorValueByPosition(i, values); } } @@ -1299,12 +1458,17 @@ public class Settings extends Observable { // Get Color object for this key array position. Get it from values array provided as argument (could be either // the current or the default settings array). - private Color getColorValueByPosition(int position, String[] values) { + private Color getColorValueByPosition(int position, String[] values) + { Color color = null; - if (position >= 0 && position < colorSettingsKeys.length) { - try { + if (position >= 0 && position < colorSettingsKeys.length) + { + try + { color = Color.decode(values[position]); - } catch (NumberFormatException ignored) { + } + catch (NumberFormatException ignored) + { } } return color; @@ -1314,10 +1478,13 @@ public class Settings extends Observable { // Maybe someday I'll convert the whole shebang to use Maps. In the meantime, we use // linear search of array. Not a huge deal as settings are little-used. // Returns index or -1 if not found. - private int getIndexOfKey(String key, String[] array) { + private int getIndexOfKey(String key, String[] array) + { int index = -1; - for (int i = 0; i < array.length; i++) { - if (array[i].equals(key)) { + for (int i = 0; i < array.length; i++) + { + if (array[i].equals(key)) + { index = i; break; } @@ -1340,33 +1507,56 @@ public class Settings extends Observable { // In that case, this method will NOT make an assignment to the settings array! // So consider it a precondition of this method: the settings arrays must already be // initialized with last-resort default values. - private boolean readSettingsFromPropertiesFile(String filename) { + private boolean readSettingsFromPropertiesFile(String filename) + { String settingValue; - try { - for (int i = 0; i < booleanSettingsKeys.length; i++) { + try + { + for (int i = 0; i < booleanSettingsKeys.length; i++) + { settingValue = Globals.getPropertyEntry(filename, booleanSettingsKeys[i]); - if (settingValue != null) { + if (settingValue != null) + { booleanSettingsValues[i] = defaultBooleanSettingsValues[i] = Boolean.parseBoolean(settingValue); } } - for (int i = 0; i < stringSettingsKeys.length; i++) { + for (int i = 0; i < stringSettingsKeys.length; i++) + { settingValue = Globals.getPropertyEntry(filename, stringSettingsKeys[i]); - if (settingValue != null) stringSettingsValues[i] = defaultStringSettingsValues[i] = settingValue; + if (settingValue != null) + { + stringSettingsValues[i] = defaultStringSettingsValues[i] = settingValue; + } } - for (int i = 0; i < fontFamilySettingsValues.length; i++) { + for (int i = 0; i < fontFamilySettingsValues.length; i++) + { settingValue = Globals.getPropertyEntry(filename, fontFamilySettingsKeys[i]); if (settingValue != null) + { fontFamilySettingsValues[i] = defaultFontFamilySettingsValues[i] = settingValue; + } settingValue = Globals.getPropertyEntry(filename, fontStyleSettingsKeys[i]); - if (settingValue != null) fontStyleSettingsValues[i] = defaultFontStyleSettingsValues[i] = settingValue; + if (settingValue != null) + { + fontStyleSettingsValues[i] = defaultFontStyleSettingsValues[i] = settingValue; + } settingValue = Globals.getPropertyEntry(filename, fontSizeSettingsKeys[i]); - if (settingValue != null) fontSizeSettingsValues[i] = defaultFontSizeSettingsValues[i] = settingValue; + if (settingValue != null) + { + fontSizeSettingsValues[i] = defaultFontSizeSettingsValues[i] = settingValue; + } } - for (int i = 0; i < colorSettingsKeys.length; i++) { + for (int i = 0; i < colorSettingsKeys.length; i++) + { settingValue = Globals.getPropertyEntry(filename, colorSettingsKeys[i]); - if (settingValue != null) colorSettingsValues[i] = defaultColorSettingsValues[i] = settingValue; + if (settingValue != null) + { + colorSettingsValues[i] = defaultColorSettingsValues[i] = settingValue; + } } - } catch (Exception e) { + } + catch (Exception e) + { return false; } return true; @@ -1379,19 +1569,24 @@ public class Settings extends Observable { // // PRECONDITION: Values arrays have already been initialized to default values from // Settings.properties file or default value arrays above! - private void getSettingsFromPreferences() { - for (int i = 0; i < booleanSettingsKeys.length; i++) { + private void getSettingsFromPreferences() + { + for (int i = 0; i < booleanSettingsKeys.length; i++) + { booleanSettingsValues[i] = preferences.getBoolean(booleanSettingsKeys[i], booleanSettingsValues[i]); } - for (int i = 0; i < stringSettingsKeys.length; i++) { + for (int i = 0; i < stringSettingsKeys.length; i++) + { stringSettingsValues[i] = preferences.get(stringSettingsKeys[i], stringSettingsValues[i]); } - for (int i = 0; i < fontFamilySettingsKeys.length; i++) { + for (int i = 0; i < fontFamilySettingsKeys.length; i++) + { fontFamilySettingsValues[i] = preferences.get(fontFamilySettingsKeys[i], fontFamilySettingsValues[i]); fontStyleSettingsValues[i] = preferences.get(fontStyleSettingsKeys[i], fontStyleSettingsValues[i]); fontSizeSettingsValues[i] = preferences.get(fontSizeSettingsKeys[i], fontSizeSettingsValues[i]); } - for (int i = 0; i < colorSettingsKeys.length; i++) { + for (int i = 0; i < colorSettingsKeys.length; i++) + { colorSettingsValues[i] = preferences.get(colorSettingsKeys[i], colorSettingsValues[i]); } getEditorSyntaxStyleSettingsFromPreferences(); @@ -1399,52 +1594,76 @@ public class Settings extends Observable { // Save the key-value pair in the Properties object and assure it is written to persisent storage. - private void saveBooleanSetting(int index) { - try { + private void saveBooleanSetting(int index) + { + try + { preferences.putBoolean(booleanSettingsKeys[index], booleanSettingsValues[index]); preferences.flush(); - } catch (SecurityException se) { + } + catch (SecurityException se) + { // cannot write to persistent storage for security reasons - } catch (BackingStoreException bse) { + } + catch (BackingStoreException bse) + { // unable to communicate with persistent storage (strange days) } } // Save the key-value pair in the Properties object and assure it is written to persisent storage. - private void saveStringSetting(int index) { - try { + private void saveStringSetting(int index) + { + try + { preferences.put(stringSettingsKeys[index], stringSettingsValues[index]); preferences.flush(); - } catch (SecurityException se) { + } + catch (SecurityException se) + { // cannot write to persistent storage for security reasons - } catch (BackingStoreException bse) { + } + catch (BackingStoreException bse) + { // unable to communicate with persistent storage (strange days) } } // Save the key-value pair in the Properties object and assure it is written to persisent storage. - private void saveFontSetting(int index, String[] settingsKeys, String[] settingsValues) { - try { + private void saveFontSetting(int index, String[] settingsKeys, String[] settingsValues) + { + try + { preferences.put(settingsKeys[index], settingsValues[index]); preferences.flush(); - } catch (SecurityException se) { + } + catch (SecurityException se) + { // cannot write to persistent storage for security reasons - } catch (BackingStoreException bse) { + } + catch (BackingStoreException bse) + { // unable to communicate with persistent storage (strange days) } } // Save the key-value pair in the Properties object and assure it is written to persisent storage. - private void saveColorSetting(int index) { - try { + private void saveColorSetting(int index) + { + try + { preferences.put(colorSettingsKeys[index], colorSettingsValues[index]); preferences.flush(); - } catch (SecurityException se) { + } + catch (SecurityException se) + { // cannot write to persistent storage for security reasons - } catch (BackingStoreException bse) { + } + catch (BackingStoreException bse) + { // unable to communicate with persistent storage (strange days) } } @@ -1456,22 +1675,27 @@ public class Settings extends Observable { * If a problem occurs with the parameter string, will fall back to the * default defined above. */ - private int[] getTextSegmentColumnOrder(String stringOfColumnIndexes) { + private int[] getTextSegmentColumnOrder(String stringOfColumnIndexes) + { StringTokenizer st = new StringTokenizer(stringOfColumnIndexes); int[] list = new int[st.countTokens()]; int index = 0, value; boolean valuesOK = true; - while (st.hasMoreTokens()) { - try { + while (st.hasMoreTokens()) + { + try + { value = Integer.parseInt(st.nextToken()); } // could be either NumberFormatException or NoSuchElementException - catch (Exception e) { + catch (Exception e) + { valuesOK = false; break; } list[index++] = value; } - if (!valuesOK && !stringOfColumnIndexes.equals(defaultStringSettingsValues[TEXT_COLUMN_ORDER])) { + if (!valuesOK && !stringOfColumnIndexes.equals(defaultStringSettingsValues[TEXT_COLUMN_ORDER])) + { return getTextSegmentColumnOrder(defaultStringSettingsValues[TEXT_COLUMN_ORDER]); } return list; diff --git a/src/main/java/mars/assembler/Assembler.java b/src/main/java/mars/assembler/Assembler.java index 51c54ef..c2c5e53 100644 --- a/src/main/java/mars/assembler/Assembler.java +++ b/src/main/java/mars/assembler/Assembler.java @@ -1,22 +1,17 @@ - package mars.assembler; +package mars.assembler; - import java.util.ArrayList; - import java.util.Collections; - import java.util.Comparator; +import mars.*; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Memory; +import mars.mips.instructions.BasicInstruction; +import mars.mips.instructions.ExtendedInstruction; +import mars.mips.instructions.Instruction; +import mars.util.Binary; +import mars.util.SystemIO; - import mars.ErrorList; - import mars.ErrorMessage; - import mars.Globals; - import mars.MIPSprogram; - import mars.ProcessingException; - import mars.ProgramStatement; - import mars.mips.hardware.AddressErrorException; - import mars.mips.hardware.Memory; - import mars.mips.instructions.BasicInstruction; - import mars.mips.instructions.ExtendedInstruction; - import mars.mips.instructions.Instruction; - import mars.util.Binary; - import mars.util.SystemIO; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; /* Copyright (c) 2003-2012, Pete Sanderson and Kenneth Vollmar @@ -47,1469 +42,1697 @@ */ /** - * An Assembler is capable of assembling a MIPS program. It has only one public - * method, assemble(), which implements a two-pass assembler. It - * translates MIPS source code into binary machine code. - * + * An Assembler is capable of assembling a MIPS program. It has only one public method, assemble(), which + * implements a two-pass assembler. It translates MIPS source code into binary machine code. + * * @author Pete Sanderson * @version August 2003 **/ - public class Assembler { - private ArrayList machineList; - private ErrorList errors; - private boolean inDataSegment; // status maintained by parser - private boolean inMacroSegment; // status maintained by parser, true if in - // macro definition segment - private int externAddress; - private boolean autoAlign; - private Directives currentDirective; - private Directives dataDirective; - private MIPSprogram fileCurrentlyBeingAssembled; - private TokenList globalDeclarationList; - private UserKernelAddressSpace textAddress; - private UserKernelAddressSpace dataAddress; - private DataSegmentForwardReferences currentFileDataSegmentForwardReferences, - accumulatedDataSegmentForwardReferences; - - /** - * Parse and generate machine code for the given MIPS program. It must have - * already been tokenized. Warnings are not considered errors. - * - * @param p - * A MIPSprogram object representing the program source. - * @param extendedAssemblerEnabled - * A boolean value that if true permits use of extended (pseudo) - * instructions in the source code. If false, these are flagged - * as errors. - * @return An ArrayList representing the assembled program. Each member of - * the list is a ProgramStatement object containing the source, - * intermediate, and machine binary representations of a program - * statement. - * - * @see ProgramStatement - **/ - public ArrayList assemble(MIPSprogram p, boolean extendedAssemblerEnabled) - throws ProcessingException { - return assemble(p, extendedAssemblerEnabled, false); - } - - /** - * Parse and generate machine code for the given MIPS program. It must have - * already been tokenized. - * - * @param p - * A MIPSprogram object representing the program source. - * @param extendedAssemblerEnabled - * A boolean value that if true permits use of extended (pseudo) - * instructions in the source code. If false, these are flagged - * as errors. - * @param warningsAreErrors - * A boolean value - true means assembler warnings will be - * considered errors and terminate the assemble; false means the - * assembler will produce warning message but otherwise ignore - * warnings. - * @return An ArrayList representing the assembled program. Each member of - * the list is a ProgramStatement object containing the source, - * intermediate, and machine binary representations of a program - * statement. - * - * @see ProgramStatement - **/ - public ArrayList assemble(MIPSprogram p, boolean extendedAssemblerEnabled, - boolean warningsAreErrors) throws ProcessingException { - ArrayList programFiles = new ArrayList(); - programFiles.add(p); - return this.assemble(programFiles, extendedAssemblerEnabled, warningsAreErrors); - } - - /** - * Get list of assembler errors and warnings - * - * @return ErrorList of any assembler errors and warnings. - */ - public ErrorList getErrorList() { - return errors; - } - - /** - * Parse and generate machine code for the given MIPS program. All source - * files must have already been tokenized. Warnings will not be considered - * errors. - * - * @param tokenizedProgramFiles - * An ArrayList of MIPSprogram objects, each produced from a - * different source code file, representing the program source. - * @param extendedAssemblerEnabled - * A boolean value that if true permits use of extended (pseudo) - * instructions in the source code. If false, these are flagged - * as errors. - * @return An ArrayList representing the assembled program. Each member of - * the list is a ProgramStatement object containing the source, - * intermediate, and machine binary representations of a program - * statement. Returns null if incoming array list is null or empty. - * - * @see ProgramStatement - **/ - public ArrayList assemble(ArrayList tokenizedProgramFiles, boolean extendedAssemblerEnabled) - throws ProcessingException { - return assemble(tokenizedProgramFiles, extendedAssemblerEnabled, false); - } - - /** - * Parse and generate machine code for the given MIPS program. All source - * files must have already been tokenized. - * - * @param tokenizedProgramFiles - * An ArrayList of MIPSprogram objects, each produced from a - * different source code file, representing the program source. - * @param extendedAssemblerEnabled - * A boolean value that if true permits use of extended (pseudo) - * instructions in the source code. If false, these are flagged - * as errors. - * @param warningsAreErrors - * A boolean value - true means assembler warnings will be - * considered errors and terminate the assemble; false means the - * assembler will produce warning message but otherwise ignore - * warnings. - * @return An ArrayList representing the assembled program. Each member of - * the list is a ProgramStatement object containing the source, - * intermediate, and machine binary representations of a program - * statement. Returns null if incoming array list is null or empty. - * - * @see ProgramStatement - **/ - public ArrayList assemble(ArrayList tokenizedProgramFiles, boolean extendedAssemblerEnabled, - boolean warningsAreErrors) throws ProcessingException { - - if (tokenizedProgramFiles == null || tokenizedProgramFiles.size() == 0) +public class Assembler +{ + private ArrayList machineList; + + private ErrorList errors; + + private boolean inDataSegment; // status maintained by parser + + private boolean inMacroSegment; // status maintained by parser, true if in + + // macro definition segment + private int externAddress; + + private boolean autoAlign; + + private Directives currentDirective; + + private Directives dataDirective; + + private MIPSprogram fileCurrentlyBeingAssembled; + + private TokenList globalDeclarationList; + + private UserKernelAddressSpace textAddress; + + private UserKernelAddressSpace dataAddress; + + private DataSegmentForwardReferences currentFileDataSegmentForwardReferences, + accumulatedDataSegmentForwardReferences; + + /** + * Parse and generate machine code for the given MIPS program. It must have already been tokenized. Warnings are not + * considered errors. + * + * @param p A MIPSprogram object representing the program source. + * @param extendedAssemblerEnabled A boolean value that if true permits use of extended (pseudo) instructions in + * the source code. If false, these are flagged as errors. + * @return An ArrayList representing the assembled program. Each member of the list is a ProgramStatement object + * containing the source, intermediate, and machine binary representations of a program statement. + * @see ProgramStatement + **/ + public ArrayList assemble(MIPSprogram p, boolean extendedAssemblerEnabled) + throws ProcessingException + { + return assemble(p, extendedAssemblerEnabled, false); + } + + /** + * Parse and generate machine code for the given MIPS program. It must have already been tokenized. + * + * @param p A MIPSprogram object representing the program source. + * @param extendedAssemblerEnabled A boolean value that if true permits use of extended (pseudo) instructions in + * the source code. If false, these are flagged as errors. + * @param warningsAreErrors A boolean value - true means assembler warnings will be considered errors and + * terminate the assemble; false means the assembler will produce warning message but otherwise ignore + * warnings. + * @return An ArrayList representing the assembled program. Each member of the list is a ProgramStatement object + * containing the source, intermediate, and machine binary representations of a program statement. + * @see ProgramStatement + **/ + public ArrayList assemble(MIPSprogram p, boolean extendedAssemblerEnabled, + boolean warningsAreErrors) throws ProcessingException + { + ArrayList programFiles = new ArrayList(); + programFiles.add(p); + return this.assemble(programFiles, extendedAssemblerEnabled, warningsAreErrors); + } + + /** + * Get list of assembler errors and warnings + * + * @return ErrorList of any assembler errors and warnings. + */ + public ErrorList getErrorList() + { + return errors; + } + + /** + * Parse and generate machine code for the given MIPS program. All source files must have already been tokenized. + * Warnings will not be considered errors. + * + * @param tokenizedProgramFiles An ArrayList of MIPSprogram objects, each produced from a different source code + * file, representing the program source. + * @param extendedAssemblerEnabled A boolean value that if true permits use of extended (pseudo) instructions in + * the source code. If false, these are flagged as errors. + * @return An ArrayList representing the assembled program. Each member of the list is a ProgramStatement object + * containing the source, intermediate, and machine binary representations of a program statement. Returns null + * if incoming array list is null or empty. + * @see ProgramStatement + **/ + public ArrayList assemble(ArrayList tokenizedProgramFiles, boolean extendedAssemblerEnabled) + throws ProcessingException + { + return assemble(tokenizedProgramFiles, extendedAssemblerEnabled, false); + } + + /** + * Parse and generate machine code for the given MIPS program. All source files must have already been tokenized. + * + * @param tokenizedProgramFiles An ArrayList of MIPSprogram objects, each produced from a different source code + * file, representing the program source. + * @param extendedAssemblerEnabled A boolean value that if true permits use of extended (pseudo) instructions in + * the source code. If false, these are flagged as errors. + * @param warningsAreErrors A boolean value - true means assembler warnings will be considered errors and + * terminate the assemble; false means the assembler will produce warning message but otherwise ignore + * warnings. + * @return An ArrayList representing the assembled program. Each member of the list is a ProgramStatement object + * containing the source, intermediate, and machine binary representations of a program statement. Returns null + * if incoming array list is null or empty. + * @see ProgramStatement + **/ + public ArrayList assemble(ArrayList tokenizedProgramFiles, boolean extendedAssemblerEnabled, + boolean warningsAreErrors) throws ProcessingException + { + + if (tokenizedProgramFiles == null || tokenizedProgramFiles.size() == 0) + { return null; - textAddress = new UserKernelAddressSpace(Memory.textBaseAddress, + } + textAddress = new UserKernelAddressSpace(Memory.textBaseAddress, Memory.kernelTextBaseAddress); - dataAddress = new UserKernelAddressSpace(Memory.dataBaseAddress, + dataAddress = new UserKernelAddressSpace(Memory.dataBaseAddress, Memory.kernelDataBaseAddress); - externAddress = Memory.externBaseAddress; - currentFileDataSegmentForwardReferences = new DataSegmentForwardReferences(); - accumulatedDataSegmentForwardReferences = new DataSegmentForwardReferences(); - Globals.symbolTable.clear(); - Globals.memory.clear(); - this.machineList = new ArrayList(); - this.errors = new ErrorList(); - if (Globals.debug) + externAddress = Memory.externBaseAddress; + currentFileDataSegmentForwardReferences = new DataSegmentForwardReferences(); + accumulatedDataSegmentForwardReferences = new DataSegmentForwardReferences(); + Globals.symbolTable.clear(); + Globals.memory.clear(); + this.machineList = new ArrayList(); + this.errors = new ErrorList(); + if (Globals.debug) + { System.out.println("Assembler first pass begins:"); - // PROCESS THE FIRST ASSEMBLY PASS FOR ALL SOURCE FILES BEFORE PROCEEDING - // TO SECOND PASS. THIS ASSURES ALL SYMBOL TABLES ARE CORRECTLY BUILT. - // THERE IS ONE GLOBAL SYMBOL TABLE (for identifiers declared .globl) PLUS - // ONE LOCAL SYMBOL TABLE FOR EACH SOURCE FILE. - for (int fileIndex = 0; fileIndex < tokenizedProgramFiles.size(); fileIndex++) { + } + // PROCESS THE FIRST ASSEMBLY PASS FOR ALL SOURCE FILES BEFORE PROCEEDING + // TO SECOND PASS. THIS ASSURES ALL SYMBOL TABLES ARE CORRECTLY BUILT. + // THERE IS ONE GLOBAL SYMBOL TABLE (for identifiers declared .globl) PLUS + // ONE LOCAL SYMBOL TABLE FOR EACH SOURCE FILE. + for (int fileIndex = 0; fileIndex < tokenizedProgramFiles.size(); fileIndex++) + { if (errors.errorLimitExceeded()) - break; - this.fileCurrentlyBeingAssembled = (MIPSprogram) tokenizedProgramFiles.get(fileIndex); - // List of labels declared ".globl". new list for each file assembled + { + break; + } + this.fileCurrentlyBeingAssembled = (MIPSprogram) tokenizedProgramFiles.get(fileIndex); + // List of labels declared ".globl". new list for each file assembled this.globalDeclarationList = new TokenList(); - // Parser begins by default in text segment until directed otherwise. + // Parser begins by default in text segment until directed otherwise. this.inDataSegment = false; - // Macro segment will be started by .macro directive + // Macro segment will be started by .macro directive this.inMacroSegment = false; - // Default is to align data from directives on appropriate boundary (word, half, byte) - // This can be turned off for remainder of current data segment with ".align 0" + // Default is to align data from directives on appropriate boundary (word, half, byte) + // This can be turned off for remainder of current data segment with ".align 0" this.autoAlign = true; - // Default data directive is .word for 4 byte data items + // Default data directive is .word for 4 byte data items this.dataDirective = Directives.WORD; - // Clear out (initialize) symbol table related structures. + // Clear out (initialize) symbol table related structures. fileCurrentlyBeingAssembled.getLocalSymbolTable().clear(); currentFileDataSegmentForwardReferences.clear(); - // sourceList is an ArrayList of String objects, one per source line. - // tokenList is an ArrayList of TokenList objects, one per source line; - // each ArrayList in tokenList consists of Token objects. + // sourceList is an ArrayList of String objects, one per source line. + // tokenList is an ArrayList of TokenList objects, one per source line; + // each ArrayList in tokenList consists of Token objects. ArrayList sourceLineList = fileCurrentlyBeingAssembled.getSourceLineList(); ArrayList tokenList = fileCurrentlyBeingAssembled.getTokenList(); ArrayList parsedList = fileCurrentlyBeingAssembled.createParsedList(); - // each file keeps its own macro definitions + // each file keeps its own macro definitions MacroPool macroPool = fileCurrentlyBeingAssembled.createMacroPool(); - // FIRST PASS OF ASSEMBLER VERIFIES SYNTAX, GENERATES SYMBOL TABLE, - // INITIALIZES DATA SEGMENT + // FIRST PASS OF ASSEMBLER VERIFIES SYNTAX, GENERATES SYMBOL TABLE, + // INITIALIZES DATA SEGMENT ArrayList statements; - for (int i = 0; i < tokenList.size(); i++) { - if (errors.errorLimitExceeded()) - break; - for (int z=0; z<((TokenList)tokenList.get(i)).size(); z++) { - Token t = ((TokenList) tokenList.get(i)).get(z); - // record this token's original source program and line #. Differs from final, if .include used - t.setOriginal(sourceLineList.get(i).getMIPSprogram(),sourceLineList.get(i).getLineNumber()); - } - statements = this.parseLine((TokenList) tokenList.get(i), - sourceLineList.get(i).getSource(), - sourceLineList.get(i).getLineNumber(), - extendedAssemblerEnabled); - if (statements != null) { - parsedList.addAll(statements); - } + for (int i = 0; i < tokenList.size(); i++) + { + if (errors.errorLimitExceeded()) + { + break; + } + for (int z = 0; z < ((TokenList) tokenList.get(i)).size(); z++) + { + Token t = ((TokenList) tokenList.get(i)).get(z); + // record this token's original source program and line #. Differs from final, if .include used + t.setOriginal(sourceLineList.get(i).getMIPSprogram(), sourceLineList.get(i).getLineNumber()); + } + statements = this.parseLine((TokenList) tokenList.get(i), + sourceLineList.get(i).getSource(), + sourceLineList.get(i).getLineNumber(), + extendedAssemblerEnabled); + if (statements != null) + { + parsedList.addAll(statements); + } } - if (inMacroSegment) { - errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, - fileCurrentlyBeingAssembled.getLocalMacroPool().getCurrent().getFromLine(), - 0, "Macro started but not ended (no .end_macro directive)")); + if (inMacroSegment) + { + errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, + fileCurrentlyBeingAssembled.getLocalMacroPool().getCurrent().getFromLine(), + 0, "Macro started but not ended (no .end_macro directive)")); } - // move ".globl" symbols from local symtab to global + // move ".globl" symbols from local symtab to global this.transferGlobals(); - // Attempt to resolve forward label references that were discovered in operand fields - // of data segment directives in current file. Those that are not resolved after this - // call are either references to global labels not seen yet, or are undefined. - // Cannot determine which until all files are parsed, so copy unresolved entries - // into accumulated list and clear out this one for re-use with the next source file. + // Attempt to resolve forward label references that were discovered in operand fields + // of data segment directives in current file. Those that are not resolved after this + // call are either references to global labels not seen yet, or are undefined. + // Cannot determine which until all files are parsed, so copy unresolved entries + // into accumulated list and clear out this one for re-use with the next source file. currentFileDataSegmentForwardReferences.resolve(fileCurrentlyBeingAssembled - .getLocalSymbolTable()); + .getLocalSymbolTable()); accumulatedDataSegmentForwardReferences.add(currentFileDataSegmentForwardReferences); currentFileDataSegmentForwardReferences.clear(); - } // end of first-pass loop for each MIPSprogram - - - - // Have processed all source files. Attempt to resolve any remaining forward label - // references from global symbol table. Those that remain unresolved are undefined - // and require error message. - accumulatedDataSegmentForwardReferences.resolve(Globals.symbolTable); - accumulatedDataSegmentForwardReferences.generateErrorMessages(errors); - - // Throw collection of errors accumulated through the first pass. - if (errors.errorsOccurred()) { + } // end of first-pass loop for each MIPSprogram + + + // Have processed all source files. Attempt to resolve any remaining forward label + // references from global symbol table. Those that remain unresolved are undefined + // and require error message. + accumulatedDataSegmentForwardReferences.resolve(Globals.symbolTable); + accumulatedDataSegmentForwardReferences.generateErrorMessages(errors); + + // Throw collection of errors accumulated through the first pass. + if (errors.errorsOccurred()) + { throw new ProcessingException(errors); - } - if (Globals.debug) + } + if (Globals.debug) + { System.out.println("Assembler second pass begins"); - // SECOND PASS OF ASSEMBLER GENERATES BASIC ASSEMBLER THEN MACHINE CODE. - // Generates basic assembler statements... - for (int fileIndex = 0; fileIndex < tokenizedProgramFiles.size(); fileIndex++) { + } + // SECOND PASS OF ASSEMBLER GENERATES BASIC ASSEMBLER THEN MACHINE CODE. + // Generates basic assembler statements... + for (int fileIndex = 0; fileIndex < tokenizedProgramFiles.size(); fileIndex++) + { if (errors.errorLimitExceeded()) - break; + { + break; + } this.fileCurrentlyBeingAssembled = (MIPSprogram) tokenizedProgramFiles.get(fileIndex); ArrayList parsedList = fileCurrentlyBeingAssembled.getParsedList(); ProgramStatement statement; - for (int i = 0; i < parsedList.size(); i++) { - statement = (ProgramStatement) parsedList.get(i); - statement.buildBasicStatementFromBasicInstruction(errors); - if (errors.errorsOccurred()) { - throw new ProcessingException(errors); - } - if (statement.getInstruction() instanceof BasicInstruction) { - this.machineList.add(statement); - } - else { - // It is a pseudo-instruction: - // 1. Fetch its basic instruction template list - // 2. For each template in the list, - // 2a. substitute operands from source statement - // 2b. tokenize the statement generated by 2a. - // 2d. call parseLine() to generate basic instrction - // 2e. add returned programStatement to the list - // The templates, and the instructions generated by filling - // in the templates, are specified - // in basic format (e.g. mnemonic register reference $zero - // already translated to $0). - // So the values substituted into the templates need to be - // in this format. Since those - // values come from the original source statement, they need - // to be translated before - // substituting. The next method call will perform this - // translation on the original - // source statement. Despite the fact that the original - // statement is a pseudo - // instruction, this method performs the necessary - // translation correctly. - ExtendedInstruction inst = (ExtendedInstruction) statement.getInstruction(); - String basicAssembly = statement.getBasicAssemblyStatement(); - int sourceLine = statement.getSourceLine(); - TokenList theTokenList = new Tokenizer().tokenizeLine(sourceLine, - basicAssembly, errors, false); - - // //////////////////////////////////////////////////////////////////////////// - // If we are using compact memory config and there is a compact expansion, use it - ArrayList templateList; - if (compactTranslationCanBeApplied(statement)) { - templateList = inst.getCompactBasicIntructionTemplateList(); - } - else { - templateList = inst.getBasicIntructionTemplateList(); - } - - // subsequent ProgramStatement constructor needs the correct text segment address. - textAddress.set(statement.getAddress()); - // Will generate one basic instruction for each template in the list. - for (int instrNumber = 0; instrNumber < templateList.size(); instrNumber++) { - String instruction = ExtendedInstruction.makeTemplateSubstitutions( - this.fileCurrentlyBeingAssembled, - (String) templateList.get(instrNumber), theTokenList); - // 23 Jan 2008 by DPS. Template substitution may result in no instruction. - // If this is the case, skip remainder of loop iteration. This should only - // happen if template substitution was for "nop" instruction but delayed branching - // is disabled so the "nop" is not generated. - if (instruction == null || instruction == "") { - continue; - } - - // All substitutions have been made so we have generated - // a valid basic instruction! - if (Globals.debug) - System.out.println("PSEUDO generated: " + instruction); - // For generated instruction: tokenize, build program - // statement, add to list. - TokenList newTokenList = new Tokenizer().tokenizeLine(sourceLine, - instruction, errors,false); - ArrayList instrMatches = this.matchInstruction(newTokenList.get(0)); - Instruction instr = OperandFormat.bestOperandMatch(newTokenList, - instrMatches); - // Only first generated instruction is linked to original source - ProgramStatement ps = new ProgramStatement( - this.fileCurrentlyBeingAssembled, - (instrNumber == 0) ? statement.getSource() : "", newTokenList, - newTokenList, instr, textAddress.get(), statement.getSourceLine()); - textAddress.increment(Instruction.INSTRUCTION_LENGTH); - ps.buildBasicStatementFromBasicInstruction(errors); - this.machineList.add(ps); - } // end of FOR loop, repeated for each template in list. - } // end of ELSE part for extended instruction. - + for (int i = 0; i < parsedList.size(); i++) + { + statement = (ProgramStatement) parsedList.get(i); + statement.buildBasicStatementFromBasicInstruction(errors); + if (errors.errorsOccurred()) + { + throw new ProcessingException(errors); + } + if (statement.getInstruction() instanceof BasicInstruction) + { + this.machineList.add(statement); + } + else + { + // It is a pseudo-instruction: + // 1. Fetch its basic instruction template list + // 2. For each template in the list, + // 2a. substitute operands from source statement + // 2b. tokenize the statement generated by 2a. + // 2d. call parseLine() to generate basic instrction + // 2e. add returned programStatement to the list + // The templates, and the instructions generated by filling + // in the templates, are specified + // in basic format (e.g. mnemonic register reference $zero + // already translated to $0). + // So the values substituted into the templates need to be + // in this format. Since those + // values come from the original source statement, they need + // to be translated before + // substituting. The next method call will perform this + // translation on the original + // source statement. Despite the fact that the original + // statement is a pseudo + // instruction, this method performs the necessary + // translation correctly. + ExtendedInstruction inst = (ExtendedInstruction) statement.getInstruction(); + String basicAssembly = statement.getBasicAssemblyStatement(); + int sourceLine = statement.getSourceLine(); + TokenList theTokenList = new Tokenizer().tokenizeLine(sourceLine, + basicAssembly, errors, false); + + // //////////////////////////////////////////////////////////////////////////// + // If we are using compact memory config and there is a compact expansion, use it + ArrayList templateList; + if (compactTranslationCanBeApplied(statement)) + { + templateList = inst.getCompactBasicIntructionTemplateList(); + } + else + { + templateList = inst.getBasicIntructionTemplateList(); + } + + // subsequent ProgramStatement constructor needs the correct text segment address. + textAddress.set(statement.getAddress()); + // Will generate one basic instruction for each template in the list. + for (int instrNumber = 0; instrNumber < templateList.size(); instrNumber++) + { + String instruction = ExtendedInstruction.makeTemplateSubstitutions( + this.fileCurrentlyBeingAssembled, + (String) templateList.get(instrNumber), theTokenList); + // 23 Jan 2008 by DPS. Template substitution may result in no instruction. + // If this is the case, skip remainder of loop iteration. This should only + // happen if template substitution was for "nop" instruction but delayed branching + // is disabled so the "nop" is not generated. + if (instruction == null || instruction == "") + { + continue; + } + + // All substitutions have been made so we have generated + // a valid basic instruction! + if (Globals.debug) + { + System.out.println("PSEUDO generated: " + instruction); + } + // For generated instruction: tokenize, build program + // statement, add to list. + TokenList newTokenList = new Tokenizer().tokenizeLine(sourceLine, + instruction, errors, false); + ArrayList instrMatches = this.matchInstruction(newTokenList.get(0)); + Instruction instr = OperandFormat.bestOperandMatch(newTokenList, + instrMatches); + // Only first generated instruction is linked to original source + ProgramStatement ps = new ProgramStatement( + this.fileCurrentlyBeingAssembled, + (instrNumber == 0) ? statement.getSource() : "", newTokenList, + newTokenList, instr, textAddress.get(), statement.getSourceLine()); + textAddress.increment(Instruction.INSTRUCTION_LENGTH); + ps.buildBasicStatementFromBasicInstruction(errors); + this.machineList.add(ps); + } // end of FOR loop, repeated for each template in list. + } // end of ELSE part for extended instruction. + } // end of assembler second pass. - } - if (Globals.debug) + } + if (Globals.debug) + { System.out.println("Code generation begins"); - ///////////// THIRD MAJOR STEP IS PRODUCE MACHINE CODE FROM ASSEMBLY ////////// - // Generates machine code statements from the list of basic assembler statements - // and writes the statement to memory. - ProgramStatement statement; - for (int i = 0; i < this.machineList.size(); i++) { + } + ///////////// THIRD MAJOR STEP IS PRODUCE MACHINE CODE FROM ASSEMBLY ////////// + // Generates machine code statements from the list of basic assembler statements + // and writes the statement to memory. + ProgramStatement statement; + for (int i = 0; i < this.machineList.size(); i++) + { if (errors.errorLimitExceeded()) - break; + { + break; + } statement = (ProgramStatement) this.machineList.get(i); statement.buildMachineStatementFromBasicStatement(errors); if (Globals.debug) - System.out.println(statement); - try { - Globals.memory.setStatement(statement.getAddress(), statement); - } - catch (AddressErrorException e) { - Token t = statement.getOriginalTokenList().get(0); - errors.add(new ErrorMessage(t.getSourceMIPSprogram(), t.getSourceLine(), t - .getStartPos(), "Invalid address for text segment: " + e.getAddress())); - } - } - // Aug. 24, 2005 Ken Vollmar - // Ensure that I/O "file descriptors" are initialized for a new program run - SystemIO.resetFiles(); - // DPS 6 Dec 2006: - // We will now sort the ArrayList of ProgramStatements by getAddress() value. - // This is for display purposes, since they have already been stored to Memory. - // Use of .ktext and .text with address operands has two implications: - // (1) the addresses may not be ordered at this point. Requires unsigned int - // sort because kernel addresses are negative. See special Comparator. - // (2) It is possible for two instructions to be placed at the same address. - // Such occurances will be flagged as errors. - // Yes, I would not have to sort here if I used SortedSet rather than ArrayList - // but in case of duplicate I like having both statements handy for error message. - Collections.sort(this.machineList, new ProgramStatementComparator()); - catchDuplicateAddresses(this.machineList, errors); - if (errors.errorsOccurred() || errors.warningsOccurred() && warningsAreErrors) { + { + System.out.println(statement); + } + try + { + Globals.memory.setStatement(statement.getAddress(), statement); + } + catch (AddressErrorException e) + { + Token t = statement.getOriginalTokenList().get(0); + errors.add(new ErrorMessage(t.getSourceMIPSprogram(), t.getSourceLine(), t + .getStartPos(), "Invalid address for text segment: " + e.getAddress())); + } + } + // Aug. 24, 2005 Ken Vollmar + // Ensure that I/O "file descriptors" are initialized for a new program run + SystemIO.resetFiles(); + // DPS 6 Dec 2006: + // We will now sort the ArrayList of ProgramStatements by getAddress() value. + // This is for display purposes, since they have already been stored to Memory. + // Use of .ktext and .text with address operands has two implications: + // (1) the addresses may not be ordered at this point. Requires unsigned int + // sort because kernel addresses are negative. See special Comparator. + // (2) It is possible for two instructions to be placed at the same address. + // Such occurances will be flagged as errors. + // Yes, I would not have to sort here if I used SortedSet rather than ArrayList + // but in case of duplicate I like having both statements handy for error message. + Collections.sort(this.machineList, new ProgramStatementComparator()); + catchDuplicateAddresses(this.machineList, errors); + if (errors.errorsOccurred() || errors.warningsOccurred() && warningsAreErrors) + { throw new ProcessingException(errors); - } - return this.machineList; - } // assemble() - - // ////////////////////////////////////////////////////////////////////// - // Will check for duplicate text addresses, which can happen inadvertantly when using - // operand on .text directive. Will generate error message for each one that occurs. - private void catchDuplicateAddresses(ArrayList instructions, ErrorList errors) { - for (int i = 0; i < instructions.size() - 1; i++) { + } + return this.machineList; + } // assemble() + + // ////////////////////////////////////////////////////////////////////// + // Will check for duplicate text addresses, which can happen inadvertantly when using + // operand on .text directive. Will generate error message for each one that occurs. + private void catchDuplicateAddresses(ArrayList instructions, ErrorList errors) + { + for (int i = 0; i < instructions.size() - 1; i++) + { ProgramStatement ps1 = (ProgramStatement) instructions.get(i); ProgramStatement ps2 = (ProgramStatement) instructions.get(i + 1); - if (ps1.getAddress() == ps2.getAddress()) { - errors.add(new ErrorMessage(ps2.getSourceMIPSprogram(), ps2.getSourceLine(), 0, - "Duplicate text segment address: " - + mars.venus.NumberDisplayBaseChooser.formatUnsignedInteger(ps2 - .getAddress(), (Globals.getSettings() - .getDisplayAddressesInHex()) ? 16 : 10) - + " already occupied by " + ps1.getSourceFile() + " line " - + ps1.getSourceLine() + " (caused by use of " - + ((Memory.inTextSegment(ps2.getAddress())) ? ".text" : ".ktext") - + " operand)")); + if (ps1.getAddress() == ps2.getAddress()) + { + errors.add(new ErrorMessage(ps2.getSourceMIPSprogram(), ps2.getSourceLine(), 0, + "Duplicate text segment address: " + + mars.venus.NumberDisplayBaseChooser.formatUnsignedInteger(ps2 + .getAddress(), (Globals.getSettings() + .getDisplayAddressesInHex()) ? 16 : 10) + + " already occupied by " + ps1.getSourceFile() + " line " + + ps1.getSourceLine() + " (caused by use of " + + ((Memory.inTextSegment(ps2.getAddress())) ? ".text" : ".ktext") + + " operand)")); } - } - } - - /** - * This method parses one line of MIPS source code. It works with the list - * of tokens, but original source is also provided. It also carries out - * directives, which includes initializing the data segment. This method is - * invoked in the assembler first pass. - * - * @param tokenList - * @param source - * @param sourceLineNumber - * @param extendedAssemblerEnabled - * @return ArrayList of ProgramStatements because parsing a macro expansion - * request will return a list of ProgramStatements expanded - */ - private ArrayList parseLine(TokenList tokenList, String source, - int sourceLineNumber, boolean extendedAssemblerEnabled) { - - ArrayList ret = new ArrayList(); - - ProgramStatement programStatement; - TokenList tokens = this.stripComment(tokenList); - - // Labels should not be processed in macro definition segment. - MacroPool macroPool = fileCurrentlyBeingAssembled.getLocalMacroPool(); - if (inMacroSegment) { + } + } + + /** + * This method parses one line of MIPS source code. It works with the list of tokens, but original source is also + * provided. It also carries out directives, which includes initializing the data segment. This method is invoked in + * the assembler first pass. + * + * @param tokenList + * @param source + * @param sourceLineNumber + * @param extendedAssemblerEnabled + * @return ArrayList of ProgramStatements because parsing a macro expansion request will return a list of + * ProgramStatements expanded + */ + private ArrayList parseLine(TokenList tokenList, String source, + int sourceLineNumber, boolean extendedAssemblerEnabled) + { + + ArrayList ret = new ArrayList(); + + ProgramStatement programStatement; + TokenList tokens = this.stripComment(tokenList); + + // Labels should not be processed in macro definition segment. + MacroPool macroPool = fileCurrentlyBeingAssembled.getLocalMacroPool(); + if (inMacroSegment) + { detectLabels(tokens, macroPool.getCurrent()); - } - else { + } + else + { stripLabels(tokens); - } - if (tokens.isEmpty()) + } + if (tokens.isEmpty()) + { return null; - // Grab first (operator) token... - Token token = tokens.get(0); - TokenTypes tokenType = token.getType(); - - // Let's handle the directives here... - if (tokenType == TokenTypes.DIRECTIVE) { + } + // Grab first (operator) token... + Token token = tokens.get(0); + TokenTypes tokenType = token.getType(); + + // Let's handle the directives here... + if (tokenType == TokenTypes.DIRECTIVE) + { this.executeDirective(tokens); return null; - } - - // don't parse if in macro segment - if (inMacroSegment) + } + + // don't parse if in macro segment + if (inMacroSegment) + { return null; - - // SPIM-style macro calling: - TokenList parenFreeTokens = tokens; - if (tokens.size() > 2 && tokens.get(1).getType() == TokenTypes.LEFT_PAREN - && tokens.get(tokens.size() - 1).getType() == TokenTypes.RIGHT_PAREN) { + } + + // SPIM-style macro calling: + TokenList parenFreeTokens = tokens; + if (tokens.size() > 2 && tokens.get(1).getType() == TokenTypes.LEFT_PAREN + && tokens.get(tokens.size() - 1).getType() == TokenTypes.RIGHT_PAREN) + { parenFreeTokens = (TokenList) tokens.clone(); parenFreeTokens.remove(tokens.size() - 1); parenFreeTokens.remove(1); - } - Macro macro = macroPool.getMatchingMacro(parenFreeTokens, sourceLineNumber);//parenFreeTokens.get(0).getSourceLine()); - - // expand macro if this line is a macro expansion call - if (macro != null) { + } + Macro macro = macroPool.getMatchingMacro(parenFreeTokens, sourceLineNumber);//parenFreeTokens.get(0).getSourceLine()); + + // expand macro if this line is a macro expansion call + if (macro != null) + { tokens = parenFreeTokens; - // get unique id for this expansion + // get unique id for this expansion int counter = macroPool.getNextCounter(); - if (macroPool.pushOnCallStack(token)) { - errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, tokens.get(0) - .getSourceLine(), 0, "Detected a macro expansion loop (recursive reference). ")); - } - else { - // for (int i = macro.getFromLine() + 1; i < macro.getToLine(); i++) { - // String substituted = macro.getSubstitutedLine(i, tokens, counter, errors); - // TokenList tokenList2 = fileCurrentlyBeingAssembled.getTokenizer().tokenizeLine( - // i, substituted, errors); - // // If token list getProcessedLine() is not empty, then .eqv was performed and it contains the modified source. - // // Put it into the line to be parsed, so it will be displayed properly in text segment display. DPS 23 Jan 2013 - // if (tokenList2.getProcessedLine().length() > 0) - // substituted = tokenList2.getProcessedLine(); - // // recursively parse lines of expanded macro - // ArrayList statements = parseLine(tokenList2, "<" + (i-macro.getFromLine()+macro.getOriginalFromLine()) + "> " - // + substituted.trim(), sourceLineNumber, extendedAssemblerEnabled); - // if (statements != null) - // ret.addAll(statements); - // } - for (int i = macro.getFromLine() + 1; i < macro.getToLine(); i++) { - - String substituted = macro.getSubstitutedLine(i, tokens, counter, errors); - TokenList tokenList2 = fileCurrentlyBeingAssembled.getTokenizer().tokenizeLine( - i, substituted, errors); - - // If token list getProcessedLine() is not empty, then .eqv was performed and it contains the modified source. - // Put it into the line to be parsed, so it will be displayed properly in text segment display. DPS 23 Jan 2013 - if (tokenList2.getProcessedLine().length() > 0) - substituted = tokenList2.getProcessedLine(); - - // recursively parse lines of expanded macro - ArrayList statements = parseLine(tokenList2, "<" + (i-macro.getFromLine()+macro.getOriginalFromLine()) + "> " - + substituted.trim(), sourceLineNumber, extendedAssemblerEnabled); - if (statements != null) - ret.addAll(statements); - } - macroPool.popFromCallStack(); + if (macroPool.pushOnCallStack(token)) + { + errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, tokens.get(0) + .getSourceLine(), 0, "Detected a macro expansion loop (recursive reference). ")); + } + else + { + // for (int i = macro.getFromLine() + 1; i < macro.getToLine(); i++) { + // String substituted = macro.getSubstitutedLine(i, tokens, counter, errors); + // TokenList tokenList2 = fileCurrentlyBeingAssembled.getTokenizer().tokenizeLine( + // i, substituted, errors); + // // If token list getProcessedLine() is not empty, then .eqv was performed and it contains the modified source. + // // Put it into the line to be parsed, so it will be displayed properly in text segment display. DPS 23 Jan 2013 + // if (tokenList2.getProcessedLine().length() > 0) + // substituted = tokenList2.getProcessedLine(); + // // recursively parse lines of expanded macro + // ArrayList statements = parseLine(tokenList2, "<" + (i-macro.getFromLine()+macro.getOriginalFromLine()) + "> " + // + substituted.trim(), sourceLineNumber, extendedAssemblerEnabled); + // if (statements != null) + // ret.addAll(statements); + // } + for (int i = macro.getFromLine() + 1; i < macro.getToLine(); i++) + { + + String substituted = macro.getSubstitutedLine(i, tokens, counter, errors); + TokenList tokenList2 = fileCurrentlyBeingAssembled.getTokenizer().tokenizeLine( + i, substituted, errors); + + // If token list getProcessedLine() is not empty, then .eqv was performed and it contains the modified source. + // Put it into the line to be parsed, so it will be displayed properly in text segment display. DPS 23 Jan 2013 + if (tokenList2.getProcessedLine().length() > 0) + { + substituted = tokenList2.getProcessedLine(); + } + + // recursively parse lines of expanded macro + ArrayList statements = parseLine(tokenList2, "<" + (i - macro.getFromLine() + macro.getOriginalFromLine()) + "> " + + substituted.trim(), sourceLineNumber, extendedAssemblerEnabled); + if (statements != null) + { + ret.addAll(statements); + } + } + macroPool.popFromCallStack(); } return ret; - } - - // DPS 14-July-2008 - // Yet Another Hack: detect unrecognized directive. MARS recognizes the same directives - // as SPIM but other MIPS assemblers recognize additional directives. Compilers such - // as MIPS-directed GCC generate assembly code containing these directives. We'd like - // the opportunity to ignore them and continue. Tokenizer would categorize an unrecognized - // directive as an TokenTypes.IDENTIFIER because it would not be matched as a directive and - // MIPS labels can start with '.' NOTE: this can also be handled by including the - // ignored directive in the Directives.java list. There is already a mechanism in place - // for generating a warning there. But I cannot anticipate the names of all directives - // so this will catch anything, including a misspelling of a valid directive (which is - // a nice thing to do). - if (tokenType == TokenTypes.IDENTIFIER && token.getValue().charAt(0) == '.') { + } + + // DPS 14-July-2008 + // Yet Another Hack: detect unrecognized directive. MARS recognizes the same directives + // as SPIM but other MIPS assemblers recognize additional directives. Compilers such + // as MIPS-directed GCC generate assembly code containing these directives. We'd like + // the opportunity to ignore them and continue. Tokenizer would categorize an unrecognized + // directive as an TokenTypes.IDENTIFIER because it would not be matched as a directive and + // MIPS labels can start with '.' NOTE: this can also be handled by including the + // ignored directive in the Directives.java list. There is already a mechanism in place + // for generating a warning there. But I cannot anticipate the names of all directives + // so this will catch anything, including a misspelling of a valid directive (which is + // a nice thing to do). + if (tokenType == TokenTypes.IDENTIFIER && token.getValue().charAt(0) == '.') + { errors.add(new ErrorMessage(ErrorMessage.WARNING, token.getSourceMIPSprogram(), token - .getSourceLine(), token.getStartPos(), "MARS does not recognize the " - + token.getValue() + " directive. Ignored.")); + .getSourceLine(), token.getStartPos(), "MARS does not recognize the " + + token.getValue() + " directive. Ignored.")); return null; - } - - // The directives with lists (.byte, .double, .float, .half, .word, .ascii, .asciiz) - // should be able to extend the list over several lines. Since this method assembles - // only one source line, state information must be stored from one invocation to - // the next, to sense the context of this continuation line. That state information - // is contained in this.dataDirective (the current data directive). - // - if (this.inDataSegment && // 30-Dec-09 DPS Added data segment guard... - (tokenType == TokenTypes.PLUS - || // because invalid instructions were being caught... - tokenType == TokenTypes.MINUS - || // here and reported as a directive in text segment! - tokenType == TokenTypes.QUOTED_STRING || tokenType == TokenTypes.IDENTIFIER - || TokenTypes.isIntegerTokenType(tokenType) || TokenTypes - .isFloatingTokenType(tokenType))) { + } + + // The directives with lists (.byte, .double, .float, .half, .word, .ascii, .asciiz) + // should be able to extend the list over several lines. Since this method assembles + // only one source line, state information must be stored from one invocation to + // the next, to sense the context of this continuation line. That state information + // is contained in this.dataDirective (the current data directive). + // + if (this.inDataSegment && // 30-Dec-09 DPS Added data segment guard... + (tokenType == TokenTypes.PLUS + || // because invalid instructions were being caught... + tokenType == TokenTypes.MINUS + || // here and reported as a directive in text segment! + tokenType == TokenTypes.QUOTED_STRING || tokenType == TokenTypes.IDENTIFIER + || TokenTypes.isIntegerTokenType(tokenType) || TokenTypes + .isFloatingTokenType(tokenType))) + { this.executeDirectiveContinuation(tokens); return null; - } - - // If we are in the text segment, the variable "token" must now refer to - // an OPERATOR - // token. If not, it is either a syntax error or the specified operator - // is not - // yet implemented. - if (!this.inDataSegment) { + } + + // If we are in the text segment, the variable "token" must now refer to + // an OPERATOR + // token. If not, it is either a syntax error or the specified operator + // is not + // yet implemented. + if (!this.inDataSegment) + { ArrayList instrMatches = this.matchInstruction(token); if (instrMatches == null) - return ret; - // OK, we've got an operator match, let's check the operands. + { + return ret; + } + // OK, we've got an operator match, let's check the operands. Instruction inst = OperandFormat.bestOperandMatch(tokens, instrMatches); - // Here's the place to flag use of extended (pseudo) instructions - // when setting disabled. - if (inst instanceof ExtendedInstruction && !extendedAssemblerEnabled) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), - "Extended (pseudo) instruction or format not permitted. See Settings.")); + // Here's the place to flag use of extended (pseudo) instructions + // when setting disabled. + if (inst instanceof ExtendedInstruction && !extendedAssemblerEnabled) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), + "Extended (pseudo) instruction or format not permitted. See Settings.")); } - if (OperandFormat.tokenOperandMatch(tokens, inst, errors)) { - programStatement = new ProgramStatement(this.fileCurrentlyBeingAssembled, source, - tokenList, tokens, inst, textAddress.get(), sourceLineNumber); - // instruction length is 4 for all basic instruction, varies for extended instruction - // Modified to permit use of compact expansion if address fits - // in 15 bits. DPS 4-Aug-2009 - int instLength = inst.getInstructionLength(); - if (compactTranslationCanBeApplied(programStatement)) { - instLength = ((ExtendedInstruction) inst).getCompactInstructionLength(); - } - textAddress.increment(instLength); - ret.add(programStatement); - return ret; + if (OperandFormat.tokenOperandMatch(tokens, inst, errors)) + { + programStatement = new ProgramStatement(this.fileCurrentlyBeingAssembled, source, + tokenList, tokens, inst, textAddress.get(), sourceLineNumber); + // instruction length is 4 for all basic instruction, varies for extended instruction + // Modified to permit use of compact expansion if address fits + // in 15 bits. DPS 4-Aug-2009 + int instLength = inst.getInstructionLength(); + if (compactTranslationCanBeApplied(programStatement)) + { + instLength = ((ExtendedInstruction) inst).getCompactInstructionLength(); + } + textAddress.increment(instLength); + ret.add(programStatement); + return ret; } - } - return null; - } // parseLine() - - private void detectLabels(TokenList tokens, Macro current) { - if (tokenListBeginsWithLabel(tokens)) + } + return null; + } // parseLine() + + private void detectLabels(TokenList tokens, Macro current) + { + if (tokenListBeginsWithLabel(tokens)) + { current.addLabel(tokens.get(0).getValue()); - } - - // Determine whether or not a compact (16-bit) translation from - // pseudo-instruction to basic instruction can be applied. If - // the argument is a basic instruction, obviously not. If an - // extended instruction, we have to be operating under a 16-bit - // memory model and the instruction has to have defined an - // alternate compact translation. - private boolean compactTranslationCanBeApplied(ProgramStatement statement) { - return (statement.getInstruction() instanceof ExtendedInstruction + } + } + + // Determine whether or not a compact (16-bit) translation from + // pseudo-instruction to basic instruction can be applied. If + // the argument is a basic instruction, obviously not. If an + // extended instruction, we have to be operating under a 16-bit + // memory model and the instruction has to have defined an + // alternate compact translation. + private boolean compactTranslationCanBeApplied(ProgramStatement statement) + { + return (statement.getInstruction() instanceof ExtendedInstruction && Globals.memory.usingCompactMemoryConfiguration() && ((ExtendedInstruction) statement - .getInstruction()).hasCompactTranslation()); - } - - // ////////////////////////////////////////////////////////////////////////////////// - // Pre-process the token list for a statement by stripping off any comment. - // NOTE: the ArrayList parameter is not modified; a new one is cloned and - // returned. - private TokenList stripComment(TokenList tokenList) { - if (tokenList.isEmpty()) + .getInstruction()).hasCompactTranslation()); + } + + // ////////////////////////////////////////////////////////////////////////////////// + // Pre-process the token list for a statement by stripping off any comment. + // NOTE: the ArrayList parameter is not modified; a new one is cloned and + // returned. + private TokenList stripComment(TokenList tokenList) + { + if (tokenList.isEmpty()) + { return tokenList; - TokenList tokens = (TokenList) tokenList.clone(); - // If there is a comment, strip it off. - int last = tokens.size() - 1; - if (tokens.get(last).getType() == TokenTypes.COMMENT) { + } + TokenList tokens = (TokenList) tokenList.clone(); + // If there is a comment, strip it off. + int last = tokens.size() - 1; + if (tokens.get(last).getType() == TokenTypes.COMMENT) + { tokens.remove(last); - } - return tokens; - } // stripComment() - - /** - * Pre-process the token list for a statement by stripping off any label, if - * either are present. Any label definition will be recorded in the symbol - * table. NOTE: the ArrayList parameter will be modified. - */ - private void stripLabels(TokenList tokens) { - // If there is a label, handle it here and strip it off. - boolean thereWasLabel = this.parseAndRecordLabel(tokens); - if (thereWasLabel) { + } + return tokens; + } // stripComment() + + /** + * Pre-process the token list for a statement by stripping off any label, if either are present. Any label + * definition will be recorded in the symbol table. NOTE: the ArrayList parameter will be modified. + */ + private void stripLabels(TokenList tokens) + { + // If there is a label, handle it here and strip it off. + boolean thereWasLabel = this.parseAndRecordLabel(tokens); + if (thereWasLabel) + { tokens.remove(0); // Remove the IDENTIFIER. tokens.remove(0); // Remove the COLON, shifted to 0 by previous remove - } - } - - // ////////////////////////////////////////////////////////////////////////////////// - // Parse and record label, if there is one. Note the identifier and its colon are - // two separate tokens, since they may be separated by spaces in source code. - private boolean parseAndRecordLabel(TokenList tokens) { - if (tokens.size() < 2) { + } + } + + // ////////////////////////////////////////////////////////////////////////////////// + // Parse and record label, if there is one. Note the identifier and its colon are + // two separate tokens, since they may be separated by spaces in source code. + private boolean parseAndRecordLabel(TokenList tokens) + { + if (tokens.size() < 2) + { return false; - } - else { + } + else + { Token token = tokens.get(0); - if (tokenListBeginsWithLabel(tokens)) { - if (token.getType() == TokenTypes.OPERATOR) { - // an instruction name was used as label (e.g. lw:), so change its token type - token.setType(TokenTypes.IDENTIFIER); - } - fileCurrentlyBeingAssembled.getLocalSymbolTable().addSymbol(token, - (this.inDataSegment) ? dataAddress.get() : textAddress.get(), - this.inDataSegment, this.errors); - return true; - } - else { - return false; + if (tokenListBeginsWithLabel(tokens)) + { + if (token.getType() == TokenTypes.OPERATOR) + { + // an instruction name was used as label (e.g. lw:), so change its token type + token.setType(TokenTypes.IDENTIFIER); + } + fileCurrentlyBeingAssembled.getLocalSymbolTable().addSymbol(token, + (this.inDataSegment) ? dataAddress.get() : textAddress.get(), + this.inDataSegment, this.errors); + return true; } - } - } // parseLabel() - - private boolean tokenListBeginsWithLabel(TokenList tokens) { - // 2-July-2010. DPS. Remove prohibition of operator names as labels - if (tokens.size() < 2) + else + { + return false; + } + } + } // parseLabel() + + private boolean tokenListBeginsWithLabel(TokenList tokens) + { + // 2-July-2010. DPS. Remove prohibition of operator names as labels + if (tokens.size() < 2) + { return false; - return (tokens.get(0).getType() == TokenTypes.IDENTIFIER || tokens.get(0).getType() == TokenTypes.OPERATOR) + } + return (tokens.get(0).getType() == TokenTypes.IDENTIFIER || tokens.get(0).getType() == TokenTypes.OPERATOR) && tokens.get(1).getType() == TokenTypes.COLON; - } - - // ////////////////////////////////////////////////////////////////////////////////// - // This source code line is a directive, not a MIPS instruction. Let's carry it out. - private void executeDirective(TokenList tokens) { - Token token = tokens.get(0); - Directives direct = Directives.matchDirective(token.getValue()); - if (Globals.debug) + } + + // ////////////////////////////////////////////////////////////////////////////////// + // This source code line is a directive, not a MIPS instruction. Let's carry it out. + private void executeDirective(TokenList tokens) + { + Token token = tokens.get(0); + Directives direct = Directives.matchDirective(token.getValue()); + if (Globals.debug) + { System.out.println("line " + token.getSourceLine() + " is directive " + direct); - if (direct == null) { + } + if (direct == null) + { errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token - .getStartPos(), "\"" + token.getValue() - + "\" directive is invalid or not implemented in MARS")); - return; - } - else if (direct == Directives.EQV) { /* EQV added by DPS 11 July 2012 */ + .getStartPos(), "\"" + token.getValue() + + "\" directive is invalid or not implemented in MARS")); + } + else if (direct == Directives.EQV) + { /* EQV added by DPS 11 July 2012 */ // Do nothing. This was vetted and processed during tokenizing. - } - else if (direct == Directives.MACRO) { - if (tokens.size() < 2) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "\"" + token.getValue() - + "\" directive requires at least one argument.")); - return; + } + else if (direct == Directives.MACRO) + { + if (tokens.size() < 2) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" directive requires at least one argument.")); + return; } - if (tokens.get(1).getType() != TokenTypes.IDENTIFIER) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - tokens.get(1).getStartPos(), "Invalid Macro name \"" - + tokens.get(1).getValue() + "\"")); - return; + if (tokens.get(1).getType() != TokenTypes.IDENTIFIER) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + tokens.get(1).getStartPos(), "Invalid Macro name \"" + + tokens.get(1).getValue() + "\"")); + return; } - if (inMacroSegment) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "Nested macros are not allowed")); - return; + if (inMacroSegment) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "Nested macros are not allowed")); + return; } inMacroSegment = true; MacroPool pool = fileCurrentlyBeingAssembled.getLocalMacroPool(); pool.beginMacro(tokens.get(1)); - for (int i = 2; i < tokens.size(); i++) { - Token arg = tokens.get(i); - if (arg.getType() == TokenTypes.RIGHT_PAREN - || arg.getType() == TokenTypes.LEFT_PAREN) - continue; - if (!Macro.tokenIsMacroParameter(arg.getValue(), true)) { - errors.add(new ErrorMessage(arg.getSourceMIPSprogram(), arg.getSourceLine(), - arg.getStartPos(), "Invalid macro argument '" + arg.getValue() + "'")); - return; - } - pool.getCurrent().addArg(arg.getValue()); + for (int i = 2; i < tokens.size(); i++) + { + Token arg = tokens.get(i); + if (arg.getType() == TokenTypes.RIGHT_PAREN + || arg.getType() == TokenTypes.LEFT_PAREN) + { + continue; + } + if (!Macro.tokenIsMacroParameter(arg.getValue(), true)) + { + errors.add(new ErrorMessage(arg.getSourceMIPSprogram(), arg.getSourceLine(), + arg.getStartPos(), "Invalid macro argument '" + arg.getValue() + "'")); + return; + } + pool.getCurrent().addArg(arg.getValue()); } - } - else if (direct == Directives.END_MACRO) { - if (tokens.size() > 1) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "invalid text after .END_MACRO")); - return; + } + else if (direct == Directives.END_MACRO) + { + if (tokens.size() > 1) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "invalid text after .END_MACRO")); + return; } - if (!inMacroSegment) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), ".END_MACRO without .MACRO")); - return; + if (!inMacroSegment) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), ".END_MACRO without .MACRO")); + return; } inMacroSegment = false; fileCurrentlyBeingAssembled.getLocalMacroPool().commitMacro(token); - } - else if (inMacroSegment) { - // should not parse lines even directives in macro segment - return; - } - else if (direct == Directives.DATA || direct == Directives.KDATA) { + } + else if (inMacroSegment) + { + // should not parse lines even directives in macro segment + } + else if (direct == Directives.DATA || direct == Directives.KDATA) + { this.inDataSegment = true; this.autoAlign = true; this.dataAddress.setAddressSpace((direct == Directives.DATA) ? this.dataAddress.USER - : this.dataAddress.KERNEL); - if (tokens.size() > 1 && TokenTypes.isIntegerTokenType(tokens.get(1).getType())) { - this.dataAddress.set(Binary.stringToInt(tokens.get(1).getValue())); // KENV 1/6/05 + : this.dataAddress.KERNEL); + if (tokens.size() > 1 && TokenTypes.isIntegerTokenType(tokens.get(1).getType())) + { + this.dataAddress.set(Binary.stringToInt(tokens.get(1).getValue())); // KENV 1/6/05 } - } - else if (direct == Directives.TEXT || direct == Directives.KTEXT) { + } + else if (direct == Directives.TEXT || direct == Directives.KTEXT) + { this.inDataSegment = false; this.textAddress.setAddressSpace((direct == Directives.TEXT) ? this.textAddress.USER - : this.textAddress.KERNEL); - if (tokens.size() > 1 && TokenTypes.isIntegerTokenType(tokens.get(1).getType())) { - this.textAddress.set(Binary.stringToInt(tokens.get(1).getValue())); // KENV 1/6/05 + : this.textAddress.KERNEL); + if (tokens.size() > 1 && TokenTypes.isIntegerTokenType(tokens.get(1).getType())) + { + this.textAddress.set(Binary.stringToInt(tokens.get(1).getValue())); // KENV 1/6/05 } - } - else if (direct == Directives.WORD || direct == Directives.HALF - || direct == Directives.BYTE || direct == Directives.FLOAT - || direct == Directives.DOUBLE) { + } + else if (direct == Directives.WORD || direct == Directives.HALF + || direct == Directives.BYTE || direct == Directives.FLOAT + || direct == Directives.DOUBLE) + { this.dataDirective = direct; - if (passesDataSegmentCheck(token) && tokens.size() > 1) { // DPS - // 11/20/06, added text segment prohibition - storeNumeric(tokens, direct, errors); + if (passesDataSegmentCheck(token) && tokens.size() > 1) + { // DPS + // 11/20/06, added text segment prohibition + storeNumeric(tokens, direct, errors); } - } - else if (direct == Directives.ASCII || direct == Directives.ASCIIZ) { + } + else if (direct == Directives.ASCII || direct == Directives.ASCIIZ) + { this.dataDirective = direct; - if (passesDataSegmentCheck(token)) { - storeStrings(tokens, direct, errors); + if (passesDataSegmentCheck(token)) + { + storeStrings(tokens, direct, errors); } - } - else if (direct == Directives.ALIGN) { - if (passesDataSegmentCheck(token)) { - if (tokens.size() != 2) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), - token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() - + "\" requires one operand")); - return; - } - if (!TokenTypes.isIntegerTokenType(tokens.get(1).getType()) - || Binary.stringToInt(tokens.get(1).getValue()) < 0) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), - token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() - + "\" requires a non-negative integer")); - return; - } - int value = Binary.stringToInt(tokens.get(1).getValue()); // KENV 1/6/05 - if (value == 0) { - this.autoAlign = false; - } - else { - this.dataAddress.set(this.alignToBoundary(this.dataAddress.get(), - (int) Math.pow(2, value))); - } + } + else if (direct == Directives.ALIGN) + { + if (passesDataSegmentCheck(token)) + { + if (tokens.size() != 2) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), + token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() + + "\" requires one operand")); + return; + } + if (!TokenTypes.isIntegerTokenType(tokens.get(1).getType()) + || Binary.stringToInt(tokens.get(1).getValue()) < 0) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), + token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() + + "\" requires a non-negative integer")); + return; + } + int value = Binary.stringToInt(tokens.get(1).getValue()); // KENV 1/6/05 + if (value == 0) + { + this.autoAlign = false; + } + else + { + this.dataAddress.set(this.alignToBoundary(this.dataAddress.get(), + (int) Math.pow(2, value))); + } } - } - else if (direct == Directives.SPACE) { - if (passesDataSegmentCheck(token)) { - if (tokens.size() != 2) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), - token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() - + "\" requires one operand")); - return; - } - if (!TokenTypes.isIntegerTokenType(tokens.get(1).getType()) - || Binary.stringToInt(tokens.get(1).getValue()) < 0) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), - token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() - + "\" requires a non-negative integer")); - return; - } - int value = Binary.stringToInt(tokens.get(1).getValue()); // KENV 1/6/05 - this.dataAddress.increment(value); + } + else if (direct == Directives.SPACE) + { + if (passesDataSegmentCheck(token)) + { + if (tokens.size() != 2) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), + token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() + + "\" requires one operand")); + return; + } + if (!TokenTypes.isIntegerTokenType(tokens.get(1).getType()) + || Binary.stringToInt(tokens.get(1).getValue()) < 0) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), + token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() + + "\" requires a non-negative integer")); + return; + } + int value = Binary.stringToInt(tokens.get(1).getValue()); // KENV 1/6/05 + this.dataAddress.increment(value); } - } - else if (direct == Directives.EXTERN) { - if (tokens.size() != 3) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "\"" + token.getValue() - + "\" directive requires two operands (label and size).")); - return; + } + else if (direct == Directives.EXTERN) + { + if (tokens.size() != 3) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" directive requires two operands (label and size).")); + return; } if (!TokenTypes.isIntegerTokenType(tokens.get(2).getType()) - || Binary.stringToInt(tokens.get(2).getValue()) < 0) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "\"" + token.getValue() - + "\" requires a non-negative integer size")); - return; + || Binary.stringToInt(tokens.get(2).getValue()) < 0) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" requires a non-negative integer size")); + return; } int size = Binary.stringToInt(tokens.get(2).getValue()); - // If label already in global symtab, do nothing. If not, add it right now. - if (Globals.symbolTable.getAddress(tokens.get(1).getValue()) == SymbolTable.NOT_FOUND) { - Globals.symbolTable.addSymbol(tokens.get(1), this.externAddress, - Symbol.DATA_SYMBOL, errors); - this.externAddress += size; + // If label already in global symtab, do nothing. If not, add it right now. + if (Globals.symbolTable.getAddress(tokens.get(1).getValue()) == SymbolTable.NOT_FOUND) + { + Globals.symbolTable.addSymbol(tokens.get(1), this.externAddress, + Symbol.DATA_SYMBOL, errors); + this.externAddress += size; } - } - else if (direct == Directives.SET) { + } + else if (direct == Directives.SET) + { errors.add(new ErrorMessage(ErrorMessage.WARNING, token.getSourceMIPSprogram(), token - .getSourceLine(), token.getStartPos(), - "MARS currently ignores the .set directive.")); - } - else if (direct == Directives.GLOBL) { - if (tokens.size() < 2) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "\"" + token.getValue() - + "\" directive requires at least one argument.")); - return; + .getSourceLine(), token.getStartPos(), + "MARS currently ignores the .set directive.")); + } + else if (direct == Directives.GLOBL) + { + if (tokens.size() < 2) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" directive requires at least one argument.")); + return; } - // SPIM limits .globl list to one label, why not extend it to a list? - for (int i = 1; i < tokens.size(); i++) { - // Add it to a list of labels to be processed at the end of the - // pass. At that point, transfer matching symbol definitions from - // local symbol table to global symbol table. - Token label = tokens.get(i); - if (label.getType() != TokenTypes.IDENTIFIER) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), - token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() - + "\" directive argument must be label.")); - return; - } - globalDeclarationList.add(label); + // SPIM limits .globl list to one label, why not extend it to a list? + for (int i = 1; i < tokens.size(); i++) + { + // Add it to a list of labels to be processed at the end of the + // pass. At that point, transfer matching symbol definitions from + // local symbol table to global symbol table. + Token label = tokens.get(i); + if (label.getType() != TokenTypes.IDENTIFIER) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), + token.getSourceLine(), token.getStartPos(), "\"" + token.getValue() + + "\" directive argument must be label.")); + return; + } + globalDeclarationList.add(label); } - } - else { + } + else + { errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token - .getStartPos(), "\"" + token.getValue() - + "\" directive recognized but not yet implemented.")); - return; - } - } // executeDirective() - - // ////////////////////////////////////////////////////////////////////////////// - // Process the list of .globl labels, if any, declared and defined in this file. - // We'll just move their symbol table entries from local symbol table to global - // symbol table at the end of the first assembly pass. - private void transferGlobals() { - for (int i = 0; i < globalDeclarationList.size(); i++) { + .getStartPos(), "\"" + token.getValue() + + "\" directive recognized but not yet implemented.")); + } + } // executeDirective() + + // ////////////////////////////////////////////////////////////////////////////// + // Process the list of .globl labels, if any, declared and defined in this file. + // We'll just move their symbol table entries from local symbol table to global + // symbol table at the end of the first assembly pass. + private void transferGlobals() + { + for (int i = 0; i < globalDeclarationList.size(); i++) + { Token label = globalDeclarationList.get(i); Symbol symtabEntry = fileCurrentlyBeingAssembled.getLocalSymbolTable().getSymbol( - label.getValue()); - if (symtabEntry == null) { - errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, label.getSourceLine(), - label.getStartPos(), "\"" + label.getValue() - + "\" declared global label but not defined.")); - } - else { - if (Globals.symbolTable.getAddress(label.getValue()) != SymbolTable.NOT_FOUND) { - errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, label.getSourceLine(), - label.getStartPos(), "\"" + label.getValue() - + "\" already defined as global in a different file.")); - } - else { - fileCurrentlyBeingAssembled.getLocalSymbolTable().removeSymbol(label); - Globals.symbolTable.addSymbol(label, symtabEntry.getAddress(), - symtabEntry.getType(), errors); - } + label.getValue()); + if (symtabEntry == null) + { + errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, label.getSourceLine(), + label.getStartPos(), "\"" + label.getValue() + + "\" declared global label but not defined.")); } - } - } - - // ////////////////////////////////////////////////////////////////////////////////// - // This source code line, if syntactically correct, is a continuation of a - // directive list begun on on previous line. - private void executeDirectiveContinuation(TokenList tokens) { - Directives direct = this.dataDirective; - if (direct == Directives.WORD || direct == Directives.HALF || direct == Directives.BYTE - || direct == Directives.FLOAT || direct == Directives.DOUBLE) { - if (tokens.size() > 0) { - storeNumeric(tokens, direct, errors); - } - } - else if (direct == Directives.ASCII || direct == Directives.ASCIIZ) { - if (passesDataSegmentCheck(tokens.get(0))) { - storeStrings(tokens, direct, errors); - } - } - } // executeDirectiveContinuation() - - // ////////////////////////////////////////////////////////////////////////////////// - // Given token, find the corresponding Instruction object. If token was not - // recognized as OPERATOR, there is a problem. - private ArrayList matchInstruction(Token token) { - if (token.getType() != TokenTypes.OPERATOR) { - if (token.getSourceMIPSprogram().getLocalMacroPool() - .matchesAnyMacroName(token.getValue())) - this.errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token - .getSourceLine(), token.getStartPos(), "forward reference or invalid parameters for macro \"" - + token.getValue() + "\"")); else - this.errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token - .getSourceLine(), token.getStartPos(), "\"" + token.getValue() - + "\" is not a recognized operator")); + { + if (Globals.symbolTable.getAddress(label.getValue()) != SymbolTable.NOT_FOUND) + { + errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, label.getSourceLine(), + label.getStartPos(), "\"" + label.getValue() + + "\" already defined as global in a different file.")); + } + else + { + fileCurrentlyBeingAssembled.getLocalSymbolTable().removeSymbol(label); + Globals.symbolTable.addSymbol(label, symtabEntry.getAddress(), + symtabEntry.getType(), errors); + } + } + } + } + + // ////////////////////////////////////////////////////////////////////////////////// + // This source code line, if syntactically correct, is a continuation of a + // directive list begun on on previous line. + private void executeDirectiveContinuation(TokenList tokens) + { + Directives direct = this.dataDirective; + if (direct == Directives.WORD || direct == Directives.HALF || direct == Directives.BYTE + || direct == Directives.FLOAT || direct == Directives.DOUBLE) + { + if (tokens.size() > 0) + { + storeNumeric(tokens, direct, errors); + } + } + else if (direct == Directives.ASCII || direct == Directives.ASCIIZ) + { + if (passesDataSegmentCheck(tokens.get(0))) + { + storeStrings(tokens, direct, errors); + } + } + } // executeDirectiveContinuation() + + // ////////////////////////////////////////////////////////////////////////////////// + // Given token, find the corresponding Instruction object. If token was not + // recognized as OPERATOR, there is a problem. + private ArrayList matchInstruction(Token token) + { + if (token.getType() != TokenTypes.OPERATOR) + { + if (token.getSourceMIPSprogram().getLocalMacroPool() + .matchesAnyMacroName(token.getValue())) + { + this.errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token + .getSourceLine(), token.getStartPos(), "forward reference or invalid parameters for macro \"" + + token.getValue() + "\"")); + } + else + { + this.errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token + .getSourceLine(), token.getStartPos(), "\"" + token.getValue() + + "\" is not a recognized operator")); + } return null; - } - ArrayList inst = Globals.instructionSet.matchOperator(token.getValue()); - if (inst == null) { // This should NEVER happen... + } + ArrayList inst = Globals.instructionSet.matchOperator(token.getValue()); + if (inst == null) + { // This should NEVER happen... this.errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "Internal Assembler error: \"" + token.getValue() - + "\" tokenized OPERATOR then not recognized")); - } - return inst; - } // matchInstruction() - - // ////////////////////////////////////////////////////////////////////////////////// - // Processes the .word/.half/.byte/.float/.double directive. - // Can also handle "directive continuations", e.g. second or subsequent line - // of a multiline list, which does not contain the directive token. Just pass the - // current directive as argument. - private void storeNumeric(TokenList tokens, Directives directive, ErrorList errors) { - Token token = tokens.get(0); - // A double-check; should have already been caught...removed ".word" exemption 11/20/06 - if (!passesDataSegmentCheck(token)) + token.getStartPos(), "Internal Assembler error: \"" + token.getValue() + + "\" tokenized OPERATOR then not recognized")); + } + return inst; + } // matchInstruction() + + // ////////////////////////////////////////////////////////////////////////////////// + // Processes the .word/.half/.byte/.float/.double directive. + // Can also handle "directive continuations", e.g. second or subsequent line + // of a multiline list, which does not contain the directive token. Just pass the + // current directive as argument. + private void storeNumeric(TokenList tokens, Directives directive, ErrorList errors) + { + Token token = tokens.get(0); + // A double-check; should have already been caught...removed ".word" exemption 11/20/06 + if (!passesDataSegmentCheck(token)) + { return; - // Correctly handles case where this is a "directive continuation" line. - int tokenStart = 0; - if (token.getType() == TokenTypes.DIRECTIVE) + } + // Correctly handles case where this is a "directive continuation" line. + int tokenStart = 0; + if (token.getType() == TokenTypes.DIRECTIVE) + { tokenStart = 1; - - // Set byte length in memory of each number (e.g. WORD is 4, BYTE is 1, etc) - int lengthInBytes = DataTypes.getLengthInBytes(directive); - - // Handle the "value : n" format, which replicates the value "n" times. - if (tokens.size() == 4 && tokens.get(2).getType() == TokenTypes.COLON) { + } + + // Set byte length in memory of each number (e.g. WORD is 4, BYTE is 1, etc) + int lengthInBytes = DataTypes.getLengthInBytes(directive); + + // Handle the "value : n" format, which replicates the value "n" times. + if (tokens.size() == 4 && tokens.get(2).getType() == TokenTypes.COLON) + { Token valueToken = tokens.get(1); Token repetitionsToken = tokens.get(3); - // DPS 15-jul-08, allow ":" for repetition for all numeric - // directives (originally just .word) - // Conditions for correctly-formed replication: - // (integer directive AND integer value OR floating directive AND - // (integer value OR floating value)) - // AND integer repetition value + // DPS 15-jul-08, allow ":" for repetition for all numeric + // directives (originally just .word) + // Conditions for correctly-formed replication: + // (integer directive AND integer value OR floating directive AND + // (integer value OR floating value)) + // AND integer repetition value if (!(Directives.isIntegerDirective(directive) - && TokenTypes.isIntegerTokenType(valueToken.getType()) || Directives - .isFloatingDirective(directive) - && (TokenTypes.isIntegerTokenType(valueToken.getType()) || TokenTypes - .isFloatingTokenType(valueToken.getType()))) - || !TokenTypes.isIntegerTokenType(repetitionsToken.getType())) { - errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, - valueToken.getSourceLine(), valueToken.getStartPos(), - "malformed expression")); - return; + && TokenTypes.isIntegerTokenType(valueToken.getType()) || Directives + .isFloatingDirective(directive) + && (TokenTypes.isIntegerTokenType(valueToken.getType()) || TokenTypes + .isFloatingTokenType(valueToken.getType()))) + || !TokenTypes.isIntegerTokenType(repetitionsToken.getType())) + { + errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, + valueToken.getSourceLine(), valueToken.getStartPos(), + "malformed expression")); + return; } int repetitions = Binary.stringToInt(repetitionsToken.getValue()); // KENV 1/6/05 - if (repetitions <= 0) { - errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, repetitionsToken - .getSourceLine(), repetitionsToken.getStartPos(), - "repetition factor must be positive")); - return; + if (repetitions <= 0) + { + errors.add(new ErrorMessage(fileCurrentlyBeingAssembled, repetitionsToken + .getSourceLine(), repetitionsToken.getStartPos(), + "repetition factor must be positive")); + return; } - if (this.inDataSegment) { - if (this.autoAlign) { - this.dataAddress - .set(this.alignToBoundary(this.dataAddress.get(), lengthInBytes)); - } - for (int i = 0; i < repetitions; i++) { - if (Directives.isIntegerDirective(directive)) { - storeInteger(valueToken, directive, errors); - } - else { - storeRealNumber(valueToken, directive, errors); - } - } + if (this.inDataSegment) + { + if (this.autoAlign) + { + this.dataAddress + .set(this.alignToBoundary(this.dataAddress.get(), lengthInBytes)); + } + for (int i = 0; i < repetitions; i++) + { + if (Directives.isIntegerDirective(directive)) + { + storeInteger(valueToken, directive, errors); + } + else + { + storeRealNumber(valueToken, directive, errors); + } + } } // WHAT ABOUT .KDATA SEGMENT? - /*************************************************************************** - * /****** NOTE of 11/20/06. Below will always throw exception b/c - * you cannot use Memory.set() with text segment addresses and the - * "not valid address" produced here is misleading. Added data - * segment check prior to this point, so this "else" will never be - * executed. I'm leaving it in just in case MARS in the future adds - * capability of writing to the text segment (e.g. ability to - * de-assemble a binary value into its corresponding MIPS - * instruction) - * - * else { // not in data segment...which we assume to mean in text - * segment. try { for (int i=0; i < repetitions; i++) { - * Globals.memory.set(this.textAddress.get(), - * Binary.stringToInt(valueToken.getValue()), lengthInBytes); - * this.textAddress.increment(lengthInBytes); } } catch - * (AddressErrorException e) { errors.add(new - * ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - * token.getStartPos(), "\""+this.textAddress.get()+ - * "\" is not a valid text segment address")); } } - ************************************************************************/ + /*************************************************************************** + * /****** NOTE of 11/20/06. Below will always throw exception b/c + * you cannot use Memory.set() with text segment addresses and the + * "not valid address" produced here is misleading. Added data + * segment check prior to this point, so this "else" will never be + * executed. I'm leaving it in just in case MARS in the future adds + * capability of writing to the text segment (e.g. ability to + * de-assemble a binary value into its corresponding MIPS + * instruction) + * + * else { // not in data segment...which we assume to mean in text + * segment. try { for (int i=0; i < repetitions; i++) { + * Globals.memory.set(this.textAddress.get(), + * Binary.stringToInt(valueToken.getValue()), lengthInBytes); + * this.textAddress.increment(lengthInBytes); } } catch + * (AddressErrorException e) { errors.add(new + * ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + * token.getStartPos(), "\""+this.textAddress.get()+ + * "\" is not a valid text segment address")); } } + ************************************************************************/ return; - } - - // if not in ".word w : n" format, must just be list of one or more values. - for (int i = tokenStart; i < tokens.size(); i++) { + } + + // if not in ".word w : n" format, must just be list of one or more values. + for (int i = tokenStart; i < tokens.size(); i++) + { token = tokens.get(i); - if (Directives.isIntegerDirective(directive)) { - storeInteger(token, directive, errors); + if (Directives.isIntegerDirective(directive)) + { + storeInteger(token, directive, errors); } - if (Directives.isFloatingDirective(directive)) { - storeRealNumber(token, directive, errors); + if (Directives.isFloatingDirective(directive)) + { + storeRealNumber(token, directive, errors); } - } - return; - } // storeNumeric() - - // ////////////////////////////////////////////////////////////////////////////// - // Store integer value given integer (word, half, byte) directive. - // Called by storeNumeric() - // NOTE: The token itself may be a label, in which case the correct action is - // to store the address of that label (into however many bytes specified). - private void storeInteger(Token token, Directives directive, ErrorList errors) { - int lengthInBytes = DataTypes.getLengthInBytes(directive); - if (TokenTypes.isIntegerTokenType(token.getType())) { - int value = Binary.stringToInt(token.getValue()); + } + } // storeNumeric() + + // ////////////////////////////////////////////////////////////////////////////// + // Store integer value given integer (word, half, byte) directive. + // Called by storeNumeric() + // NOTE: The token itself may be a label, in which case the correct action is + // to store the address of that label (into however many bytes specified). + private void storeInteger(Token token, Directives directive, ErrorList errors) + { + int lengthInBytes = DataTypes.getLengthInBytes(directive); + if (TokenTypes.isIntegerTokenType(token.getType())) + { + int value = Binary.stringToInt(token.getValue()); int fullvalue = value; // DPS 4-Jan-2013. Overriding 6-Jan-2005 KENV changes. - // If value is out of range for the directive, will simply truncate - // the leading bits (includes sign bits). This is what SPIM does. - // But will issue a warning (not error) which SPIM does not do. - if (directive == Directives.BYTE) { - value = value & 0x000000FF; - } - else if (directive == Directives.HALF) { - value = value & 0x0000FFFF; + // If value is out of range for the directive, will simply truncate + // the leading bits (includes sign bits). This is what SPIM does. + // But will issue a warning (not error) which SPIM does not do. + if (directive == Directives.BYTE) + { + value = value & 0x000000FF; } - - if (DataTypes.outOfRange(directive, fullvalue)) { - errors.add(new ErrorMessage(ErrorMessage.WARNING, token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "\"" + token.getValue() - + "\" is out-of-range for a signed value and possibly truncated")); + else if (directive == Directives.HALF) + { + value = value & 0x0000FFFF; } - if (this.inDataSegment) { - writeToDataSegment(value, lengthInBytes, token, errors); + + if (DataTypes.outOfRange(directive, fullvalue)) + { + errors.add(new ErrorMessage(ErrorMessage.WARNING, token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" is out-of-range for a signed value and possibly truncated")); + } + if (this.inDataSegment) + { + writeToDataSegment(value, lengthInBytes, token, errors); } /****** - * NOTE of 11/20/06. "try" below will always throw exception b/c you - * cannot use Memory.set() with text segment addresses and the - * "not valid address" produced here is misleading. Added data - * segment check prior to this point, so this "else" will never be - * executed. I'm leaving it in just in case MARS in the future adds - * capability of writing to the text segment (e.g. ability to - * de-assemble a binary value into its corresponding MIPS - * instruction) - ********/ - else { - try { - Globals.memory.set(this.textAddress.get(), value, lengthInBytes); - } - catch (AddressErrorException e) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), + * NOTE of 11/20/06. "try" below will always throw exception b/c you + * cannot use Memory.set() with text segment addresses and the + * "not valid address" produced here is misleading. Added data + * segment check prior to this point, so this "else" will never be + * executed. I'm leaving it in just in case MARS in the future adds + * capability of writing to the text segment (e.g. ability to + * de-assemble a binary value into its corresponding MIPS + * instruction) + ********/ + else + { + try + { + Globals.memory.set(this.textAddress.get(), value, lengthInBytes); + } + catch (AddressErrorException e) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token.getStartPos(), "\"" - + this.textAddress.get() - + "\" is not a valid text segment address")); - return; - } - this.textAddress.increment(lengthInBytes); + + this.textAddress.get() + + "\" is not a valid text segment address")); + return; + } + this.textAddress.increment(lengthInBytes); } - } // end of "if integer token type" - else if (token.getType() == TokenTypes.IDENTIFIER) { - if (this.inDataSegment) { - int value = fileCurrentlyBeingAssembled.getLocalSymbolTable() - .getAddressLocalOrGlobal(token.getValue()); - if (value == SymbolTable.NOT_FOUND) { - // Record value 0 for now, then set up backpatch entry - int dataAddress = writeToDataSegment(0, lengthInBytes, token, errors); - currentFileDataSegmentForwardReferences.add(dataAddress, lengthInBytes, token); - } - else { // label already defined, so write its address - writeToDataSegment(value, lengthInBytes, token, errors); - } + } // end of "if integer token type" + else if (token.getType() == TokenTypes.IDENTIFIER) + { + if (this.inDataSegment) + { + int value = fileCurrentlyBeingAssembled.getLocalSymbolTable() + .getAddressLocalOrGlobal(token.getValue()); + if (value == SymbolTable.NOT_FOUND) + { + // Record value 0 for now, then set up backpatch entry + int dataAddress = writeToDataSegment(0, lengthInBytes, token, errors); + currentFileDataSegmentForwardReferences.add(dataAddress, lengthInBytes, token); + } + else + { // label already defined, so write its address + writeToDataSegment(value, lengthInBytes, token, errors); + } } // Data segment check done previously, so this "else" will not be. // See 11/20/06 note above. - else { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "\"" + token.getValue() - + "\" label as directive operand not permitted in text segment")); + else + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" label as directive operand not permitted in text segment")); } - } // end of "if label" - else { + } // end of "if label" + else + { errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token - .getStartPos(), "\"" + token.getValue() - + "\" is not a valid integer constant or label")); - } - }// storeInteger - - // ////////////////////////////////////////////////////////////////////////////// - // Store real (fixed or floating point) value given floating (float, double) directive. - // Called by storeNumeric() - private void storeRealNumber(Token token, Directives directive, ErrorList errors) { - int lengthInBytes = DataTypes.getLengthInBytes(directive); - double value; - - if (TokenTypes.isIntegerTokenType(token.getType()) - || TokenTypes.isFloatingTokenType(token.getType())) { - try { - value = Double.parseDouble(token.getValue()); - } - catch (NumberFormatException nfe) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "\"" + token.getValue() - + "\" is not a valid floating point constant")); - return; - } - if (DataTypes.outOfRange(directive, value)) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "\"" + token.getValue() - + "\" is an out-of-range value")); - return; + .getStartPos(), "\"" + token.getValue() + + "\" is not a valid integer constant or label")); + } + }// storeInteger + + // ////////////////////////////////////////////////////////////////////////////// + // Store real (fixed or floating point) value given floating (float, double) directive. + // Called by storeNumeric() + private void storeRealNumber(Token token, Directives directive, ErrorList errors) + { + int lengthInBytes = DataTypes.getLengthInBytes(directive); + double value; + + if (TokenTypes.isIntegerTokenType(token.getType()) + || TokenTypes.isFloatingTokenType(token.getType())) + { + try + { + value = Double.parseDouble(token.getValue()); } - } - else { + catch (NumberFormatException nfe) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" is not a valid floating point constant")); + return; + } + if (DataTypes.outOfRange(directive, value)) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" is an out-of-range value")); + return; + } + } + else + { errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token - .getStartPos(), "\"" + token.getValue() - + "\" is not a valid floating point constant")); + .getStartPos(), "\"" + token.getValue() + + "\" is not a valid floating point constant")); return; - } - - // Value has been validated; let's store it. - - if (directive == Directives.FLOAT) { + } + + // Value has been validated; let's store it. + + if (directive == Directives.FLOAT) + { writeToDataSegment(Float.floatToIntBits((float) value), lengthInBytes, token, errors); - } - if (directive == Directives.DOUBLE) { + } + if (directive == Directives.DOUBLE) + { writeDoubleToDataSegment(value, token, errors); - } - - } // storeRealNumber - - // ////////////////////////////////////////////////////////////////////////////////// - // Use directive argument to distinguish between ASCII and ASCIIZ. The - // latter stores a terminating null byte. Can handle a list of one or more - // strings on a single line. - private void storeStrings(TokenList tokens, Directives direct, ErrorList errors) { - Token token; - // Correctly handles case where this is a "directive continuation" line. - int tokenStart = 0; - if (tokens.get(0).getType() == TokenTypes.DIRECTIVE) { + } + + } // storeRealNumber + + // ////////////////////////////////////////////////////////////////////////////////// + // Use directive argument to distinguish between ASCII and ASCIIZ. The + // latter stores a terminating null byte. Can handle a list of one or more + // strings on a single line. + private void storeStrings(TokenList tokens, Directives direct, ErrorList errors) + { + Token token; + // Correctly handles case where this is a "directive continuation" line. + int tokenStart = 0; + if (tokens.get(0).getType() == TokenTypes.DIRECTIVE) + { tokenStart = 1; - } - for (int i = tokenStart; i < tokens.size(); i++) { + } + for (int i = tokenStart; i < tokens.size(); i++) + { token = tokens.get(i); - if (token.getType() != TokenTypes.QUOTED_STRING) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), - token.getStartPos(), "\"" + token.getValue() - + "\" is not a valid character string")); - } - else { - String quote = token.getValue(); - char theChar; - for (int j = 1; j < quote.length() - 1; j++) { - theChar = quote.charAt(j); - if (theChar == '\\') { - theChar = quote.charAt(++j); - switch (theChar) { - case 'n': - theChar = '\n'; - break; - case 't': - theChar = '\t'; - break; - case 'r': - theChar = '\r'; - break; - case '\\': - theChar = '\\'; - break; - case '\'': - theChar = '\''; - break; - case '"': - theChar = '"'; - break; - case 'b': - theChar = '\b'; - break; - case 'f': - theChar = '\f'; - break; - case '0': - theChar = '\0'; - break; - // Not implemented: \ n = octal character (n is number) - // \ x n = hex character (n is number) - // \ u n = unicode character (n is number) - // There are of course no spaces in these escape - // codes... - } - } - try { - Globals.memory.set(this.dataAddress.get(), (int) theChar, - DataTypes.CHAR_SIZE); - } - catch (AddressErrorException e) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token - .getSourceLine(), token.getStartPos(), "\"" - + this.dataAddress.get() + "\" is not a valid data segment address")); - } - this.dataAddress.increment(DataTypes.CHAR_SIZE); - } - if (direct == Directives.ASCIIZ) { - try { - Globals.memory.set(this.dataAddress.get(), 0, DataTypes.CHAR_SIZE); - } - catch (AddressErrorException e) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token - .getSourceLine(), token.getStartPos(), "\"" - + this.dataAddress.get() + "\" is not a valid data segment address")); - } - this.dataAddress.increment(DataTypes.CHAR_SIZE); - } + if (token.getType() != TokenTypes.QUOTED_STRING) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" is not a valid character string")); } - } - } // storeStrings() - - // ////////////////////////////////////////////////////////////////////////////////// - // Simply check to see if we are in data segment. Generate error if not. - private boolean passesDataSegmentCheck(Token token) { - if (!this.inDataSegment) { + else + { + String quote = token.getValue(); + char theChar; + for (int j = 1; j < quote.length() - 1; j++) + { + theChar = quote.charAt(j); + if (theChar == '\\') + { + theChar = quote.charAt(++j); + switch (theChar) + { + case 'n': + theChar = '\n'; + break; + case 't': + theChar = '\t'; + break; + case 'r': + theChar = '\r'; + break; + case '\\': + theChar = '\\'; + break; + case '\'': + theChar = '\''; + break; + case '"': + theChar = '"'; + break; + case 'b': + theChar = '\b'; + break; + case 'f': + theChar = '\f'; + break; + case '0': + theChar = '\0'; + break; + // Not implemented: \ n = octal character (n is number) + // \ x n = hex character (n is number) + // \ u n = unicode character (n is number) + // There are of course no spaces in these escape + // codes... + } + } + try + { + Globals.memory.set(this.dataAddress.get(), theChar, + DataTypes.CHAR_SIZE); + } + catch (AddressErrorException e) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token + .getSourceLine(), token.getStartPos(), "\"" + + this.dataAddress.get() + "\" is not a valid data segment address")); + } + this.dataAddress.increment(DataTypes.CHAR_SIZE); + } + if (direct == Directives.ASCIIZ) + { + try + { + Globals.memory.set(this.dataAddress.get(), 0, DataTypes.CHAR_SIZE); + } + catch (AddressErrorException e) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token + .getSourceLine(), token.getStartPos(), "\"" + + this.dataAddress.get() + "\" is not a valid data segment address")); + } + this.dataAddress.increment(DataTypes.CHAR_SIZE); + } + } + } + } // storeStrings() + + // ////////////////////////////////////////////////////////////////////////////////// + // Simply check to see if we are in data segment. Generate error if not. + private boolean passesDataSegmentCheck(Token token) + { + if (!this.inDataSegment) + { errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token - .getStartPos(), "\"" + token.getValue() - + "\" directive cannot appear in text segment")); + .getStartPos(), "\"" + token.getValue() + + "\" directive cannot appear in text segment")); return false; - } - else { + } + else + { return true; - } - } - - // ////////////////////////////////////////////////////////////////////////////////// - // Writes the given int value into current data segment address. Works for - // all the integer types plus float (caller is responsible for doing floatToIntBits). - // Returns address at which the value was stored. - private int writeToDataSegment(int value, int lengthInBytes, Token token, ErrorList errors) { - if (this.autoAlign) { + } + } + + // ////////////////////////////////////////////////////////////////////////////////// + // Writes the given int value into current data segment address. Works for + // all the integer types plus float (caller is responsible for doing floatToIntBits). + // Returns address at which the value was stored. + private int writeToDataSegment(int value, int lengthInBytes, Token token, ErrorList errors) + { + if (this.autoAlign) + { this.dataAddress.set(this.alignToBoundary(this.dataAddress.get(), lengthInBytes)); - } - try { + } + try + { Globals.memory.set(this.dataAddress.get(), value, lengthInBytes); - } - catch (AddressErrorException e) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token - .getStartPos(), "\"" + this.dataAddress.get() - + "\" is not a valid data segment address")); - return this.dataAddress.get(); - } - int address = this.dataAddress.get(); - this.dataAddress.increment(lengthInBytes); - return address; - } - - // ////////////////////////////////////////////////////////////////////////////////// - // Writes the given double value into current data segment address. Works - // only for DOUBLE floating - // point values -- Memory class doesn't have method for writing 8 bytes, so - // use setWord twice. - private void writeDoubleToDataSegment(double value, Token token, ErrorList errors) { - int lengthInBytes = DataTypes.DOUBLE_SIZE; - if (this.autoAlign) { + } + catch (AddressErrorException e) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token + .getStartPos(), "\"" + this.dataAddress.get() + + "\" is not a valid data segment address")); + return this.dataAddress.get(); + } + int address = this.dataAddress.get(); + this.dataAddress.increment(lengthInBytes); + return address; + } + + // ////////////////////////////////////////////////////////////////////////////////// + // Writes the given double value into current data segment address. Works + // only for DOUBLE floating + // point values -- Memory class doesn't have method for writing 8 bytes, so + // use setWord twice. + private void writeDoubleToDataSegment(double value, Token token, ErrorList errors) + { + int lengthInBytes = DataTypes.DOUBLE_SIZE; + if (this.autoAlign) + { this.dataAddress.set(this.alignToBoundary(this.dataAddress.get(), lengthInBytes)); - } - try { + } + try + { Globals.memory.setDouble(this.dataAddress.get(), value); - } - catch (AddressErrorException e) { - errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token - .getStartPos(), "\"" + this.dataAddress.get() - + "\" is not a valid data segment address")); - return; - } - this.dataAddress.increment(lengthInBytes); - } - - // ////////////////////////////////////////////////////////////////////////////////// - // If address is multiple of byte boundary, returns address. Otherwise, returns address - // which is next higher multiple of the byte boundary. Used for aligning data segment. - // For instance if args are 6 and 4, returns 8 (next multiple of 4 higher than 6). - // NOTE: it will fix any symbol table entries for this address too. See else part. - private int alignToBoundary(int address, int byteBoundary) { - int remainder = address % byteBoundary; - if (remainder == 0) { + } + catch (AddressErrorException e) + { + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), token + .getStartPos(), "\"" + this.dataAddress.get() + + "\" is not a valid data segment address")); + return; + } + this.dataAddress.increment(lengthInBytes); + } + + // ////////////////////////////////////////////////////////////////////////////////// + // If address is multiple of byte boundary, returns address. Otherwise, returns address + // which is next higher multiple of the byte boundary. Used for aligning data segment. + // For instance if args are 6 and 4, returns 8 (next multiple of 4 higher than 6). + // NOTE: it will fix any symbol table entries for this address too. See else part. + private int alignToBoundary(int address, int byteBoundary) + { + int remainder = address % byteBoundary; + if (remainder == 0) + { return address; - } - else { + } + else + { int alignedAddress = address + byteBoundary - remainder; fileCurrentlyBeingAssembled.getLocalSymbolTable().fixSymbolTableAddress(address, - alignedAddress); + alignedAddress); return alignedAddress; - } - } - - // /////////////////////////////////////////////////////////////////////////////////// - // Private class used as Comparator to sort the final ArrayList of - // ProgramStatements. - // Sorting is based on unsigned integer value of - // ProgramStatement.getAddress() - private class ProgramStatementComparator implements Comparator { - // Will be used to sort the collection. Unsigned int compare, because - // all kernel 32-bit - // addresses have 1 in high order bit, which makes the int negative. - // "Unsigned" compare - // is needed when signs of the two operands differ. - public int compare(Object obj1, Object obj2) { - if (obj1 instanceof ProgramStatement && obj2 instanceof ProgramStatement) { - int addr1 = ((ProgramStatement) obj1).getAddress(); - int addr2 = ((ProgramStatement) obj2).getAddress(); - return (addr1 < 0 && addr2 >= 0 || addr1 >= 0 && addr2 < 0) ? addr2 : addr1 - addr2; - } - else { - throw new ClassCastException(); + } + } + + // /////////////////////////////////////////////////////////////////////////////////// + // Private class used as Comparator to sort the final ArrayList of + // ProgramStatements. + // Sorting is based on unsigned integer value of + // ProgramStatement.getAddress() + private class ProgramStatementComparator implements Comparator + { + // Will be used to sort the collection. Unsigned int compare, because + // all kernel 32-bit + // addresses have 1 in high order bit, which makes the int negative. + // "Unsigned" compare + // is needed when signs of the two operands differ. + public int compare(Object obj1, Object obj2) + { + if (obj1 instanceof ProgramStatement && obj2 instanceof ProgramStatement) + { + int addr1 = ((ProgramStatement) obj1).getAddress(); + int addr2 = ((ProgramStatement) obj2).getAddress(); + return (addr1 < 0 && addr2 >= 0 || addr1 >= 0 && addr2 < 0) ? addr2 : addr1 - addr2; } - } - - // Take a hard line. - public boolean equals(Object obj) { + else + { + throw new ClassCastException(); + } + } + + // Take a hard line. + public boolean equals(Object obj) + { return this == obj; - } - } - - // /////////////////////////////////////////////////////////////////////////////////// - // Private class to simultaneously track addresses in both user and kernel - // address spaces. - // Instantiate one for data segment and one for text segment. - private class UserKernelAddressSpace { - int[] address; - int currentAddressSpace; - private final int USER = 0, KERNEL = 1; - - // Initially use user address space, not kernel. - private UserKernelAddressSpace(int userBase, int kernelBase) { + } + } + + // /////////////////////////////////////////////////////////////////////////////////// + // Private class to simultaneously track addresses in both user and kernel + // address spaces. + // Instantiate one for data segment and one for text segment. + private class UserKernelAddressSpace + { + private final int USER = 0, KERNEL = 1; + + int[] address; + + int currentAddressSpace; + + // Initially use user address space, not kernel. + private UserKernelAddressSpace(int userBase, int kernelBase) + { address = new int[2]; address[USER] = userBase; address[KERNEL] = kernelBase; currentAddressSpace = USER; - } - - private int get() { + } + + private int get() + { return address[currentAddressSpace]; - } - - private void set(int value) { + } + + private void set(int value) + { address[currentAddressSpace] = value; - } - - private void increment(int increment) { + } + + private void increment(int increment) + { address[currentAddressSpace] += increment; - } - - private void setAddressSpace(int addressSpace) { - if (addressSpace == USER || addressSpace == KERNEL) { - currentAddressSpace = addressSpace; - } - else { - throw new IllegalArgumentException(); + } + + private void setAddressSpace(int addressSpace) + { + if (addressSpace == USER || addressSpace == KERNEL) + { + currentAddressSpace = addressSpace; } - } - } - - // ////////////////////////////////////////////////////////////////////////// - // Handy class to handle forward label references appearing as data - // segment operands. This is needed because the data segment is comletely - // processed by the end of the first assembly pass, and its directives may - // contain labels as operands. When this occurs, the label's associated - // address becomes the operand value. If it is a forward reference, we will - // save the necessary information in this object for finding and patching in - // the correct address at the end of the first pass (for this file or for all - // files if more than one). - // - // If such a parsed label refers to a local or global label not defined yet, - // pertinent information is added to this object: - // - memory address that needs the label's address, - // - number of bytes (addresses are 4 bytes but may be used with any of - // the integer directives: .word, .half, .byte) - // - the label's token. Normally need only the name but error message needs more. - private class DataSegmentForwardReferences { - private ArrayList forwardReferenceList; - - private DataSegmentForwardReferences() { + else + { + throw new IllegalArgumentException(); + } + } + } + + // ////////////////////////////////////////////////////////////////////////// + // Handy class to handle forward label references appearing as data + // segment operands. This is needed because the data segment is comletely + // processed by the end of the first assembly pass, and its directives may + // contain labels as operands. When this occurs, the label's associated + // address becomes the operand value. If it is a forward reference, we will + // save the necessary information in this object for finding and patching in + // the correct address at the end of the first pass (for this file or for all + // files if more than one). + // + // If such a parsed label refers to a local or global label not defined yet, + // pertinent information is added to this object: + // - memory address that needs the label's address, + // - number of bytes (addresses are 4 bytes but may be used with any of + // the integer directives: .word, .half, .byte) + // - the label's token. Normally need only the name but error message needs more. + private class DataSegmentForwardReferences + { + private final ArrayList forwardReferenceList; + + private DataSegmentForwardReferences() + { forwardReferenceList = new ArrayList(); - } - - private int size() { + } + + private int size() + { return forwardReferenceList.size(); - } - - // Add a new forward reference entry. Client must supply the following: - // - memory address to receive the label's address once resolved - // - number of address bytes to store (1 for .byte, 2 for .half, 4 for .word) - // - the label's token. All its information will be needed if error message generated. - private void add(int patchAddress, int length, Token token) { + } + + // Add a new forward reference entry. Client must supply the following: + // - memory address to receive the label's address once resolved + // - number of address bytes to store (1 for .byte, 2 for .half, 4 for .word) + // - the label's token. All its information will be needed if error message generated. + private void add(int patchAddress, int length, Token token) + { forwardReferenceList.add(new DataSegmentForwardReference(patchAddress, length, token)); - } - - // Add the entries of another DataSegmentForwardReferences object to this one. - // Can be used at the end of each source file to dump all unresolved references - // into a common list to be processed after all source files parsed. - private void add(DataSegmentForwardReferences another) { + } + + // Add the entries of another DataSegmentForwardReferences object to this one. + // Can be used at the end of each source file to dump all unresolved references + // into a common list to be processed after all source files parsed. + private void add(DataSegmentForwardReferences another) + { forwardReferenceList.addAll(another.forwardReferenceList); - } - - // Clear out the list. Allows you to re-use it. - private void clear() { + } + + // Clear out the list. Allows you to re-use it. + private void clear() + { forwardReferenceList.clear(); - } - - // Will traverse the list of forward references, attempting to resolve them. - // For each entry it will first search the provided local symbol table and - // failing that, the global one. If passed the global symbol table, it will - // perform a second, redundant, search. If search is successful, the patch - // is applied and the forward reference removed. If search is not successful, - // the forward reference remains (it is either undefined or a global label - // defined in a file not yet parsed). - private int resolve(SymbolTable localSymtab) { + } + + // Will traverse the list of forward references, attempting to resolve them. + // For each entry it will first search the provided local symbol table and + // failing that, the global one. If passed the global symbol table, it will + // perform a second, redundant, search. If search is successful, the patch + // is applied and the forward reference removed. If search is not successful, + // the forward reference remains (it is either undefined or a global label + // defined in a file not yet parsed). + private int resolve(SymbolTable localSymtab) + { int count = 0; int labelAddress; DataSegmentForwardReference entry; - for (int i = 0; i < forwardReferenceList.size(); i++) { - entry = (DataSegmentForwardReference) forwardReferenceList.get(i); - labelAddress = localSymtab.getAddressLocalOrGlobal(entry.token.getValue()); - if (labelAddress != SymbolTable.NOT_FOUND) { - // patch address has to be valid b/c we already stored there... - try { - Globals.memory.set(entry.patchAddress, labelAddress, entry.length); - } - catch (AddressErrorException aee) { - } - forwardReferenceList.remove(i); - i--; // needed because removal shifted the remaining list indices down - count++; - } + for (int i = 0; i < forwardReferenceList.size(); i++) + { + entry = (DataSegmentForwardReference) forwardReferenceList.get(i); + labelAddress = localSymtab.getAddressLocalOrGlobal(entry.token.getValue()); + if (labelAddress != SymbolTable.NOT_FOUND) + { + // patch address has to be valid b/c we already stored there... + try + { + Globals.memory.set(entry.patchAddress, labelAddress, entry.length); + } + catch (AddressErrorException aee) + { + } + forwardReferenceList.remove(i); + i--; // needed because removal shifted the remaining list indices down + count++; + } } return count; - } - - // Call this when you are confident that remaining list entries are to - // undefined labels. - private void generateErrorMessages(ErrorList errors) { + } + + // Call this when you are confident that remaining list entries are to + // undefined labels. + private void generateErrorMessages(ErrorList errors) + { DataSegmentForwardReference entry; - for (int i = 0; i < forwardReferenceList.size(); i++) { - entry = (DataSegmentForwardReference) forwardReferenceList.get(i); - errors.add(new ErrorMessage(entry.token.getSourceMIPSprogram(), entry.token - .getSourceLine(), entry.token.getStartPos(), "Symbol \"" - + entry.token.getValue() + "\" not found in symbol table.")); + for (int i = 0; i < forwardReferenceList.size(); i++) + { + entry = (DataSegmentForwardReference) forwardReferenceList.get(i); + errors.add(new ErrorMessage(entry.token.getSourceMIPSprogram(), entry.token + .getSourceLine(), entry.token.getStartPos(), "Symbol \"" + + entry.token.getValue() + "\" not found in symbol table.")); } - } - - // inner-inner class to hold each entry of the forward reference list. - private class DataSegmentForwardReference { + } + + // inner-inner class to hold each entry of the forward reference list. + private class DataSegmentForwardReference + { int patchAddress; + int length; + Token token; - - DataSegmentForwardReference(int patchAddress, int length, Token token) { - this.patchAddress = patchAddress; - this.length = length; - this.token = token; + + DataSegmentForwardReference(int patchAddress, int length, Token token) + { + this.patchAddress = patchAddress; + this.length = length; + this.token = token; } - } - - } - } + } + + } +} diff --git a/src/main/java/mars/assembler/DataTypes.java b/src/main/java/mars/assembler/DataTypes.java index 175e393..6937b69 100644 --- a/src/main/java/mars/assembler/DataTypes.java +++ b/src/main/java/mars/assembler/DataTypes.java @@ -31,101 +31,134 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Information about MIPS data types. + * * @author Pete Sanderson * @version August 2003 **/ - -public final class DataTypes { -/** Number of bytes occupied by MIPS double is 8. **/ - public static final int DOUBLE_SIZE = 8; -/** Number of bytes occupied by MIPS float is 4. **/ - public static final int FLOAT_SIZE = 4; -/** Number of bytes occupied by MIPS word is 4. **/ - public static final int WORD_SIZE = 4; -/** Number of bytes occupied by MIPS halfword is 2. **/ - public static final int HALF_SIZE = 2; -/** Number of bytes occupied by MIPS byte is 1. **/ - public static final int BYTE_SIZE = 1; -/** Number of bytes occupied by MIPS character is 1. **/ - public static final int CHAR_SIZE = 1; -/** Maximum value that can be stored in a MIPS word is 231-1 **/ - public static final int MAX_WORD_VALUE = Integer.MAX_VALUE; -/** Lowest value that can be stored in a MIPS word is -231 **/ - public static final int MIN_WORD_VALUE = Integer.MIN_VALUE; -/** Maximum value that can be stored in a MIPS halfword is 215-1 **/ - public static final int MAX_HALF_VALUE = 32767; //(int)Math.pow(2,15) - 1; -/** Lowest value that can be stored in a MIPS halfword is -215 **/ - public static final int MIN_HALF_VALUE = -32768; //0 - (int) Math.pow(2,15); -/** Maximum value that can be stored in an unsigned MIPS halfword is 216-1 **/ - public static final int MAX_UHALF_VALUE = 65535; -/** Lowest value that can be stored in na unsigned MIPS halfword is 0 **/ - public static final int MIN_UHALF_VALUE = 0; -/** Maximum value that can be stored in a MIPS byte is 27-1 **/ - public static final int MAX_BYTE_VALUE = Byte.MAX_VALUE; -/** Lowest value that can be stored in a MIPS byte is -27 **/ - public static final int MIN_BYTE_VALUE = Byte.MIN_VALUE; -/** Maximum positive finite value that can be stored in a MIPS float is same as Java Float **/ - public static final double MAX_FLOAT_VALUE = Float.MAX_VALUE; -/** Largest magnitude negative value that can be stored in a MIPS float (negative of the max) **/ - public static final double LOW_FLOAT_VALUE = -Float.MAX_VALUE; -/** Maximum positive finite value that can be stored in a MIPS double is same as Java Double **/ - public static final double MAX_DOUBLE_VALUE = Double.MAX_VALUE; -/** Largest magnitude negative value that can be stored in a MIPS double(negative of the max) **/ - public static final double LOW_DOUBLE_VALUE = -Double.MAX_VALUE; - - /** - * Get length in bytes for numeric MIPS directives. - * @param direct Directive to be measured. - * @return Returns length in bytes for values of that type. If type is not numeric - * (or not implemented yet), returns 0. - **/ - - public static int getLengthInBytes(Directives direct) { - if (direct == Directives.FLOAT) - return FLOAT_SIZE; - else if (direct == Directives.DOUBLE) - return DOUBLE_SIZE; - else if (direct == Directives.WORD) - return WORD_SIZE; - else if (direct == Directives.HALF) - return HALF_SIZE; - else if (direct == Directives.BYTE) - return BYTE_SIZE; - else - return 0; - } - - /** - * Determines whether given integer value falls within value range for given directive. - * @param direct Directive that controls storage allocation for value. - * @param value The value to be stored. - * @return Returns true if value can be stored in the number of bytes allowed - * by the given directive (.word, .half, .byte), false otherwise. - **/ - public static boolean outOfRange(Directives direct, int value) { - if (direct == Directives.HALF && (value < MIN_HALF_VALUE || value > MAX_HALF_VALUE)) - return true; - else if (direct == Directives.BYTE && (value < MIN_BYTE_VALUE || value > MAX_BYTE_VALUE)) - return true; - else - return false; - } - - /** - * Determines whether given floating point value falls within value range for given directive. - * For float, this refers to range of the data type, not precision. Example: 1.23456789012345 - * be stored in a float with loss of precision. It's within the range. But 1.23e500 cannot be - * stored in a float because the exponent 500 is too large (float allows 8 bits for exponent). - * @param direct Directive that controls storage allocation for value. - * @param value The value to be stored. - * @return Returns true if value is within range of - * the given directive (.float, .double), false otherwise. - **/ - public static boolean outOfRange(Directives direct, double value) { - if (direct == Directives.FLOAT && (value < LOW_FLOAT_VALUE || value > MAX_FLOAT_VALUE)) - return true; - else - return false; - } +public final class DataTypes +{ + /** Number of bytes occupied by MIPS double is 8. **/ + public static final int DOUBLE_SIZE = 8; + + /** Number of bytes occupied by MIPS float is 4. **/ + public static final int FLOAT_SIZE = 4; + + /** Number of bytes occupied by MIPS word is 4. **/ + public static final int WORD_SIZE = 4; + + /** Number of bytes occupied by MIPS halfword is 2. **/ + public static final int HALF_SIZE = 2; + + /** Number of bytes occupied by MIPS byte is 1. **/ + public static final int BYTE_SIZE = 1; + + /** Number of bytes occupied by MIPS character is 1. **/ + public static final int CHAR_SIZE = 1; + + /** Maximum value that can be stored in a MIPS word is 231-1 **/ + public static final int MAX_WORD_VALUE = Integer.MAX_VALUE; + + /** Lowest value that can be stored in a MIPS word is -231 **/ + public static final int MIN_WORD_VALUE = Integer.MIN_VALUE; + + /** Maximum value that can be stored in a MIPS halfword is 215-1 **/ + public static final int MAX_HALF_VALUE = 32767; //(int)Math.pow(2,15) - 1; + + /** Lowest value that can be stored in a MIPS halfword is -215 **/ + public static final int MIN_HALF_VALUE = -32768; //0 - (int) Math.pow(2,15); + + /** Maximum value that can be stored in an unsigned MIPS halfword is 216-1 **/ + public static final int MAX_UHALF_VALUE = 65535; + + /** Lowest value that can be stored in na unsigned MIPS halfword is 0 **/ + public static final int MIN_UHALF_VALUE = 0; + + /** Maximum value that can be stored in a MIPS byte is 27-1 **/ + public static final int MAX_BYTE_VALUE = Byte.MAX_VALUE; + + /** Lowest value that can be stored in a MIPS byte is -27 **/ + public static final int MIN_BYTE_VALUE = Byte.MIN_VALUE; + + /** Maximum positive finite value that can be stored in a MIPS float is same as Java Float **/ + public static final double MAX_FLOAT_VALUE = Float.MAX_VALUE; + + /** Largest magnitude negative value that can be stored in a MIPS float (negative of the max) **/ + public static final double LOW_FLOAT_VALUE = -Float.MAX_VALUE; + + /** Maximum positive finite value that can be stored in a MIPS double is same as Java Double **/ + public static final double MAX_DOUBLE_VALUE = Double.MAX_VALUE; + + /** Largest magnitude negative value that can be stored in a MIPS double(negative of the max) **/ + public static final double LOW_DOUBLE_VALUE = -Double.MAX_VALUE; + + /** + * Get length in bytes for numeric MIPS directives. + * + * @param direct Directive to be measured. + * @return Returns length in bytes for values of that type. If type is not numeric (or not implemented yet), + * returns 0. + **/ + + public static int getLengthInBytes(Directives direct) + { + if (direct == Directives.FLOAT) + { + return FLOAT_SIZE; + } + else if (direct == Directives.DOUBLE) + { + return DOUBLE_SIZE; + } + else if (direct == Directives.WORD) + { + return WORD_SIZE; + } + else if (direct == Directives.HALF) + { + return HALF_SIZE; + } + else if (direct == Directives.BYTE) + { + return BYTE_SIZE; + } + else + { + return 0; + } + } + + + /** + * Determines whether given integer value falls within value range for given directive. + * + * @param direct Directive that controls storage allocation for value. + * @param value The value to be stored. + * @return Returns true if value can be stored in the number of bytes allowed by the given directive + * (.word, .half, .byte), false otherwise. + **/ + public static boolean outOfRange(Directives direct, int value) + { + if (direct == Directives.HALF && (value < MIN_HALF_VALUE || value > MAX_HALF_VALUE)) + { + return true; + } + else return direct == Directives.BYTE && (value < MIN_BYTE_VALUE || value > MAX_BYTE_VALUE); + } + + /** + * Determines whether given floating point value falls within value range for given directive. For float, this + * refers to range of the data type, not precision. Example: 1.23456789012345 be stored in a float with loss of + * precision. It's within the range. But 1.23e500 cannot be stored in a float because the exponent 500 is too + * large (float allows 8 bits for exponent). + * + * @param direct Directive that controls storage allocation for value. + * @param value The value to be stored. + * @return Returns true if value is within range of the given directive (.float, .double), false + * otherwise. + **/ + public static boolean outOfRange(Directives direct, double value) + { + return direct == Directives.FLOAT && (value < LOW_FLOAT_VALUE || value > MAX_FLOAT_VALUE); + } } diff --git a/src/main/java/mars/assembler/Directives.java b/src/main/java/mars/assembler/Directives.java index cc025ac..4817d86 100644 --- a/src/main/java/mars/assembler/Directives.java +++ b/src/main/java/mars/assembler/Directives.java @@ -1,6 +1,6 @@ - package mars.assembler; +package mars.assembler; - import java.util.ArrayList; +import java.util.ArrayList; /* Copyright (c) 2003-2012, Pete Sanderson and Kenneth Vollmar @@ -31,160 +31,191 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Class representing MIPS assembler directives. If Java had enumerated types, these - * would probably be implemented that way. Each directive is represented by a unique object. - * The directive name is indicative of the directive it represents. For example, DATA - * represents the MIPS .data directive. - * + * Class representing MIPS assembler directives. If Java had enumerated types, these would probably be implemented that + * way. Each directive is represented by a unique object. The directive name is indicative of the directive it + * represents. For example, DATA represents the MIPS .data directive. + * * @author Pete Sanderson * @version August 2003 **/ - public final class Directives { - - private static ArrayList directiveList = new ArrayList(); - public static final Directives DATA = new Directives(".data", "Subsequent items stored in Data segment at next available address"); - public static final Directives TEXT = new Directives(".text", "Subsequent items (instructions) stored in Text segment at next available address"); - public static final Directives WORD = new Directives(".word", "Store the listed value(s) as 32 bit words on word boundary"); - public static final Directives ASCII = new Directives(".ascii", "Store the string in the Data segment but do not add null terminator"); - public static final Directives ASCIIZ = new Directives(".asciiz", "Store the string in the Data segment and add null terminator"); - public static final Directives BYTE = new Directives(".byte", "Store the listed value(s) as 8 bit bytes"); - public static final Directives ALIGN = new Directives(".align", "Align next data item on specified byte boundary (0=byte, 1=half, 2=word, 3=double)"); - public static final Directives HALF = new Directives(".half", "Store the listed value(s) as 16 bit halfwords on halfword boundary"); - public static final Directives SPACE = new Directives(".space", "Reserve the next specified number of bytes in Data segment"); - public static final Directives DOUBLE = new Directives(".double", "Store the listed value(s) as double precision floating point"); - public static final Directives FLOAT = new Directives(".float", "Store the listed value(s) as single precision floating point"); - public static final Directives EXTERN = new Directives(".extern", "Declare the listed label and byte length to be a global data field"); - public static final Directives KDATA = new Directives(".kdata", "Subsequent items stored in Kernel Data segment at next available address"); - public static final Directives KTEXT = new Directives(".ktext", "Subsequent items (instructions) stored in Kernel Text segment at next available address"); - public static final Directives GLOBL = new Directives(".globl", "Declare the listed label(s) as global to enable referencing from other files"); - public static final Directives SET = new Directives(".set", "Set assembler variables. Currently ignored but included for SPIM compatability"); - /* EQV added by DPS 11 July 2012 */ - public static final Directives EQV = new Directives(".eqv", "Substitute second operand for first. First operand is symbol, second operand is expression (like #define)"); - /* MACRO and END_MACRO added by Mohammad Sekhavat Oct 2012 */ - public static final Directives MACRO = new Directives(".macro", "Begin macro definition. See .end_macro"); - public static final Directives END_MACRO = new Directives(".end_macro", "End macro definition. See .macro"); - /* INCLUDE added by DPS 11 Jan 2013 */ - public static final Directives INCLUDE = new Directives(".include", "Insert the contents of the specified file. Put filename in quotes."); - - - private String descriptor; - private String description; // help text - - private Directives() { - // private ctor assures no objects can be created other than those above. - this.descriptor = "generic"; - this.description = ""; - directiveList.add(this); - } - - private Directives(String name, String description) { - this.descriptor = name; - this.description = description; - directiveList.add(this); - } - - /** - * Find Directive object, if any, which matches the given String. - * - * @param str A String containing candidate directive name (e.g. ".ascii") - * @return If match is found, returns matching Directives object, else returns null. - **/ - - public static Directives matchDirective(String str) { - Directives match; - for (int i=0; inull. + **/ + + public static Directives matchDirective(String str) + { + Directives match; + for (int i = 0; i < directiveList.size(); i++) + { match = (Directives) directiveList.get(i); - if (str.equalsIgnoreCase(match.descriptor)) { - return match; + if (str.equalsIgnoreCase(match.descriptor)) + { + return match; } - } - return null; - } - - - /** - * Find Directive object, if any, which contains the given string as a prefix. For example, - * ".a" will match ".ascii", ".asciiz" and ".align" - * - * @param str A String - * @return If match is found, returns ArrayList of matching Directives objects, else returns null. - **/ - - public static ArrayList prefixMatchDirectives(String str) { - ArrayList matches = null; - for (int i=0; inull. + **/ + + public static ArrayList prefixMatchDirectives(String str) + { + ArrayList matches = null; + for (int i = 0; i < directiveList.size(); i++) + { + if (((Directives) directiveList.get(i)).descriptor.toLowerCase().startsWith(str.toLowerCase())) + { + if (matches == null) + { + matches = new ArrayList(); + } + matches.add(directiveList.get(i)); } - } - return matches; - } - - - /** - * Produces String-ified version of Directive object - * - * @return String representing Directive: its MIPS name - **/ - - public String toString() { - return this.descriptor; - } - - - /** - * Get name of this Directives object - * - * @return name of this MIPS directive as a String - **/ - - public String getName() { - return this.descriptor; - } - - /** - * Get description of this Directives object - * - * @return description of this MIPS directive (for help purposes) - **/ - - public String getDescription() { - return this.description; - } - - /** - * Produces List of Directive objects - * - * @return MIPS Directive - **/ - public static ArrayList getDirectiveList() { - return directiveList; - } - - - /** - * Lets you know whether given directive is for integer (WORD,HALF,BYTE). - * - * @param direct a MIPS directive - * @return true if given directive is FLOAT or DOUBLE, false otherwise - **/ - public static boolean isIntegerDirective(Directives direct) { - return direct == Directives.WORD || direct == Directives.HALF || direct == Directives.BYTE; - } - - - /** - * Lets you know whether given directive is for floating number (FLOAT,DOUBLE). - * - * @param direct a MIPS directive - * @return true if given directive is FLOAT or DOUBLE, false otherwise. - **/ - public static boolean isFloatingDirective(Directives direct) { - return direct == Directives.FLOAT || direct == Directives.DOUBLE; - } - - } \ No newline at end of file + } + return matches; + } + + /** + * Produces List of Directive objects + * + * @return MIPS Directive + **/ + public static ArrayList getDirectiveList() + { + return directiveList; + } + + /** + * Lets you know whether given directive is for integer (WORD,HALF,BYTE). + * + * @param direct a MIPS directive + * @return true if given directive is FLOAT or DOUBLE, false otherwise + **/ + public static boolean isIntegerDirective(Directives direct) + { + return direct == Directives.WORD || direct == Directives.HALF || direct == Directives.BYTE; + } + + /** + * Lets you know whether given directive is for floating number (FLOAT,DOUBLE). + * + * @param direct a MIPS directive + * @return true if given directive is FLOAT or DOUBLE, false otherwise. + **/ + public static boolean isFloatingDirective(Directives direct) + { + return direct == Directives.FLOAT || direct == Directives.DOUBLE; + } + + /** + * Produces String-ified version of Directive object + * + * @return String representing Directive: its MIPS name + **/ + + public String toString() + { + return this.descriptor; + } + + /** + * Get name of this Directives object + * + * @return name of this MIPS directive as a String + **/ + + public String getName() + { + return this.descriptor; + } + + /** + * Get description of this Directives object + * + * @return description of this MIPS directive (for help purposes) + **/ + + public String getDescription() + { + return this.description; + } + +} diff --git a/src/main/java/mars/assembler/Macro.java b/src/main/java/mars/assembler/Macro.java index 8ae418d..7565d15 100644 --- a/src/main/java/mars/assembler/Macro.java +++ b/src/main/java/mars/assembler/Macro.java @@ -1,15 +1,14 @@ package mars.assembler; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; - import mars.ErrorList; import mars.ErrorMessage; import mars.MIPSprogram; -import mars.mips.hardware.RegisterFile; import mars.mips.hardware.Coprocessor0; import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.RegisterFile; + +import java.util.ArrayList; +import java.util.Collections; /* Copyright (c) 2013-2014. @@ -38,217 +37,252 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Stores information of a macro definition. - * + * * @author M.H.Sekhavat */ -public class Macro { - private String name; - private MIPSprogram program; - private ArrayList labels; +public class Macro +{ + private String name; -/** - * first and last line number of macro definition. first line starts with - * .macro directive and last line is .end_macro directive. - */ - private int fromLine, toLine; - private int origFromLine, origToLine; -/** - * arguments like %arg will be substituted by macro expansion - */ - private ArrayList args; + private MIPSprogram program; - public Macro() { - name = ""; - program = null; - fromLine = toLine = 0; - origFromLine = origToLine = 0; - args = new ArrayList(); - labels = new ArrayList(); - } + private final ArrayList labels; - public String getName() { - return name; - } + /** + * first and last line number of macro definition. first line starts with .macro directive and last line is + * .end_macro directive. + */ + private int fromLine, toLine; - public void setName(String name) { - this.name = name; - } + private int origFromLine, origToLine; - public MIPSprogram getProgram() { - return program; - } + /** + * arguments like %arg will be substituted by macro expansion + */ + private ArrayList args; - public void setProgram(MIPSprogram program) { - this.program = program; - } + public Macro() + { + name = ""; + program = null; + fromLine = toLine = 0; + origFromLine = origToLine = 0; + args = new ArrayList(); + labels = new ArrayList(); + } - public int getFromLine() { - return fromLine; - } - - public int getOriginalFromLine() { - return this.origFromLine; - } - - public void setFromLine(int fromLine) { - this.fromLine = fromLine; - } - - public void setOriginalFromLine(int origFromLine) { - this.origFromLine = origFromLine; - } - - public int getToLine() { - return toLine; - } - - public int getOriginalToLine() { - return this.origToLine; - } - - public void setToLine(int toLine) { - this.toLine = toLine; - } - - public void setOriginalToLine(int origToLine) { - this.origToLine = origToLine; - } - - public ArrayList getArgs() { - return args; - } - - public void setArgs(ArrayList args) { - this.args = args; - } - -/** - * @param obj - * {@link Macro} object to check if their name and count of - * arguments are same - */ - @Override - public boolean equals(Object obj) { - if (obj instanceof Macro) { - Macro macro = (Macro) obj; - return macro.getName().equals(name) && (macro.args.size() == args.size()); - } - return super.equals(obj); - } - - public void addArg(String value) { - args.add(value); - } - -/** - * Substitutes macro arguments in a line of source code inside macro - * definition to be parsed after macro expansion.
- * Also appends "_M#" to all labels defined inside macro body where # is value of counter - * - * @param line - * source line number in macro definition to be substituted - * @param args - * @param counter - * unique macro expansion id - * @param errors - * @return line-th line of source code, with substituted - * arguments - */ - - public String getSubstitutedLine(int line, TokenList args, long counter, ErrorList errors) { - TokenList tokens = (TokenList) program.getTokenList().get(line - 1); - String s = program.getSourceLine(line); - - for (int i = tokens.size() - 1; i >= 0; i--) { - Token token = tokens.get(i); - if (tokenIsMacroParameter(token.getValue(), true)) { - int repl = -1; - for (int j = 0; j < this.args.size(); j++) { - if (this.args.get(j).equals(token.getValue())) { - repl = j; - break; - } + /** + * returns whether tokenValue is macro parameter or not + * + * @param tokenValue + * @param acceptSpimStyleParameters accepts SPIM-style parameters which begin with '$' if true + * @return + */ + public static boolean tokenIsMacroParameter(String tokenValue, boolean acceptSpimStyleParameters) + { + if (acceptSpimStyleParameters) + { + // Bug fix: SPIM accepts parameter names that start with $ instead of %. This can + // lead to problems since register names also start with $. This IF condition + // should filter out register names. Originally filtered those from regular set but not + // from Coprocessor0 or Coprocessor1 register sets. Expanded the condition. + // DPS 7-July-2014. + if (tokenValue.length() > 0 && tokenValue.charAt(0) == '$' && + RegisterFile.getUserRegister(tokenValue) == null && + Coprocessor0.getRegister(tokenValue) == null && // added 7-July-2014 + Coprocessor1.getRegister(tokenValue) == null) // added 7-July-2014 + { + return true; } - String substitute = token.getValue(); - if (repl != -1) - substitute = args.get(repl + 1).toString(); - else { - errors.add(new ErrorMessage(program, token.getSourceLine(), - token.getStartPos(), "Unknown macro parameter")); - } - s = replaceToken(s, token, substitute); - } - else if (tokenIsMacroLabel(token.getValue())){ - String substitute = token.getValue()+"_M"+counter; - s=replaceToken(s, token, substitute); - } - } - return s; - } + } + return tokenValue.length() > 1 && tokenValue.charAt(0) == '%'; + } + public String getName() + { + return name; + } -/** - * returns true if value is name of a label defined in this macro's body. - * @param value - * @return - */ - private boolean tokenIsMacroLabel(String value) { - return (Collections.binarySearch(labels, value)>=0); - } + public void setName(String name) + { + this.name = name; + } -/** - * replaces token tokenToBeReplaced which is occured in source with substitute. - * @param source - * @param tokenToBeReplaced - * @param substitute - * @return - */ -// Initially the position of the substitute was based on token position but that proved problematic -// in that the source string does not always match the token list from which the token comes. The -// token list has already had .eqv equivalences applied whereas the source may not. This is because -// the source comes from a macro definition? That has proven to be a tough question to answer. -// DPS 12-feb-2013 - private String replaceToken(String source, Token tokenToBeReplaced, String substitute) { - String stringToBeReplaced = tokenToBeReplaced.getValue(); - int pos = source.indexOf(stringToBeReplaced); - return (pos < 0) ? source : source.substring(0, pos) + substitute + source.substring(pos+stringToBeReplaced.length()); - } - -/** - * returns whether tokenValue is macro parameter or not - * @param tokenValue - * @param acceptSpimStyleParameters accepts SPIM-style parameters which begin with '$' if true - * @return - */ - public static boolean tokenIsMacroParameter(String tokenValue, boolean acceptSpimStyleParameters) { - if (acceptSpimStyleParameters) { - // Bug fix: SPIM accepts parameter names that start with $ instead of %. This can - // lead to problems since register names also start with $. This IF condition - // should filter out register names. Originally filtered those from regular set but not - // from Coprocessor0 or Coprocessor1 register sets. Expanded the condition. - // DPS 7-July-2014. - if (tokenValue.length() > 0 && tokenValue.charAt(0) == '$' && - RegisterFile.getUserRegister(tokenValue) == null && - Coprocessor0.getRegister(tokenValue) == null && // added 7-July-2014 - Coprocessor1.getRegister(tokenValue) == null) // added 7-July-2014 - { - return true; - } - } - return tokenValue.length() > 1 && tokenValue.charAt(0) == '%'; - } + public MIPSprogram getProgram() + { + return program; + } - public void addLabel(String value) { - labels.add(value); - } + public void setProgram(MIPSprogram program) + { + this.program = program; + } -/** - * Operations to be done on this macro before it is committed in macro pool. - */ - public void readyForCommit() { - Collections.sort(labels); - } + public int getFromLine() + { + return fromLine; + } + public void setFromLine(int fromLine) + { + this.fromLine = fromLine; + } + + public int getOriginalFromLine() + { + return this.origFromLine; + } + + public void setOriginalFromLine(int origFromLine) + { + this.origFromLine = origFromLine; + } + + public int getToLine() + { + return toLine; + } + + public void setToLine(int toLine) + { + this.toLine = toLine; + } + + public int getOriginalToLine() + { + return this.origToLine; + } + + public void setOriginalToLine(int origToLine) + { + this.origToLine = origToLine; + } + + public ArrayList getArgs() + { + return args; + } + + public void setArgs(ArrayList args) + { + this.args = args; + } + + /** + * @param obj {@link Macro} object to check if their name and count of arguments are same + */ + @Override + public boolean equals(Object obj) + { + if (obj instanceof Macro) + { + Macro macro = (Macro) obj; + return macro.getName().equals(name) && (macro.args.size() == args.size()); + } + return super.equals(obj); + } + + public void addArg(String value) + { + args.add(value); + } + + /** + * Substitutes macro arguments in a line of source code inside macro definition to be parsed after macro expansion. + *
Also appends "_M#" to all labels defined inside macro body where # is value of counter + * + * @param line source line number in macro definition to be substituted + * @param args + * @param counter unique macro expansion id + * @param errors + * @return line-th line of source code, with substituted + * arguments + */ + + public String getSubstitutedLine(int line, TokenList args, long counter, ErrorList errors) + { + TokenList tokens = (TokenList) program.getTokenList().get(line - 1); + String s = program.getSourceLine(line); + + for (int i = tokens.size() - 1; i >= 0; i--) + { + Token token = tokens.get(i); + if (tokenIsMacroParameter(token.getValue(), true)) + { + int repl = -1; + for (int j = 0; j < this.args.size(); j++) + { + if (this.args.get(j).equals(token.getValue())) + { + repl = j; + break; + } + } + String substitute = token.getValue(); + if (repl != -1) + { + substitute = args.get(repl + 1).toString(); + } + else + { + errors.add(new ErrorMessage(program, token.getSourceLine(), + token.getStartPos(), "Unknown macro parameter")); + } + s = replaceToken(s, token, substitute); + } + else if (tokenIsMacroLabel(token.getValue())) + { + String substitute = token.getValue() + "_M" + counter; + s = replaceToken(s, token, substitute); + } + } + return s; + } + + /** + * returns true if value is name of a label defined in this macro's body. + * + * @param value + * @return + */ + private boolean tokenIsMacroLabel(String value) + { + return (Collections.binarySearch(labels, value) >= 0); + } + + /** + * replaces token tokenToBeReplaced which is occured in source with + * substitute. + * + * @param source + * @param tokenToBeReplaced + * @param substitute + * @return + */ + // Initially the position of the substitute was based on token position but that proved problematic + // in that the source string does not always match the token list from which the token comes. The + // token list has already had .eqv equivalences applied whereas the source may not. This is because + // the source comes from a macro definition? That has proven to be a tough question to answer. + // DPS 12-feb-2013 + private String replaceToken(String source, Token tokenToBeReplaced, String substitute) + { + String stringToBeReplaced = tokenToBeReplaced.getValue(); + int pos = source.indexOf(stringToBeReplaced); + return (pos < 0) ? source : source.substring(0, pos) + substitute + source.substring(pos + stringToBeReplaced.length()); + } + + public void addLabel(String value) + { + labels.add(value); + } + + /** + * Operations to be done on this macro before it is committed in macro pool. + */ + public void readyForCommit() + { + Collections.sort(labels); + } } diff --git a/src/main/java/mars/assembler/MacroPool.java b/src/main/java/mars/assembler/MacroPool.java index a963990..db1cd62 100644 --- a/src/main/java/mars/assembler/MacroPool.java +++ b/src/main/java/mars/assembler/MacroPool.java @@ -1,10 +1,8 @@ - package mars.assembler; +package mars.assembler; - import java.util.ArrayList; - import java.util.Stack; +import mars.MIPSprogram; - import mars.ErrorList; - import mars.MIPSprogram; +import java.util.ArrayList; /* Copyright (c) 2013. @@ -30,173 +28,194 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + /** - * Stores information of macros defined by now.
- * Will be used in first pass of assembling MIPS source code. When reached + * Stores information of macros defined by now.
Will be used in first pass of assembling MIPS source code. When + * reached * .macro directive, parser calls - * {@link MacroPool#BeginMacro(String, int)} and skips source code lines until - * reaches .end_macro directive. then calls - * {@link MacroPool#CommitMacro(int)} and the macro information stored in a - * {@link Macro} instance will be added to {@link #macroList}.
- * Each {@link MIPSprogram} will have one {@link MacroPool}
- * NOTE: Forward referencing macros (macro expansion before its definition in - * source code) and Nested macro definition (defining a macro inside other macro - * definition) are not supported. - * + * {@link MacroPool#BeginMacro(String, int)} and skips source code lines until reaches .end_macro + * directive. then calls {@link MacroPool#CommitMacro(int)} and the macro information stored in a {@link Macro} instance + * will be added to {@link #macroList}.
Each {@link MIPSprogram} will have one {@link MacroPool}
NOTE: Forward + * referencing macros (macro expansion before its definition in source code) and Nested macro definition (defining a + * macro inside other macro definition) are not supported. + * * @author M.H.Sekhavat */ - public class MacroPool { - private MIPSprogram program; - /** - * List of macros defined by now - */ - private ArrayList macroList; - /** - * @see #BeginMacro(String, int) - */ - private Macro current; - private ArrayList callStack; - private ArrayList callStackOrigLines; - /** - * @see #getNextCounter() - */ - private int counter; - - - /** - * Create an empty MacroPool for given program - * @param mipsProgram associated MIPS program - */ - public MacroPool(MIPSprogram mipsProgram) { - this.program = mipsProgram; - macroList = new ArrayList(); - callStack=new ArrayList(); - callStackOrigLines=new ArrayList(); - current = null; - counter = 0; - } - - /** - * This method will be called by parser when reached .macro - * directive.
- * Instantiates a new {@link Macro} object and stores it in {@link #current} - * . {@link #current} will be added to {@link #macroList} by - * {@link #CommitMacro(int)} - * - * @param nameToken - * Token containing name of macro after .macro directive - */ - - public void beginMacro(Token nameToken) { - current = new Macro(); - current.setName(nameToken.getValue()); - current.setFromLine(nameToken.getSourceLine()); - current.setOriginalFromLine(nameToken.getOriginalSourceLine()); - current.setProgram(program); - } - - /** - * This method will be called by parser when reached .end_macro - * directive.
- * Adds/Replaces {@link #current} macro into the {@link #macroList}. - * - * @param endToken - * Token containing .end_macro directive in source code - */ - - public void commitMacro(Token endToken) { - current.setToLine(endToken.getSourceLine()); - current.setOriginalToLine(endToken.getOriginalSourceLine()); - current.readyForCommit(); - macroList.add(current); - current = null; - } - - /** - * Will be called by parser when reaches a macro expansion call - * - * @param tokens - * tokens passed to macro expansion call - * @return {@link Macro} object matching the name and argument count of - * tokens passed - */ - public Macro getMatchingMacro(TokenList tokens, int callerLine) { - if (tokens.size() < 1) +public class MacroPool +{ + private final MIPSprogram program; + + /** + * List of macros defined by now + */ + private final ArrayList macroList; + + /** + * @see #BeginMacro(String, int) + */ + private Macro current; + + private final ArrayList callStack; + + private final ArrayList callStackOrigLines; + + /** + * @see #getNextCounter() + */ + private int counter; + + + /** + * Create an empty MacroPool for given program + * + * @param mipsProgram associated MIPS program + */ + public MacroPool(MIPSprogram mipsProgram) + { + this.program = mipsProgram; + macroList = new ArrayList(); + callStack = new ArrayList(); + callStackOrigLines = new ArrayList(); + current = null; + counter = 0; + } + + /** + * This method will be called by parser when reached .macro directive.
Instantiates a new + * {@link Macro} object and stores it in {@link #current} . {@link #current} will be added to {@link #macroList} by + * {@link #CommitMacro(int)} + * + * @param nameToken Token containing name of macro after .macro directive + */ + + public void beginMacro(Token nameToken) + { + current = new Macro(); + current.setName(nameToken.getValue()); + current.setFromLine(nameToken.getSourceLine()); + current.setOriginalFromLine(nameToken.getOriginalSourceLine()); + current.setProgram(program); + } + + /** + * This method will be called by parser when reached .end_macro directive.
Adds/Replaces + * {@link #current} macro into the {@link #macroList}. + * + * @param endToken Token containing .end_macro directive in source code + */ + + public void commitMacro(Token endToken) + { + current.setToLine(endToken.getSourceLine()); + current.setOriginalToLine(endToken.getOriginalSourceLine()); + current.readyForCommit(); + macroList.add(current); + current = null; + } + + /** + * Will be called by parser when reaches a macro expansion call + * + * @param tokens tokens passed to macro expansion call + * @return {@link Macro} object matching the name and argument count of tokens passed + */ + public Macro getMatchingMacro(TokenList tokens, int callerLine) + { + if (tokens.size() < 1) + { return null; - Macro ret = null; - Token firstToken = tokens.get(0); - for (Macro macro : macroList) { + } + Macro ret = null; + Token firstToken = tokens.get(0); + for (Macro macro : macroList) + { if (macro.getName().equals(firstToken.getValue()) - && macro.getArgs().size() + 1 == tokens.size() - //&& macro.getToLine() < callerLine // condition removed; doesn't work nicely in conjunction with .include, and does not seem necessary. DPS 8-MAR-2013 - && (ret == null || ret.getFromLine() < macro.getFromLine())) - ret = macro; - } - return ret; - } - - /** - * @param value - * @return true if any macros have been defined with name value - * by now, not concerning arguments count. - */ - public boolean matchesAnyMacroName(String value) { - for (Macro macro : macroList) + && macro.getArgs().size() + 1 == tokens.size() + //&& macro.getToLine() < callerLine // condition removed; doesn't work nicely in conjunction with .include, and does not seem necessary. DPS 8-MAR-2013 + && (ret == null || ret.getFromLine() < macro.getFromLine())) + { + ret = macro; + } + } + return ret; + } + + /** + * @param value + * @return true if any macros have been defined with name value by now, not concerning arguments count. + */ + public boolean matchesAnyMacroName(String value) + { + for (Macro macro : macroList) + { if (macro.getName().equals(value)) - return true; - return false; - } - - - public Macro getCurrent() { - return current; - } - - public void setCurrent(Macro current) { - this.current = current; - } - - /** - * {@link #counter} will be set to 0 on construction of this class and will - * be incremented by each call. parser calls this method once for every - * expansions. it will be a unique id for each expansion of macro in a file - * - * @return counter value - */ - public int getNextCounter() { - return counter++; - } - - - public ArrayList getCallStack() { - return callStack; - } - - - public boolean pushOnCallStack(Token token) { //returns true if detected expansion loop - int sourceLine = token.getSourceLine(); - int origSourceLine = token.getOriginalSourceLine(); - if (callStack.contains(sourceLine)) + { + return true; + } + } + return false; + } + + + public Macro getCurrent() + { + return current; + } + + public void setCurrent(Macro current) + { + this.current = current; + } + + /** + * {@link #counter} will be set to 0 on construction of this class and will be incremented by each call. parser + * calls this method once for every expansions. it will be a unique id for each expansion of macro in a file + * + * @return counter value + */ + public int getNextCounter() + { + return counter++; + } + + + public ArrayList getCallStack() + { + return callStack; + } + + + public boolean pushOnCallStack(Token token) + { //returns true if detected expansion loop + int sourceLine = token.getSourceLine(); + int origSourceLine = token.getOriginalSourceLine(); + if (callStack.contains(sourceLine)) + { return true; - callStack.add(sourceLine); - callStackOrigLines.add(origSourceLine); - return false; - } - - public void popFromCallStack() { - callStack.remove(callStack.size()-1); - callStackOrigLines.remove(callStackOrigLines.size()-1); - } - - - public String getExpansionHistory() { - String ret=""; - for (int i=0; i0) - ret+="->"; - ret+=callStackOrigLines.get(i).toString(); - } - return ret; - } - } + } + callStack.add(sourceLine); + callStackOrigLines.add(origSourceLine); + return false; + } + + public void popFromCallStack() + { + callStack.remove(callStack.size() - 1); + callStackOrigLines.remove(callStackOrigLines.size() - 1); + } + + + public String getExpansionHistory() + { + String ret = ""; + for (int i = 0; i < callStackOrigLines.size(); i++) + { + if (i > 0) + { + ret += "->"; + } + ret += callStackOrigLines.get(i).toString(); + } + return ret; + } +} diff --git a/src/main/java/mars/assembler/OperandFormat.java b/src/main/java/mars/assembler/OperandFormat.java index 0965edc..f74c3eb 100644 --- a/src/main/java/mars/assembler/OperandFormat.java +++ b/src/main/java/mars/assembler/OperandFormat.java @@ -1,8 +1,12 @@ - package mars.assembler; - import mars.*; - import mars.util.Binary; - import mars.mips.instructions.*; - import java.util.*; +package mars.assembler; + +import mars.ErrorList; +import mars.ErrorMessage; +import mars.Globals; +import mars.mips.instructions.Instruction; +import mars.util.Binary; + +import java.util.ArrayList; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -34,127 +38,157 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Provides utility method related to MIPS operand formats. - * - * @author Pete Sanderson + * + * @author Pete Sanderson * @version August 2003 */ - public class OperandFormat { - - private OperandFormat() { - } - - /* - * Syntax test for correct match in both numbers and types of operands. - * - * @param candidateList List of tokens generated from programmer's MIPS statement. - * @param spec The (resumably best matched) MIPS instruction. - * @param errors ErrorList into which any error messages generated here will be added. - * - * @return Returns true if the programmer's statement matches the MIPS - * specification, else returns false. - */ - - static boolean tokenOperandMatch(TokenList candidateList, Instruction inst, ErrorList errors) { - if (!numOperandsCheck(candidateList, inst, errors)) +public class OperandFormat +{ + + private OperandFormat() + { + } + + /* + * Syntax test for correct match in both numbers and types of operands. + * + * @param candidateList List of tokens generated from programmer's MIPS statement. + * @param spec The (resumably best matched) MIPS instruction. + * @param errors ErrorList into which any error messages generated here will be added. + * + * @return Returns true if the programmer's statement matches the MIPS + * specification, else returns false. + */ + + static boolean tokenOperandMatch(TokenList candidateList, Instruction inst, ErrorList errors) + { + if (!numOperandsCheck(candidateList, inst, errors)) + { return false; - if (!operandTypeCheck(candidateList, inst, errors)) - return false; - return true; - } - - /* - * If candidate operator token matches more than one instruction mnemonic, then select - * first such Instruction that has an exact operand match. If none match, - * return the first Instruction and let client deal with operand mismatches. - */ - static Instruction bestOperandMatch(TokenList tokenList, ArrayList instrMatches) { - if (instrMatches == null) + } + return operandTypeCheck(candidateList, inst, errors); + } + + /* + * If candidate operator token matches more than one instruction mnemonic, then select + * first such Instruction that has an exact operand match. If none match, + * return the first Instruction and let client deal with operand mismatches. + */ + static Instruction bestOperandMatch(TokenList tokenList, ArrayList instrMatches) + { + if (instrMatches == null) + { return null; - if (instrMatches.size() == 1) + } + if (instrMatches.size() == 1) + { return (Instruction) instrMatches.get(0); - for (int i=0; i=DataTypes.MIN_HALF_VALUE && temp<=DataTypes.MAX_HALF_VALUE) - continue; - if (specType == TokenTypes.INTEGER_16U && candType == TokenTypes.INTEGER_16 && - temp>=DataTypes.MIN_UHALF_VALUE && temp<=DataTypes.MAX_UHALF_VALUE) - continue; + (specType == TokenTypes.INTEGER_16U && candType == TokenTypes.INTEGER_5) || + (specType == TokenTypes.INTEGER_32 && candType == TokenTypes.INTEGER_5) || + (specType == TokenTypes.INTEGER_32 && candType == TokenTypes.INTEGER_16U) || + (specType == TokenTypes.INTEGER_32 && candType == TokenTypes.INTEGER_16)) + { + continue; + } + if (candType == TokenTypes.INTEGER_16U || candType == TokenTypes.INTEGER_16) + { + int temp = Binary.stringToInt(candToken.getValue()); + if (specType == TokenTypes.INTEGER_16 && candType == TokenTypes.INTEGER_16U && + temp >= DataTypes.MIN_HALF_VALUE && temp <= DataTypes.MAX_HALF_VALUE) + { + continue; + } + if (specType == TokenTypes.INTEGER_16U && candType == TokenTypes.INTEGER_16 && + temp >= DataTypes.MIN_UHALF_VALUE && temp <= DataTypes.MAX_UHALF_VALUE) + { + continue; + } } if ((specType == TokenTypes.INTEGER_5 && candType == TokenTypes.INTEGER_16) || (specType == TokenTypes.INTEGER_5 && candType == TokenTypes.INTEGER_16U) || @@ -162,37 +196,39 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (specType == TokenTypes.INTEGER_16 && candType == TokenTypes.INTEGER_16U) || (specType == TokenTypes.INTEGER_16U && candType == TokenTypes.INTEGER_16) || (specType == TokenTypes.INTEGER_16U && candType == TokenTypes.INTEGER_32) || - (specType == TokenTypes.INTEGER_16 && candType == TokenTypes.INTEGER_32)) { - generateMessage(candToken, "operand is out of range", errors); - return false; + (specType == TokenTypes.INTEGER_16 && candType == TokenTypes.INTEGER_32)) + { + generateMessage(candToken, "operand is out of range", errors); + return false; } - if (candType != specType) { - generateMessage(candToken, "operand is of incorrect type", errors); - return false; + if (candType != specType) + { + generateMessage(candToken, "operand is of incorrect type", errors); + return false; } + } + + /******** nice little debugging code to see which operand format + ******** the operands for this source code instruction matched. + System.out.print("Candidate: "); + for (int i=1; ipos is < 0 or >= size() - */ - public void remove(int pos) { + /** + * Removes Token object at specified list position. Uses ArrayList remove method. + * + * @param pos Position in token list. Subsequent Tokens are shifted one position left. + * @throws IndexOutOfBoundsException if pos is < 0 or >= size() + */ + public void remove(int pos) + { tokenList.remove(pos); } - /** - * Returns empty/non-empty status of list. - * - * @return true if list has no tokens, else false. - */ - public boolean isEmpty() { + /** + * Returns empty/non-empty status of list. + * + * @return true if list has no tokens, else false. + */ + public boolean isEmpty() + { return tokenList.isEmpty(); } - /** - * Get a String representing the token list. - * - * @return String version of the token list - * (a blank is inserted after each token). - */ - - public String toString() { - String stringified = ""; - for (int i=0; inull. - **/ - - public static TokenTypes matchTokenType(String value) - { - - TokenTypes type = null; - // If it starts with single quote ('), it is a mal-formed character literal - // because a well-formed character literal was converted to string-ified - // integer before getting here... - if (value.charAt(0) == '\'') - return TokenTypes.ERROR; - - // See if it is a comment - if (value.charAt(0) == '#') - return TokenTypes.COMMENT; - - // See if it is one of the simple tokens - if (value.length() == 1) { - switch (value.charAt(0)) { - case '(' : - return TokenTypes.LEFT_PAREN; - case ')' : - return TokenTypes.RIGHT_PAREN; - case ':' : - return TokenTypes.COLON; - case '+' : - return TokenTypes.PLUS; - case '-' : - return TokenTypes.MINUS; - } - } +public final class TokenTypes +{ - // See if it is a macro parameter - if (Macro.tokenIsMacroParameter(value, false)) - return TokenTypes.MACRO_PARAMETER; - - // See if it is a register - Register reg = RegisterFile.getUserRegister(value); - if (reg != null) - if (reg.getName().equals(value)) - return TokenTypes.REGISTER_NAME; + public static final String TOKEN_DELIMITERS = "\t ,()"; + + public static final TokenTypes COMMENT = new TokenTypes("COMMENT"); + + public static final TokenTypes DIRECTIVE = new TokenTypes("DIRECTIVE"); + + public static final TokenTypes OPERATOR = new TokenTypes("OPERATOR"); + + public static final TokenTypes DELIMITER = new TokenTypes("DELIMITER"); + + /** + * note: REGISTER_NAME is token of form $zero whereas REGISTER_NUMBER is token of form $0. The former is part of + * extended assembler, and latter is part of basic assembler. + **/ + public static final TokenTypes REGISTER_NAME = new TokenTypes("REGISTER_NAME"); // mnemonic + + public static final TokenTypes REGISTER_NUMBER = new TokenTypes("REGISTER_NUMBER"); + + public static final TokenTypes FP_REGISTER_NAME = new TokenTypes("FP_REGISTER_NAME"); + + public static final TokenTypes IDENTIFIER = new TokenTypes("IDENTIFIER"); + + public static final TokenTypes LEFT_PAREN = new TokenTypes("LEFT_PAREN"); + + public static final TokenTypes RIGHT_PAREN = new TokenTypes("RIGHT_PAREN"); + + //public static final TokenTypes INTEGER = new TokenTypes("INTEGER"); + public static final TokenTypes INTEGER_5 = new TokenTypes("INTEGER_5"); + + public static final TokenTypes INTEGER_16 = new TokenTypes("INTEGER_16"); + + public static final TokenTypes INTEGER_16U = new TokenTypes("INTEGER_16U"); + + public static final TokenTypes INTEGER_32 = new TokenTypes("INTEGER_32"); + + public static final TokenTypes REAL_NUMBER = new TokenTypes("REAL_NUMBER"); + + public static final TokenTypes QUOTED_STRING = new TokenTypes("QUOTED_STRING"); + + public static final TokenTypes PLUS = new TokenTypes("PLUS"); + + public static final TokenTypes MINUS = new TokenTypes("MINUS"); + + public static final TokenTypes COLON = new TokenTypes("COLON"); + + public static final TokenTypes ERROR = new TokenTypes("ERROR"); + + public static final TokenTypes MACRO_PARAMETER = new TokenTypes("MACRO_PARAMETER"); + + private final String descriptor; + + private TokenTypes() + { + // private ctor assures no objects can be created other than those above. + descriptor = "generic"; + } + + private TokenTypes(String name) + { + descriptor = name; + } + + /** + * Classifies the given token into one of the MIPS types. + * + * @param value String containing candidate language element, extracted from MIPS program. + * @return Returns the corresponding TokenTypes object if the parameter matches a defined MIPS token type, else + * returns null. + **/ + + public static TokenTypes matchTokenType(String value) + { + + TokenTypes type = null; + // If it starts with single quote ('), it is a mal-formed character literal + // because a well-formed character literal was converted to string-ified + // integer before getting here... + if (value.charAt(0) == '\'') + { + return TokenTypes.ERROR; + } + + // See if it is a comment + if (value.charAt(0) == '#') + { + return TokenTypes.COMMENT; + } + + // See if it is one of the simple tokens + if (value.length() == 1) + { + switch (value.charAt(0)) + { + case '(': + return TokenTypes.LEFT_PAREN; + case ')': + return TokenTypes.RIGHT_PAREN; + case ':': + return TokenTypes.COLON; + case '+': + return TokenTypes.PLUS; + case '-': + return TokenTypes.MINUS; + } + } + + // See if it is a macro parameter + if (Macro.tokenIsMacroParameter(value, false)) + { + return TokenTypes.MACRO_PARAMETER; + } + + // See if it is a register + Register reg = RegisterFile.getUserRegister(value); + if (reg != null) + { + if (reg.getName().equals(value)) + { + return TokenTypes.REGISTER_NAME; + } else - return TokenTypes.REGISTER_NUMBER; - - // See if it is a floating point register - - reg = Coprocessor1.getRegister(value); - if (reg != null) + { + return TokenTypes.REGISTER_NUMBER; + } + } + + // See if it is a floating point register + + reg = Coprocessor1.getRegister(value); + if (reg != null) + { return TokenTypes.FP_REGISTER_NAME; - - // See if it is an immediate (constant) integer value - // Classify based on # bits needed to represent in binary - // This is needed because most immediate operands limited to 16 bits - // others limited to 5 bits unsigned (shift amounts) others 32 bits. - try { + } + + // See if it is an immediate (constant) integer value + // Classify based on # bits needed to represent in binary + // This is needed because most immediate operands limited to 16 bits + // others limited to 5 bits unsigned (shift amounts) others 32 bits. + try + { int i = Binary.stringToInt(value); // KENV 1/6/05 - - /*************************************************************************** - * MODIFICATION AND COMMENT, DPS 3-July-2008 - * - * The modifications of January 2005 documented below are being rescinded. - * All hexadecimal immediate values are considered 32 bits in length and - * their classification as INTEGER_5, INTEGER_16, INTEGER_16U (new) - * or INTEGER_32 depends on their 32 bit value. So 0xFFFF will be - * equivalent to 0x0000FFFF instead of 0xFFFFFFFF. This change, along with - * the introduction of INTEGER_16U (adopted from Greg Gibeling of Berkeley), - * required extensive changes to instruction templates especially for - * pseudo-instructions. - * - * This modification also appears inbuildBasicStatementFromBasicInstruction() - * in mars.ProgramStatement. - * - * ///// Begin modification 1/4/05 KENV /////////////////////////////////////////// - * // We have decided to interpret non-signed (no + or -) 16-bit hexadecimal immediate - * // operands as signed values in the range -32768 to 32767. So 0xffff will represent - * // -1, not 65535 (bit 15 as sign bit), 0x8000 will represent -32768 not 32768. - * // NOTE: 32-bit hexadecimal immediate operands whose values fall into this range - * // will be likewise affected, but they are used only in pseudo-instructions. The - * // code in ExtendedInstruction.java to split this number into upper 16 bits for "lui" - * // and lower 16 bits for "ori" works with the original source code token, so it is - * // not affected by this tweak. 32-bit immediates in data segment directives - * // are also processed elsewhere so are not affected either. - * //////////////////////////////////////////////////////////////////////////////// - * - * if ( Binary.isHex(value) && - * (i >= 32768) && - * (i <= 65535) ) // Range 0x8000 ... 0xffff - * { - * // Subtract the 0xffff bias, because strings in the - * // range "0x8000" ... "0xffff" are used to represent - * // 16-bit negative numbers, not positive numbers. - * i = i - 65536; - * } - * // ------------- END KENV 1/4/05 MODIFICATIONS -------------- - * - ************************** END DPS 3-July-2008 COMMENTS *******************************/ - // shift operands must be in range 0-31 - if (i>=0 && i<=31) { - return TokenTypes.INTEGER_5; - } - if (i>=DataTypes.MIN_UHALF_VALUE && i<=DataTypes.MAX_UHALF_VALUE) { - return TokenTypes.INTEGER_16U; - } - if (i>=DataTypes.MIN_HALF_VALUE && i<=DataTypes.MAX_HALF_VALUE) { - return TokenTypes.INTEGER_16; - } - return TokenTypes.INTEGER_32; // default when no other type is applicable - } - catch(NumberFormatException e) + + /*************************************************************************** + * MODIFICATION AND COMMENT, DPS 3-July-2008 + * + * The modifications of January 2005 documented below are being rescinded. + * All hexadecimal immediate values are considered 32 bits in length and + * their classification as INTEGER_5, INTEGER_16, INTEGER_16U (new) + * or INTEGER_32 depends on their 32 bit value. So 0xFFFF will be + * equivalent to 0x0000FFFF instead of 0xFFFFFFFF. This change, along with + * the introduction of INTEGER_16U (adopted from Greg Gibeling of Berkeley), + * required extensive changes to instruction templates especially for + * pseudo-instructions. + * + * This modification also appears inbuildBasicStatementFromBasicInstruction() + * in mars.ProgramStatement. + * + * ///// Begin modification 1/4/05 KENV /////////////////////////////////////////// + * // We have decided to interpret non-signed (no + or -) 16-bit hexadecimal immediate + * // operands as signed values in the range -32768 to 32767. So 0xffff will represent + * // -1, not 65535 (bit 15 as sign bit), 0x8000 will represent -32768 not 32768. + * // NOTE: 32-bit hexadecimal immediate operands whose values fall into this range + * // will be likewise affected, but they are used only in pseudo-instructions. The + * // code in ExtendedInstruction.java to split this number into upper 16 bits for "lui" + * // and lower 16 bits for "ori" works with the original source code token, so it is + * // not affected by this tweak. 32-bit immediates in data segment directives + * // are also processed elsewhere so are not affected either. + * //////////////////////////////////////////////////////////////////////////////// + * + * if ( Binary.isHex(value) && + * (i >= 32768) && + * (i <= 65535) ) // Range 0x8000 ... 0xffff + * { + * // Subtract the 0xffff bias, because strings in the + * // range "0x8000" ... "0xffff" are used to represent + * // 16-bit negative numbers, not positive numbers. + * i = i - 65536; + * } + * // ------------- END KENV 1/4/05 MODIFICATIONS -------------- + * + ************************** END DPS 3-July-2008 COMMENTS *******************************/ + // shift operands must be in range 0-31 + if (i >= 0 && i <= 31) { - // NO ACTION -- exception suppressed + return TokenTypes.INTEGER_5; } - - // See if it is a real (fixed or floating point) number. Note that parseDouble() - // accepts integer values but if it were an integer literal we wouldn't get this far. - try { + if (i >= DataTypes.MIN_UHALF_VALUE && i <= DataTypes.MAX_UHALF_VALUE) + { + return TokenTypes.INTEGER_16U; + } + if (i >= DataTypes.MIN_HALF_VALUE && i <= DataTypes.MAX_HALF_VALUE) + { + return TokenTypes.INTEGER_16; + } + return TokenTypes.INTEGER_32; // default when no other type is applicable + } + catch (NumberFormatException e) + { + // NO ACTION -- exception suppressed + } + + // See if it is a real (fixed or floating point) number. Note that parseDouble() + // accepts integer values but if it were an integer literal we wouldn't get this far. + try + { Double.parseDouble(value); return TokenTypes.REAL_NUMBER; - } - catch (NumberFormatException e) - { + } + catch (NumberFormatException e) + { // NO ACTION -- exception suppressed - } - - // See if it is an instruction operator - if (Globals.instructionSet.matchOperator(value) != null) + } + + // See if it is an instruction operator + if (Globals.instructionSet.matchOperator(value) != null) + { return TokenTypes.OPERATOR; - - // See if it is a directive - if (value.charAt(0) == '.' && Directives.matchDirective(value) != null) { + } + + // See if it is a directive + if (value.charAt(0) == '.' && Directives.matchDirective(value) != null) + { return TokenTypes.DIRECTIVE; - } - - // See if it is a quoted string - if (value.charAt(0) == '"') + } + + // See if it is a quoted string + if (value.charAt(0) == '"') + { return TokenTypes.QUOTED_STRING; - - // Test for identifier goes last because I have defined tokens for various - // MIPS constructs (such as operators and directives) that also could fit - // the lexical specifications of an identifier, and those need to be - // recognized first. - if (isValidIdentifier(value)) + } + + // Test for identifier goes last because I have defined tokens for various + // MIPS constructs (such as operators and directives) that also could fit + // the lexical specifications of an identifier, and those need to be + // recognized first. + if (isValidIdentifier(value)) + { return TokenTypes.IDENTIFIER; - - // Matches no MIPS language token. - return TokenTypes.ERROR; - } - - /** - * - * Lets you know if given tokentype is for integers (INTGER_5, INTEGER_16, INTEGER_32). - * - * @param type the TokenType of interest - * @return true if type is an integer type, false otherwise. - **/ - public static boolean isIntegerTokenType(TokenTypes type) { - return type == TokenTypes.INTEGER_5 || type == TokenTypes.INTEGER_16 || - type == TokenTypes.INTEGER_16U || type == TokenTypes.INTEGER_32; - } + } + // Matches no MIPS language token. + return TokenTypes.ERROR; + } - /** - * - * Lets you know if given tokentype is for floating point numbers (REAL_NUMBER). - * - * @param type the TokenType of interest - * @return true if type is an floating point type, false otherwise. - **/ - public static boolean isFloatingTokenType(TokenTypes type) { - return type == TokenTypes.REAL_NUMBER; - } - - - // COD2, A-51: "Identifiers are a sequence of alphanumeric characters, - // underbars (_), and dots (.) that do not begin with a number." - // Ideally this would be in a separate Identifier class but I did not see an immediate - // need beyond this method (refactoring effort would probably identify other uses - // related to symbol table). - // - // DPS 14-Jul-2008: added '$' as valid symbol. Permits labels to include $. - // MIPS-target GCC will produce labels that start with $. - public static boolean isValidIdentifier(String value) { - boolean result = - (Character.isLetter(value.charAt(0)) || value.charAt(0)=='_' || value.charAt(0)=='.' || value.charAt(0)=='$'); - int index = 1; - while (result && index < value.length()) { - if (!(Character.isLetterOrDigit(value.charAt(index)) || value.charAt(index)=='_' || value.charAt(index)=='.' || value.charAt(index)=='$')) - result = false; + /** + * Lets you know if given tokentype is for integers (INTGER_5, INTEGER_16, INTEGER_32). + * + * @param type the TokenType of interest + * @return true if type is an integer type, false otherwise. + **/ + public static boolean isIntegerTokenType(TokenTypes type) + { + return type == TokenTypes.INTEGER_5 || type == TokenTypes.INTEGER_16 || + type == TokenTypes.INTEGER_16U || type == TokenTypes.INTEGER_32; + } + + /** + * Lets you know if given tokentype is for floating point numbers (REAL_NUMBER). + * + * @param type the TokenType of interest + * @return true if type is an floating point type, false otherwise. + **/ + public static boolean isFloatingTokenType(TokenTypes type) + { + return type == TokenTypes.REAL_NUMBER; + } + + // COD2, A-51: "Identifiers are a sequence of alphanumeric characters, + // underbars (_), and dots (.) that do not begin with a number." + // Ideally this would be in a separate Identifier class but I did not see an immediate + // need beyond this method (refactoring effort would probably identify other uses + // related to symbol table). + // + // DPS 14-Jul-2008: added '$' as valid symbol. Permits labels to include $. + // MIPS-target GCC will produce labels that start with $. + public static boolean isValidIdentifier(String value) + { + boolean result = + (Character.isLetter(value.charAt(0)) || value.charAt(0) == '_' || value.charAt(0) == '.' || value.charAt(0) == '$'); + int index = 1; + while (result && index < value.length()) + { + if (!(Character.isLetterOrDigit(value.charAt(index)) || value.charAt(index) == '_' || value.charAt(index) == '.' || value.charAt(index) == '$')) + { + result = false; + } index++; - } - return result; - } - - } + } + return result; + } + + /** + * Produces String equivalent of this token type, which is its name. + * + * @return String containing descriptive name for token type. + **/ + public String toString() + { + return descriptor; + } + +} diff --git a/src/main/java/mars/assembler/Tokenizer.java b/src/main/java/mars/assembler/Tokenizer.java index 7186f8d..b4f757c 100644 --- a/src/main/java/mars/assembler/Tokenizer.java +++ b/src/main/java/mars/assembler/Tokenizer.java @@ -1,7 +1,11 @@ - package mars.assembler; - import mars.*; - import java.util.*; - import java.io.*; +package mars.assembler; + +import mars.*; + +import java.io.File; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; /* Copyright (c) 2003-2013, Pete Sanderson and Kenneth Vollmar @@ -32,546 +36,612 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * A tokenizer is capable of tokenizing a complete MIPS program, or a given line from - * a MIPS program. Since MIPS is line-oriented, each line defines a complete statement. - * Tokenizing is the process of analyzing the input MIPS program for the purpose of - * recognizing each MIPS language element. The types of language elements are known as "tokens". - * MIPS tokens are defined in the TokenTypes class.

- * Example:
- * The MIPS statement here: lw $t3, 8($t4) #load third member of array
- * generates the following token list
- * IDENTIFIER, COLON, OPERATOR, REGISTER_NAME, COMMA, INTEGER_5, LEFT_PAREN, - * REGISTER_NAME, RIGHT_PAREN, COMMENT
- * + * A tokenizer is capable of tokenizing a complete MIPS program, or a given line from a MIPS program. Since MIPS is + * line-oriented, each line defines a complete statement. Tokenizing is the process of analyzing the input MIPS program + * for the purpose of recognizing each MIPS language element. The types of language elements are known as "tokens". + * MIPS tokens are defined in the TokenTypes class.

Example:
The MIPS statement here: lw $t3, 8($t4) + * #load third member of array
generates the following token list
IDENTIFIER, COLON, OPERATOR, + * REGISTER_NAME, COMMA, INTEGER_5, LEFT_PAREN, REGISTER_NAME, RIGHT_PAREN, COMMENT
+ * * @author Pete Sanderson * @version August 2003 **/ - public class Tokenizer { - - private ErrorList errors; - private MIPSprogram sourceMIPSprogram; - private HashMap equivalents; // DPS 11-July-2012 - // The 8 escaped characters are: single quote, double quote, backslash, newline (linefeed), - // tab, backspace, return, form feed. The characters and their corresponding decimal codes: - private static final String escapedCharacters = "'\"\\ntbrf0"; - private static final String[] escapedCharactersValues = {"39","34","92","10","9","8","13","12","0"}; - - /** - * Simple constructor. Initializes empty error list. - */ - - public Tokenizer() { - this(null); - } - - /** - * Constructor for use with existing MIPSprogram. Designed to be used with Macro feature. - * @param program A previously-existing MIPSprogram object or null if none. - */ - public Tokenizer(MIPSprogram program){ - errors = new ErrorList(); - sourceMIPSprogram = program; - } - - /** - * Will tokenize a complete MIPS program. MIPS is line oriented (not free format), - * so we will be line-oriented too. - * - * @param p The MIPSprogram to be tokenized. - * @return An ArrayList representing the tokenized program. Each list member is a TokenList - * that represents a tokenized source statement from the MIPS program. - **/ - - public ArrayList tokenize(MIPSprogram p) throws ProcessingException { - sourceMIPSprogram = p; - equivalents = new HashMap(); // DPS 11-July-2012 - ArrayList tokenList = new ArrayList(); - //ArrayList source = p.getSourceList(); - ArrayList source = processIncludes(p, new HashMap()); // DPS 9-Jan-2013 - p.setSourceLineList(source); - TokenList currentLineTokens; - String sourceLine; - for (int i=0; i equivalents; // DPS 11-July-2012 + + /** + * Simple constructor. Initializes empty error list. + */ + + public Tokenizer() + { + this(null); + } + + /** + * Constructor for use with existing MIPSprogram. Designed to be used with Macro feature. + * + * @param program A previously-existing MIPSprogram object or null if none. + */ + public Tokenizer(MIPSprogram program) + { + errors = new ErrorList(); + sourceMIPSprogram = program; + } + + /** + * Will tokenize a complete MIPS program. MIPS is line oriented (not free format), so we will be line-oriented + * too. + * + * @param p The MIPSprogram to be tokenized. + * @return An ArrayList representing the tokenized program. Each list member is a TokenList that represents a + * tokenized source statement from the MIPS program. + **/ + + public ArrayList tokenize(MIPSprogram p) throws ProcessingException + { + sourceMIPSprogram = p; + equivalents = new HashMap(); // DPS 11-July-2012 + ArrayList tokenList = new ArrayList(); + //ArrayList source = p.getSourceList(); + ArrayList source = processIncludes(p, new HashMap()); // DPS 9-Jan-2013 + p.setSourceLineList(source); + TokenList currentLineTokens; + String sourceLine; + for (int i = 0; i < source.size(); i++) + { + sourceLine = source.get(i).getSource(); + currentLineTokens = this.tokenizeLine(i + 1, sourceLine); + tokenList.add(currentLineTokens); // DPS 03-Jan-2013. Related to 11-July-2012. If source code substitution was made - // based on .eqv directive during tokenizing, the processed line, a String, is - // not the same object as the original line. Thus I can use != instead of !equals() - // This IF statement will replace original source with source modified by .eqv substitution. - // Not needed by assembler, but looks better in the Text Segment Display. - if (sourceLine.length() > 0 && sourceLine != currentLineTokens.getProcessedLine()) { - source.set(i,new SourceLine(currentLineTokens.getProcessedLine(),source.get(i).getMIPSprogram(), source.get(i).getLineNumber())); - } - } - if (errors.errorsOccurred()) { + // based on .eqv directive during tokenizing, the processed line, a String, is + // not the same object as the original line. Thus I can use != instead of !equals() + // This IF statement will replace original source with source modified by .eqv substitution. + // Not needed by assembler, but looks better in the Text Segment Display. + if (sourceLine.length() > 0 && sourceLine != currentLineTokens.getProcessedLine()) + { + source.set(i, new SourceLine(currentLineTokens.getProcessedLine(), source.get(i).getMIPSprogram(), source.get(i).getLineNumber())); + } + } + if (errors.errorsOccurred()) + { throw new ProcessingException(errors); - } - return tokenList; - } - - - - // pre-pre-processing pass through source code to process any ".include" directives. - // When one is encountered, the contents of the included file are inserted at that - // point. If no .include statements, the return value is a new array list but - // with the same lines of source code. Uses recursion to correctly process included - // files that themselves have .include. Plus it will detect and report recursive - // includes both direct and indirect. - // DPS 11-Jan-2013 - private ArrayList processIncludes(MIPSprogram program, Map inclFiles) throws ProcessingException { - ArrayList source = program.getSourceList(); - ArrayList result = new ArrayList(source.size()); - for (int i=0; i processIncludes(MIPSprogram program, Map inclFiles) throws ProcessingException + { + ArrayList source = program.getSourceList(); + ArrayList result = new ArrayList(source.size()); + for (int i = 0; i < source.size(); i++) + { String line = (String) source.get(i); - TokenList tl = tokenizeLine(program, i+1, line, false); + TokenList tl = tokenizeLine(program, i + 1, line, false); boolean hasInclude = false; - for (int ii=0; ii ii+1) - && tl.get(ii+1).getType() == TokenTypes.QUOTED_STRING) { - String filename = tl.get(ii+1).getValue(); - filename = filename.substring(1, filename.length()-1); // get rid of quotes - // Handle either absolute or relative pathname for .include file - if (!new File(filename).isAbsolute()) { - filename = new File(program.getFilename()).getParent()+File.separator+filename; - } - if (inclFiles.containsKey(filename)) { - // This is a recursive include. Generate error message and return immediately. - Token t = tl.get(ii+1); - errors.add(new ErrorMessage(program, t.getSourceLine(),t.getStartPos(), - "Recursive include of file "+filename)); - throw new ProcessingException(errors); - } - inclFiles.put(filename, filename); - MIPSprogram incl = new MIPSprogram(); - try { - incl.readSource(filename); - } - catch (ProcessingException p) { - Token t = tl.get(ii+1); - errors.add(new ErrorMessage(program, t.getSourceLine(),t.getStartPos(), - "Error reading include file "+filename)); + for (int ii = 0; ii < tl.size(); ii++) + { + if (tl.get(ii).getValue().equalsIgnoreCase(Directives.INCLUDE.getName()) + && (tl.size() > ii + 1) + && tl.get(ii + 1).getType() == TokenTypes.QUOTED_STRING) + { + String filename = tl.get(ii + 1).getValue(); + filename = filename.substring(1, filename.length() - 1); // get rid of quotes + // Handle either absolute or relative pathname for .include file + if (!new File(filename).isAbsolute()) + { + filename = new File(program.getFilename()).getParent() + File.separator + filename; + } + if (inclFiles.containsKey(filename)) + { + // This is a recursive include. Generate error message and return immediately. + Token t = tl.get(ii + 1); + errors.add(new ErrorMessage(program, t.getSourceLine(), t.getStartPos(), + "Recursive include of file " + filename)); throw new ProcessingException(errors); - } - ArrayList allLines = processIncludes(incl, inclFiles); - result.addAll(allLines); - hasInclude = true; - break; - } + } + inclFiles.put(filename, filename); + MIPSprogram incl = new MIPSprogram(); + try + { + incl.readSource(filename); + } + catch (ProcessingException p) + { + Token t = tl.get(ii + 1); + errors.add(new ErrorMessage(program, t.getSourceLine(), t.getStartPos(), + "Error reading include file " + filename)); + throw new ProcessingException(errors); + } + ArrayList allLines = processIncludes(incl, inclFiles); + result.addAll(allLines); + hasInclude = true; + break; + } } - if (!hasInclude){ - result.add(new SourceLine(line, program, i+1));//line); + if (!hasInclude) + { + result.add(new SourceLine(line, program, i + 1));//line); } - } - return result; - } - - /** - * Used only to create a token list for the example provided with each instruction - * specification. - * - * @param example The example MIPS instruction to be tokenized. - * - * @return An TokenList representing the tokenized instruction. Each list member is a Token - * that represents one language element. - * - * @throws ProcessingException This occurs only if the instruction specification itself - * contains one or more lexical (i.e. token) errors. - **/ - - public TokenList tokenizeExampleInstruction(String example) throws ProcessingException { - TokenList result = new TokenList(); - result = tokenizeLine(sourceMIPSprogram, 0, example, false); - if (errors.errorsOccurred()) { + } + return result; + } + + /** + * Used only to create a token list for the example provided with each instruction specification. + * + * @param example The example MIPS instruction to be tokenized. + * @return An TokenList representing the tokenized instruction. Each list member is a Token that represents one + * language element. + * @throws ProcessingException This occurs only if the instruction specification itself contains one or more + * lexical (i.e. token) errors. + **/ + + public TokenList tokenizeExampleInstruction(String example) throws ProcessingException + { + TokenList result = new TokenList(); + result = tokenizeLine(sourceMIPSprogram, 0, example, false); + if (errors.errorsOccurred()) + { throw new ProcessingException(errors); - } - return result; - } - - - /** - * Will tokenize one line of source code. If lexical errors are discovered, - * they are noted in an ErrorMessage object which is added to the ErrorList. - * Will NOT throw an exception yet because we want to persevere beyond first error. - * - * @param lineNum line number from source code (used in error message) - * @param theLine String containing source code - * @return the generated token list for that line - * - **/ + } + return result; + } + + + /** + * Will tokenize one line of source code. If lexical errors are discovered, they are noted in an ErrorMessage + * object which is added to the ErrorList. Will NOT throw an exception yet because we want to persevere beyond first + * error. + * + * @param lineNum line number from source code (used in error message) + * @param theLine String containing source code + * @return the generated token list for that line + **/ /* - * - * Tokenizing is not as easy as it appears at first blush, because the typical - * delimiters: space, tab, comma, can all appear inside MIPS quoted ASCII strings! - * Also, spaces are not as necessary as they seem, the following line is accepted - * and parsed correctly by SPIM: label:lw,$t4,simple#comment - * as is this weird variation: label :lw $t4 ,simple , , , # comment - * - * as is this line: stuff:.asciiz"# ,\n\"","aaaaa" (interestingly, if you put - * additional characters after the \", they are ignored!!) - * - * I also would like to know the starting character position in the line of each - * token, for error reporting purposes. StringTokenizer cannot give you this. - * - * Given all the above, it is just as easy to "roll my own" as to use StringTokenizer - */ - - // Modified for release 4.3, to preserve existing API. - public TokenList tokenizeLine(int lineNum, String theLine) { - return tokenizeLine(sourceMIPSprogram, lineNum, theLine, true); - } + * + * Tokenizing is not as easy as it appears at first blush, because the typical + * delimiters: space, tab, comma, can all appear inside MIPS quoted ASCII strings! + * Also, spaces are not as necessary as they seem, the following line is accepted + * and parsed correctly by SPIM: label:lw,$t4,simple#comment + * as is this weird variation: label :lw $t4 ,simple , , , # comment + * + * as is this line: stuff:.asciiz"# ,\n\"","aaaaa" (interestingly, if you put + * additional characters after the \", they are ignored!!) + * + * I also would like to know the starting character position in the line of each + * token, for error reporting purposes. StringTokenizer cannot give you this. + * + * Given all the above, it is just as easy to "roll my own" as to use StringTokenizer + */ - /** - * Will tokenize one line of source code. If lexical errors are discovered, - * they are noted in an ErrorMessage object which is added to the provided ErrorList - * instead of the Tokenizer's error list. Will NOT throw an exception. - * - * @param lineNum line number from source code (used in error message) - * @param theLine String containing source code - * @param callerErrorList errors will go into this list instead of tokenizer's list. - * @return the generated token list for that line - * - **/ - public TokenList tokenizeLine(int lineNum, String theLine, ErrorList callerErrorList) { - ErrorList saveList = this.errors; - this.errors = callerErrorList; - TokenList tokens = this.tokenizeLine(lineNum, theLine); - this.errors = saveList; - return tokens; - } + // Modified for release 4.3, to preserve existing API. + public TokenList tokenizeLine(int lineNum, String theLine) + { + return tokenizeLine(sourceMIPSprogram, lineNum, theLine, true); + } + + /** + * Will tokenize one line of source code. If lexical errors are discovered, they are noted in an ErrorMessage + * object which is added to the provided ErrorList instead of the Tokenizer's error list. Will NOT throw an + * exception. + * + * @param lineNum line number from source code (used in error message) + * @param theLine String containing source code + * @param callerErrorList errors will go into this list instead of tokenizer's list. + * @return the generated token list for that line + **/ + public TokenList tokenizeLine(int lineNum, String theLine, ErrorList callerErrorList) + { + ErrorList saveList = this.errors; + this.errors = callerErrorList; + TokenList tokens = this.tokenizeLine(lineNum, theLine); + this.errors = saveList; + return tokens; + } - /** - * Will tokenize one line of source code. If lexical errors are discovered, - * they are noted in an ErrorMessage object which is added to the provided ErrorList - * instead of the Tokenizer's error list. Will NOT throw an exception. - * - * @param lineNum line number from source code (used in error message) - * @param theLine String containing source code - * @param callerErrorList errors will go into this list instead of tokenizer's list. - * @param doEqvSubstitutse boolean param set true to perform .eqv substitutions, else false - * @return the generated token list for that line - * - **/ - public TokenList tokenizeLine(int lineNum, String theLine, ErrorList callerErrorList, boolean doEqvSubstitutes) { - ErrorList saveList = this.errors; - this.errors = callerErrorList; - TokenList tokens = this.tokenizeLine(sourceMIPSprogram, lineNum, theLine,doEqvSubstitutes); - this.errors = saveList; - return tokens; - } + /** + * Will tokenize one line of source code. If lexical errors are discovered, they are noted in an ErrorMessage + * object which is added to the provided ErrorList instead of the Tokenizer's error list. Will NOT throw an + * exception. + * + * @param lineNum line number from source code (used in error message) + * @param theLine String containing source code + * @param callerErrorList errors will go into this list instead of tokenizer's list. + * @param doEqvSubstitutse boolean param set true to perform .eqv substitutions, else false + * @return the generated token list for that line + **/ + public TokenList tokenizeLine(int lineNum, String theLine, ErrorList callerErrorList, boolean doEqvSubstitutes) + { + ErrorList saveList = this.errors; + this.errors = callerErrorList; + TokenList tokens = this.tokenizeLine(sourceMIPSprogram, lineNum, theLine, doEqvSubstitutes); + this.errors = saveList; + return tokens; + } - /** - * Will tokenize one line of source code. If lexical errors are discovered, - * they are noted in an ErrorMessage object which is added to the provided ErrorList - * instead of the Tokenizer's error list. Will NOT throw an exception. - * - * @param program MIPSprogram containing this line of source - * @param lineNum line number from source code (used in error message) - * @param theLine String containing source code - * @param doEqvSubstitutes boolean param set true to perform .eqv substitutions, else false - * @return the generated token list for that line - * - **/ - public TokenList tokenizeLine(MIPSprogram program, int lineNum, String theLine, boolean doEqvSubstitutes) { - TokenTypes tokenType; - TokenList result = new TokenList(); - if (theLine.length() == 0) + /** + * Will tokenize one line of source code. If lexical errors are discovered, they are noted in an ErrorMessage + * object which is added to the provided ErrorList instead of the Tokenizer's error list. Will NOT throw an + * exception. + * + * @param program MIPSprogram containing this line of source + * @param lineNum line number from source code (used in error message) + * @param theLine String containing source code + * @param doEqvSubstitutes boolean param set true to perform .eqv substitutions, else false + * @return the generated token list for that line + **/ + public TokenList tokenizeLine(MIPSprogram program, int lineNum, String theLine, boolean doEqvSubstitutes) + { + TokenTypes tokenType; + TokenList result = new TokenList(); + if (theLine.length() == 0) + { return result; - // will be faster to work with char arrays instead of strings - char c; - char[] line = theLine.toCharArray(); - int linePos = 0; - char[] token = new char[line.length]; - int tokenPos = 0; - int tokenStartPos = 1; - boolean insideQuotedString = false; - if (Globals.debug) - System.out.println("source line --->"+theLine+"<---"); - // Each iteration of this loop processes one character in the source line. - while (linePos < line.length) { + } + // will be faster to work with char arrays instead of strings + char c; + char[] line = theLine.toCharArray(); + int linePos = 0; + char[] token = new char[line.length]; + int tokenPos = 0; + int tokenStartPos = 1; + boolean insideQuotedString = false; + if (Globals.debug) + { + System.out.println("source line --->" + theLine + "<---"); + } + // Each iteration of this loop processes one character in the source line. + while (linePos < line.length) + { c = line[linePos]; - if (insideQuotedString) { // everything goes into token - token[tokenPos++] = c; - if (c == '"' && token[tokenPos-2] != '\\') { // If quote not preceded by backslash, this is end - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - insideQuotedString = false; - } - } - else { // not inside a quoted string, so be sensitive to delimiters - switch(c) { - case '#' : // # denotes comment that takes remainder of line - if (tokenPos > 0) { + if (insideQuotedString) + { // everything goes into token + token[tokenPos++] = c; + if (c == '"' && token[tokenPos - 2] != '\\') + { // If quote not preceded by backslash, this is end + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + insideQuotedString = false; + } + } + else + { // not inside a quoted string, so be sensitive to delimiters + switch (c) + { + case '#': // # denotes comment that takes remainder of line + if (tokenPos > 0) + { + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + } + tokenStartPos = linePos + 1; + tokenPos = line.length - linePos; + System.arraycopy(line, linePos, token, 0, tokenPos); + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + linePos = line.length; + tokenPos = 0; + break; + case ' ': + case '\t': + case ',': // space, tab or comma is delimiter + if (tokenPos > 0) + { + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + } + break; + // These two guys are special. Will be recognized as unary if and only if two conditions hold: + // 1. Immediately followed by a digit (will use look-ahead for this). + // 2. Previous token, if any, is _not_ an IDENTIFIER + // Otherwise considered binary and thus a separate token. This is a slight hack but reasonable. + case '+': + case '-': + // Here's the REAL hack: recognizing signed exponent in E-notation floating point! + // (e.g. 1.2e-5) Add the + or - to the token and keep going. DPS 17 Aug 2005 + if (tokenPos > 0 && line.length >= linePos + 2 && Character.isDigit(line[linePos + 1]) && + (line[linePos - 1] == 'e' || line[linePos - 1] == 'E')) + { + token[tokenPos++] = c; + break; + } + // End of REAL hack. + if (tokenPos > 0) + { + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + } + tokenStartPos = linePos + 1; + token[tokenPos++] = c; + if (!((result.isEmpty() || result.get(result.size() - 1).getType() != TokenTypes.IDENTIFIER) && + (line.length >= linePos + 2 && Character.isDigit(line[linePos + 1])))) + { + // treat it as binary..... + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + } + break; + // these are other single-character tokens + case ':': + case '(': + case ')': + if (tokenPos > 0) + { + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + } + tokenStartPos = linePos + 1; + token[tokenPos++] = c; this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); tokenPos = 0; - } - tokenStartPos = linePos+1; - tokenPos = line.length-linePos; - System.arraycopy(line, linePos, token, 0, tokenPos); - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - linePos = line.length; - tokenPos = 0; - break; - case ' ' : - case '\t': - case ',' : // space, tab or comma is delimiter - if (tokenPos > 0) { + break; + case '"': // we're not inside a quoted string, so start a new token... + if (tokenPos > 0) + { + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + } + tokenStartPos = linePos + 1; + token[tokenPos++] = c; + insideQuotedString = true; + break; + case '\'': // start of character constant (single quote). + if (tokenPos > 0) + { + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + } + // Our strategy is to process the whole thing right now... + tokenStartPos = linePos + 1; + token[tokenPos++] = c; // Put the quote in token[0] + int lookaheadChars = line.length - linePos - 1; + // need minimum 2 more characters, 1 for char and 1 for ending quote + if (lookaheadChars < 2) + { + break; // gonna be an error + } + c = line[++linePos]; + token[tokenPos++] = c; // grab second character, put it in token[1] + if (c == '\'') + { + break; // gonna be an error: nothing between the quotes + } + c = line[++linePos]; + token[tokenPos++] = c; // grab third character, put it in token[2] + // Process if we've either reached second, non-escaped, quote or end of line. + if (c == '\'' && token[1] != '\\' || lookaheadChars == 2) + { + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + tokenStartPos = linePos + 1; + break; + } + // At this point, there is at least one more character on this line. If we're + // still here after seeing a second quote, it was escaped. Not done yet; + // we either have an escape code, an octal code (also escaped) or invalid. + c = line[++linePos]; + token[tokenPos++] = c; // grab fourth character, put it in token[3] + // Process, if this is ending quote for escaped character or if at end of line + if (c == '\'' || lookaheadChars == 3) + { + this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); + tokenPos = 0; + tokenStartPos = linePos + 1; + break; + } + // At this point, we've handled all legal possibilities except octal, e.g. '\377' + // Proceed, if enough characters remain to finish off octal. + if (lookaheadChars >= 5) + { + c = line[++linePos]; + token[tokenPos++] = c; // grab fifth character, put it in token[4] + if (c != '\'') + { + // still haven't reached end, last chance for validity! + c = line[++linePos]; + token[tokenPos++] = c; // grab sixth character, put it in token[5] + } + } + // process no matter what...we either have a valid character by now or not this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); tokenPos = 0; - } - break; - // These two guys are special. Will be recognized as unary if and only if two conditions hold: - // 1. Immediately followed by a digit (will use look-ahead for this). - // 2. Previous token, if any, is _not_ an IDENTIFIER - // Otherwise considered binary and thus a separate token. This is a slight hack but reasonable. - case '+' : - case '-' : - // Here's the REAL hack: recognizing signed exponent in E-notation floating point! - // (e.g. 1.2e-5) Add the + or - to the token and keep going. DPS 17 Aug 2005 - if (tokenPos > 0 && line.length >= linePos+2 && Character.isDigit(line[linePos+1]) && - (line[linePos-1]=='e' || line[linePos-1]=='E')) { + tokenStartPos = linePos + 1; + break; + default: + if (tokenPos == 0) + { + tokenStartPos = linePos + 1; + } token[tokenPos++] = c; break; - } - // End of REAL hack. - if (tokenPos > 0) { - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - } - tokenStartPos = linePos+1; - token[tokenPos++] = c; - if ( !((result.isEmpty() || ((Token)result.get(result.size()-1)).getType() != TokenTypes.IDENTIFIER) && - (line.length >= linePos+2 && Character.isDigit(line[linePos+1]))) ) { - // treat it as binary..... - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - } - break; - // these are other single-character tokens - case ':' : - case '(' : - case ')' : - if (tokenPos > 0) { - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - } - tokenStartPos = linePos+1; - token[tokenPos++] = c; - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - break; - case '"' : // we're not inside a quoted string, so start a new token... - if (tokenPos > 0) { - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - } - tokenStartPos = linePos+1; - token[tokenPos++] = c; - insideQuotedString = true; - break; - case '\'' : // start of character constant (single quote). - if (tokenPos > 0) { - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - } - // Our strategy is to process the whole thing right now... - tokenStartPos = linePos+1; - token[tokenPos++] = c; // Put the quote in token[0] - int lookaheadChars = line.length - linePos - 1; - // need minimum 2 more characters, 1 for char and 1 for ending quote - if (lookaheadChars < 2) - break; // gonna be an error - c = line[++linePos]; - token[tokenPos++] = c; // grab second character, put it in token[1] - if (c == '\'') - break; // gonna be an error: nothing between the quotes - c = line[++linePos]; - token[tokenPos++] = c; // grab third character, put it in token[2] - // Process if we've either reached second, non-escaped, quote or end of line. - if (c == '\'' && token[1] != '\\' || lookaheadChars==2) { - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - tokenStartPos = linePos+1; - break; - } - // At this point, there is at least one more character on this line. If we're - // still here after seeing a second quote, it was escaped. Not done yet; - // we either have an escape code, an octal code (also escaped) or invalid. - c = line[++linePos]; - token[tokenPos++] = c; // grab fourth character, put it in token[3] - // Process, if this is ending quote for escaped character or if at end of line - if (c == '\'' || lookaheadChars==3) { - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - tokenStartPos = linePos+1; - break; - } - // At this point, we've handled all legal possibilities except octal, e.g. '\377' - // Proceed, if enough characters remain to finish off octal. - if (lookaheadChars >= 5) { - c = line[++linePos]; - token[tokenPos++] = c; // grab fifth character, put it in token[4] - if (c != '\'') { - // still haven't reached end, last chance for validity! - c = line[++linePos]; - token[tokenPos++] = c; // grab sixth character, put it in token[5] - } - } - // process no matter what...we either have a valid character by now or not - this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); - tokenPos = 0; - tokenStartPos = linePos+1; - break; - default : - if (tokenPos == 0) - tokenStartPos = linePos+1; - token[tokenPos++] = c; - break; - } // switch + } // switch } // if (insideQuotedString) linePos++; - } // while - if (tokenPos > 0) { + } // while + if (tokenPos > 0) + { this.processCandidateToken(token, program, lineNum, theLine, tokenPos, tokenStartPos, result); tokenPos = 0; - } - if (doEqvSubstitutes) { + } + if (doEqvSubstitutes) + { result = processEqv(program, lineNum, theLine, result); // DPS 11-July-2012 - } - return result; - } - - // Process the .eqv directive, which needs to be applied prior to tokenizing of subsequent statements. - // This handles detecting that theLine contains a .eqv directive, in which case it needs - // to be added to the HashMap of equivalents. It also handles detecting that theLine - // contains a symbol that was previously defined in an .eqv directive, in which case - // the substitution needs to be made. - // DPS 11-July-2012 - private TokenList processEqv(MIPSprogram program, int lineNum, String theLine, TokenList tokens) { - // See if it is .eqv directive. If so, record it... - // Have to assure it is a well-formed statement right now (can't wait for assembler). - - if (tokens.size()>2 && (tokens.get(0).getType() == TokenTypes.DIRECTIVE || tokens.get(2).getType() == TokenTypes.DIRECTIVE)) { + } + return result; + } + + // Process the .eqv directive, which needs to be applied prior to tokenizing of subsequent statements. + // This handles detecting that theLine contains a .eqv directive, in which case it needs + // to be added to the HashMap of equivalents. It also handles detecting that theLine + // contains a symbol that was previously defined in an .eqv directive, in which case + // the substitution needs to be made. + // DPS 11-July-2012 + private TokenList processEqv(MIPSprogram program, int lineNum, String theLine, TokenList tokens) + { + // See if it is .eqv directive. If so, record it... + // Have to assure it is a well-formed statement right now (can't wait for assembler). + + if (tokens.size() > 2 && (tokens.get(0).getType() == TokenTypes.DIRECTIVE || tokens.get(2).getType() == TokenTypes.DIRECTIVE)) + { // There should not be a label but if there is, the directive is in token position 2 (ident, colon, directive). - int dirPos = (tokens.get(0).getType() == TokenTypes.DIRECTIVE) ? 0 : 2; - if (Directives.matchDirective(tokens.get(dirPos).getValue()) == Directives.EQV) { - // Get position in token list of last non-comment token - int tokenPosLastOperand = tokens.size() - ((tokens.get(tokens.size()-1).getType()==TokenTypes.COMMENT)? 2 : 1); - // There have to be at least two non-comment tokens beyond the directive - if (tokenPosLastOperand < dirPos+2) { - errors.add(new ErrorMessage(program, lineNum,tokens.get(dirPos).getStartPos(), - "Too few operands for "+Directives.EQV.getName()+" directive")); - return tokens; - } - // Token following the directive has to be IDENTIFIER - if (tokens.get(dirPos+1).getType() != TokenTypes.IDENTIFIER) { - errors.add(new ErrorMessage(program, lineNum,tokens.get(dirPos).getStartPos(), - "Malformed "+Directives.EQV.getName()+" directive")); - return tokens; - } - String symbol = tokens.get(dirPos+1).getValue(); - // Make sure the symbol is not contained in the expression. Not likely to occur but if left - // undetected it will result in infinite recursion. e.g. .eqv ONE, (ONE) - for (int i=dirPos+2; i 0 && value.charAt(0)=='\'') value = preprocessCharacterLiteral(value); - TokenTypes type = TokenTypes.matchTokenType(value); - if (type == TokenTypes.ERROR) { - errors.add(new ErrorMessage(program, line, tokenStartPos, - theLine+"\nInvalid language element: "+value)); - } - Token toke = new Token(type, value, program, line, tokenStartPos); - tokenList.add(toke); - return; - } - - - - // If passed a candidate character literal, attempt to translate it into integer constant. - // If the translation fails, return original value. - private String preprocessCharacterLiteral(String value) { - // must start and end with quote and have something in between - if (value.length() < 3 || value.charAt(0) != '\'' || value.charAt(value.length()-1) != '\'') { - return value; - } - String quotesRemoved = value.substring(1, value.length()-1); - // if not escaped, then if one character left return its value else return original. - if (quotesRemoved.charAt(0) != '\\') { - return (quotesRemoved.length() == 1) ? Integer.toString((int)quotesRemoved.charAt(0)) : value; - } - // now we know it is escape sequence and have to decode which of the 8: ',",\,n,t,b,r,f - if (quotesRemoved.length() == 2) { + } + tokens.setProcessedLine(theLine); // DPS 03-Jan-2013. Related to changes of 11-July-2012. + + return (substitutionMade) ? tokenizeLine(lineNum, theLine) : tokens; + } + + + /** + * Fetch this Tokenizer's error list. + * + * @return the error list + */ + public ErrorList getErrors() + { + return errors; + } + + + // Given candidate token and its position, will classify and record it. + private void processCandidateToken(char[] token, MIPSprogram program, int line, String theLine, + int tokenPos, int tokenStartPos, TokenList tokenList) + { + String value = new String(token, 0, tokenPos); + if (value.length() > 0 && value.charAt(0) == '\'') + { + value = preprocessCharacterLiteral(value); + } + TokenTypes type = TokenTypes.matchTokenType(value); + if (type == TokenTypes.ERROR) + { + errors.add(new ErrorMessage(program, line, tokenStartPos, + theLine + "\nInvalid language element: " + value)); + } + Token toke = new Token(type, value, program, line, tokenStartPos); + tokenList.add(toke); + } + + + // If passed a candidate character literal, attempt to translate it into integer constant. + // If the translation fails, return original value. + private String preprocessCharacterLiteral(String value) + { + // must start and end with quote and have something in between + if (value.length() < 3 || value.charAt(0) != '\'' || value.charAt(value.length() - 1) != '\'') + { + return value; + } + String quotesRemoved = value.substring(1, value.length() - 1); + // if not escaped, then if one character left return its value else return original. + if (quotesRemoved.charAt(0) != '\\') + { + return (quotesRemoved.length() == 1) ? Integer.toString(quotesRemoved.charAt(0)) : value; + } + // now we know it is escape sequence and have to decode which of the 8: ',",\,n,t,b,r,f + if (quotesRemoved.length() == 2) + { int escapedCharacterIndex = escapedCharacters.indexOf(quotesRemoved.charAt(1)); - return (escapedCharacterIndex >= 0) ? escapedCharactersValues[escapedCharacterIndex] : value; - } - // last valid possibility is 3 digit octal code 000 through 377 - if (quotesRemoved.length() == 4) { - try { - int intValue = Integer.parseInt(quotesRemoved.substring(1),8); - if (intValue >= 0 && intValue <= 255) { - return Integer.toString(intValue); - } - } - catch (NumberFormatException nfe) { } // if not valid octal, will fall through and reject - } - return value; - } - } + return (escapedCharacterIndex >= 0) ? escapedCharactersValues[escapedCharacterIndex] : value; + } + // last valid possibility is 3 digit octal code 000 through 377 + if (quotesRemoved.length() == 4) + { + try + { + int intValue = Integer.parseInt(quotesRemoved.substring(1), 8); + if (intValue >= 0 && intValue <= 255) + { + return Integer.toString(intValue); + } + } + catch (NumberFormatException nfe) + { + } // if not valid octal, will fall through and reject + } + return value; + } +} diff --git a/src/main/java/mars/assembler/TranslationCode.java b/src/main/java/mars/assembler/TranslationCode.java index d7cf5c8..6ac5a22 100644 --- a/src/main/java/mars/assembler/TranslationCode.java +++ b/src/main/java/mars/assembler/TranslationCode.java @@ -29,20 +29,19 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * This interface is intended for use by ExtendedInstruction objects to define, using - * the translate() method, how to translate the extended (pseudo) instruction into - * a sequence of one or more basic instructions, which can then be translated into - * binary machine code. - * + * This interface is intended for use by ExtendedInstruction objects to define, using the translate() method, how to + * translate the extended (pseudo) instruction into a sequence of one or more basic instructions, which can then be + * translated into binary machine code. + * * @author Pete Sanderson * @version August 2003 */ -public interface TranslationCode { - /** - * This is a callback method defined in anonymous class specified as - * argument to ExtendedInstruction constructor. It is called when - * assembler finds a program statement matching that ExtendedInstruction, - */ - public void translate(); +public interface TranslationCode +{ + /** + * This is a callback method defined in anonymous class specified as argument to ExtendedInstruction constructor. + * It is called when assembler finds a program statement matching that ExtendedInstruction, + */ + void translate(); } diff --git a/src/main/java/mars/mips/dump/AbstractDumpFormat.java b/src/main/java/mars/mips/dump/AbstractDumpFormat.java index 1efdadc..aa62c43 100644 --- a/src/main/java/mars/mips/dump/AbstractDumpFormat.java +++ b/src/main/java/mars/mips/dump/AbstractDumpFormat.java @@ -1,7 +1,9 @@ - package mars.mips.dump; +package mars.mips.dump; - import mars.mips.hardware.*; - import java.io.*; +import mars.mips.hardware.AddressErrorException; + +import java.io.File; +import java.io.IOException; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -31,88 +33,99 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Abstract class for memory dump file formats. Provides constructors and - * defaults for everything except the dumpMemoryRange method itself. - * - * @author Pete Sanderson + * Abstract class for memory dump file formats. Provides constructors and defaults for everything except the + * dumpMemoryRange method itself. + * + * @author Pete Sanderson * @version December 2007 */ - public abstract class AbstractDumpFormat implements DumpFormat { - - private String name, commandDescriptor, description, extension; - - /** - * Typical constructor. Note you cannot creates objects from this - * class but subclass constructor can call this one. - * @param name Brief descriptive name to be displayed in selection list. - * @param commandDescriptor One-word descriptive name to be used by MARS command mode parser and user. - * Any spaces in this string will be removed. - * @param description Description to go with standard file extension for - * display in file save dialog or to be used as tool tip. - * @param extension Standard file extension for this format. Null if none. - */ - public AbstractDumpFormat(String name, String commandDescriptor, - String description, String extension) { - this.name = name; - this.commandDescriptor = (commandDescriptor==null) ? null : commandDescriptor.replaceAll(" ",""); - this.description = description; - this.extension = extension; - } - - - /** - * Get the file extension associated with this format. - * @return String containing file extension -- without the leading "." -- or - * null if there is no standard extension. - */ - public String getFileExtension() { - return extension; - } - - /** - * Get a short description of the format, suitable for displaying along with - * the extension, in the file save dialog, or as a tool tip. - * @return String containing short description to go with the extension - * or for use as tool tip. Possibly null. - */ - public String getDescription() { - return description; - } - - /** - * String representing this object. - * @return Name given for this object. - * - */ - public String toString() { - return name; - } - - /** - * One-word description of format to be used by MARS command mode parser - * and user in conjunction with the "dump" option. - * @return One-word String describing the format. - * - */ - public String getCommandDescriptor() { - return commandDescriptor; - } - - /** - * Write MIPS memory contents according to the - * specification for this format. - * - * @param file File in which to store MIPS memory contents. - * @param firstAddress first (lowest) memory address to dump. In bytes but - * must be on word boundary. - * @param lastAddress last (highest) memory address to dump. In bytes but - * must be on word boundary. Will dump the word that starts at this address. - * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. - * @throws IOException if error occurs during file output. - */ - public abstract void dumpMemoryRange(File file, int firstAddress, int lastAddress) - throws AddressErrorException, IOException; - - } \ No newline at end of file +public abstract class AbstractDumpFormat implements DumpFormat +{ + + private final String name; + + private final String commandDescriptor; + + private final String description; + + private final String extension; + + /** + * Typical constructor. Note you cannot creates objects from this class but subclass constructor can call this + * one. + * + * @param name Brief descriptive name to be displayed in selection list. + * @param commandDescriptor One-word descriptive name to be used by MARS command mode parser and user. Any + * spaces in this string will be removed. + * @param description Description to go with standard file extension for display in file save dialog or to be + * used as tool tip. + * @param extension Standard file extension for this format. Null if none. + */ + public AbstractDumpFormat(String name, String commandDescriptor, + String description, String extension) + { + this.name = name; + this.commandDescriptor = (commandDescriptor == null) ? null : commandDescriptor.replaceAll(" ", ""); + this.description = description; + this.extension = extension; + } + + + /** + * Get the file extension associated with this format. + * + * @return String containing file extension -- without the leading "." -- or null if there is no standard extension. + */ + public String getFileExtension() + { + return extension; + } + + /** + * Get a short description of the format, suitable for displaying along with the extension, in the file save dialog, + * or as a tool tip. + * + * @return String containing short description to go with the extension or for use as tool tip. Possibly null. + */ + public String getDescription() + { + return description; + } + + /** + * String representing this object. + * + * @return Name given for this object. + */ + public String toString() + { + return name; + } + + /** + * One-word description of format to be used by MARS command mode parser and user in conjunction with the "dump" + * option. + * + * @return One-word String describing the format. + */ + public String getCommandDescriptor() + { + return commandDescriptor; + } + + /** + * Write MIPS memory contents according to the specification for this format. + * + * @param file File in which to store MIPS memory contents. + * @param firstAddress first (lowest) memory address to dump. In bytes but must be on word boundary. + * @param lastAddress last (highest) memory address to dump. In bytes but must be on word boundary. Will dump + * the word that starts at this address. + * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. + * @throws IOException if error occurs during file output. + */ + public abstract void dumpMemoryRange(File file, int firstAddress, int lastAddress) + throws AddressErrorException, IOException; + +} diff --git a/src/main/java/mars/mips/dump/AsciiTextDumpFormat.java b/src/main/java/mars/mips/dump/AsciiTextDumpFormat.java index f59cf69..de5a878 100644 --- a/src/main/java/mars/mips/dump/AsciiTextDumpFormat.java +++ b/src/main/java/mars/mips/dump/AsciiTextDumpFormat.java @@ -1,9 +1,14 @@ - package mars.mips.dump; +package mars.mips.dump; - import mars.util.Binary; - import mars.Globals; - import mars.mips.hardware.*; - import java.io.*; +import mars.Globals; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Memory; +import mars.util.Binary; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; /* Copyright (c) 2003-2011, Pete Sanderson and Kenneth Vollmar @@ -33,60 +38,63 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Class that represents the "ASCII text" memory dump format. Memory contents - * are interpreted as ASCII codes. The output - * is a text file with one word of MIPS memory per line. The word is formatted - * to leave three spaces for each character. Non-printing characters - * rendered as period (.) as placeholder. Common escaped characters - * rendered using backslash and single-character descriptor, e.g. \t for tab. - * @author Pete Sanderson + * Class that represents the "ASCII text" memory dump format. Memory contents are interpreted as ASCII codes. The output + * is a text file with one word of MIPS memory per line. The word is formatted to leave three spaces for each + * character. Non-printing characters rendered as period (.) as placeholder. Common escaped characters rendered using + * backslash and single-character descriptor, e.g. \t for tab. + * + * @author Pete Sanderson * @version December 2010 */ - public class AsciiTextDumpFormat extends AbstractDumpFormat { - - /** - * Constructor. There is no standard file extension for this format. - */ - public AsciiTextDumpFormat() { - super("ASCII Text", "AsciiText", "Memory contents interpreted as ASCII characters", null); - } - - - /** - * Interpret MIPS memory contents as ASCII characters. Each line of - * text contains one memory word written in ASCII characters. Those - * corresponding to tab, newline, null, etc are rendered as backslash - * followed by single-character code, e.g. \t for tab, \0 for null. - * Non-printing character (control code, - * values above 127) is rendered as a period (.). Written - * using PrintStream's println() method. - * Adapted by Pete Sanderson from code written by Greg Gibeling. - * - * @param file File in which to store MIPS memory contents. - * @param firstAddress first (lowest) memory address to dump. In bytes but - * must be on word boundary. - * @param lastAddress last (highest) memory address to dump. In bytes but - * must be on word boundary. Will dump the word that starts at this address. - * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. - * @throws IOException if error occurs during file output. - */ - public void dumpMemoryRange(File file, int firstAddress, int lastAddress) - throws AddressErrorException, IOException { - PrintStream out = new PrintStream(new FileOutputStream(file)); - String string = null; - try { - for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) { - Integer temp = Globals.memory.getRawWordOrNull(address); - if (temp == null) - break; - out.println(Binary.intToAscii(temp.intValue())); +public class AsciiTextDumpFormat extends AbstractDumpFormat +{ + + /** + * Constructor. There is no standard file extension for this format. + */ + public AsciiTextDumpFormat() + { + super("ASCII Text", "AsciiText", "Memory contents interpreted as ASCII characters", null); + } + + + /** + * Interpret MIPS memory contents as ASCII characters. Each line of text contains one memory word written in ASCII + * characters. Those corresponding to tab, newline, null, etc are rendered as backslash followed by + * single-character code, e.g. \t for tab, \0 for null. Non-printing character (control code, values above 127) is + * rendered as a period (.). Written using PrintStream's println() method. Adapted by Pete Sanderson from code + * written by Greg Gibeling. + * + * @param file File in which to store MIPS memory contents. + * @param firstAddress first (lowest) memory address to dump. In bytes but must be on word boundary. + * @param lastAddress last (highest) memory address to dump. In bytes but must be on word boundary. Will dump + * the word that starts at this address. + * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. + * @throws IOException if error occurs during file output. + */ + public void dumpMemoryRange(File file, int firstAddress, int lastAddress) + throws AddressErrorException, IOException + { + PrintStream out = new PrintStream(new FileOutputStream(file)); + String string = null; + try + { + for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) + { + Integer temp = Globals.memory.getRawWordOrNull(address); + if (temp == null) + { + break; + } + out.println(Binary.intToAscii(temp.intValue())); } - } - finally { - out.close(); - } - } - - } \ No newline at end of file + } + finally + { + out.close(); + } + } + +} diff --git a/src/main/java/mars/mips/dump/BinaryDumpFormat.java b/src/main/java/mars/mips/dump/BinaryDumpFormat.java index 8e37355..35be73b 100644 --- a/src/main/java/mars/mips/dump/BinaryDumpFormat.java +++ b/src/main/java/mars/mips/dump/BinaryDumpFormat.java @@ -1,8 +1,13 @@ - package mars.mips.dump; +package mars.mips.dump; - import mars.Globals; - import mars.mips.hardware.*; - import java.io.*; +import mars.Globals; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Memory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -32,53 +37,61 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Class that represents the "binary" memory dump format. The output - * is a binary file containing the memory words as a byte stream. Output - * is produced using PrintStream's write() method. - * @author Pete Sanderson + * Class that represents the "binary" memory dump format. The output is a binary file containing the memory words as a + * byte stream. Output is produced using PrintStream's write() method. + * + * @author Pete Sanderson * @version December 2007 */ - public class BinaryDumpFormat extends AbstractDumpFormat { - - /** - * Constructor. There is no standard file extension for this format. - */ - public BinaryDumpFormat() { - super("Binary", "Binary", "Written as byte stream to binary file", null); - } - - - /** - * Write MIPS memory contents in pure binary format. One byte at a time - * using PrintStream's write() method. Adapted by Pete Sanderson from - * code written by Greg Gibeling. - * - * @param file File in which to store MIPS memory contents. - * @param firstAddress first (lowest) memory address to dump. In bytes but - * must be on word boundary. - * @param lastAddress last (highest) memory address to dump. In bytes but - * must be on word boundary. Will dump the word that starts at this address. - * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. - * @throws IOException if error occurs during file output. - */ - public void dumpMemoryRange(File file, int firstAddress, int lastAddress) - throws AddressErrorException, IOException { - PrintStream out = new PrintStream(new FileOutputStream(file)); - try { - for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) { - Integer temp = Globals.memory.getRawWordOrNull(address); - if (temp == null) - break; - int word = temp.intValue(); - for (int i = 0; i < 4; i++) - out.write((word >>> (i << 3)) & 0xFF); +public class BinaryDumpFormat extends AbstractDumpFormat +{ + + /** + * Constructor. There is no standard file extension for this format. + */ + public BinaryDumpFormat() + { + super("Binary", "Binary", "Written as byte stream to binary file", null); + } + + + /** + * Write MIPS memory contents in pure binary format. One byte at a time using PrintStream's write() method. + * Adapted by Pete Sanderson from code written by Greg Gibeling. + * + * @param file File in which to store MIPS memory contents. + * @param firstAddress first (lowest) memory address to dump. In bytes but must be on word boundary. + * @param lastAddress last (highest) memory address to dump. In bytes but must be on word boundary. Will dump + * the word that starts at this address. + * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. + * @throws IOException if error occurs during file output. + */ + public void dumpMemoryRange(File file, int firstAddress, int lastAddress) + throws AddressErrorException, IOException + { + PrintStream out = new PrintStream(new FileOutputStream(file)); + try + { + for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) + { + Integer temp = Globals.memory.getRawWordOrNull(address); + if (temp == null) + { + break; + } + int word = temp.intValue(); + for (int i = 0; i < 4; i++) + { + out.write((word >>> (i << 3)) & 0xFF); + } } - } - finally { - out.close(); - } - } - - } \ No newline at end of file + } + finally + { + out.close(); + } + } + +} diff --git a/src/main/java/mars/mips/dump/BinaryTextDumpFormat.java b/src/main/java/mars/mips/dump/BinaryTextDumpFormat.java index 6747c6c..b1e7ad3 100644 --- a/src/main/java/mars/mips/dump/BinaryTextDumpFormat.java +++ b/src/main/java/mars/mips/dump/BinaryTextDumpFormat.java @@ -1,8 +1,13 @@ - package mars.mips.dump; +package mars.mips.dump; - import mars.Globals; - import mars.mips.hardware.*; - import java.io.*; +import mars.Globals; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Memory; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -32,57 +37,64 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Class that represents the "binary text" memory dump format. The output - * is a text file with one word of MIPS memory per line. The word is formatted - * using '0' and '1' characters, e.g. 01110101110000011111110101010011. - * @author Pete Sanderson + * Class that represents the "binary text" memory dump format. The output is a text file with one word of MIPS memory + * per line. The word is formatted using '0' and '1' characters, e.g. 01110101110000011111110101010011. + * + * @author Pete Sanderson * @version December 2007 */ - public class BinaryTextDumpFormat extends AbstractDumpFormat { - - /** - * Constructor. There is no standard file extension for this format. - */ - public BinaryTextDumpFormat() { - super("Binary Text", "BinaryText", "Written as '0' and '1' characters to text file", null); - } - - - /** - * Write MIPS memory contents in binary text format. Each line of - * text contains one memory word written as 32 '0' and '1' characters. Written - * using PrintStream's println() method. - * Adapted by Pete Sanderson from code written by Greg Gibeling. - * - * @param file File in which to store MIPS memory contents. - * @param firstAddress first (lowest) memory address to dump. In bytes but - * must be on word boundary. - * @param lastAddress last (highest) memory address to dump. In bytes but - * must be on word boundary. Will dump the word that starts at this address. - * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. - * @throws IOException if error occurs during file output. - */ - public void dumpMemoryRange(File file, int firstAddress, int lastAddress) - throws AddressErrorException, IOException { - PrintStream out = new PrintStream(new FileOutputStream(file)); - String string = null; - try { - for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) { - Integer temp = Globals.memory.getRawWordOrNull(address); - if (temp == null) - break; - string = Integer.toBinaryString(temp.intValue()); - while (string.length() < 32) { - string = '0' + string; - } - out.println(string); +public class BinaryTextDumpFormat extends AbstractDumpFormat +{ + + /** + * Constructor. There is no standard file extension for this format. + */ + public BinaryTextDumpFormat() + { + super("Binary Text", "BinaryText", "Written as '0' and '1' characters to text file", null); + } + + + /** + * Write MIPS memory contents in binary text format. Each line of text contains one memory word written as 32 '0' + * and '1' characters. Written using PrintStream's println() method. Adapted by Pete Sanderson from code written by + * Greg Gibeling. + * + * @param file File in which to store MIPS memory contents. + * @param firstAddress first (lowest) memory address to dump. In bytes but must be on word boundary. + * @param lastAddress last (highest) memory address to dump. In bytes but must be on word boundary. Will dump + * the word that starts at this address. + * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. + * @throws IOException if error occurs during file output. + */ + public void dumpMemoryRange(File file, int firstAddress, int lastAddress) + throws AddressErrorException, IOException + { + PrintStream out = new PrintStream(new FileOutputStream(file)); + String string = null; + try + { + for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) + { + Integer temp = Globals.memory.getRawWordOrNull(address); + if (temp == null) + { + break; + } + string = Integer.toBinaryString(temp.intValue()); + while (string.length() < 32) + { + string = '0' + string; + } + out.println(string); } - } - finally { - out.close(); - } - } - - } \ No newline at end of file + } + finally + { + out.close(); + } + } + +} diff --git a/src/main/java/mars/mips/dump/DumpFormat.java b/src/main/java/mars/mips/dump/DumpFormat.java index a1d649b..46ecafb 100644 --- a/src/main/java/mars/mips/dump/DumpFormat.java +++ b/src/main/java/mars/mips/dump/DumpFormat.java @@ -1,7 +1,9 @@ - package mars.mips.dump; +package mars.mips.dump; - import mars.mips.hardware.*; - import java.io.*; +import mars.mips.hardware.AddressErrorException; + +import java.io.File; +import java.io.IOException; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -31,62 +33,57 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Interface for memory dump file formats. All MARS needs to be able - * to do is save an assembled program or data in the specified manner for - * a given format. Formats are specified through classes - * that implement this interface. - * - * @author Pete Sanderson + * Interface for memory dump file formats. All MARS needs to be able to do is save an assembled program or data in the + * specified manner for a given format. Formats are specified through classes that implement this interface. + * + * @author Pete Sanderson * @version December 2007 */ - public interface DumpFormat { - - /** - * Get the file extension associated with this format. - * @return String containing file extension -- without the leading "." -- or - * null if there is no standard extension. - */ - public String getFileExtension(); - - /** - * Get a short description of the format, suitable - * for displaying along with the extension, if any, in the file - * save dialog and also for displaying as a tool tip. - * @return String containing short description to go with the extension - * or as tool tip when mouse hovers over GUI component representing - * this format. - */ - public String getDescription(); +public interface DumpFormat +{ - /** - * A short one-word descriptor that will be used by the MARS - * command line parser (and the MARS command line user) to specify - * that this format is to be used. - */ - public String getCommandDescriptor(); - - /** - * Descriptive name for the format. - * @return Format name. - * - */ - public String toString(); - - /** - * Write MIPS memory contents according to the - * specification for this format. - * - * @param file File in which to store MIPS memory contents. - * @param firstAddress first (lowest) memory address to dump. In bytes but - * must be on word boundary. - * @param lastAddress last (highest) memory address to dump. In bytes but - * must be on word boundary. Will dump the word that starts at this address. - * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. - * @throws IOException if error occurs during file output. - */ - public void dumpMemoryRange(File file, int firstAddress, int lastAddress) - throws AddressErrorException, IOException; - - } \ No newline at end of file + /** + * Get the file extension associated with this format. + * + * @return String containing file extension -- without the leading "." -- or null if there is no standard extension. + */ + String getFileExtension(); + + /** + * Get a short description of the format, suitable for displaying along with the extension, if any, in the file save + * dialog and also for displaying as a tool tip. + * + * @return String containing short description to go with the extension or as tool tip when mouse hovers over GUI + * component representing this format. + */ + String getDescription(); + + /** + * A short one-word descriptor that will be used by the MARS command line parser (and the MARS command line user) to + * specify that this format is to be used. + */ + String getCommandDescriptor(); + + /** + * Descriptive name for the format. + * + * @return Format name. + */ + String toString(); + + /** + * Write MIPS memory contents according to the specification for this format. + * + * @param file File in which to store MIPS memory contents. + * @param firstAddress first (lowest) memory address to dump. In bytes but must be on word boundary. + * @param lastAddress last (highest) memory address to dump. In bytes but must be on word boundary. Will dump + * the word that starts at this address. + * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. + * @throws IOException if error occurs during file output. + */ + void dumpMemoryRange(File file, int firstAddress, int lastAddress) + throws AddressErrorException, IOException; + +} diff --git a/src/main/java/mars/mips/dump/DumpFormatLoader.java b/src/main/java/mars/mips/dump/DumpFormatLoader.java index 4059b35..dca14df 100644 --- a/src/main/java/mars/mips/dump/DumpFormatLoader.java +++ b/src/main/java/mars/mips/dump/DumpFormatLoader.java @@ -1,8 +1,9 @@ - package mars.mips.dump; - import mars.*; - import mars.util.*; - import java.util.*; - import java.lang.reflect.*; +package mars.mips.dump; + +import mars.util.FilenameFinder; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -32,66 +33,78 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /****************************************************************************/ - /* This class provides functionality to bring external memory dump format definitions - * into MARS. This is adapted from the ToolLoader class, which is in turn adapted - * from Bret Barker's GameServer class from the book "Developing Games In Java". + +/****************************************************************************/ +/* This class provides functionality to bring external memory dump format definitions + * into MARS. This is adapted from the ToolLoader class, which is in turn adapted + * from Bret Barker's GameServer class from the book "Developing Games In Java". + */ + +public class DumpFormatLoader +{ + + private static final String CLASS_PREFIX = "mars.mips.dump."; + + private static final String DUMP_DIRECTORY_PATH = "mars/mips/dump"; + + private static final String SYSCALL_INTERFACE = "DumpFormat.class"; + + private static final String CLASS_EXTENSION = "class"; + + private static ArrayList formatList = null; + + public static DumpFormat findDumpFormatGivenCommandDescriptor(ArrayList formatList, String formatCommandDescriptor) + { + DumpFormat match = null; + for (int i = 0; i < formatList.size(); i++) + { + if (((DumpFormat) formatList.get(i)).getCommandDescriptor().equals(formatCommandDescriptor)) + { + match = (DumpFormat) formatList.get(i); + break; + } + } + return match; + } + + /** + * Dynamically loads dump formats into an ArrayList. This method is adapted from the loadGameControllers() method + * in Bret Barker's GameServer class. Barker (bret@hypefiend.com) is co-author of the book "Developing Games in + * Java". Also see the ToolLoader and SyscallLoader classes elsewhere in MARS. */ - - public class DumpFormatLoader { - - private static final String CLASS_PREFIX = "mars.mips.dump."; - private static final String DUMP_DIRECTORY_PATH = "mars/mips/dump"; - private static final String SYSCALL_INTERFACE = "DumpFormat.class"; - private static final String CLASS_EXTENSION = "class"; - - private static ArrayList formatList = null; - - /** - * Dynamically loads dump formats into an ArrayList. This method is adapted from - * the loadGameControllers() method in Bret Barker's GameServer class. - * Barker (bret@hypefiend.com) is co-author of the book "Developing Games - * in Java". Also see the ToolLoader and SyscallLoader classes elsewhere in MARS. - */ - - public ArrayList loadDumpFormats() { - // The list will be populated only the first time this method is called. - if (formatList == null) { + + public ArrayList loadDumpFormats() + { + // The list will be populated only the first time this method is called. + if (formatList == null) + { formatList = new ArrayList(); - // grab all class files in the dump directory - ArrayList candidates = FilenameFinder.getFilenameList(this.getClass( ).getClassLoader(), - DUMP_DIRECTORY_PATH, CLASS_EXTENSION); - for( int i = 0; i < candidates.size(); i++) { - String file = (String) candidates.get(i); - try { - // grab the class, make sure it implements DumpFormat, instantiate, add to list - String formatClassName = CLASS_PREFIX+file.substring(0, file.indexOf(CLASS_EXTENSION)-1); - Class clas = Class.forName(formatClassName); - if (DumpFormat.class.isAssignableFrom(clas) && - !Modifier.isAbstract(clas.getModifiers()) && - !Modifier.isInterface(clas.getModifiers()) ) { - formatList.add(clas.newInstance()); - } - } - catch (Exception e) { - System.out.println("Error instantiating DumpFormat from file " + file + ": "+e); - } + // grab all class files in the dump directory + ArrayList candidates = FilenameFinder.getFilenameList(this.getClass().getClassLoader(), + DUMP_DIRECTORY_PATH, CLASS_EXTENSION); + for (int i = 0; i < candidates.size(); i++) + { + String file = (String) candidates.get(i); + try + { + // grab the class, make sure it implements DumpFormat, instantiate, add to list + String formatClassName = CLASS_PREFIX + file.substring(0, file.indexOf(CLASS_EXTENSION) - 1); + Class clas = Class.forName(formatClassName); + if (DumpFormat.class.isAssignableFrom(clas) && + !Modifier.isAbstract(clas.getModifiers()) && + !Modifier.isInterface(clas.getModifiers())) + { + formatList.add(clas.newInstance()); + } + } + catch (Exception e) + { + System.out.println("Error instantiating DumpFormat from file " + file + ": " + e); + } } - } - return formatList; - } - - public static DumpFormat findDumpFormatGivenCommandDescriptor(ArrayList formatList, String formatCommandDescriptor) { - DumpFormat match = null; - for (int i=0; i>8); - tmp_chksum += 0xFF & temp.intValue(); - tmp_chksum += 0xFF & (temp.intValue()>>8); - tmp_chksum += 0xFF & (temp.intValue()>>16); - tmp_chksum += 0xFF & (temp.intValue()>>24); - tmp_chksum = tmp_chksum % 256; - tmp_chksum = ~tmp_chksum + 1; - chksum = Integer.toHexString(0xFF & tmp_chksum); - if(chksum.length()==1) chksum = '0' + chksum; - String finalstr = ":04"+addr+"00"+string+chksum; - out.println(finalstr.toUpperCase()); - } - out.println(":00000001FF"); - } - finally { - out.close(); +public class IntelHexDumpFormat extends AbstractDumpFormat +{ + + /** + * Constructor. File extention is "hex". + */ + public IntelHexDumpFormat() + { + super("Intel hex format", "HEX", "Written as Intel Hex Memory File", "hex"); + } + + /** + * Write MIPS memory contents according to the Memory Initialization File (MIF) specification. + * + * @param file File in which to store MIPS memory contents. + * @param firstAddress first (lowest) memory address to dump. In bytes but must be on word boundary. + * @param lastAddress last (highest) memory address to dump. In bytes but must be on word boundary. Will dump + * the word that starts at this address. + * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. + * @throws IOException if error occurs during file output. + */ + public void dumpMemoryRange(File file, int firstAddress, int lastAddress) + throws AddressErrorException, IOException + { + PrintStream out = new PrintStream(new FileOutputStream(file)); + String string = null; + try + { + for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) + { + Integer temp = Globals.memory.getRawWordOrNull(address); + if (temp == null) + { + break; + } + string = Integer.toHexString(temp.intValue()); + while (string.length() < 8) + { + string = '0' + string; + } + String addr = Integer.toHexString(address - firstAddress); + while (addr.length() < 4) + { + addr = '0' + addr; + } + String chksum; + int tmp_chksum = 0; + tmp_chksum += 4; + tmp_chksum += 0xFF & (address - firstAddress); + tmp_chksum += 0xFF & ((address - firstAddress) >> 8); + tmp_chksum += 0xFF & temp.intValue(); + tmp_chksum += 0xFF & (temp.intValue() >> 8); + tmp_chksum += 0xFF & (temp.intValue() >> 16); + tmp_chksum += 0xFF & (temp.intValue() >> 24); + tmp_chksum = tmp_chksum % 256; + tmp_chksum = ~tmp_chksum + 1; + chksum = Integer.toHexString(0xFF & tmp_chksum); + if (chksum.length() == 1) + { + chksum = '0' + chksum; + } + String finalstr = ":04" + addr + "00" + string + chksum; + out.println(finalstr.toUpperCase()); } - - } - } + out.println(":00000001FF"); + } + finally + { + out.close(); + } + + } +} diff --git a/src/main/java/mars/mips/dump/MIFDumpFormat.java b/src/main/java/mars/mips/dump/MIFDumpFormat.java index e21c026..3994223 100644 --- a/src/main/java/mars/mips/dump/MIFDumpFormat.java +++ b/src/main/java/mars/mips/dump/MIFDumpFormat.java @@ -1,8 +1,9 @@ - package mars.mips.dump; +package mars.mips.dump; - import mars.Globals; - import mars.mips.hardware.*; - import java.io.*; +import mars.mips.hardware.AddressErrorException; + +import java.io.File; +import java.io.IOException; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -32,40 +33,40 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * The Memory Initialization File (.mif) VHDL-supported file format - * This is documented for the Altera platform at + * The Memory Initialization File (.mif) VHDL-supported file format This is documented for the Altera platform at * www.altera.com/support/software/nativelink/quartus2/glossary/def_mif.html. - * - * @author Pete Sanderson + * + * @author Pete Sanderson * @version December 2007 */ // NOT READY FOR PRIME TIME. WHEN IT IS, UNCOMMENT THE "extends" CLAUSE // AND THE SUPERCLASS CONSTRUCTOR CALL SO THE FORMAT LOADER WILL ACCEPT IT // AND IT WILL BE ADDED TO THE LIST. - public class MIFDumpFormat { //extends AbstractDumpFormat { - - /** - * Constructor. File extention is "mif". - */ - public MIFDumpFormat() { - // super("MIF", "MIF", "Written as Memory Initialization File (Altera)", "mif"); - } - - /** - * Write MIPS memory contents according to the Memory Initialization File - * (MIF) specification. - * - * @param file File in which to store MIPS memory contents. - * @param firstAddress first (lowest) memory address to dump. In bytes but - * must be on word boundary. - * @param lastAddress last (highest) memory address to dump. In bytes but - * must be on word boundary. Will dump the word that starts at this address. - * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. - * @throws IOException if error occurs during file output. - */ - public void dumpMemoryRange(File file, int firstAddress, int lastAddress) - throws AddressErrorException, IOException { - - } - } \ No newline at end of file +public class MIFDumpFormat +{ //extends AbstractDumpFormat { + + /** + * Constructor. File extention is "mif". + */ + public MIFDumpFormat() + { + // super("MIF", "MIF", "Written as Memory Initialization File (Altera)", "mif"); + } + + /** + * Write MIPS memory contents according to the Memory Initialization File (MIF) specification. + * + * @param file File in which to store MIPS memory contents. + * @param firstAddress first (lowest) memory address to dump. In bytes but must be on word boundary. + * @param lastAddress last (highest) memory address to dump. In bytes but must be on word boundary. Will dump + * the word that starts at this address. + * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. + * @throws IOException if error occurs during file output. + */ + public void dumpMemoryRange(File file, int firstAddress, int lastAddress) + throws AddressErrorException, IOException + { + + } +} diff --git a/src/main/java/mars/mips/dump/SegmentWindowDumpFormat.java b/src/main/java/mars/mips/dump/SegmentWindowDumpFormat.java index 23ea15c..ee72770 100644 --- a/src/main/java/mars/mips/dump/SegmentWindowDumpFormat.java +++ b/src/main/java/mars/mips/dump/SegmentWindowDumpFormat.java @@ -1,10 +1,15 @@ - package mars.mips.dump; +package mars.mips.dump; - import mars.Globals; - import mars.ProgramStatement; - import mars.util.Binary; - import mars.mips.hardware.*; - import java.io.*; +import mars.Globals; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Memory; +import mars.util.Binary; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -34,117 +39,129 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** + * Dump MIPS memory contents in Segment Window format. Each line of text output resembles the Text Segment Window or + * Data Segment Window depending on which segment is selected for the dump. Written using PrintStream's println() + * method. Each line of Text Segment Window represents one word of text segment memory. The line includes (1) address, + * (2) machine code in hex, (3) basic instruction, (4) source line. Each line of Data Segment Window represents 8 words + * of data segment memory. The line includes address of first word for that line followed by 8 32-bit values. + *

+ * In either case, addresses and values are displayed in decimal or hexadecimal representation according to the + * corresponding settings. * - * Dump MIPS memory contents in Segment Window format. Each line of - * text output resembles the Text Segment Window or Data Segment Window - * depending on which segment is selected for the dump. Written - * using PrintStream's println() method. Each line of Text Segment - * Window represents one word of text segment memory. The line - * includes (1) address, (2) machine code in hex, (3) basic instruction, - * (4) source line. Each line of Data Segment Window represents 8 - * words of data segment memory. The line includes address of first - * word for that line followed by 8 32-bit values. - * - * In either case, addresses and values are displayed in decimal or - * hexadecimal representation according to the corresponding settings. - * - * @author Pete Sanderson + * @author Pete Sanderson * @version January 2008 */ - public class SegmentWindowDumpFormat extends AbstractDumpFormat { - - /** - * Constructor. There is no standard file extension for this format. - */ - public SegmentWindowDumpFormat() { - super("Text/Data Segment Window", "SegmentWindow", " Text Segment Window or Data Segment Window format to text file", null); - } - - - /** - * Write MIPS memory contents in Segment Window format. Each line of - * text output resembles the Text Segment Window or Data Segment Window - * depending on which segment is selected for the dump. Written - * using PrintStream's println() method. - * - * @param file File in which to store MIPS memory contents. - * @param firstAddress first (lowest) memory address to dump. In bytes but - * must be on word boundary. - * @param lastAddress last (highest) memory address to dump. In bytes but - * must be on word boundary. Will dump the word that starts at this address. - * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. - * @throws IOException if error occurs during file output. - */ - public void dumpMemoryRange(File file, int firstAddress, int lastAddress) - throws AddressErrorException, IOException { - - PrintStream out = new PrintStream(new FileOutputStream(file)); - - boolean hexAddresses = Globals.getSettings().getDisplayAddressesInHex(); - - // If address in data segment, print in same format as Data Segment Window - if (Memory.inDataSegment(firstAddress)) { +public class SegmentWindowDumpFormat extends AbstractDumpFormat +{ + + /** + * Constructor. There is no standard file extension for this format. + */ + public SegmentWindowDumpFormat() + { + super("Text/Data Segment Window", "SegmentWindow", " Text Segment Window or Data Segment Window format to text file", null); + } + + + /** + * Write MIPS memory contents in Segment Window format. Each line of text output resembles the Text Segment Window + * or Data Segment Window depending on which segment is selected for the dump. Written using PrintStream's + * println() method. + * + * @param file File in which to store MIPS memory contents. + * @param firstAddress first (lowest) memory address to dump. In bytes but must be on word boundary. + * @param lastAddress last (highest) memory address to dump. In bytes but must be on word boundary. Will dump + * the word that starts at this address. + * @throws AddressErrorException if firstAddress is invalid or not on a word boundary. + * @throws IOException if error occurs during file output. + */ + public void dumpMemoryRange(File file, int firstAddress, int lastAddress) + throws AddressErrorException, IOException + { + + PrintStream out = new PrintStream(new FileOutputStream(file)); + + boolean hexAddresses = Globals.getSettings().getDisplayAddressesInHex(); + + // If address in data segment, print in same format as Data Segment Window + if (Memory.inDataSegment(firstAddress)) + { boolean hexValues = Globals.getSettings().getDisplayValuesInHex(); int offset = 0; - String string=""; - try { - for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) { - if (offset % 8 == 0) { - string = ((hexAddresses) ? Binary.intToHexString(address) : Binary.unsignedIntToIntString(address)) + " "; - } - offset++; - Integer temp = Globals.memory.getRawWordOrNull(address); - if (temp == null) - break; - string += ((hexValues) - ? Binary.intToHexString(temp.intValue()) - : (" "+temp).substring(temp.toString().length()) - ) + " "; - if (offset % 8 == 0) { - out.println(string); - string = ""; - } - } - } - finally { - out.close(); - } - return; - } - - if (!Memory.inTextSegment(firstAddress)) { - return; - } - // If address in text segment, print in same format as Text Segment Window - out.println(" Address Code Basic Source"); - // 12345678901234567890123456789012345678901234567890 - // 1 2 3 4 5 - out.println(); - String string = null; - try { - for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) { - string = ((hexAddresses) ? Binary.intToHexString(address) : Binary.unsignedIntToIntString(address)) + " "; - Integer temp = Globals.memory.getRawWordOrNull(address); - if (temp == null) - break; - string += Binary.intToHexString(temp.intValue()) + " "; - try { - ProgramStatement ps = Globals.memory.getStatement(address); - string += (ps.getPrintableBasicAssemblyStatement()+" ").substring(0,22); - string += (((ps.getSource()=="") ? "" : new Integer(ps.getSourceLine()).toString())+" ").substring(0,5); - string += ps.getSource(); - } - catch (AddressErrorException aee) { - } - out.println(string); + String string = ""; + try + { + for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) + { + if (offset % 8 == 0) + { + string = ((hexAddresses) ? Binary.intToHexString(address) : Binary.unsignedIntToIntString(address)) + " "; + } + offset++; + Integer temp = Globals.memory.getRawWordOrNull(address); + if (temp == null) + { + break; + } + string += ((hexValues) + ? Binary.intToHexString(temp.intValue()) + : (" " + temp).substring(temp.toString().length()) + ) + " "; + if (offset % 8 == 0) + { + out.println(string); + string = ""; + } + } } - } - finally { - out.close(); - } - } - - - } \ No newline at end of file + finally + { + out.close(); + } + return; + } + + if (!Memory.inTextSegment(firstAddress)) + { + return; + } + // If address in text segment, print in same format as Text Segment Window + out.println(" Address Code Basic Source"); + // 12345678901234567890123456789012345678901234567890 + // 1 2 3 4 5 + out.println(); + String string = null; + try + { + for (int address = firstAddress; address <= lastAddress; address += Memory.WORD_LENGTH_BYTES) + { + string = ((hexAddresses) ? Binary.intToHexString(address) : Binary.unsignedIntToIntString(address)) + " "; + Integer temp = Globals.memory.getRawWordOrNull(address); + if (temp == null) + { + break; + } + string += Binary.intToHexString(temp.intValue()) + " "; + try + { + ProgramStatement ps = Globals.memory.getStatement(address); + string += (ps.getPrintableBasicAssemblyStatement() + " ").substring(0, 22); + string += (((ps.getSource() == "") ? "" : Integer.valueOf(ps.getSourceLine()).toString()) + " ").substring(0, 5); + string += ps.getSource(); + } + catch (AddressErrorException aee) + { + } + out.println(string); + } + } + finally + { + out.close(); + } + } + + +} diff --git a/src/main/java/mars/mips/hardware/AccessNotice.java b/src/main/java/mars/mips/hardware/AccessNotice.java index ac05d0b..680e29c 100644 --- a/src/main/java/mars/mips/hardware/AccessNotice.java +++ b/src/main/java/mars/mips/hardware/AccessNotice.java @@ -29,61 +29,78 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Object provided to Observers of runtime access to MIPS memory or registers. - * The access types READ and WRITE defined here; use subclasses defined for - * MemoryAccessNotice and RegisterAccessNotice. This is abstract class. - * - * @author Pete Sanderson + * Object provided to Observers of runtime access to MIPS memory or registers. The access types READ and WRITE defined + * here; use subclasses defined for MemoryAccessNotice and RegisterAccessNotice. This is abstract class. + * + * @author Pete Sanderson * @version July 2005 */ -public abstract class AccessNotice { - /** Indicates the purpose of access was to read. */ - public static final int READ = 0; - /** Indicates the purpose of access was to write. */ - public static final int WRITE = 1; - - private int accessType; - private Thread thread; - - protected AccessNotice(int type) { - if (type != READ && type != WRITE) { - throw new IllegalArgumentException(); - } - accessType = type; - thread = Thread.currentThread(); - } - /** Get the access type: READ or WRITE. - * @return Access type, either AccessNotice.READ or AccessNotice.WRITE - */ - public int getAccessType() { - return accessType; - } - - /** Get reference to thread that created this notice - * @return Return reference to the thread that created this notice. - */ - public Thread getThread() { - return thread; - } +public abstract class AccessNotice +{ + /** Indicates the purpose of access was to read. */ + public static final int READ = 0; - /** Query whether the access originated from MARS GUI (AWT event queue) - * @return true if this access originated from MARS GUI, false otherwise - */ - // 'A' is the first character of the main AWT event queue thread name. - // "AWT-EventQueue-0" - public boolean accessIsFromGUI() { - return thread.getName().startsWith("AWT"); - } - - /** Query whether the access originated from executing MIPS program - * @return true if this access originated from executing MIPS program, false otherwise - */ - // Thread to execute the MIPS program is instantiated in SwingWorker.java. - // There it is given the name "MIPS" to replace the default "Thread-x". - public boolean accessIsFromMIPS() { - return thread.getName().startsWith("MIPS"); - } - -} \ No newline at end of file + /** Indicates the purpose of access was to write. */ + public static final int WRITE = 1; + + private final int accessType; + + private final Thread thread; + + protected AccessNotice(int type) + { + if (type != READ && type != WRITE) + { + throw new IllegalArgumentException(); + } + accessType = type; + thread = Thread.currentThread(); + } + + /** + * Get the access type: READ or WRITE. + * + * @return Access type, either AccessNotice.READ or AccessNotice.WRITE + */ + public int getAccessType() + { + return accessType; + } + + /** + * Get reference to thread that created this notice + * + * @return Return reference to the thread that created this notice. + */ + public Thread getThread() + { + return thread; + } + + /** + * Query whether the access originated from MARS GUI (AWT event queue) + * + * @return true if this access originated from MARS GUI, false otherwise + */ + // 'A' is the first character of the main AWT event queue thread name. + // "AWT-EventQueue-0" + public boolean accessIsFromGUI() + { + return thread.getName().startsWith("AWT"); + } + + /** + * Query whether the access originated from executing MIPS program + * + * @return true if this access originated from executing MIPS program, false otherwise + */ + // Thread to execute the MIPS program is instantiated in SwingWorker.java. + // There it is given the name "MIPS" to replace the default "Thread-x". + public boolean accessIsFromMIPS() + { + return thread.getName().startsWith("MIPS"); + } + +} diff --git a/src/main/java/mars/mips/hardware/AddressErrorException.java b/src/main/java/mars/mips/hardware/AddressErrorException.java index 948e654..a4c31c3 100644 --- a/src/main/java/mars/mips/hardware/AddressErrorException.java +++ b/src/main/java/mars/mips/hardware/AddressErrorException.java @@ -1,5 +1,6 @@ package mars.mips.hardware; -import mars.util.*; + +import mars.util.Binary; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -30,44 +31,49 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Represents MIPS AddressErrorException. This is generated by the assembler when the - * source code references a memory address not valid for the context. - * - * @author Pete Sanderson + * Represents MIPS AddressErrorException. This is generated by the assembler when the source code references a memory + * address not valid for the context. + * + * @author Pete Sanderson * @version August 2003 **/ -public class AddressErrorException extends Exception { - private int address; - private int type; // Exceptions.ADDRESS_EXCEPTION_LOAD,Exceptions.ADDRESS_EXCEPTION_STORE +public class AddressErrorException extends Exception +{ + private final int address; + + private final int type; // Exceptions.ADDRESS_EXCEPTION_LOAD,Exceptions.ADDRESS_EXCEPTION_STORE - /** - * Constructor for the AddressErrorException class - * - * @param addr The erroneous memory address. - **/ - - public AddressErrorException(String message, int exceptType, int addr) { - super(message+Binary.intToHexString(addr)); - address = addr; - type = exceptType; - } + /** + * Constructor for the AddressErrorException class + * + * @param addr The erroneous memory address. + **/ - /** - * Get the erroneous memory address. - * - * @return The erroneous memory address. - **/ - public int getAddress() { - return address; - } - - /** - * Get the exception type (load or store). - * - * @return Exception type: Exceptions.ADDRESS_EXCEPTION_LOAD, Exceptions.ADDRESS_EXCEPTION_STORE - **/ - public int getType() { - return type; - } + public AddressErrorException(String message, int exceptType, int addr) + { + super(message + Binary.intToHexString(addr)); + address = addr; + type = exceptType; + } + + /** + * Get the erroneous memory address. + * + * @return The erroneous memory address. + **/ + public int getAddress() + { + return address; + } + + /** + * Get the exception type (load or store). + * + * @return Exception type: Exceptions.ADDRESS_EXCEPTION_LOAD, Exceptions.ADDRESS_EXCEPTION_STORE + **/ + public int getType() + { + return type; + } } diff --git a/src/main/java/mars/mips/hardware/Coprocessor0.java b/src/main/java/mars/mips/hardware/Coprocessor0.java index eeb8b18..a90df6d 100644 --- a/src/main/java/mars/mips/hardware/Coprocessor0.java +++ b/src/main/java/mars/mips/hardware/Coprocessor0.java @@ -1,6 +1,8 @@ - package mars.mips.hardware; - import mars.Globals; - import java.util.*; +package mars.mips.hardware; + +import mars.Globals; + +import java.util.Observer; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -31,185 +33,225 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Represents Coprocessor 0. We will use only its interrupt/exception registers. - * @author Pete Sanderson - * @version August 2005 - **/ + * Represents Coprocessor 0. We will use only its interrupt/exception registers. + * + * @author Pete Sanderson + * @version August 2005 + **/ - public class Coprocessor0 { - /** Coprocessor register names - */ - public static final int VADDR = 8; - public static final int STATUS = 12; - public static final int CAUSE = 13; - public static final int EPC = 14; - - public static final int EXCEPTION_LEVEL = 1; // bit position in STATUS register - // bits 8-15 (mask for interrupt levels) all set, bit 4 (user mode) set, - // bit 1 (exception level) not set, bit 0 (interrupt enable) set. - public static final int DEFAULT_STATUS_VALUE = 0x0000FF11; - - private static Register [] registers = - { new Register("$8 (vaddr)", 8, 0), +public class Coprocessor0 +{ + /** + * Coprocessor register names + */ + public static final int VADDR = 8; + + public static final int STATUS = 12; + + public static final int CAUSE = 13; + + public static final int EPC = 14; + + public static final int EXCEPTION_LEVEL = 1; // bit position in STATUS register + + // bits 8-15 (mask for interrupt levels) all set, bit 4 (user mode) set, + // bit 1 (exception level) not set, bit 0 (interrupt enable) set. + public static final int DEFAULT_STATUS_VALUE = 0x0000FF11; + + private static final Register[] registers = + {new Register("$8 (vaddr)", 8, 0), new Register("$12 (status)", 12, DEFAULT_STATUS_VALUE), - new Register("$13 (cause)", 13, 0), - new Register("$14 (epc)", 14, 0) - }; - - - /** - * Method for displaying the register values for debugging. - **/ - - public static void showRegisters(){ - for (int i=0; i< registers.length; i++){ + new Register("$13 (cause)", 13, 0), + new Register("$14 (epc)", 14, 0) + }; + + + /** + * Method for displaying the register values for debugging. + **/ + + public static void showRegisters() + { + for (int i = 0; i < registers.length; i++) + { System.out.println("Name: " + registers[i].getName()); System.out.println("Number: " + registers[i].getNumber()); - System.out.println("Value: " + registers[i].getValue()); - System.out.println(""); - } - } - - /** - * Sets the value of the register given to the value given. - * @param n name of register to set the value of ($n, where n is reg number). - * @param val The desired value for the register. - * @return old value in register prior to update - **/ - - public static int updateRegister(String n, int val){ - int oldValue = 0; - for (int i=0; i< registers.length; i++){ - if(("$"+registers[i].getNumber()).equals(n) || registers[i].getName().equals(n)) { - oldValue = registers[i].getValue(); - registers[i].setValue(val); - break; - } - } - return oldValue; - } - - /** - * This method updates the register value who's number is num. - * @param num Number of register to set the value of. - * @param val The desired value for the register. - * @return old value in register prior to update - **/ - public static int updateRegister(int num, int val){ - int old = 0; - for (int i=0; i< registers.length; i++){ - if(registers[i].getNumber()== num) { - old = (Globals.getSettings().getBackSteppingEnabled()) - ? Globals.program.getBackStepper().addCoprocessor0Restore(num,registers[i].setValue(val)) - : registers[i].setValue(val); - break; - } - } - return old; - } + System.out.println("Value: " + registers[i].getValue()); + System.out.println(); + } + } - - /** - * Returns the value of the register who's number is num. - * @param num The register number. - * @return The value of the given register. 0 for non-implemented registers - **/ - - public static int getValue(int num){ - for (int i=0; i< registers.length; i++){ - if(registers[i].getNumber()== num) { - return registers[i].getValue(); - } - } - return 0; - } - - /** - * For getting the number representation of the register. - * @param n The string formatted register name to look for. - * @return The number of the register represented by the string. -1 if no match. - **/ - - public static int getNumber(String n){ - for (int i=0; i< registers.length; i++){ - if(("$"+registers[i].getNumber()).equals(n) || registers[i].getName().equals(n)) { - return registers[i].getNumber(); - } - } - return -1; - } - - /** - * For returning the set of registers. - * @return The set of registers. - **/ - - public static Register[] getRegisters(){ - return registers; - } + /** + * Sets the value of the register given to the value given. + * + * @param n name of register to set the value of ($n, where n is reg number). + * @param val The desired value for the register. + * @return old value in register prior to update + **/ - - /** - * Coprocessor0 implements only selected registers, so the register number - * (8, 12, 13, 14) does not correspond to its position in the list of registers - * (0, 1, 2, 3). - * @param r A coprocessor0 Register - * @return the list position of given register, -1 if not found. - **/ - - public static int getRegisterPosition(Register r){ - for (int i=0; i< registers.length; i++){ - if(registers[i]==r) { - return i; + public static int updateRegister(String n, int val) + { + int oldValue = 0; + for (int i = 0; i < registers.length; i++) + { + if (("$" + registers[i].getNumber()).equals(n) || registers[i].getName().equals(n)) + { + oldValue = registers[i].getValue(); + registers[i].setValue(val); + break; } - } - return -1; - } - - /** - * Get register object corresponding to given name. If no match, return null. - * @param rname The register name, in $0 format. - * @return The register object,or null if not found. - **/ - - public static Register getRegister(String rname) { - for (int i=0; i< registers.length; i++){ - if(("$"+registers[i].getNumber()).equals(rname) || registers[i].getName().equals(rname)) { - return registers[i]; + } + return oldValue; + } + + /** + * This method updates the register value who's number is num. + * + * @param num Number of register to set the value of. + * @param val The desired value for the register. + * @return old value in register prior to update + **/ + public static int updateRegister(int num, int val) + { + int old = 0; + for (int i = 0; i < registers.length; i++) + { + if (registers[i].getNumber() == num) + { + old = (Globals.getSettings().getBackSteppingEnabled()) + ? Globals.program.getBackStepper().addCoprocessor0Restore(num, registers[i].setValue(val)) + : registers[i].setValue(val); + break; } - } - return null; - } - - - /** - * Method to reinitialize the values of the registers. - **/ - - public static void resetRegisters(){ - for(int i=0; i< registers.length; i++){ + } + return old; + } + + + /** + * Returns the value of the register who's number is num. + * + * @param num The register number. + * @return The value of the given register. 0 for non-implemented registers + **/ + + public static int getValue(int num) + { + for (int i = 0; i < registers.length; i++) + { + if (registers[i].getNumber() == num) + { + return registers[i].getValue(); + } + } + return 0; + } + + /** + * For getting the number representation of the register. + * + * @param n The string formatted register name to look for. + * @return The number of the register represented by the string. -1 if no match. + **/ + + public static int getNumber(String n) + { + for (int i = 0; i < registers.length; i++) + { + if (("$" + registers[i].getNumber()).equals(n) || registers[i].getName().equals(n)) + { + return registers[i].getNumber(); + } + } + return -1; + } + + /** + * For returning the set of registers. + * + * @return The set of registers. + **/ + + public static Register[] getRegisters() + { + return registers; + } + + + /** + * Coprocessor0 implements only selected registers, so the register number (8, 12, 13, 14) does not correspond to + * its position in the list of registers (0, 1, 2, 3). + * + * @param r A coprocessor0 Register + * @return the list position of given register, -1 if not found. + **/ + + public static int getRegisterPosition(Register r) + { + for (int i = 0; i < registers.length; i++) + { + if (registers[i] == r) + { + return i; + } + } + return -1; + } + + /** + * Get register object corresponding to given name. If no match, return null. + * + * @param rname The register name, in $0 format. + * @return The register object,or null if not found. + **/ + + public static Register getRegister(String rname) + { + for (int i = 0; i < registers.length; i++) + { + if (("$" + registers[i].getNumber()).equals(rname) || registers[i].getName().equals(rname)) + { + return registers[i]; + } + } + return null; + } + + + /** + * Method to reinitialize the values of the registers. + **/ + + public static void resetRegisters() + { + for (int i = 0; i < registers.length; i++) + { registers[i].resetValue(); - } - } - - /** - * Each individual register is a separate object and Observable. This handy method - * will add the given Observer to each one. - */ - public static void addRegistersObserver(Observer observer) { - for (int i=0; i= 0 && reg < registers.length) { - registers[reg].setValue(Float.floatToRawIntBits(val)); - } - } - - /** - * Sets the value of the FPU register given to the 32-bit - * pattern given by the int parameter. - * @param reg Register to set the value of. - * @param val The desired int bit pattern for the register. - **/ - - public static void setRegisterToInt(String reg, int val){ - setRegisterToInt(getRegisterNumber(reg), val); - } - - - /** - * Sets the value of the FPU register given to the 32-bit - * pattern given by the int parameter. - * @param reg Register to set the value of. - * @param val The desired int bit pattern for the register. - **/ - - public static void setRegisterToInt(int reg, int val){ - if(reg >= 0 && reg < registers.length) { - registers[reg].setValue(val); - } - } - - - /** - * Sets the value of the FPU register given to the double value given. The register - * must be even-numbered, and the low order 32 bits are placed in it. The high order - * 32 bits are placed in the (odd numbered) register that follows it. - * @param reg Register to set the value of. - * @param val The desired double value for the register. - * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. - **/ - - public static void setRegisterPairToDouble(int reg, double val) - throws InvalidRegisterAccessException { - if (reg % 2 != 0) { - throw new InvalidRegisterAccessException(); - } - long bits = Double.doubleToRawLongBits(val); - registers[reg+1].setValue(Binary.highOrderLongToInt(bits)); // high order 32 bits - registers[reg].setValue(Binary.lowOrderLongToInt(bits)); // low order 32 bits - } - - - /** - * Sets the value of the FPU register given to the double value given. The register - * must be even-numbered, and the low order 32 bits are placed in it. The high order - * 32 bits are placed in the (odd numbered) register that follows it. - * @param reg Register to set the value of. - * @param val The desired double value for the register. - * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. - **/ - public static void setRegisterPairToDouble(String reg, double val) - throws InvalidRegisterAccessException { - setRegisterPairToDouble(getRegisterNumber(reg), val); - } - - - /** - * Sets the value of the FPU register pair given to the long value containing 64 bit pattern - * given. The register - * must be even-numbered, and the low order 32 bits from the long are placed in it. The high order - * 32 bits from the long are placed in the (odd numbered) register that follows it. - * @param reg Register to set the value of. Must be even register of even/odd pair. - * @param val The desired double value for the register. - * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. - **/ - - public static void setRegisterPairToLong(int reg, long val) - throws InvalidRegisterAccessException { - if (reg % 2 != 0) { - throw new InvalidRegisterAccessException(); - } - registers[reg+1].setValue(Binary.highOrderLongToInt(val)); // high order 32 bits - registers[reg].setValue(Binary.lowOrderLongToInt(val)); // low order 32 bits - } - - - /** - * Sets the value of the FPU register pair given to the long value containing 64 bit pattern - * given. The register - * must be even-numbered, and the low order 32 bits from the long are placed in it. The high order - * 32 bits from the long are placed in the (odd numbered) register that follows it. - * @param reg Register to set the value of. Must be even register of even/odd pair. - * @param val The desired long value containing the 64 bits for the register pair. - * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. - **/ - public static void setRegisterPairToLong(String reg, long val) - throws InvalidRegisterAccessException { - setRegisterPairToLong(getRegisterNumber(reg), val); - } - - - - /** - * Gets the float value stored in the given FPU register. - * @param reg Register to get the value of. - * @return The float value stored by that register. - **/ - - public static float getFloatFromRegister(int reg){ - float result = 0F; - if(reg >= 0 && reg < registers.length) { - result = Float.intBitsToFloat(registers[reg].getValue()); - } - return result; - } - - - /** - * Gets the float value stored in the given FPU register. - * @param reg Register to get the value of. - * @return The float value stored by that register. - **/ - - public static float getFloatFromRegister(String reg) { - return getFloatFromRegister(getRegisterNumber(reg)); - } - - - /** - * Gets the 32-bit int bit pattern stored in the given FPU register. - * @param reg Register to get the value of. - * @return The int bit pattern stored by that register. - **/ - - public static int getIntFromRegister(int reg){ - int result = 0; - if(reg >= 0 && reg < registers.length) { - result = registers[reg].getValue(); - } - return result; - } - - - /** - * Gets the 32-bit int bit pattern stored in the given FPU register. - * @param reg Register to get the value of. - * @return The int bit pattern stored by that register. - **/ - - public static int getIntFromRegister(String reg) { - return getIntFromRegister(getRegisterNumber(reg)); - } - - - /** - * Gets the double value stored in the given FPU register. The register - * must be even-numbered. - * @param reg Register to get the value of. Must be even number of even/odd pair. - * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. - **/ - - public static double getDoubleFromRegisterPair(int reg) - throws InvalidRegisterAccessException { - double result = 0.0; - if (reg % 2 != 0) { - throw new InvalidRegisterAccessException(); - } - long bits = Binary.twoIntsToLong(registers[reg+1].getValue(),registers[reg].getValue()); - return Double.longBitsToDouble(bits); - } - - - /** - * Gets the double value stored in the given FPU register. The register - * must be even-numbered. - * @param reg Register to get the value of. Must be even number of even/odd pair. - * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. - **/ - - public static double getDoubleFromRegisterPair(String reg) - throws InvalidRegisterAccessException { - return getDoubleFromRegisterPair(getRegisterNumber(reg)); - } - - - /** - * Gets a long representing the double value stored in the given double - * precision FPU register. - * The register must be even-numbered. - * @param reg Register to get the value of. Must be even number of even/odd pair. - * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. - **/ - - public static long getLongFromRegisterPair(int reg) - throws InvalidRegisterAccessException { - double result = 0.0; - if (reg % 2 != 0) { - throw new InvalidRegisterAccessException(); - } - return Binary.twoIntsToLong(registers[reg+1].getValue(),registers[reg].getValue()); - } - - - /** - * Gets the double value stored in the given FPU register. The register - * must be even-numbered. - * @param reg Register to get the value of. Must be even number of even/odd pair. - * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. - **/ - - public static long getLongFromRegisterPair(String reg) - throws InvalidRegisterAccessException { - return getLongFromRegisterPair(getRegisterNumber(reg)); - } - - - /** - * This method updates the FPU register value who's number is num. Note the - * registers themselves hold an int value. There are helper methods available - * to which you can give a float or double to store. - * @param num FPU register to set the value of. - * @param val The desired int value for the register. - **/ - - public static int updateRegister(int num, int val){ - int old = 0; - for (int i=0; i< registers.length; i++){ - if(registers[i].getNumber()== num) { - old = (Globals.getSettings().getBackSteppingEnabled()) - ? Globals.program.getBackStepper().addCoprocessor1Restore(num,registers[i].setValue(val)) - : registers[i].setValue(val); - break; - } - } - return old; - } - - /** - * Returns the value of the FPU register who's number is num. Returns the - * raw int value actually stored there. If you need a float, use - * Float.intBitsToFloat() to get the equivent float. - * @param num The FPU register number. - * @return The int value of the given register. - **/ - - public static int getValue(int num){ - return registers[num].getValue(); - } - - /** - * For getting the number representation of the FPU register. - * @param n The string formatted register name to look for. - * @return The number of the register represented by the string. - **/ - - public static int getRegisterNumber(String n){ - int j=-1; - for (int i=0; i< registers.length; i++){ - if(registers[i].getName().equals(n)) { - j= registers[i].getNumber(); - break; - } - } - return j; - } - - /** - * For returning the set of registers. - * @return The set of registers. - **/ - - public static Register[] getRegisters(){ - return registers; - } - - /** - * Get register object corresponding to given name. If no match, return null. - * @param rName The FPU register name, must be "$f0" through "$f31". - * @return The register object,or null if not found. - **/ - - public static Register getRegister(String rName) { - Register reg = null; - if (rName.charAt(0) == '$' && rName.length() > 1 && rName.charAt(1) == 'f') { - try { - // check for register number 0-31. - reg = registers[Binary.stringToInt(rName.substring(2))]; // KENV 1/6/05 - } - catch (Exception e) { - // handles both NumberFormat and ArrayIndexOutOfBounds - reg = null; - } - } - return reg; - } - - - /** - * Method to reinitialize the values of the registers. - **/ - - public static void resetRegisters(){ - for(int i=0; i < registers.length; i++) - registers[i].resetValue(); - clearConditionFlags(); - } - - - /** - * Each individual register is a separate object and Observable. This handy method - * will add the given Observer to each one. - */ - public static void addRegistersObserver(Observer observer) { - for (int i=0; i= 0 && reg < registers.length) + { + registers[reg].setValue(Float.floatToRawIntBits(val)); + } + } + + /** + * Sets the value of the FPU register given to the 32-bit pattern given by the int parameter. + * + * @param reg Register to set the value of. + * @param val The desired int bit pattern for the register. + **/ + + public static void setRegisterToInt(String reg, int val) + { + setRegisterToInt(getRegisterNumber(reg), val); + } + + + /** + * Sets the value of the FPU register given to the 32-bit pattern given by the int parameter. + * + * @param reg Register to set the value of. + * @param val The desired int bit pattern for the register. + **/ + + public static void setRegisterToInt(int reg, int val) + { + if (reg >= 0 && reg < registers.length) + { + registers[reg].setValue(val); + } + } + + + /** + * Sets the value of the FPU register given to the double value given. The register must be even-numbered, and the + * low order 32 bits are placed in it. The high order 32 bits are placed in the (odd numbered) register that + * follows it. + * + * @param reg Register to set the value of. + * @param val The desired double value for the register. + * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. + **/ + + public static void setRegisterPairToDouble(int reg, double val) + throws InvalidRegisterAccessException + { + if (reg % 2 != 0) + { + throw new InvalidRegisterAccessException(); + } + long bits = Double.doubleToRawLongBits(val); + registers[reg + 1].setValue(Binary.highOrderLongToInt(bits)); // high order 32 bits + registers[reg].setValue(Binary.lowOrderLongToInt(bits)); // low order 32 bits + } + + + /** + * Sets the value of the FPU register given to the double value given. The register must be even-numbered, and the + * low order 32 bits are placed in it. The high order 32 bits are placed in the (odd numbered) register that + * follows it. + * + * @param reg Register to set the value of. + * @param val The desired double value for the register. + * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. + **/ + public static void setRegisterPairToDouble(String reg, double val) + throws InvalidRegisterAccessException + { + setRegisterPairToDouble(getRegisterNumber(reg), val); + } + + + /** + * Sets the value of the FPU register pair given to the long value containing 64 bit pattern given. The register + * must be even-numbered, and the low order 32 bits from the long are placed in it. The high order 32 bits from the + * long are placed in the (odd numbered) register that follows it. + * + * @param reg Register to set the value of. Must be even register of even/odd pair. + * @param val The desired double value for the register. + * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. + **/ + + public static void setRegisterPairToLong(int reg, long val) + throws InvalidRegisterAccessException + { + if (reg % 2 != 0) + { + throw new InvalidRegisterAccessException(); + } + registers[reg + 1].setValue(Binary.highOrderLongToInt(val)); // high order 32 bits + registers[reg].setValue(Binary.lowOrderLongToInt(val)); // low order 32 bits + } + + + /** + * Sets the value of the FPU register pair given to the long value containing 64 bit pattern given. The register + * must be even-numbered, and the low order 32 bits from the long are placed in it. The high order 32 bits from the + * long are placed in the (odd numbered) register that follows it. + * + * @param reg Register to set the value of. Must be even register of even/odd pair. + * @param val The desired long value containing the 64 bits for the register pair. + * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. + **/ + public static void setRegisterPairToLong(String reg, long val) + throws InvalidRegisterAccessException + { + setRegisterPairToLong(getRegisterNumber(reg), val); + } + + + /** + * Gets the float value stored in the given FPU register. + * + * @param reg Register to get the value of. + * @return The float value stored by that register. + **/ + + public static float getFloatFromRegister(int reg) + { + float result = 0F; + if (reg >= 0 && reg < registers.length) + { + result = Float.intBitsToFloat(registers[reg].getValue()); + } + return result; + } + + + /** + * Gets the float value stored in the given FPU register. + * + * @param reg Register to get the value of. + * @return The float value stored by that register. + **/ + + public static float getFloatFromRegister(String reg) + { + return getFloatFromRegister(getRegisterNumber(reg)); + } + + + /** + * Gets the 32-bit int bit pattern stored in the given FPU register. + * + * @param reg Register to get the value of. + * @return The int bit pattern stored by that register. + **/ + + public static int getIntFromRegister(int reg) + { + int result = 0; + if (reg >= 0 && reg < registers.length) + { + result = registers[reg].getValue(); + } + return result; + } + + + /** + * Gets the 32-bit int bit pattern stored in the given FPU register. + * + * @param reg Register to get the value of. + * @return The int bit pattern stored by that register. + **/ + + public static int getIntFromRegister(String reg) + { + return getIntFromRegister(getRegisterNumber(reg)); + } + + + /** + * Gets the double value stored in the given FPU register. The register must be even-numbered. + * + * @param reg Register to get the value of. Must be even number of even/odd pair. + * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. + **/ + + public static double getDoubleFromRegisterPair(int reg) + throws InvalidRegisterAccessException + { + double result = 0.0; + if (reg % 2 != 0) + { + throw new InvalidRegisterAccessException(); + } + long bits = Binary.twoIntsToLong(registers[reg + 1].getValue(), registers[reg].getValue()); + return Double.longBitsToDouble(bits); + } + + + /** + * Gets the double value stored in the given FPU register. The register must be even-numbered. + * + * @param reg Register to get the value of. Must be even number of even/odd pair. + * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. + **/ + + public static double getDoubleFromRegisterPair(String reg) + throws InvalidRegisterAccessException + { + return getDoubleFromRegisterPair(getRegisterNumber(reg)); + } + + + /** + * Gets a long representing the double value stored in the given double precision FPU register. The register must be + * even-numbered. + * + * @param reg Register to get the value of. Must be even number of even/odd pair. + * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. + **/ + + public static long getLongFromRegisterPair(int reg) + throws InvalidRegisterAccessException + { + double result = 0.0; + if (reg % 2 != 0) + { + throw new InvalidRegisterAccessException(); + } + return Binary.twoIntsToLong(registers[reg + 1].getValue(), registers[reg].getValue()); + } + + + /** + * Gets the double value stored in the given FPU register. The register must be even-numbered. + * + * @param reg Register to get the value of. Must be even number of even/odd pair. + * @throws InvalidRegisterAccessException if register ID is invalid or odd-numbered. + **/ + + public static long getLongFromRegisterPair(String reg) + throws InvalidRegisterAccessException + { + return getLongFromRegisterPair(getRegisterNumber(reg)); + } + + + /** + * This method updates the FPU register value who's number is num. Note the registers themselves hold an int value. + * There are helper methods available to which you can give a float or double to store. + * + * @param num FPU register to set the value of. + * @param val The desired int value for the register. + **/ + + public static int updateRegister(int num, int val) + { + int old = 0; + for (int i = 0; i < registers.length; i++) + { + if (registers[i].getNumber() == num) + { + old = (Globals.getSettings().getBackSteppingEnabled()) + ? Globals.program.getBackStepper().addCoprocessor1Restore(num, registers[i].setValue(val)) + : registers[i].setValue(val); + break; + } + } + return old; + } + + /** + * Returns the value of the FPU register who's number is num. Returns the raw int value actually stored there. If + * you need a float, use Float.intBitsToFloat() to get the equivent float. + * + * @param num The FPU register number. + * @return The int value of the given register. + **/ + + public static int getValue(int num) + { + return registers[num].getValue(); + } + + /** + * For getting the number representation of the FPU register. + * + * @param n The string formatted register name to look for. + * @return The number of the register represented by the string. + **/ + + public static int getRegisterNumber(String n) + { + int j = -1; + for (int i = 0; i < registers.length; i++) + { + if (registers[i].getName().equals(n)) + { + j = registers[i].getNumber(); + break; + } + } + return j; + } + + /** + * For returning the set of registers. + * + * @return The set of registers. + **/ + + public static Register[] getRegisters() + { + return registers; + } + + /** + * Get register object corresponding to given name. If no match, return null. + * + * @param rName The FPU register name, must be "$f0" through "$f31". + * @return The register object,or null if not found. + **/ + + public static Register getRegister(String rName) + { + Register reg = null; + if (rName.charAt(0) == '$' && rName.length() > 1 && rName.charAt(1) == 'f') + { + try + { + // check for register number 0-31. + reg = registers[Binary.stringToInt(rName.substring(2))]; // KENV 1/6/05 + } + catch (Exception e) + { + // handles both NumberFormat and ArrayIndexOutOfBounds + reg = null; + } + } + return reg; + } + + + /** + * Method to reinitialize the values of the registers. + **/ + + public static void resetRegisters() + { + for (int i = 0; i < registers.length; i++) + { + registers[i].resetValue(); + } + clearConditionFlags(); + } + + + /** + * Each individual register is a separate object and Observable. This handy method will add the given Observer to + * each one. + */ + public static void addRegistersObserver(Observer observer) + { + for (int i = 0; i < registers.length; i++) + { + registers[i].addObserver(observer); + } + } + + + /** + * Each individual register is a separate object and Observable. This handy method will delete the given Observer + * from each one. + */ + public static void deleteRegistersObserver(Observer observer) + { + for (int i = 0; i < registers.length; i++) + { registers[i].deleteObserver(observer); - } - } - - /** - * Set condition flag to 1 (true). - * - * @param flag condition flag number (0-7) - * @return previous flag setting (0 or 1) - */ - public static int setConditionFlag(int flag) { - int old = 0; - if (flag >= 0 && flag < numConditionFlags) { + } + } + + /** + * Set condition flag to 1 (true). + * + * @param flag condition flag number (0-7) + * @return previous flag setting (0 or 1) + */ + public static int setConditionFlag(int flag) + { + int old = 0; + if (flag >= 0 && flag < numConditionFlags) + { old = getConditionFlag(flag); - condition.setValue(Binary.setBit(condition.getValue(),flag)); + condition.setValue(Binary.setBit(condition.getValue(), flag)); if (Globals.getSettings().getBackSteppingEnabled()) - if (old==0) { - Globals.program.getBackStepper().addConditionFlagClear(flag); - } - else { - Globals.program.getBackStepper().addConditionFlagSet(flag); - } - } - return old; - } - - /** - * Set condition flag to 0 (false). - * - * @param flag condition flag number (0-7) - * @return previous flag setting (0 or 1) - */ - public static int clearConditionFlag(int flag) { - int old = 0; - if (flag >= 0 && flag < numConditionFlags) { + { + if (old == 0) + { + Globals.program.getBackStepper().addConditionFlagClear(flag); + } + else + { + Globals.program.getBackStepper().addConditionFlagSet(flag); + } + } + } + return old; + } + + /** + * Set condition flag to 0 (false). + * + * @param flag condition flag number (0-7) + * @return previous flag setting (0 or 1) + */ + public static int clearConditionFlag(int flag) + { + int old = 0; + if (flag >= 0 && flag < numConditionFlags) + { old = getConditionFlag(flag); - condition.setValue(Binary.clearBit(condition.getValue(),flag)); + condition.setValue(Binary.clearBit(condition.getValue(), flag)); if (Globals.getSettings().getBackSteppingEnabled()) - if (old==0) { - Globals.program.getBackStepper().addConditionFlagClear(flag); - } - else { - Globals.program.getBackStepper().addConditionFlagSet(flag); - } - } - return old; - } - - - /** - * Get value of specified condition flag (0-7). - * - * @param flag condition flag number (0-7) - * @return 0 if condition is false, 1 if condition is true - */ - public static int getConditionFlag(int flag) { - if (flag < 0 || flag >= numConditionFlags) + { + if (old == 0) + { + Globals.program.getBackStepper().addConditionFlagClear(flag); + } + else + { + Globals.program.getBackStepper().addConditionFlagSet(flag); + } + } + } + return old; + } + + + /** + * Get value of specified condition flag (0-7). + * + * @param flag condition flag number (0-7) + * @return 0 if condition is false, 1 if condition is true + */ + public static int getConditionFlag(int flag) + { + if (flag < 0 || flag >= numConditionFlags) + { flag = 0; - return Binary.bitValue(condition.getValue(), flag); - } - - - /** - * Get array of condition flags (0-7). - * - * @return array of int condition flags - */ - public static int getConditionFlags() { - return condition.getValue(); - } - - - /** - * Clear all condition flags (0-7). - * - */ - public static void clearConditionFlags() { - condition.setValue(0); // sets all 32 bits to 0. - } - - /** - * Set all condition flags (0-7). - * - */ - public static void setConditionFlags() { - condition.setValue(-1); // sets all 32 bits to 1. - } - - /** - * Get count of condition flags. - * - * @return number of condition flags - */ - public static int getConditionFlagCount() { - return numConditionFlags; - } - } + } + return Binary.bitValue(condition.getValue(), flag); + } + + + /** + * Get array of condition flags (0-7). + * + * @return array of int condition flags + */ + public static int getConditionFlags() + { + return condition.getValue(); + } + + + /** + * Clear all condition flags (0-7). + */ + public static void clearConditionFlags() + { + condition.setValue(0); // sets all 32 bits to 0. + } + + /** + * Set all condition flags (0-7). + */ + public static void setConditionFlags() + { + condition.setValue(-1); // sets all 32 bits to 1. + } + + /** + * Get count of condition flags. + * + * @return number of condition flags + */ + public static int getConditionFlagCount() + { + return numConditionFlags; + } +} diff --git a/src/main/java/mars/mips/hardware/InvalidRegisterAccessException.java b/src/main/java/mars/mips/hardware/InvalidRegisterAccessException.java index 138a4d9..8167743 100644 --- a/src/main/java/mars/mips/hardware/InvalidRegisterAccessException.java +++ b/src/main/java/mars/mips/hardware/InvalidRegisterAccessException.java @@ -1,5 +1,6 @@ package mars.mips.hardware; -import mars.*; + +import mars.ErrorList; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -30,22 +31,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Represents attempt to access double precision register using an odd - * (e.g. $f1, $f23) register name. - * + * Represents attempt to access double precision register using an odd (e.g. $f1, $f23) register name. + * * @author Pete Sanderson * @version July 2005 **/ -public class InvalidRegisterAccessException extends Exception { - private ErrorList errs; +public class InvalidRegisterAccessException extends Exception +{ + private ErrorList errs; - /** - * Constructor for IllegalRegisterException. - * - **/ - public InvalidRegisterAccessException() { - } + /** + * Constructor for IllegalRegisterException. + **/ + public InvalidRegisterAccessException() + { + } - } - \ No newline at end of file +} diff --git a/src/main/java/mars/mips/hardware/Memory.java b/src/main/java/mars/mips/hardware/Memory.java index eed800e..c56a095 100644 --- a/src/main/java/mars/mips/hardware/Memory.java +++ b/src/main/java/mars/mips/hardware/Memory.java @@ -39,8 +39,8 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Represents MIPS memory. Different segments are represented by different data structs. - * - * @author Pete Sanderson + * + * @author Pete Sanderson * @version August 2003 */ @@ -48,53 +48,77 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // NOTE: This implementation is purely big-endian. MIPS can handle either one. ///////////////////////////////////////////////////////////////////// - public class Memory extends Observable { - - /** base address for (user) text segment: 0x00400000 **/ - public static int textBaseAddress = MemoryConfigurations.getDefaultTextBaseAddress(); //0x00400000; - /** base address for (user) data segment: 0x10000000 **/ - public static int dataSegmentBaseAddress = MemoryConfigurations.getDefaultDataSegmentBaseAddress(); //0x10000000; - /** base address for .extern directive: 0x10000000 **/ - public static int externBaseAddress = MemoryConfigurations.getDefaultExternBaseAddress(); //0x10000000; - /** base address for storing globals **/ - public static int globalPointer = MemoryConfigurations.getDefaultGlobalPointer(); //0x10008000; - /** base address for storage of non-global static data in data segment: 0x10010000 (from SPIM) **/ - public static int dataBaseAddress = MemoryConfigurations.getDefaultDataBaseAddress(); //0x10010000; // from SPIM not MIPS - /** base address for heap: 0x10040000 (I think from SPIM not MIPS) **/ - public static int heapBaseAddress = MemoryConfigurations.getDefaultHeapBaseAddress(); //0x10040000; // I think from SPIM not MIPS - /** starting address for stack: 0x7fffeffc (this is from SPIM not MIPS) **/ - public static int stackPointer = MemoryConfigurations.getDefaultStackPointer(); //0x7fffeffc; - /** base address for stack: 0x7ffffffc (this is mine - start of highest word below kernel space) **/ - public static int stackBaseAddress = MemoryConfigurations.getDefaultStackBaseAddress(); //0x7ffffffc; - /** highest address accessible in user (not kernel) mode. **/ - public static int userHighAddress = MemoryConfigurations.getDefaultUserHighAddress(); //0x7fffffff; - /** kernel boundary. Only OS can access this or higher address **/ - public static int kernelBaseAddress = MemoryConfigurations.getDefaultKernelBaseAddress(); //0x80000000; - /** base address for kernel text segment: 0x80000000 **/ - public static int kernelTextBaseAddress = MemoryConfigurations.getDefaultKernelTextBaseAddress(); //0x80000000; - /** starting address for exception handlers: 0x80000180 **/ - public static int exceptionHandlerAddress = MemoryConfigurations.getDefaultExceptionHandlerAddress(); //0x80000180; - /** base address for kernel data segment: 0x90000000 **/ - public static int kernelDataBaseAddress = MemoryConfigurations.getDefaultKernelDataBaseAddress(); //0x90000000; - /** starting address for memory mapped I/O: 0xffff0000 (-65536) **/ - public static int memoryMapBaseAddress = MemoryConfigurations.getDefaultMemoryMapBaseAddress(); //0xffff0000; - /** highest address acessible in kernel mode. **/ - public static int kernelHighAddress = MemoryConfigurations.getDefaultKernelHighAddress(); //0xffffffff; - +public class Memory extends Observable +{ + /** MIPS word length in bytes. **/ // NOTE: Much of the code is hardwired for 4 byte words. Refactoring this is low priority. - public static final int WORD_LENGTH_BYTES = 4; - /** Constant representing byte order of each memory word. Little-endian means lowest - numbered byte is right most [3][2][1][0]. */ - public static final boolean LITTLE_ENDIAN = true; - /** Constant representing byte order of each memory word. Big-endian means lowest - numbered byte is left most [0][1][2][3]. */ - public static final boolean BIG_ENDIAN = false; - /** Current setting for endian (default LITTLE_ENDIAN) **/ - private static boolean byteOrder = LITTLE_ENDIAN; - - public static int heapAddress; - + public static final int WORD_LENGTH_BYTES = 4; + + /** + * Constant representing byte order of each memory word. Little-endian means lowest numbered byte is right most + * [3][2][1][0]. + */ + public static final boolean LITTLE_ENDIAN = true; + + /** + * Constant representing byte order of each memory word. Big-endian means lowest numbered byte is left most + * [0][1][2][3]. + */ + public static final boolean BIG_ENDIAN = false; + + private static final int BLOCK_LENGTH_WORDS = 1024; // allocated blocksize 1024 ints == 4K bytes + + private static final int BLOCK_TABLE_LENGTH = 1024; // Each entry of table points to a block. + + private static final int MMIO_TABLE_LENGTH = 16; // Each entry of table points to a 4K block. + + private static final int TEXT_BLOCK_LENGTH_WORDS = 1024; // allocated blocksize 1024 ints == 4K bytes + + private static final int TEXT_BLOCK_TABLE_LENGTH = 1024; // Each entry of table points to a block. + + //////////////////////////////////////////////////////////////////////////////// + // + // Helper method to store 1, 2 or 4 byte value in table that represents MIPS + // memory. Originally used just for data segment, but now also used for stack. + // Both use different tables but same storage method and same table size + // and block size. + // Modified 29 Dec 2005 to return old value of replaced bytes. + // + private static final boolean STORE = true; + + private static final boolean FETCH = false; + + /** base address for (user) text segment: 0x00400000 **/ + public static int textBaseAddress = MemoryConfigurations.getDefaultTextBaseAddress(); //0x00400000; + + /** base address for (user) data segment: 0x10000000 **/ + public static int dataSegmentBaseAddress = MemoryConfigurations.getDefaultDataSegmentBaseAddress(); //0x10000000; + + /** base address for .extern directive: 0x10000000 **/ + public static int externBaseAddress = MemoryConfigurations.getDefaultExternBaseAddress(); //0x10000000; + + /** base address for storing globals **/ + public static int globalPointer = MemoryConfigurations.getDefaultGlobalPointer(); //0x10008000; + + /** base address for storage of non-global static data in data segment: 0x10010000 (from SPIM) **/ + public static int dataBaseAddress = MemoryConfigurations.getDefaultDataBaseAddress(); //0x10010000; // from SPIM not MIPS + + /** base address for heap: 0x10040000 (I think from SPIM not MIPS) **/ + public static int heapBaseAddress = MemoryConfigurations.getDefaultHeapBaseAddress(); //0x10040000; // I think from SPIM not MIPS + + /** starting address for stack: 0x7fffeffc (this is from SPIM not MIPS) **/ + public static int stackPointer = MemoryConfigurations.getDefaultStackPointer(); //0x7fffeffc; + + /** base address for stack: 0x7ffffffc (this is mine - start of highest word below kernel space) **/ + public static int stackBaseAddress = MemoryConfigurations.getDefaultStackBaseAddress(); //0x7ffffffc; + + /** highest address accessible in user (not kernel) mode. **/ + public static int userHighAddress = MemoryConfigurations.getDefaultUserHighAddress(); //0x7fffffff; + + /** kernel boundary. Only OS can access this or higher address **/ + public static int kernelBaseAddress = MemoryConfigurations.getDefaultKernelBaseAddress(); //0x80000000; + // Memory will maintain a collection of observables. Each one is associated // with a specific memory address or address range, and each will have at least // one observer registered with it. When memory access is made, make sure only @@ -106,9 +130,10 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // key for insertion into the tree would be based on Comparable using both low // and high end of address range, but retrieval from the tree has to be based // on target address being ANYWHERE IN THE RANGE (not an exact key match). - - Collection observables = getNewMemoryObserversCollection(); - + + /** base address for kernel text segment: 0x80000000 **/ + public static int kernelTextBaseAddress = MemoryConfigurations.getDefaultKernelTextBaseAddress(); //0x80000000; + // The data segment is allocated in blocks of 1024 ints (4096 bytes). Each block is // referenced by a "block table" entry, and the table has 1024 entries. The capacity // is thus 1024 entries * 4096 bytes = 4 MB. Should be enough to cover most @@ -132,12 +157,19 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // (I don't have a reference for that offhand...) Using my scheme, 0x10040000 falls at // the start of the 65'th block -- table entry 64. That leaves (1024-64) * 4096 = 3,932,160 // bytes of space available without going indirect. - - private static final int BLOCK_LENGTH_WORDS = 1024; // allocated blocksize 1024 ints == 4K bytes - private static final int BLOCK_TABLE_LENGTH = 1024; // Each entry of table points to a block. - private int[][] dataBlockTable; - private int[][] kernelDataBlockTable; - + + /** starting address for exception handlers: 0x80000180 **/ + public static int exceptionHandlerAddress = MemoryConfigurations.getDefaultExceptionHandlerAddress(); //0x80000180; + + /** base address for kernel data segment: 0x90000000 **/ + public static int kernelDataBaseAddress = MemoryConfigurations.getDefaultKernelDataBaseAddress(); //0x90000000; + + /** starting address for memory mapped I/O: 0xffff0000 (-65536) **/ + public static int memoryMapBaseAddress = MemoryConfigurations.getDefaultMemoryMapBaseAddress(); //0xffff0000; + + /** highest address acessible in kernel mode. **/ + public static int kernelHighAddress = MemoryConfigurations.getDefaultKernelHighAddress(); //0xffffffff; + // The stack is modeled similarly to the data segment. It cannot share the same // data structure because the stack base address is very large. To store it in the // same data structure would require implementation of indirect blocks, which has not @@ -152,9 +184,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // from desired address). Thus as the address gets smaller the offset gets larger. // Everything else works the same, so it shares some private helper methods with // data segment algorithms. - - private int[][] stackBlockTable; - + + public static int heapAddress; + // Memory mapped I/O is simulated with a separate table using the same structure and // logic as data segment. Memory is allocated in 4K byte blocks. But since MMIO // address range is limited to 0xffff0000 to 0xfffffffc, there are only 64K bytes @@ -164,109 +196,115 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // MMIO addresses are interpreted by Java as negative numbers since it does not // have unsigned types. As long as the absolute address is correctly translated // into a table offset, this is of no concern. - - private static final int MMIO_TABLE_LENGTH = 16; // Each entry of table points to a 4K block. - private int[][] memoryMapBlockTable; - + + public static int dataSegmentLimitAddress = dataSegmentBaseAddress + + BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; + + public static int textLimitAddress = textBaseAddress + + TEXT_BLOCK_LENGTH_WORDS * TEXT_BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; + // I use a similar scheme for storing instructions. MIPS text segment ranges from // 0x00400000 all the way to data segment (0x10000000) a range of about 250 MB! So // I'll provide table of blocks with similar capacity. This differs from data segment // somewhat in that the block entries do not contain int's, but instead contain // references to ProgramStatement objects. - - private static final int TEXT_BLOCK_LENGTH_WORDS = 1024; // allocated blocksize 1024 ints == 4K bytes - private static final int TEXT_BLOCK_TABLE_LENGTH = 1024; // Each entry of table points to a block. - private ProgramStatement[][] textBlockTable; - private ProgramStatement[][] kernelTextBlockTable; - + + public static int kernelDataSegmentLimitAddress = kernelDataBaseAddress + + BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; + + public static int kernelTextLimitAddress = kernelTextBaseAddress + + TEXT_BLOCK_LENGTH_WORDS * TEXT_BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; + + public static int stackLimitAddress = stackBaseAddress - + BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; + + public static int memoryMapLimitAddress = memoryMapBaseAddress + + BLOCK_LENGTH_WORDS * MMIO_TABLE_LENGTH * WORD_LENGTH_BYTES; + // Set "top" address boundary to go with each "base" address. This determines permissable // address range for user program. Currently limit is 4MB, or 1024 * 1024 * 4 bytes based // on the table structures described above (except memory mapped IO, limited to 64KB by range). - - public static int dataSegmentLimitAddress = dataSegmentBaseAddress + - BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; - public static int textLimitAddress = textBaseAddress + - TEXT_BLOCK_LENGTH_WORDS * TEXT_BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; - public static int kernelDataSegmentLimitAddress = kernelDataBaseAddress + - BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; - public static int kernelTextLimitAddress = kernelTextBaseAddress + - TEXT_BLOCK_LENGTH_WORDS * TEXT_BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; - public static int stackLimitAddress = stackBaseAddress - - BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES; - public static int memoryMapLimitAddress = memoryMapBaseAddress + - BLOCK_LENGTH_WORDS * MMIO_TABLE_LENGTH * WORD_LENGTH_BYTES; + + /** Current setting for endian (default LITTLE_ENDIAN) **/ + private static boolean byteOrder = LITTLE_ENDIAN; + + private static final Memory uniqueMemoryInstance = new Memory(); + + Collection observables = getNewMemoryObserversCollection(); + + private int[][] dataBlockTable; + + private int[][] kernelDataBlockTable; + + private int[][] stackBlockTable; // This will be a Singleton class, only one instance is ever created. Since I know the // Memory object is always needed, I'll go ahead and create it at the time of class loading. // (greedy rather than lazy instantiation). The constructor is private and getInstance() // always returns this instance. - - private static Memory uniqueMemoryInstance = new Memory(); - - + + private int[][] memoryMapBlockTable; + + private ProgramStatement[][] textBlockTable; + + private ProgramStatement[][] kernelTextBlockTable; + /* - * Private constructor for Memory. Separate data structures for text and data segments. + * Private constructor for Memory. Separate data structures for text and data segments. **/ - private Memory() { - initialize(); - } - - /** - * Returns the unique Memory instance, which becomes in essence global. - */ - - public static Memory getInstance() { - return uniqueMemoryInstance; - } - - /** - * Explicitly clear the contents of memory. Typically done at start of assembly. - */ - - public void clear() { - setConfiguration(); - initialize(); - } - - /** - * Sets current memory configuration for simulated MIPS. Configuration is - * collection of memory segment addresses. e.g. text segment starting at - * address 0x00400000. Configuration can be modified starting with MARS 3.7. + private Memory() + { + initialize(); + } + + /** + * Returns the unique Memory instance, which becomes in essence global. */ - - public static void setConfiguration() { - textBaseAddress = MemoryConfigurations.getCurrentConfiguration().getTextBaseAddress(); //0x00400000; - dataSegmentBaseAddress = MemoryConfigurations.getCurrentConfiguration().getDataSegmentBaseAddress(); //0x10000000; - externBaseAddress = MemoryConfigurations.getCurrentConfiguration().getExternBaseAddress(); //0x10000000; - globalPointer = MemoryConfigurations.getCurrentConfiguration().getGlobalPointer(); //0x10008000; - dataBaseAddress = MemoryConfigurations.getCurrentConfiguration().getDataBaseAddress(); //0x10010000; // from SPIM not MIPS - heapBaseAddress = MemoryConfigurations.getCurrentConfiguration().getHeapBaseAddress(); //0x10040000; // I think from SPIM not MIPS - stackPointer = MemoryConfigurations.getCurrentConfiguration().getStackPointer(); //0x7fffeffc; - stackBaseAddress = MemoryConfigurations.getCurrentConfiguration().getStackBaseAddress(); //0x7ffffffc; - userHighAddress = MemoryConfigurations.getCurrentConfiguration().getUserHighAddress(); //0x7fffffff; - kernelBaseAddress = MemoryConfigurations.getCurrentConfiguration().getKernelBaseAddress(); //0x80000000; - kernelTextBaseAddress = MemoryConfigurations.getCurrentConfiguration().getKernelTextBaseAddress(); //0x80000000; - exceptionHandlerAddress = MemoryConfigurations.getCurrentConfiguration().getExceptionHandlerAddress(); //0x80000180; - kernelDataBaseAddress = MemoryConfigurations.getCurrentConfiguration().getKernelDataBaseAddress(); //0x90000000; - memoryMapBaseAddress = MemoryConfigurations.getCurrentConfiguration().getMemoryMapBaseAddress(); //0xffff0000; - kernelHighAddress = MemoryConfigurations.getCurrentConfiguration().getKernelHighAddress(); //0xffffffff; - dataSegmentLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getDataSegmentLimitAddress(), - dataSegmentBaseAddress + - BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); - textLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getTextLimitAddress(), - textBaseAddress + - TEXT_BLOCK_LENGTH_WORDS * TEXT_BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); - kernelDataSegmentLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getKernelDataSegmentLimitAddress(), - kernelDataBaseAddress + - BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); - kernelTextLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getKernelTextLimitAddress(), - kernelTextBaseAddress + - TEXT_BLOCK_LENGTH_WORDS * TEXT_BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); - stackLimitAddress = Math.max(MemoryConfigurations.getCurrentConfiguration().getStackLimitAddress(), - stackBaseAddress - - BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); - memoryMapLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getMemoryMapLimitAddress(), - memoryMapBaseAddress + - BLOCK_LENGTH_WORDS * MMIO_TABLE_LENGTH * WORD_LENGTH_BYTES); + + public static Memory getInstance() + { + return uniqueMemoryInstance; + } + + /** + * Sets current memory configuration for simulated MIPS. Configuration is collection of memory segment addresses. + * e.g. text segment starting at address 0x00400000. Configuration can be modified starting with MARS 3.7. + */ + + public static void setConfiguration() + { + textBaseAddress = MemoryConfigurations.getCurrentConfiguration().getTextBaseAddress(); //0x00400000; + dataSegmentBaseAddress = MemoryConfigurations.getCurrentConfiguration().getDataSegmentBaseAddress(); //0x10000000; + externBaseAddress = MemoryConfigurations.getCurrentConfiguration().getExternBaseAddress(); //0x10000000; + globalPointer = MemoryConfigurations.getCurrentConfiguration().getGlobalPointer(); //0x10008000; + dataBaseAddress = MemoryConfigurations.getCurrentConfiguration().getDataBaseAddress(); //0x10010000; // from SPIM not MIPS + heapBaseAddress = MemoryConfigurations.getCurrentConfiguration().getHeapBaseAddress(); //0x10040000; // I think from SPIM not MIPS + stackPointer = MemoryConfigurations.getCurrentConfiguration().getStackPointer(); //0x7fffeffc; + stackBaseAddress = MemoryConfigurations.getCurrentConfiguration().getStackBaseAddress(); //0x7ffffffc; + userHighAddress = MemoryConfigurations.getCurrentConfiguration().getUserHighAddress(); //0x7fffffff; + kernelBaseAddress = MemoryConfigurations.getCurrentConfiguration().getKernelBaseAddress(); //0x80000000; + kernelTextBaseAddress = MemoryConfigurations.getCurrentConfiguration().getKernelTextBaseAddress(); //0x80000000; + exceptionHandlerAddress = MemoryConfigurations.getCurrentConfiguration().getExceptionHandlerAddress(); //0x80000180; + kernelDataBaseAddress = MemoryConfigurations.getCurrentConfiguration().getKernelDataBaseAddress(); //0x90000000; + memoryMapBaseAddress = MemoryConfigurations.getCurrentConfiguration().getMemoryMapBaseAddress(); //0xffff0000; + kernelHighAddress = MemoryConfigurations.getCurrentConfiguration().getKernelHighAddress(); //0xffffffff; + dataSegmentLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getDataSegmentLimitAddress(), + dataSegmentBaseAddress + + BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); + textLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getTextLimitAddress(), + textBaseAddress + + TEXT_BLOCK_LENGTH_WORDS * TEXT_BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); + kernelDataSegmentLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getKernelDataSegmentLimitAddress(), + kernelDataBaseAddress + + BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); + kernelTextLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getKernelTextLimitAddress(), + kernelTextBaseAddress + + TEXT_BLOCK_LENGTH_WORDS * TEXT_BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); + stackLimitAddress = Math.max(MemoryConfigurations.getCurrentConfiguration().getStackLimitAddress(), + stackBaseAddress - + BLOCK_LENGTH_WORDS * BLOCK_TABLE_LENGTH * WORD_LENGTH_BYTES); + memoryMapLimitAddress = Math.min(MemoryConfigurations.getCurrentConfiguration().getMemoryMapLimitAddress(), + memoryMapBaseAddress + + BLOCK_LENGTH_WORDS * MMIO_TABLE_LENGTH * WORD_LENGTH_BYTES); /* System.out.println("dataSegmentLimitAddress "+Binary.intToHexString(dataSegmentLimitAddress)); System.out.println("textLimitAddress "+Binary.intToHexString(textLimitAddress)); System.out.println("kernelDataSegmentLimitAddress "+Binary.intToHexString(kernelDataSegmentLimitAddress)); @@ -274,623 +312,845 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. System.out.println("stackLimitAddress "+Binary.intToHexString(stackLimitAddress)); System.out.println("memoryMapLimitAddress "+Binary.intToHexString(memoryMapLimitAddress)); */ - } - - + } + /** - * Determine whether the current memory configuration has a maximum address that can be stored - * in 16 bits. - * @return true if maximum address can be stored in 16 bits or less, false otherwise - */ - public boolean usingCompactMemoryConfiguration() { - return (kernelHighAddress & 0x00007fff) == kernelHighAddress; - } - - - - private void initialize() { - heapAddress = heapBaseAddress; - textBlockTable = new ProgramStatement[TEXT_BLOCK_TABLE_LENGTH][]; - dataBlockTable = new int[BLOCK_TABLE_LENGTH][]; // array of null int[] references - kernelTextBlockTable = new ProgramStatement[TEXT_BLOCK_TABLE_LENGTH][]; - kernelDataBlockTable = new int[BLOCK_TABLE_LENGTH][]; - stackBlockTable = new int[BLOCK_TABLE_LENGTH][]; - memoryMapBlockTable = new int[MMIO_TABLE_LENGTH][]; - System.gc(); // call garbage collector on any Table memory just deallocated. - } - - /** - * Returns the next available word-aligned heap address. There is no recycling and - * no heap management! There is however nearly 4MB of heap space available in Mars. - * - * @param numBytes Number of bytes requested. Should be multiple of 4, otherwise next higher multiple of 4 allocated. - * @return address of allocated heap storage. - * @throws IllegalArgumentException if number of requested bytes is negative or exceeds available heap storage - */ - public int allocateBytesFromHeap(int numBytes) throws IllegalArgumentException { - int result = heapAddress; - if (numBytes < 0) { - throw new IllegalArgumentException("request ("+numBytes+") is negative heap amount"); - } - int newHeapAddress = heapAddress + numBytes; - if (newHeapAddress % 4 != 0) { - newHeapAddress = newHeapAddress + (4 - newHeapAddress % 4) ; // next higher multiple of 4 - } - if (newHeapAddress >= dataSegmentLimitAddress) { - throw new IllegalArgumentException("request ("+numBytes+") exceeds available heap storage"); - } - heapAddress = newHeapAddress; - return result; - } - - - /** - * Set byte order to either LITTLE_ENDIAN or BIG_ENDIAN. Default is LITTLE_ENDIAN. - * - * @param order either LITTLE_ENDIAN or BIG_ENDIAN - */ - public void setByteOrder(boolean order) { - byteOrder = order; - } - - /** - * Retrieve memory byte order. Default is LITTLE_ENDIAN (like PCs). - * - * @return either LITTLE_ENDIAN or BIG_ENDIAN - */ - public boolean getByteOrder() { - return byteOrder; - } - - - /* ******************************* THE SETTER METHODS ******************************/ - - + * Utility to determine if given address is word-aligned. + * + * @param address the address to check + * @return true if address is word-aligned, false otherwise + */ + public static boolean wordAligned(int address) + { + return (address % WORD_LENGTH_BYTES == 0); + } + + /** + * Utility to determine if given address is doubleword-aligned. + * + * @param address the address to check + * @return true if address is doubleword-aligned, false otherwise + */ + public static boolean doublewordAligned(int address) + { + return (address % (WORD_LENGTH_BYTES + WORD_LENGTH_BYTES) == 0); + } + + /** + * Utility method to align given address to next full word boundary, if not already aligned. + * + * @param address a memory address (any int value is potentially valid) + * @return address aligned to next word boundary (divisible by 4) + */ + public static int alignToWordBoundary(int address) + { + if (!wordAligned(address)) + { + if (address > 0) + { + address += (4 - (address % WORD_LENGTH_BYTES)); + } + else + { + address -= (4 - (address % WORD_LENGTH_BYTES)); + } + } + return address; + } + + /** + * Handy little utility to find out if given address is in MARS text segment (starts at Memory.textBaseAddress). + * Note that MARS does not implement the entire MIPS text segment space, but it does implement enough for hundreds + * of thousands of lines of code. + * + * @param address integer memory address + * @return true if that address is within MARS-defined text segment, false otherwise. + */ + public static boolean inTextSegment(int address) + { + return address >= textBaseAddress && address < textLimitAddress; + } + + + /* ******************************* THE SETTER METHODS ******************************/ + + /////////////////////////////////////////////////////////////////////////////////////// - /** - * Starting at the given address, write the given value over the given number of bytes. - * This one does not check for word boundaries, and copies one byte at a time. - * If length == 1, takes value from low order byte. If 2, takes from low order half-word. - * - * @param address Starting address of Memory address to be set. - * @param value Value to be stored starting at that address. - * @param length Number of bytes to be written. - * @return old value that was replaced by the set operation - **/ - - // Allocates blocks if necessary. - public int set(int address, int value, int length) throws AddressErrorException { - int oldValue = 0; - if (Globals.debug) System.out.println("memory["+address+"] set to "+value+"("+length+" bytes)"); - int relativeByteAddress; - if (inDataSegment(address)) { - // in data segment. Will write one byte at a time, w/o regard to boundaries. + + /** + * Handy little utility to find out if given address is in MARS kernel text segment (starts at + * Memory.kernelTextBaseAddress). + * + * @param address integer memory address + * @return true if that address is within MARS-defined kernel text segment, false otherwise. + */ + public static boolean inKernelTextSegment(int address) + { + return address >= kernelTextBaseAddress && address < kernelTextLimitAddress; + } + + /////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handy little utility to find out if given address is in MARS data segment (starts at + * Memory.dataSegmentBaseAddress). Note that MARS does not implement the entire MIPS data segment space, but it does + * support at least 4MB. + * + * @param address integer memory address + * @return true if that address is within MARS-defined data segment, false otherwise. + */ + public static boolean inDataSegment(int address) + { + return address >= dataSegmentBaseAddress && address < dataSegmentLimitAddress; + } + + /////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handy little utility to find out if given address is in MARS kernel data segment (starts at + * Memory.kernelDataSegmentBaseAddress). + * + * @param address integer memory address + * @return true if that address is within MARS-defined kernel data segment, false otherwise. + */ + public static boolean inKernelDataSegment(int address) + { + return address >= kernelDataBaseAddress && address < kernelDataSegmentLimitAddress; + } + + + /////////////////////////////////////////////////////////////////////////////////////// + + /** + * Handy little utility to find out if given address is in the Memory Map area starts at + * Memory.memoryMapBaseAddress, range 0xffff0000 to 0xffffffff. + * + * @param address integer memory address + * @return true if that address is within MARS-defined memory map (MMIO) area, false otherwise. + */ + public static boolean inMemoryMapSegment(int address) + { + return address >= memoryMapBaseAddress && address < kernelHighAddress; + } + + /////////////////////////////////////////////////////////////////////////////////////// + + /** + * Explicitly clear the contents of memory. Typically done at start of assembly. + */ + + public void clear() + { + setConfiguration(); + initialize(); + } + + /////////////////////////////////////////////////////////////////////////////////////// + + /** + * Determine whether the current memory configuration has a maximum address that can be stored in 16 bits. + * + * @return true if maximum address can be stored in 16 bits or less, false otherwise + */ + public boolean usingCompactMemoryConfiguration() + { + return (kernelHighAddress & 0x00007fff) == kernelHighAddress; + } + + + //////////////////////////////////////////////////////////////////////////////// + + private void initialize() + { + heapAddress = heapBaseAddress; + textBlockTable = new ProgramStatement[TEXT_BLOCK_TABLE_LENGTH][]; + dataBlockTable = new int[BLOCK_TABLE_LENGTH][]; // array of null int[] references + kernelTextBlockTable = new ProgramStatement[TEXT_BLOCK_TABLE_LENGTH][]; + kernelDataBlockTable = new int[BLOCK_TABLE_LENGTH][]; + stackBlockTable = new int[BLOCK_TABLE_LENGTH][]; + memoryMapBlockTable = new int[MMIO_TABLE_LENGTH][]; + System.gc(); // call garbage collector on any Table memory just deallocated. + } + + + /******************************** THE GETTER METHODS ******************************/ + + ////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Returns the next available word-aligned heap address. There is no recycling and no heap management! There is + * however nearly 4MB of heap space available in Mars. + * + * @param numBytes Number of bytes requested. Should be multiple of 4, otherwise next higher multiple of 4 + * allocated. + * @return address of allocated heap storage. + * @throws IllegalArgumentException if number of requested bytes is negative or exceeds available heap storage + */ + public int allocateBytesFromHeap(int numBytes) throws IllegalArgumentException + { + int result = heapAddress; + if (numBytes < 0) + { + throw new IllegalArgumentException("request (" + numBytes + ") is negative heap amount"); + } + int newHeapAddress = heapAddress + numBytes; + if (newHeapAddress % 4 != 0) + { + newHeapAddress = newHeapAddress + (4 - newHeapAddress % 4); // next higher multiple of 4 + } + if (newHeapAddress >= dataSegmentLimitAddress) + { + throw new IllegalArgumentException("request (" + numBytes + ") exceeds available heap storage"); + } + heapAddress = newHeapAddress; + return result; + } + + /** + * Retrieve memory byte order. Default is LITTLE_ENDIAN (like PCs). + * + * @return either LITTLE_ENDIAN or BIG_ENDIAN + */ + public boolean getByteOrder() + { + return byteOrder; + } + + + ///////////////////////////////////////////////////////////////////////// + + /** + * Set byte order to either LITTLE_ENDIAN or BIG_ENDIAN. Default is LITTLE_ENDIAN. + * + * @param order either LITTLE_ENDIAN or BIG_ENDIAN + */ + public void setByteOrder(boolean order) + { + byteOrder = order; + } + + ///////////////////////////////////////////////////////////////////////// + + /** + * Starting at the given address, write the given value over the given number of bytes. This one does not check for + * word boundaries, and copies one byte at a time. If length == 1, takes value from low order byte. If 2, takes + * from low order half-word. + * + * @param address Starting address of Memory address to be set. + * @param value Value to be stored starting at that address. + * @param length Number of bytes to be written. + * @return old value that was replaced by the set operation + **/ + + // Allocates blocks if necessary. + public int set(int address, int value, int length) throws AddressErrorException + { + int oldValue = 0; + if (Globals.debug) + { + System.out.println("memory[" + address + "] set to " + value + "(" + length + " bytes)"); + } + int relativeByteAddress; + if (inDataSegment(address)) + { + // in data segment. Will write one byte at a time, w/o regard to boundaries. relativeByteAddress = address - dataSegmentBaseAddress; // relative to data segment start, in bytes oldValue = storeBytesInTable(dataBlockTable, relativeByteAddress, length, value); - } - else if (address > stackLimitAddress && address <= stackBaseAddress) { - // in stack. Handle similarly to data segment write, except relative byte - // address calculated "backward" because stack addresses grow down from base. - relativeByteAddress = stackBaseAddress - address; + } + else if (address > stackLimitAddress && address <= stackBaseAddress) + { + // in stack. Handle similarly to data segment write, except relative byte + // address calculated "backward" because stack addresses grow down from base. + relativeByteAddress = stackBaseAddress - address; oldValue = storeBytesInTable(stackBlockTable, relativeByteAddress, length, value); - } - else if (inTextSegment(address)) { - // Burch Mod (Jan 2013): replace throw with call to setStatement - // DPS adaptation 5-Jul-2013: either throw or call, depending on setting - - if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) { - ProgramStatement oldStatement = getStatementNoNotify(address); - if (oldStatement != null) { - oldValue = oldStatement.getBinaryStatement(); - } - setStatement(address, new ProgramStatement(value, address)); - } - else { - throw new AddressErrorException( - "Cannot write directly to text segment!", - Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + else if (inTextSegment(address)) + { + // Burch Mod (Jan 2013): replace throw with call to setStatement + // DPS adaptation 5-Jul-2013: either throw or call, depending on setting + + if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) + { + ProgramStatement oldStatement = getStatementNoNotify(address); + if (oldStatement != null) + { + oldValue = oldStatement.getBinaryStatement(); + } + setStatement(address, new ProgramStatement(value, address)); } - } - else if (address >= memoryMapBaseAddress && address < memoryMapLimitAddress) { - // memory mapped I/O. + else + { + throw new AddressErrorException( + "Cannot write directly to text segment!", + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + } + else if (address >= memoryMapBaseAddress && address < memoryMapLimitAddress) + { + // memory mapped I/O. relativeByteAddress = address - memoryMapBaseAddress; oldValue = storeBytesInTable(memoryMapBlockTable, relativeByteAddress, length, value); - } - else if (inKernelDataSegment(address)) { - // in kernel data segment. Will write one byte at a time, w/o regard to boundaries. + } + else if (inKernelDataSegment(address)) + { + // in kernel data segment. Will write one byte at a time, w/o regard to boundaries. relativeByteAddress = address - kernelDataBaseAddress; // relative to data segment start, in bytes oldValue = storeBytesInTable(kernelDataBlockTable, relativeByteAddress, length, value); - } - else if (inKernelTextSegment(address)) { - // DEVELOPER: PLEASE USE setStatement() TO WRITE TO KERNEL TEXT SEGMENT... + } + else if (inKernelTextSegment(address)) + { + // DEVELOPER: PLEASE USE setStatement() TO WRITE TO KERNEL TEXT SEGMENT... throw new AddressErrorException( - "DEVELOPER: You must use setStatement() to write to kernel text segment!", - Exceptions.ADDRESS_EXCEPTION_STORE, address); - } - else { - // falls outside Mars addressing range + "DEVELOPER: You must use setStatement() to write to kernel text segment!", + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + else + { + // falls outside Mars addressing range throw new AddressErrorException("address out of range ", - Exceptions.ADDRESS_EXCEPTION_STORE, address); - } - notifyAnyObservers(AccessNotice.WRITE, address, length, value); - return oldValue; - } - - /////////////////////////////////////////////////////////////////////////////////////// - /** - * Starting at the given word address, write the given value over 4 bytes (a word). - * It must be written as is, without adjusting for byte order (little vs big endian). - * Address must be word-aligned. - * + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + notifyAnyObservers(AccessNotice.WRITE, address, length, value); + return oldValue; + } + + /** + * Starting at the given word address, write the given value over 4 bytes (a word). It must be written as is, + * without adjusting for byte order (little vs big endian). Address must be word-aligned. + * * @param address Starting address of Memory address to be set. * @param value Value to be stored starting at that address. * @return old value that was replaced by the set operation. * @throws AddressErrorException If address is not on word boundary. - **/ - public int setRawWord(int address, int value) throws AddressErrorException { - int relative, oldValue=0; - if (address % WORD_LENGTH_BYTES != 0) { + **/ + public int setRawWord(int address, int value) throws AddressErrorException + { + int relative, oldValue = 0; + if (address % WORD_LENGTH_BYTES != 0) + { throw new AddressErrorException("store address not aligned on word boundary ", - Exceptions.ADDRESS_EXCEPTION_STORE, address); - } - if (inDataSegment(address)) { - // in data segment + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + if (inDataSegment(address)) + { + // in data segment relative = (address - dataSegmentBaseAddress) >> 2; // convert byte address to words oldValue = storeWordInTable(dataBlockTable, relative, value); - } - else if (address > stackLimitAddress && address <= stackBaseAddress) { - // in stack. Handle similarly to data segment write, except relative - // address calculated "backward" because stack addresses grow down from base. + } + else if (address > stackLimitAddress && address <= stackBaseAddress) + { + // in stack. Handle similarly to data segment write, except relative + // address calculated "backward" because stack addresses grow down from base. relative = (stackBaseAddress - address) >> 2; // convert byte address to words oldValue = storeWordInTable(stackBlockTable, relative, value); - } - else if (inTextSegment(address)) { - // Burch Mod (Jan 2013): replace throw with call to setStatement - // DPS adaptation 5-Jul-2013: either throw or call, depending on setting - if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) { - ProgramStatement oldStatement = getStatementNoNotify(address); - if (oldStatement != null) { - oldValue = oldStatement.getBinaryStatement(); - } - setStatement(address, new ProgramStatement(value, address)); - } - else { - throw new AddressErrorException( - "Cannot write directly to text segment!", - Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + else if (inTextSegment(address)) + { + // Burch Mod (Jan 2013): replace throw with call to setStatement + // DPS adaptation 5-Jul-2013: either throw or call, depending on setting + if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) + { + ProgramStatement oldStatement = getStatementNoNotify(address); + if (oldStatement != null) + { + oldValue = oldStatement.getBinaryStatement(); + } + setStatement(address, new ProgramStatement(value, address)); } - } - else if (address >= memoryMapBaseAddress && address < memoryMapLimitAddress) { - // memory mapped I/O. + else + { + throw new AddressErrorException( + "Cannot write directly to text segment!", + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + } + else if (address >= memoryMapBaseAddress && address < memoryMapLimitAddress) + { + // memory mapped I/O. relative = (address - memoryMapBaseAddress) >> 2; // convert byte address to word oldValue = storeWordInTable(memoryMapBlockTable, relative, value); - } - else if (inKernelDataSegment(address)) { - // in data segment + } + else if (inKernelDataSegment(address)) + { + // in data segment relative = (address - kernelDataBaseAddress) >> 2; // convert byte address to words oldValue = storeWordInTable(kernelDataBlockTable, relative, value); - } - else if (inKernelTextSegment(address)) { - // DEVELOPER: PLEASE USE setStatement() TO WRITE TO KERNEL TEXT SEGMENT... + } + else if (inKernelTextSegment(address)) + { + // DEVELOPER: PLEASE USE setStatement() TO WRITE TO KERNEL TEXT SEGMENT... throw new AddressErrorException( - "DEVELOPER: You must use setStatement() to write to kernel text segment!", - Exceptions.ADDRESS_EXCEPTION_STORE, address); - } - else { - // falls outside Mars addressing range + "DEVELOPER: You must use setStatement() to write to kernel text segment!", + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + else + { + // falls outside Mars addressing range throw new AddressErrorException("store address out of range ", - Exceptions.ADDRESS_EXCEPTION_STORE, address); - } - notifyAnyObservers(AccessNotice.WRITE, address, WORD_LENGTH_BYTES, value); - if (Globals.getSettings().getBackSteppingEnabled()) { - Globals.program.getBackStepper().addMemoryRestoreRawWord(address,oldValue); - } - return oldValue; - } - + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + notifyAnyObservers(AccessNotice.WRITE, address, WORD_LENGTH_BYTES, value); + if (Globals.getSettings().getBackSteppingEnabled()) + { + Globals.program.getBackStepper().addMemoryRestoreRawWord(address, oldValue); + } + return oldValue; + } + + /////////////////////////////////////////////////////////////////////////////////////// - /** - * Starting at the given word address, write the given value over 4 bytes (a word). - * The address must be word-aligned. - * + + /** + * Starting at the given word address, write the given value over 4 bytes (a word). The address must be + * word-aligned. + * * @param address Starting address of Memory address to be set. * @param value Value to be stored starting at that address. * @return old value that was replaced by setWord operation. * @throws AddressErrorException If address is not on word boundary. - **/ - public int setWord(int address, int value) throws AddressErrorException { - if (address % WORD_LENGTH_BYTES != 0) { + **/ + public int setWord(int address, int value) throws AddressErrorException + { + if (address % WORD_LENGTH_BYTES != 0) + { throw new AddressErrorException( - "store address not aligned on word boundary ", - Exceptions.ADDRESS_EXCEPTION_STORE,address); - } - return (Globals.getSettings().getBackSteppingEnabled()) - ? Globals.program.getBackStepper().addMemoryRestoreWord(address,set(address, value, WORD_LENGTH_BYTES)) + "store address not aligned on word boundary ", + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + return (Globals.getSettings().getBackSteppingEnabled()) + ? Globals.program.getBackStepper().addMemoryRestoreWord(address, set(address, value, WORD_LENGTH_BYTES)) : set(address, value, WORD_LENGTH_BYTES); - } - - + } + /////////////////////////////////////////////////////////////////////////////////////// - /** - * Starting at the given halfword address, write the lower 16 bits of given value - * into 2 bytes (a halfword). - * + + /** + * Starting at the given halfword address, write the lower 16 bits of given value into 2 bytes (a halfword). + * * @param address Starting address of Memory address to be set. * @param value Value to be stored starting at that address. Only low order 16 bits used. * @return old value that was replaced by setHalf operation. * @throws AddressErrorException If address is not on halfword boundary. - **/ - public int setHalf(int address, int value) throws AddressErrorException { - if (address % 2 != 0) { + **/ + public int setHalf(int address, int value) throws AddressErrorException + { + if (address % 2 != 0) + { throw new AddressErrorException("store address not aligned on halfword boundary ", - Exceptions.ADDRESS_EXCEPTION_STORE, address); - } - return (Globals.getSettings().getBackSteppingEnabled()) - ? Globals.program.getBackStepper().addMemoryRestoreHalf(address,set(address,value,2)) + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + return (Globals.getSettings().getBackSteppingEnabled()) + ? Globals.program.getBackStepper().addMemoryRestoreHalf(address, set(address, value, 2)) : set(address, value, 2); - } - + } + + /////////////////////////////////////////////////////////////////////////////////////// - /** - * Writes low order 8 bits of given value into specified Memory byte. - * + + /** + * Writes low order 8 bits of given value into specified Memory byte. + * * @param address Address of Memory byte to be set. * @param value Value to be stored at that address. Only low order 8 bits used. * @return old value that was replaced by setByte operation. **/ - - public int setByte(int address, int value) throws AddressErrorException { - return (Globals.getSettings().getBackSteppingEnabled()) - ? Globals.program.getBackStepper().addMemoryRestoreByte(address,set(address,value,1)) + + public int setByte(int address, int value) throws AddressErrorException + { + return (Globals.getSettings().getBackSteppingEnabled()) + ? Globals.program.getBackStepper().addMemoryRestoreByte(address, set(address, value, 1)) : set(address, value, 1); - } - + } + + /////////////////////////////////////////////////////////////////////////////////////// - /** - * Writes 64 bit double value starting at specified Memory address. Note that - * high-order 32 bits are stored in higher (second) memory word regardless - * of "endianness". - * + + /** + * Writes 64 bit double value starting at specified Memory address. Note that high-order 32 bits are stored in + * higher (second) memory word regardless of "endianness". + * * @param address Starting address of Memory address to be set. - * @param value Value to be stored at that address. + * @param value Value to be stored at that address. * @return old value that was replaced by setDouble operation. - **/ - public double setDouble(int address, double value) throws AddressErrorException { - int oldHighOrder, oldLowOrder; - long longValue = Double.doubleToLongBits(value); - oldHighOrder = set(address+4, Binary.highOrderLongToInt(longValue),4); - oldLowOrder = set(address, Binary.lowOrderLongToInt(longValue),4); - return Double.longBitsToDouble(Binary.twoIntsToLong(oldHighOrder, oldLowOrder)); - } - - - //////////////////////////////////////////////////////////////////////////////// - /** - * Stores ProgramStatement in Text Segment. - * @param address Starting address of Memory address to be set. Must be word boundary. - * @param statement Machine code to be stored starting at that address -- for simulation - * purposes, actually stores reference to ProgramStatement instead of 32-bit machine code. - * @throws AddressErrorException If address is not on word boundary or is outside Text Segment. - * @see ProgramStatement - **/ - - public void setStatement(int address, ProgramStatement statement) throws AddressErrorException { - if (address % 4 != 0 || !(inTextSegment(address) || inKernelTextSegment(address))) { + **/ + public double setDouble(int address, double value) throws AddressErrorException + { + int oldHighOrder, oldLowOrder; + long longValue = Double.doubleToLongBits(value); + oldHighOrder = set(address + 4, Binary.highOrderLongToInt(longValue), 4); + oldLowOrder = set(address, Binary.lowOrderLongToInt(longValue), 4); + return Double.longBitsToDouble(Binary.twoIntsToLong(oldHighOrder, oldLowOrder)); + } + + //////////////////////////////////////////////////////////////////////////////// + + /** + * Stores ProgramStatement in Text Segment. + * + * @param address Starting address of Memory address to be set. Must be word boundary. + * @param statement Machine code to be stored starting at that address -- for simulation purposes, actually + * stores reference to ProgramStatement instead of 32-bit machine code. + * @throws AddressErrorException If address is not on word boundary or is outside Text Segment. + * @see ProgramStatement + **/ + + public void setStatement(int address, ProgramStatement statement) throws AddressErrorException + { + if (address % 4 != 0 || !(inTextSegment(address) || inKernelTextSegment(address))) + { throw new AddressErrorException( - "store address to text segment out of range or not aligned to word boundary ", - Exceptions.ADDRESS_EXCEPTION_STORE, address); - } - if (Globals.debug) System.out.println("memory["+address+"] set to "+statement.getBinaryStatement()); - if (inTextSegment(address)) { + "store address to text segment out of range or not aligned to word boundary ", + Exceptions.ADDRESS_EXCEPTION_STORE, address); + } + if (Globals.debug) + { + System.out.println("memory[" + address + "] set to " + statement.getBinaryStatement()); + } + if (inTextSegment(address)) + { storeProgramStatement(address, statement, textBaseAddress, textBlockTable); - } - else { + } + else + { storeProgramStatement(address, statement, kernelTextBaseAddress, kernelTextBlockTable); - } - } - - - - /******************************** THE GETTER METHODS ******************************/ - - ////////////////////////////////////////////////////////////////////////////////////////// - /** - * Starting at the given word address, read the given number of bytes (max 4). - * This one does not check for word boundaries, and copies one byte at a time. - * If length == 1, puts value in low order byte. If 2, puts into low order half-word. - * @param address Starting address of Memory address to be read. - * @param length Number of bytes to be read. - * @return Value stored starting at that address. - **/ - - public int get(int address, int length) throws AddressErrorException { - return get(address, length, true); - } - - // Does the real work, but includes option to NOT notify observers. - private int get(int address, int length, boolean notify) throws AddressErrorException { - int value = 0; - int relativeByteAddress; - if (inDataSegment(address)) { - // in data segment. Will read one byte at a time, w/o regard to boundaries. + } + } + + //////////////////////////////////////////////////////////////////////////////// + + /** + * Starting at the given word address, read the given number of bytes (max 4). This one does not check for word + * boundaries, and copies one byte at a time. If length == 1, puts value in low order byte. If 2, puts into low + * order half-word. + * + * @param address Starting address of Memory address to be read. + * @param length Number of bytes to be read. + * @return Value stored starting at that address. + **/ + + public int get(int address, int length) throws AddressErrorException + { + return get(address, length, true); + } + + ////////// + + // Does the real work, but includes option to NOT notify observers. + private int get(int address, int length, boolean notify) throws AddressErrorException + { + int value = 0; + int relativeByteAddress; + if (inDataSegment(address)) + { + // in data segment. Will read one byte at a time, w/o regard to boundaries. relativeByteAddress = address - dataSegmentBaseAddress; // relative to data segment start, in bytes value = fetchBytesFromTable(dataBlockTable, relativeByteAddress, length); - } - else if (address > stackLimitAddress && address <= stackBaseAddress) { - // in stack. Similar to data, except relative address computed "backward" + } + else if (address > stackLimitAddress && address <= stackBaseAddress) + { + // in stack. Similar to data, except relative address computed "backward" relativeByteAddress = stackBaseAddress - address; value = fetchBytesFromTable(stackBlockTable, relativeByteAddress, length); - } - - else if (address >= memoryMapBaseAddress && address < memoryMapLimitAddress) { - // memory mapped I/O. + } + + else if (address >= memoryMapBaseAddress && address < memoryMapLimitAddress) + { + // memory mapped I/O. relativeByteAddress = address - memoryMapBaseAddress; value = fetchBytesFromTable(memoryMapBlockTable, relativeByteAddress, length); - } - else if (inTextSegment(address)) { - // Burch Mod (Jan 2013): replace throw with calls to getStatementNoNotify & getBinaryStatement - // DPS adaptation 5-Jul-2013: either throw or call, depending on setting - if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) { - ProgramStatement stmt = getStatementNoNotify(address); - value = stmt == null ? 0 : stmt.getBinaryStatement(); - } - else { - throw new AddressErrorException( - "Cannot read directly from text segment!", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + else if (inTextSegment(address)) + { + // Burch Mod (Jan 2013): replace throw with calls to getStatementNoNotify & getBinaryStatement + // DPS adaptation 5-Jul-2013: either throw or call, depending on setting + if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) + { + ProgramStatement stmt = getStatementNoNotify(address); + value = stmt == null ? 0 : stmt.getBinaryStatement(); } - } - else if (inKernelDataSegment(address)) { - // in kernel data segment. Will read one byte at a time, w/o regard to boundaries. + else + { + throw new AddressErrorException( + "Cannot read directly from text segment!", + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + } + else if (inKernelDataSegment(address)) + { + // in kernel data segment. Will read one byte at a time, w/o regard to boundaries. relativeByteAddress = address - kernelDataBaseAddress; // relative to data segment start, in bytes value = fetchBytesFromTable(kernelDataBlockTable, relativeByteAddress, length); - } - else if (inKernelTextSegment(address)) { - // DEVELOPER: PLEASE USE getStatement() TO READ FROM KERNEL TEXT SEGMENT... + } + else if (inKernelTextSegment(address)) + { + // DEVELOPER: PLEASE USE getStatement() TO READ FROM KERNEL TEXT SEGMENT... throw new AddressErrorException( - "DEVELOPER: You must use getStatement() to read from kernel text segment!", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - else { - // falls outside Mars addressing range + "DEVELOPER: You must use getStatement() to read from kernel text segment!", + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + else + { + // falls outside Mars addressing range throw new AddressErrorException("address out of range ", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - if (notify) notifyAnyObservers(AccessNotice.READ, address, length, value); - return value; - } - - - ///////////////////////////////////////////////////////////////////////// - /** - * Starting at the given word address, read a 4 byte word as an int. - * It transfers the 32 bit value "raw" as stored in memory, and does not adjust - * for byte order (big or little endian). Address must be word-aligned. - * + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + if (notify) + { + notifyAnyObservers(AccessNotice.READ, address, length, value); + } + return value; + } + + + /********************************* THE UTILITIES *************************************/ + + /** + * Starting at the given word address, read a 4 byte word as an int. It transfers the 32 bit value "raw" as stored + * in memory, and does not adjust for byte order (big or little endian). Address must be word-aligned. + * * @param address Starting address of word to be read. - * @return Word (4-byte value) stored starting at that address. + * @return Word (4-byte value) stored starting at that address. * @throws AddressErrorException If address is not on word boundary. - **/ - + **/ + // Note: the logic here is repeated in getRawWordOrNull() below. Logic is - // simplified by having this method just call getRawWordOrNull() then + // simplified by having this method just call getRawWordOrNull() then // return either the int of its return value, or 0 if it returns null. // Doing so would be detrimental to simulation runtime performance, so // I decided to keep the duplicate logic. - public int getRawWord(int address) throws AddressErrorException { - int value = 0; - int relative; - if (address % WORD_LENGTH_BYTES != 0) { + public int getRawWord(int address) throws AddressErrorException + { + int value = 0; + int relative; + if (address % WORD_LENGTH_BYTES != 0) + { throw new AddressErrorException("address for fetch not aligned on word boundary", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - if (inDataSegment(address)) { - // in data segment + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + if (inDataSegment(address)) + { + // in data segment relative = (address - dataSegmentBaseAddress) >> 2; // convert byte address to words value = fetchWordFromTable(dataBlockTable, relative); - } - else if (address > stackLimitAddress && address <= stackBaseAddress) { - // in stack. Similar to data, except relative address computed "backward" + } + else if (address > stackLimitAddress && address <= stackBaseAddress) + { + // in stack. Similar to data, except relative address computed "backward" relative = (stackBaseAddress - address) >> 2; // convert byte address to words value = fetchWordFromTable(stackBlockTable, relative); - } - else if (address >= memoryMapBaseAddress && address < memoryMapLimitAddress) { - // memory mapped I/O. + } + else if (address >= memoryMapBaseAddress && address < memoryMapLimitAddress) + { + // memory mapped I/O. relative = (address - memoryMapBaseAddress) >> 2; value = fetchWordFromTable(memoryMapBlockTable, relative); - } - else if (inTextSegment(address)) { - // Burch Mod (Jan 2013): replace throw with calls to getStatementNoNotify & getBinaryStatement - // DPS adaptation 5-Jul-2013: either throw or call, depending on setting - if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) { - ProgramStatement stmt = getStatementNoNotify(address); - value = stmt == null ? 0 : stmt.getBinaryStatement(); - } - else { - throw new AddressErrorException( - "Cannot read directly from text segment!", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + else if (inTextSegment(address)) + { + // Burch Mod (Jan 2013): replace throw with calls to getStatementNoNotify & getBinaryStatement + // DPS adaptation 5-Jul-2013: either throw or call, depending on setting + if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) + { + ProgramStatement stmt = getStatementNoNotify(address); + value = stmt == null ? 0 : stmt.getBinaryStatement(); } - } - else if (inKernelDataSegment(address)) { - // in kernel data segment + else + { + throw new AddressErrorException( + "Cannot read directly from text segment!", + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + } + else if (inKernelDataSegment(address)) + { + // in kernel data segment relative = (address - kernelDataBaseAddress) >> 2; // convert byte address to words value = fetchWordFromTable(kernelDataBlockTable, relative); - } - else if (inKernelTextSegment(address)) { - // DEVELOPER: PLEASE USE getStatement() TO READ FROM KERNEL TEXT SEGMENT... + } + else if (inKernelTextSegment(address)) + { + // DEVELOPER: PLEASE USE getStatement() TO READ FROM KERNEL TEXT SEGMENT... throw new AddressErrorException( - "DEVELOPER: You must use getStatement() to read from kernel text segment!", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - else { - // falls outside Mars addressing range - throw new AddressErrorException("address out of range ", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - notifyAnyObservers(AccessNotice.READ, address, Memory.WORD_LENGTH_BYTES,value); - return value; - } - - ///////////////////////////////////////////////////////////////////////// - /** - * Starting at the given word address, read a 4 byte word as an int and return Integer. - * It transfers the 32 bit value "raw" as stored in memory, and does not adjust - * for byte order (big or little endian). Address must be word-aligned. - * - * Returns null if reading from text segment and there is no instruction at the - * requested address. Returns null if reading from data segment and this is the - * first reference to the MARS 4K memory allocation block (i.e., an array to - * hold the memory has not been allocated). - * - * This method was developed by Greg Giberling of UC Berkeley to support the memory - * dump feature that he implemented in Fall 2007. - * - * @param address Starting address of word to be read. - * @return Word (4-byte value) stored starting at that address as an Integer. Conditions - * that cause return value null are described above. - * @throws AddressErrorException If address is not on word boundary. - **/ - - // See note above, with getRawWord(), concerning duplicated logic. - - public Integer getRawWordOrNull(int address) throws AddressErrorException { - Integer value = null; - int relative; - if (address % WORD_LENGTH_BYTES != 0) { + "DEVELOPER: You must use getStatement() to read from kernel text segment!", + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + else + { + // falls outside Mars addressing range + throw new AddressErrorException("address out of range ", + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + notifyAnyObservers(AccessNotice.READ, address, Memory.WORD_LENGTH_BYTES, value); + return value; + } + + /** + * Starting at the given word address, read a 4 byte word as an int and return Integer. It transfers the 32 bit + * value "raw" as stored in memory, and does not adjust for byte order (big or little endian). Address must be + * word-aligned. + *

+ * Returns null if reading from text segment and there is no instruction at the requested address. Returns null if + * reading from data segment and this is the first reference to the MARS 4K memory allocation block (i.e., an array + * to hold the memory has not been allocated). + *

+ * This method was developed by Greg Giberling of UC Berkeley to support the memory dump feature that he implemented + * in Fall 2007. + * + * @param address Starting address of word to be read. + * @return Word (4-byte value) stored starting at that address as an Integer. Conditions that cause return value + * null are described above. + * @throws AddressErrorException If address is not on word boundary. + **/ + + // See note above, with getRawWord(), concerning duplicated logic. + public Integer getRawWordOrNull(int address) throws AddressErrorException + { + Integer value = null; + int relative; + if (address % WORD_LENGTH_BYTES != 0) + { throw new AddressErrorException("address for fetch not aligned on word boundary", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - if (inDataSegment(address)) { - // in data segment + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + if (inDataSegment(address)) + { + // in data segment relative = (address - dataSegmentBaseAddress) >> 2; // convert byte address to words value = fetchWordOrNullFromTable(dataBlockTable, relative); - } - else if (address > stackLimitAddress && address <= stackBaseAddress) { - // in stack. Similar to data, except relative address computed "backward" + } + else if (address > stackLimitAddress && address <= stackBaseAddress) + { + // in stack. Similar to data, except relative address computed "backward" relative = (stackBaseAddress - address) >> 2; // convert byte address to words value = fetchWordOrNullFromTable(stackBlockTable, relative); - } - else if (inTextSegment(address) || inKernelTextSegment(address)) { - try { - value = (getStatementNoNotify(address) == null) ? null : getStatementNoNotify(address).getBinaryStatement(); - } catch (AddressErrorException aee) { - value = null; - } - } - else if (inKernelDataSegment(address)) { - // in kernel data segment + } + else if (inTextSegment(address) || inKernelTextSegment(address)) + { + try + { + value = (getStatementNoNotify(address) == null) ? null : getStatementNoNotify(address).getBinaryStatement(); + } + catch (AddressErrorException aee) + { + value = null; + } + } + else if (inKernelDataSegment(address)) + { + // in kernel data segment relative = (address - kernelDataBaseAddress) >> 2; // convert byte address to words value = fetchWordOrNullFromTable(kernelDataBlockTable, relative); - } - else { - // falls outside Mars addressing range + } + else + { + // falls outside Mars addressing range throw new AddressErrorException("address out of range ", Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - // Do not notify observers. This read operation is initiated by the - // dump feature, not the executing MIPS program. - return value; - } - - /** - * Look for first "null" memory value in an address range. For text segment (binary code), this - * represents a word that does not contain an instruction. Normally use this to find the end of - * the program. For data segment, this represents the first block of simulated memory (block length - * currently 4K words) that has not been referenced by an assembled/executing program. - * - * @param baseAddress lowest MIPS address to be searched; the starting point - * @param limitAddress highest MIPS address to be searched - * @return lowest address within specified range that contains "null" value as described above. - * @throws AddressErrorException if the base address is not on a word boundary - */ - public int getAddressOfFirstNull(int baseAddress, int limitAddress) throws AddressErrorException { - int address = baseAddress; - for (; address < limitAddress; address += Memory.WORD_LENGTH_BYTES) { - if (getRawWordOrNull(address) == null) { - break; + } + // Do not notify observers. This read operation is initiated by the + // dump feature, not the executing MIPS program. + return value; + } + + /** + * Look for first "null" memory value in an address range. For text segment (binary code), this represents a word + * that does not contain an instruction. Normally use this to find the end of the program. For data segment, this + * represents the first block of simulated memory (block length currently 4K words) that has not been referenced by + * an assembled/executing program. + * + * @param baseAddress lowest MIPS address to be searched; the starting point + * @param limitAddress highest MIPS address to be searched + * @return lowest address within specified range that contains "null" value as described above. + * @throws AddressErrorException if the base address is not on a word boundary + */ + public int getAddressOfFirstNull(int baseAddress, int limitAddress) throws AddressErrorException + { + int address = baseAddress; + for (; address < limitAddress; address += Memory.WORD_LENGTH_BYTES) + { + if (getRawWordOrNull(address) == null) + { + break; } - } - return address; - } - - - /////////////////////////////////////////////////////////////////////////////////////// - /** - * Starting at the given word address, read a 4 byte word as an int. - * Does not use "get()"; we can do it faster here knowing we're working only - * with full words. - * + } + return address; + } + + /** + * Starting at the given word address, read a 4 byte word as an int. Does not use "get()"; we can do it faster here + * knowing we're working only with full words. + * * @param address Starting address of word to be read. - * @return Word (4-byte value) stored starting at that address. + * @return Word (4-byte value) stored starting at that address. * @throws AddressErrorException If address is not on word boundary. - **/ - public int getWord(int address) throws AddressErrorException { - if (address % WORD_LENGTH_BYTES != 0) { + **/ + public int getWord(int address) throws AddressErrorException + { + if (address % WORD_LENGTH_BYTES != 0) + { throw new AddressErrorException("fetch address not aligned on word boundary ", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - return get(address, WORD_LENGTH_BYTES, true); - } - - /////////////////////////////////////////////////////////////////////////////////////// - /** - * Starting at the given word address, read a 4 byte word as an int. - * Does not use "get()"; we can do it faster here knowing we're working only - * with full words. Observers are NOT notified. - * + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + return get(address, WORD_LENGTH_BYTES, true); + } + + /** + * Starting at the given word address, read a 4 byte word as an int. Does not use "get()"; we can do it faster here + * knowing we're working only with full words. Observers are NOT notified. + * * @param address Starting address of word to be read. - * @return Word (4-byte value) stored starting at that address. + * @return Word (4-byte value) stored starting at that address. * @throws AddressErrorException If address is not on word boundary. - **/ - public int getWordNoNotify(int address) throws AddressErrorException { - if (address % WORD_LENGTH_BYTES != 0) { + **/ + public int getWordNoNotify(int address) throws AddressErrorException + { + if (address % WORD_LENGTH_BYTES != 0) + { throw new AddressErrorException("fetch address not aligned on word boundary ", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - return get(address, WORD_LENGTH_BYTES, false); - } - - - - /////////////////////////////////////////////////////////////////////////////////////// - /** - * Starting at the given word address, read a 2 byte word into lower 16 bits of int. - * + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + return get(address, WORD_LENGTH_BYTES, false); + } + + /** + * Starting at the given word address, read a 2 byte word into lower 16 bits of int. + * * @param address Starting address of word to be read. - * @return Halfword (2-byte value) stored starting at that address, stored in lower 16 bits. + * @return Halfword (2-byte value) stored starting at that address, stored in lower 16 bits. * @throws AddressErrorException If address is not on halfword boundary. - **/ - public int getHalf(int address) throws AddressErrorException { - if (address % 2 != 0) { + **/ + public int getHalf(int address) throws AddressErrorException + { + if (address % 2 != 0) + { throw new AddressErrorException("fetch address not aligned on halfword boundary ", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - return get(address, 2); - } - - - /////////////////////////////////////////////////////////////////////////////////////// - /** - * Reads specified Memory byte into low order 8 bits of int. - * + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + return get(address, 2); + } + + /** + * Reads specified Memory byte into low order 8 bits of int. + * * @param address Address of Memory byte to be read. * @return Value stored at that address. Only low order 8 bits used. **/ - public int getByte(int address) throws AddressErrorException { - return get(address, 1); - } - - //////////////////////////////////////////////////////////////////////////////// - /** - * Gets ProgramStatement from Text Segment. - * @param address Starting address of Memory address to be read. Must be word boundary. - * @return reference to ProgramStatement object associated with that address, or null if none. - * @throws AddressErrorException If address is not on word boundary or is outside Text Segment. - * @see ProgramStatement - **/ - - public ProgramStatement getStatement(int address) throws AddressErrorException { - return getStatement(address, true); + public int getByte(int address) throws AddressErrorException + { + return get(address, 1); + } + + /** + * Gets ProgramStatement from Text Segment. + * + * @param address Starting address of Memory address to be read. Must be word boundary. + * @return reference to ProgramStatement object associated with that address, or null if none. + * @throws AddressErrorException If address is not on word boundary or is outside Text Segment. + * @see ProgramStatement + **/ + + public ProgramStatement getStatement(int address) throws AddressErrorException + { + return getStatement(address, true); /* if (address % 4 != 0 || !(inTextSegment(address) || inKernelTextSegment(address))) { throw new AddressErrorException( @@ -899,24 +1159,32 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. } if (inTextSegment(address)) { return readProgramStatement(address, textBaseAddress, textBlockTable, true); - } + } else { return readProgramStatement(address, kernelTextBaseAddress, kernelTextBlockTable,true); } */ - } - - //////////////////////////////////////////////////////////////////////////////// - /** - * Gets ProgramStatement from Text Segment without notifying observers. - * @param address Starting address of Memory address to be read. Must be word boundary. - * @return reference to ProgramStatement object associated with that address, or null if none. - * @throws AddressErrorException If address is not on word boundary or is outside Text Segment. - * @see ProgramStatement - **/ - - public ProgramStatement getStatementNoNotify(int address) throws AddressErrorException { - return getStatement(address, false); + } + + + /////////////////////////////////////////////////////////////////////////// + // ALL THE OBSERVABLE STUFF GOES HERE. FOR COMPATIBILITY, Memory IS STILL + // EXTENDING OBSERVABLE, BUT WILL NOT USE INHERITED METHODS. WILL INSTEAD + // USE A COLLECTION OF MemoryObserver OBJECTS, EACH OF WHICH IS COMBINATION + // OF AN OBSERVER WITH AN ADDRESS RANGE. + + /** + * Gets ProgramStatement from Text Segment without notifying observers. + * + * @param address Starting address of Memory address to be read. Must be word boundary. + * @return reference to ProgramStatement object associated with that address, or null if none. + * @throws AddressErrorException If address is not on word boundary or is outside Text Segment. + * @see ProgramStatement + **/ + + public ProgramStatement getStatementNoNotify(int address) throws AddressErrorException + { + return getStatement(address, false); /* if (address % 4 != 0 || !(inTextSegment(address) || inKernelTextSegment(address))) { throw new AddressErrorException( @@ -925,535 +1193,492 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. } if (inTextSegment(address)) { return readProgramStatement(address, textBaseAddress, textBlockTable, false); - } + } else { return readProgramStatement(address, kernelTextBaseAddress, kernelTextBlockTable, false); } */ - } - - ////////// - - private ProgramStatement getStatement(int address, boolean notify) throws AddressErrorException { - if (!wordAligned(address)) { + } + + private ProgramStatement getStatement(int address, boolean notify) throws AddressErrorException + { + if (!wordAligned(address)) + { throw new AddressErrorException( - "fetch address for text segment not aligned to word boundary ", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - if (!Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED) - && !(inTextSegment(address) || inKernelTextSegment(address))) { + "fetch address for text segment not aligned to word boundary ", + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + if (!Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED) + && !(inTextSegment(address) || inKernelTextSegment(address))) + { throw new AddressErrorException( - "fetch address for text segment out of range ", - Exceptions.ADDRESS_EXCEPTION_LOAD, address); - } - if (inTextSegment(address)) + "fetch address for text segment out of range ", + Exceptions.ADDRESS_EXCEPTION_LOAD, address); + } + if (inTextSegment(address)) + { return readProgramStatement(address, textBaseAddress, textBlockTable, notify); - else if (inKernelTextSegment(address)) + } + else if (inKernelTextSegment(address)) + { return readProgramStatement(address, kernelTextBaseAddress, kernelTextBlockTable, notify); - else + } + else + { return new ProgramStatement(get(address, WORD_LENGTH_BYTES), address); - } - - - /********************************* THE UTILITIES *************************************/ - - /** - * Utility to determine if given address is word-aligned. - * @param address the address to check - * @return true if address is word-aligned, false otherwise - */ - public static boolean wordAligned(int address) { - return (address % WORD_LENGTH_BYTES == 0); - } - - /** - * Utility to determine if given address is doubleword-aligned. - * @param address the address to check - * @return true if address is doubleword-aligned, false otherwise - */ - public static boolean doublewordAligned(int address) { - return (address % (WORD_LENGTH_BYTES+WORD_LENGTH_BYTES) == 0); - } - - /** - * Utility method to align given address to next full word boundary, if not already - * aligned. - * @param address a memory address (any int value is potentially valid) - * @return address aligned to next word boundary (divisible by 4) - */ - public static int alignToWordBoundary(int address) { - if (!wordAligned(address)) { - if (address > 0) - address += (4-(address % WORD_LENGTH_BYTES)); - else - address -= (4-(address % WORD_LENGTH_BYTES)); - } - return address; - } - - /** - * Handy little utility to find out if given address is in MARS text - * segment (starts at Memory.textBaseAddress). - * Note that MARS does not implement the entire MIPS text segment space, - * but it does implement enough for hundreds of thousands of lines - * of code. - * @param address integer memory address - * @return true if that address is within MARS-defined text segment, - * false otherwise. - */ - public static boolean inTextSegment(int address) { - return address >= textBaseAddress && address < textLimitAddress; - } - - /** - * Handy little utility to find out if given address is in MARS kernel - * text segment (starts at Memory.kernelTextBaseAddress). - * @param address integer memory address - * @return true if that address is within MARS-defined kernel text segment, - * false otherwise. - */ - public static boolean inKernelTextSegment(int address) { - return address >= kernelTextBaseAddress && address < kernelTextLimitAddress; - } - + } + } + /** - * Handy little utility to find out if given address is in MARS data - * segment (starts at Memory.dataSegmentBaseAddress). - * Note that MARS does not implement the entire MIPS data segment space, - * but it does support at least 4MB. - * @param address integer memory address - * @return true if that address is within MARS-defined data segment, - * false otherwise. - */ - public static boolean inDataSegment(int address) { - return address >= dataSegmentBaseAddress && address < dataSegmentLimitAddress; - } - - /** - * Handy little utility to find out if given address is in MARS kernel data - * segment (starts at Memory.kernelDataSegmentBaseAddress). - * @param address integer memory address - * @return true if that address is within MARS-defined kernel data segment, - * false otherwise. - */ - public static boolean inKernelDataSegment(int address) { - return address >= kernelDataBaseAddress && address < kernelDataSegmentLimitAddress; - } - - - /** - * Handy little utility to find out if given address is in the Memory Map area - * starts at Memory.memoryMapBaseAddress, range 0xffff0000 to 0xffffffff. - * @param address integer memory address - * @return true if that address is within MARS-defined memory map (MMIO) area, - * false otherwise. - */ - public static boolean inMemoryMapSegment(int address) { - return address >= memoryMapBaseAddress && address < kernelHighAddress; - } - - - - - - /////////////////////////////////////////////////////////////////////////// - // ALL THE OBSERVABLE STUFF GOES HERE. FOR COMPATIBILITY, Memory IS STILL - // EXTENDING OBSERVABLE, BUT WILL NOT USE INHERITED METHODS. WILL INSTEAD - // USE A COLLECTION OF MemoryObserver OBJECTS, EACH OF WHICH IS COMBINATION - // OF AN OBSERVER WITH AN ADDRESS RANGE. - - /** - * Method to accept registration from observer for any memory address. Overrides - * inherited method. Note to observers: this class delegates Observable operations - * so notices will come from the delegate, not the memory object. - * @param obs the observer - */ - - public void addObserver(Observer obs) { - try { // split so start address always >= end address + * Method to accept registration from observer for any memory address. Overrides inherited method. Note to + * observers: this class delegates Observable operations so notices will come from the delegate, not the memory + * object. + * + * @param obs the observer + */ + + public void addObserver(Observer obs) + { + try + { // split so start address always >= end address this.addObserver(obs, 0, 0x7ffffffc); - this.addObserver(obs,0x80000000,0xfffffffc); - } - catch (AddressErrorException aee) { - System.out.println("Internal Error in Memory.addObserver: "+aee); - } - } - - /** - * Method to accept registration from observer for specific address. This includes - * the memory word starting at the given address. Note to observers: this class delegates Observable operations - * so notices will come from the delegate, not the memory object. - * - * @param obs the observer - * @param addr the memory address which must be on word boundary - */ - - public void addObserver(Observer obs, int addr) throws AddressErrorException { - this.addObserver(obs, addr, addr); - } - - - /** - * Method to accept registration from observer for specific address range. The - * last byte included in the address range is the last byte of the word specified - * by the ending address. Note to observers: this class delegates Observable operations - * so notices will come from the delegate, not the memory object. - * - * @param obs the observer - * @param startAddr the low end of memory address range, must be on word boundary - * @param endAddr the high end of memory address range, must be on word boundary - */ - public void addObserver(Observer obs, int startAddr, int endAddr) throws AddressErrorException { - if (startAddr % WORD_LENGTH_BYTES != 0) { + this.addObserver(obs, 0x80000000, 0xfffffffc); + } + catch (AddressErrorException aee) + { + System.out.println("Internal Error in Memory.addObserver: " + aee); + } + } + + /** + * Method to accept registration from observer for specific address. This includes the memory word starting at the + * given address. Note to observers: this class delegates Observable operations so notices will come from the + * delegate, not the memory object. + * + * @param obs the observer + * @param addr the memory address which must be on word boundary + */ + + public void addObserver(Observer obs, int addr) throws AddressErrorException + { + this.addObserver(obs, addr, addr); + } + + /** + * Method to accept registration from observer for specific address range. The last byte included in the address + * range is the last byte of the word specified by the ending address. Note to observers: this class delegates + * Observable operations so notices will come from the delegate, not the memory object. + * + * @param obs the observer + * @param startAddr the low end of memory address range, must be on word boundary + * @param endAddr the high end of memory address range, must be on word boundary + */ + public void addObserver(Observer obs, int startAddr, int endAddr) throws AddressErrorException + { + if (startAddr % WORD_LENGTH_BYTES != 0) + { throw new AddressErrorException("address not aligned on word boundary ", - Exceptions.ADDRESS_EXCEPTION_LOAD, startAddr); - } - if (endAddr!=startAddr && endAddr % WORD_LENGTH_BYTES != 0) { + Exceptions.ADDRESS_EXCEPTION_LOAD, startAddr); + } + if (endAddr != startAddr && endAddr % WORD_LENGTH_BYTES != 0) + { throw new AddressErrorException("address not aligned on word boundary ", - Exceptions.ADDRESS_EXCEPTION_LOAD, startAddr); - } - // upper half of address space (above 0x7fffffff) has sign bit 1 thus is seen as - // negative. - if (startAddr>=0 && endAddr<0) { + Exceptions.ADDRESS_EXCEPTION_LOAD, startAddr); + } + // upper half of address space (above 0x7fffffff) has sign bit 1 thus is seen as + // negative. + if (startAddr >= 0 && endAddr < 0) + { throw new AddressErrorException("range cannot cross 0x8000000; please split it up", - Exceptions.ADDRESS_EXCEPTION_LOAD, startAddr); - } - if (endAddr= lowAddress && address <= highAddress-1+WORD_LENGTH_BYTES); - } - - public void notifyObserver(MemoryAccessNotice notice) { - this.setChanged(); - this.notifyObservers(notice); - } - - // Useful to have for future refactoring, if it actually becomes worthwhile to sort - // these or put 'em in a tree (rather than sequential search through list). - public int compareTo(Object obj) { - if (!(obj instanceof MemoryObservable)) { - throw new ClassCastException(); - } - MemoryObservable mo = (MemoryObservable) obj; - if (this.lowAddress < mo.lowAddress || this.lowAddress==mo.lowAddress && this.highAddress < mo.highAddress) { - return -1; - } - if (this.lowAddress > mo.lowAddress || this.lowAddress==mo.lowAddress && this.highAddress > mo.highAddress) { - return -1; - } - return 0; // they have to be equal at this point. - } - } - - - /********************************* THE HELPERS *************************************/ - - - //////////////////////////////////////////////////////////////////////////////// - // - // Method to notify any observers of memory operation that has just occurred. - // - // The "|| Globals.getGui()==null" is a hack added 19 July 2012 DPS. IF MIPS simulation - // is from command mode, Globals.program is null but still want ability to observe. - private void notifyAnyObservers(int type, int address, int length, int value) { - if ((Globals.program != null || Globals.getGui()==null) && this.observables.size() > 0) { + Exceptions.ADDRESS_EXCEPTION_LOAD, startAddr); + } + observables.add(new MemoryObservable(obs, startAddr, endAddr)); + } + + /** + * Return number of observers + */ + public int countObservers() + { + return observables.size(); + } + + /** + * Remove specified memory observers + * + * @param obs Observer to be removed + */ + public void deleteObserver(Observer obs) + { + Iterator it = observables.iterator(); + while (it.hasNext()) + { + ((MemoryObservable) it.next()).deleteObserver(obs); + } + } + + /** + * Remove all memory observers + */ + public void deleteObservers() + { + // just drop the collection + observables = getNewMemoryObserversCollection(); + } + + /** + * Overridden to be unavailable. The notice that an Observer receives does not come from the memory object itself, + * but instead from a delegate. + * + * @throws UnsupportedOperationException + */ + public void notifyObservers() + { + throw new UnsupportedOperationException(); + } + + /** + * Overridden to be unavailable. The notice that an Observer receives does not come from the memory object itself, + * but instead from a delegate. + * + * @throws UnsupportedOperationException + */ + public void notifyObservers(Object obj) + { + throw new UnsupportedOperationException(); + } + + private Collection getNewMemoryObserversCollection() + { + return new Vector(); // Vectors are thread-safe + } + + /********************************* THE HELPERS *************************************/ + + + //////////////////////////////////////////////////////////////////////////////// + // + // Method to notify any observers of memory operation that has just occurred. + // + // The "|| Globals.getGui()==null" is a hack added 19 July 2012 DPS. IF MIPS simulation + // is from command mode, Globals.program is null but still want ability to observe. + private void notifyAnyObservers(int type, int address, int length, int value) + { + if ((Globals.program != null || Globals.getGui() == null) && this.observables.size() > 0) + { Iterator it = this.observables.iterator(); MemoryObservable mo; - while (it.hasNext()) { - mo = (MemoryObservable)it.next(); - if (mo.match(address)) { - mo.notifyObserver(new MemoryAccessNotice(type, address, length, value)); - } + while (it.hasNext()) + { + mo = (MemoryObservable) it.next(); + if (mo.match(address)) + { + mo.notifyObserver(new MemoryAccessNotice(type, address, length, value)); + } } - } - } - - //////////////////////////////////////////////////////////////////////////////// - // - // Helper method to store 1, 2 or 4 byte value in table that represents MIPS - // memory. Originally used just for data segment, but now also used for stack. - // Both use different tables but same storage method and same table size - // and block size. - // Modified 29 Dec 2005 to return old value of replaced bytes. - // - private static final boolean STORE = true; - private static final boolean FETCH = false; - - private int storeBytesInTable(int [][] blockTable, - int relativeByteAddress, int length, int value) { - return storeOrFetchBytesInTable(blockTable, relativeByteAddress, length, value, STORE); - } - - //////////////////////////////////////////////////////////////////////////////// - // - // Helper method to fetch 1, 2 or 4 byte value from table that represents MIPS - // memory. Originally used just for data segment, but now also used for stack. - // Both use different tables but same storage method and same table size - // and block size. - // - - private int fetchBytesFromTable(int[][] blockTable, int relativeByteAddress, int length) { - return storeOrFetchBytesInTable(blockTable, relativeByteAddress, length, 0, FETCH); - } - - //////////////////////////////////////////////////////////////////////////////// - // - // The helper's helper. Works for either storing or fetching, little or big endian. - // When storing/fetching bytes, most of the work is calculating the correct array element(s) - // and element byte(s). This method performs either store or fetch, as directed by its - // client using STORE or FETCH in last arg. - // Modified 29 Dec 2005 to return old value of replaced bytes, for STORE. - // - private synchronized int storeOrFetchBytesInTable(int [][] blockTable, - int relativeByteAddress, int length, int value, boolean op) { - int relativeWordAddress, block, offset, bytePositionInMemory, bytePositionInValue; - int oldValue = 0; // for STORE, return old values of replaced bytes - int loopStopper = 3-length; - // IF added DPS 22-Dec-2008. NOTE: has NOT been tested with Big-Endian. - // Fix provided by Saul Spatz; comments that follow are his. - // If address in stack segment is 4k + m, with 0 < m < 4, then the - // relativeByteAddress we want is stackBaseAddress - 4k + m, but the - // address actually passed in is stackBaseAddress - (4k + m), so we - // need to add 2m. Because of the change in sign, we get the - // expression 4-delta below in place of m. - if (blockTable == stackBlockTable) { + } + } + + private int storeBytesInTable(int[][] blockTable, + int relativeByteAddress, int length, int value) + { + return storeOrFetchBytesInTable(blockTable, relativeByteAddress, length, value, STORE); + } + + private int fetchBytesFromTable(int[][] blockTable, int relativeByteAddress, int length) + { + return storeOrFetchBytesInTable(blockTable, relativeByteAddress, length, 0, FETCH); + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Helper method to fetch 1, 2 or 4 byte value from table that represents MIPS + // memory. Originally used just for data segment, but now also used for stack. + // Both use different tables but same storage method and same table size + // and block size. + // + + //////////////////////////////////////////////////////////////////////////////// + // + // The helper's helper. Works for either storing or fetching, little or big endian. + // When storing/fetching bytes, most of the work is calculating the correct array element(s) + // and element byte(s). This method performs either store or fetch, as directed by its + // client using STORE or FETCH in last arg. + // Modified 29 Dec 2005 to return old value of replaced bytes, for STORE. + // + private synchronized int storeOrFetchBytesInTable(int[][] blockTable, + int relativeByteAddress, int length, int value, boolean op) + { + int relativeWordAddress, block, offset, bytePositionInMemory, bytePositionInValue; + int oldValue = 0; // for STORE, return old values of replaced bytes + int loopStopper = 3 - length; + // IF added DPS 22-Dec-2008. NOTE: has NOT been tested with Big-Endian. + // Fix provided by Saul Spatz; comments that follow are his. + // If address in stack segment is 4k + m, with 0 < m < 4, then the + // relativeByteAddress we want is stackBaseAddress - 4k + m, but the + // address actually passed in is stackBaseAddress - (4k + m), so we + // need to add 2m. Because of the change in sign, we get the + // expression 4-delta below in place of m. + if (blockTable == stackBlockTable) + { int delta = relativeByteAddress % 4; - if (delta != 0) { - relativeByteAddress += ( 4 - delta ) << 1; + if (delta != 0) + { + relativeByteAddress += (4 - delta) << 1; } - } - for (bytePositionInValue = 3; bytePositionInValue > loopStopper; bytePositionInValue--) { + } + for (bytePositionInValue = 3; bytePositionInValue > loopStopper; bytePositionInValue--) + { bytePositionInMemory = relativeByteAddress % 4; relativeWordAddress = relativeByteAddress >> 2; block = relativeWordAddress / BLOCK_LENGTH_WORDS; // Block number offset = relativeWordAddress % BLOCK_LENGTH_WORDS; // Word within that block - if (blockTable[block] == null) { - if (op==STORE) - blockTable[block] = new int[BLOCK_LENGTH_WORDS]; - else - return 0; + if (blockTable[block] == null) + { + if (op == STORE) + { + blockTable[block] = new int[BLOCK_LENGTH_WORDS]; + } + else + { + return 0; + } } - if (byteOrder == LITTLE_ENDIAN) bytePositionInMemory = 3 - bytePositionInMemory; - if (op == STORE) { - oldValue = replaceByte(blockTable[block][offset], bytePositionInMemory, - oldValue, bytePositionInValue); - blockTable[block][offset] = replaceByte(value, bytePositionInValue, - blockTable[block][offset], bytePositionInMemory); - } - else {// op == FETCH - value = replaceByte(blockTable[block][offset], bytePositionInMemory, - value, bytePositionInValue); + if (byteOrder == LITTLE_ENDIAN) + { + bytePositionInMemory = 3 - bytePositionInMemory; + } + if (op == STORE) + { + oldValue = replaceByte(blockTable[block][offset], bytePositionInMemory, + oldValue, bytePositionInValue); + blockTable[block][offset] = replaceByte(value, bytePositionInValue, + blockTable[block][offset], bytePositionInMemory); + } + else + {// op == FETCH + value = replaceByte(blockTable[block][offset], bytePositionInMemory, + value, bytePositionInValue); } relativeByteAddress++; - } - return (op == STORE) ? oldValue : value; - } - - //////////////////////////////////////////////////////////////////////////////// - // - // Helper method to store 4 byte value in table that represents MIPS memory. - // Originally used just for data segment, but now also used for stack. - // Both use different tables but same storage method and same table size - // and block size. Assumes address is word aligned, no endian processing. - // Modified 29 Dec 2005 to return overwritten value. - - private synchronized int storeWordInTable(int[][] blockTable, int relative, int value) { - int block, offset, oldValue; - block = relative / BLOCK_LENGTH_WORDS; - offset = relative % BLOCK_LENGTH_WORDS; - if (blockTable[block] == null) { - // First time writing to this block, so allocate the space. + } + return (op == STORE) ? oldValue : value; + } + + private synchronized int storeWordInTable(int[][] blockTable, int relative, int value) + { + int block, offset, oldValue; + block = relative / BLOCK_LENGTH_WORDS; + offset = relative % BLOCK_LENGTH_WORDS; + if (blockTable[block] == null) + { + // First time writing to this block, so allocate the space. blockTable[block] = new int[BLOCK_LENGTH_WORDS]; - } - oldValue = blockTable[block][offset]; - blockTable[block][offset] = value; - return oldValue; - } - - //////////////////////////////////////////////////////////////////////////////// - // - // Helper method to fetch 4 byte value from table that represents MIPS memory. - // Originally used just for data segment, but now also used for stack. - // Both use different tables but same storage method and same table size - // and block size. Assumes word alignment, no endian processing. - // - - private synchronized int fetchWordFromTable(int[][] blockTable, int relative) { - int value = 0; - int block, offset; - block = relative / BLOCK_LENGTH_WORDS; - offset = relative % BLOCK_LENGTH_WORDS; - if (blockTable[block] == null) { - // first reference to an address in this block. Assume initialized to 0. + } + oldValue = blockTable[block][offset]; + blockTable[block][offset] = value; + return oldValue; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Helper method to store 4 byte value in table that represents MIPS memory. + // Originally used just for data segment, but now also used for stack. + // Both use different tables but same storage method and same table size + // and block size. Assumes address is word aligned, no endian processing. + // Modified 29 Dec 2005 to return overwritten value. + + private synchronized int fetchWordFromTable(int[][] blockTable, int relative) + { + int value = 0; + int block, offset; + block = relative / BLOCK_LENGTH_WORDS; + offset = relative % BLOCK_LENGTH_WORDS; + if (blockTable[block] == null) + { + // first reference to an address in this block. Assume initialized to 0. value = 0; - } - else { + } + else + { value = blockTable[block][offset]; - } - return value; - } - - //////////////////////////////////////////////////////////////////////////////// - // - // Helper method to fetch 4 byte value from table that represents MIPS memory. - // Originally used just for data segment, but now also used for stack. - // Both use different tables but same storage method and same table size - // and block size. Assumes word alignment, no endian processing. - // - // This differs from "fetchWordFromTable()" in that it returns an Integer and - // returns null instead of 0 if the 4K table has not been allocated. Developed - // by Greg Gibeling of UC Berkeley, fall 2007. - // - - private synchronized Integer fetchWordOrNullFromTable(int[][] blockTable, int relative) { - int value = 0; - int block, offset; - block = relative / BLOCK_LENGTH_WORDS; - offset = relative % BLOCK_LENGTH_WORDS; - if (blockTable[block] == null) { - // first reference to an address in this block. Assume initialized to 0. + } + return value; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Helper method to fetch 4 byte value from table that represents MIPS memory. + // Originally used just for data segment, but now also used for stack. + // Both use different tables but same storage method and same table size + // and block size. Assumes word alignment, no endian processing. + // + + private synchronized Integer fetchWordOrNullFromTable(int[][] blockTable, int relative) + { + int value = 0; + int block, offset; + block = relative / BLOCK_LENGTH_WORDS; + offset = relative % BLOCK_LENGTH_WORDS; + if (blockTable[block] == null) + { + // first reference to an address in this block. Assume initialized to 0. return null; - } - else { + } + else + { value = blockTable[block][offset]; - } - return value; - } - - //////////////////////////////////////////////////////////////////////////////////// - // Returns result of substituting specified byte of source value into specified byte - // of destination value. Byte positions are 0-1-2-3, listed from most to least - // significant. No endian issues. This is a private helper method used by get() & set(). - private int replaceByte(int sourceValue, int bytePosInSource, int destValue, int bytePosInDest) { - return + } + return value; + } + + //////////////////////////////////////////////////////////////////////////////// + // + // Helper method to fetch 4 byte value from table that represents MIPS memory. + // Originally used just for data segment, but now also used for stack. + // Both use different tables but same storage method and same table size + // and block size. Assumes word alignment, no endian processing. + // + // This differs from "fetchWordFromTable()" in that it returns an Integer and + // returns null instead of 0 if the 4K table has not been allocated. Developed + // by Greg Gibeling of UC Berkeley, fall 2007. + // + + //////////////////////////////////////////////////////////////////////////////////// + // Returns result of substituting specified byte of source value into specified byte + // of destination value. Byte positions are 0-1-2-3, listed from most to least + // significant. No endian issues. This is a private helper method used by get() & set(). + private int replaceByte(int sourceValue, int bytePosInSource, int destValue, int bytePosInDest) + { + return // Set source byte value into destination byte position; set other 24 bits to 0's... - ((sourceValue >> (24 - (bytePosInSource << 3)) & 0xFF) - << (24 - (bytePosInDest << 3))) - // and bitwise-OR it with... - | - // Set 8 bits in destination byte position to 0's, other 24 bits are unchanged. - (destValue & ~(0xFF << (24 - (bytePosInDest << 3)))); - } - - /////////////////////////////////////////////////////////////////////// - // Reverses byte sequence of given value. Can use to convert between big and - // little endian if needed. - private int reverseBytes(int source) { - return (source >> 24 & 0x000000FF) | - (source >> 8 & 0x0000FF00) | - (source << 8 & 0x00FF0000) | - (source << 24); - } - - /////////////////////////////////////////////////////////////////////// - // Store a program statement at the given address. Address has already been verified - // as valid. It may be either in user or kernel text segment, as specified by arguments. - private void storeProgramStatement(int address, ProgramStatement statement, - int baseAddress, ProgramStatement[][] blockTable) { - int relative = (address - baseAddress) >> 2; // convert byte address to words - int block = relative / BLOCK_LENGTH_WORDS; - int offset = relative % BLOCK_LENGTH_WORDS; - if (block < TEXT_BLOCK_TABLE_LENGTH) { - if (blockTable[block] == null) { - // No instructions are stored in this block, so allocate the block. - blockTable[block] = new ProgramStatement[BLOCK_LENGTH_WORDS]; + ((sourceValue >> (24 - (bytePosInSource << 3)) & 0xFF) + << (24 - (bytePosInDest << 3))) + // and bitwise-OR it with... + | + // Set 8 bits in destination byte position to 0's, other 24 bits are unchanged. + (destValue & ~(0xFF << (24 - (bytePosInDest << 3)))); + } + + /////////////////////////////////////////////////////////////////////// + // Reverses byte sequence of given value. Can use to convert between big and + // little endian if needed. + private int reverseBytes(int source) + { + return (source >> 24 & 0x000000FF) | + (source >> 8 & 0x0000FF00) | + (source << 8 & 0x00FF0000) | + (source << 24); + } + + /////////////////////////////////////////////////////////////////////// + // Store a program statement at the given address. Address has already been verified + // as valid. It may be either in user or kernel text segment, as specified by arguments. + private void storeProgramStatement(int address, ProgramStatement statement, + int baseAddress, ProgramStatement[][] blockTable) + { + int relative = (address - baseAddress) >> 2; // convert byte address to words + int block = relative / BLOCK_LENGTH_WORDS; + int offset = relative % BLOCK_LENGTH_WORDS; + if (block < TEXT_BLOCK_TABLE_LENGTH) + { + if (blockTable[block] == null) + { + // No instructions are stored in this block, so allocate the block. + blockTable[block] = new ProgramStatement[BLOCK_LENGTH_WORDS]; } blockTable[block][offset] = statement; - } - } - - - /////////////////////////////////////////////////////////////////////// - // Read a program statement from the given address. Address has already been verified - // as valid. It may be either in user or kernel text segment, as specified by arguments. - // Returns associated ProgramStatement or null if none. - // Last parameter controls whether or not observers will be notified. - private ProgramStatement readProgramStatement(int address, int baseAddress, ProgramStatement[][] blockTable, boolean notify) { - int relative = (address - baseAddress) >> 2; // convert byte address to words - int block = relative / TEXT_BLOCK_LENGTH_WORDS; - int offset = relative % TEXT_BLOCK_LENGTH_WORDS; - if (block < TEXT_BLOCK_TABLE_LENGTH) { - if (blockTable[block] == null || blockTable[block][offset] == null) { - // No instructions are stored in this block or offset. - if (notify) notifyAnyObservers(AccessNotice.READ, address, Instruction.INSTRUCTION_LENGTH,0); - return null; - } - else { - if (notify) notifyAnyObservers(AccessNotice.READ, address, Instruction.INSTRUCTION_LENGTH, blockTable[block][offset].getBinaryStatement()); - return blockTable[block][offset]; + } + } + + /////////////////////////////////////////////////////////////////////// + // Read a program statement from the given address. Address has already been verified + // as valid. It may be either in user or kernel text segment, as specified by arguments. + // Returns associated ProgramStatement or null if none. + // Last parameter controls whether or not observers will be notified. + private ProgramStatement readProgramStatement(int address, int baseAddress, ProgramStatement[][] blockTable, boolean notify) + { + int relative = (address - baseAddress) >> 2; // convert byte address to words + int block = relative / TEXT_BLOCK_LENGTH_WORDS; + int offset = relative % TEXT_BLOCK_LENGTH_WORDS; + if (block < TEXT_BLOCK_TABLE_LENGTH) + { + if (blockTable[block] == null || blockTable[block][offset] == null) + { + // No instructions are stored in this block or offset. + if (notify) + { + notifyAnyObservers(AccessNotice.READ, address, Instruction.INSTRUCTION_LENGTH, 0); + } + return null; } - } - if (notify) notifyAnyObservers(AccessNotice.READ, address, Instruction.INSTRUCTION_LENGTH,0); - return null; - } - - } + else + { + if (notify) + { + notifyAnyObservers(AccessNotice.READ, address, Instruction.INSTRUCTION_LENGTH, blockTable[block][offset].getBinaryStatement()); + } + return blockTable[block][offset]; + } + } + if (notify) + { + notifyAnyObservers(AccessNotice.READ, address, Instruction.INSTRUCTION_LENGTH, 0); + } + return null; + } + + ///////////////////////////////////////////////////////////////////////// + // Private class whose objects will represent an observable-observer pair + // for a given memory address or range. + private class MemoryObservable extends Observable implements Comparable + { + private final int lowAddress; + + private final int highAddress; + + public MemoryObservable(Observer obs, int startAddr, int endAddr) + { + lowAddress = startAddr; + highAddress = endAddr; + this.addObserver(obs); + } + + public boolean match(int address) + { + return (address >= lowAddress && address <= highAddress - 1 + WORD_LENGTH_BYTES); + } + + public void notifyObserver(MemoryAccessNotice notice) + { + this.setChanged(); + this.notifyObservers(notice); + } + + // Useful to have for future refactoring, if it actually becomes worthwhile to sort + // these or put 'em in a tree (rather than sequential search through list). + public int compareTo(Object obj) + { + if (!(obj instanceof MemoryObservable)) + { + throw new ClassCastException(); + } + MemoryObservable mo = (MemoryObservable) obj; + if (this.lowAddress < mo.lowAddress || this.lowAddress == mo.lowAddress && this.highAddress < mo.highAddress) + { + return -1; + } + if (this.lowAddress > mo.lowAddress || this.lowAddress == mo.lowAddress && this.highAddress > mo.highAddress) + { + return -1; + } + return 0; // they have to be equal at this point. + } + } + +} diff --git a/src/main/java/mars/mips/hardware/MemoryAccessNotice.java b/src/main/java/mars/mips/hardware/MemoryAccessNotice.java index 13e6cc5..9051937 100644 --- a/src/main/java/mars/mips/hardware/MemoryAccessNotice.java +++ b/src/main/java/mars/mips/hardware/MemoryAccessNotice.java @@ -29,51 +29,65 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Object provided to Observers of runtime access to MIPS memory. - * Observer can get the access type (R/W), address and length in bytes (4,2,1). - * - * @author Pete Sanderson + * Object provided to Observers of runtime access to MIPS memory. Observer can get the access type (R/W), address and + * length in bytes (4,2,1). + * + * @author Pete Sanderson * @version July 2005 */ - -public class MemoryAccessNotice extends AccessNotice { - private int address; - private int length; - private int value; - /** Constructor will be called only within this package, so assume - * address and length are in valid ranges. - */ - MemoryAccessNotice(int type, int address, int length, int value) { - super(type); - this.address = address; - this.length = length; - this.value = value; - } - /** Constructor will be called only within this package, so assume - * address is in valid range. - */ - public MemoryAccessNotice(int type, int address, int value) { - super(type); - this.address = address; - this.length = Memory.WORD_LENGTH_BYTES; - this.value = value; - } - /** Fetch the memory address that was accessed. */ - public int getAddress() { - return address; - } - /** Fetch the length in bytes of the access operation (4,2,1). */ - public int getLength() { - return length; - } - /** Fetch the value of the access operation (the value read or written). */ - public int getValue() { - return value; - } - /** String representation indicates access type, address and length in bytes */ - public String toString() { - return ((this.getAccessType()==AccessNotice.READ) ? "R " : "W ") + - "Mem " + address + " " + length + "B = "+value; - } -} \ No newline at end of file +public class MemoryAccessNotice extends AccessNotice +{ + private final int address; + + private final int length; + + private final int value; + + /** + * Constructor will be called only within this package, so assume address and length are in valid ranges. + */ + MemoryAccessNotice(int type, int address, int length, int value) + { + super(type); + this.address = address; + this.length = length; + this.value = value; + } + + /** + * Constructor will be called only within this package, so assume address is in valid range. + */ + public MemoryAccessNotice(int type, int address, int value) + { + super(type); + this.address = address; + this.length = Memory.WORD_LENGTH_BYTES; + this.value = value; + } + + /** Fetch the memory address that was accessed. */ + public int getAddress() + { + return address; + } + + /** Fetch the length in bytes of the access operation (4,2,1). */ + public int getLength() + { + return length; + } + + /** Fetch the value of the access operation (the value read or written). */ + public int getValue() + { + return value; + } + + /** String representation indicates access type, address and length in bytes */ + public String toString() + { + return ((this.getAccessType() == AccessNotice.READ) ? "R " : "W ") + + "Mem " + address + " " + length + "B = " + value; + } +} diff --git a/src/main/java/mars/mips/hardware/MemoryConfiguration.java b/src/main/java/mars/mips/hardware/MemoryConfiguration.java index b942702..15d5085 100644 --- a/src/main/java/mars/mips/hardware/MemoryConfiguration.java +++ b/src/main/java/mars/mips/hardware/MemoryConfiguration.java @@ -1,4 +1,4 @@ - package mars.mips.hardware; +package mars.mips.hardware; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -29,129 +29,158 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Models the memory configuration for the simulated MIPS machine. - * "configuration" refers to the starting memory addresses for - * the various memory segments. - * The default configuration is based on SPIM. Starting with MARS 3.7, - * the configuration can be changed. - * - * @author Pete Sanderson + * Models the memory configuration for the simulated MIPS machine. "configuration" refers to the starting memory + * addresses for the various memory segments. The default configuration is based on SPIM. Starting with MARS 3.7, the + * configuration can be changed. + * + * @author Pete Sanderson * @version August 2009 */ - public class MemoryConfiguration { - // Identifier is used for saving setting; name is used for display - private String configurationIdentifier, configurationName; - private String[] configurationItemNames; - private int[] configurationItemValues; - - - public MemoryConfiguration(String ident, String name, String[] items, int[] values) { - this.configurationIdentifier = ident; - this.configurationName = name; - this.configurationItemNames = items; - this.configurationItemValues = values; - } - - public String getConfigurationIdentifier() { - return configurationIdentifier; - } - - public String getConfigurationName() { - return configurationName; - } - - public int[] getConfigurationItemValues() { - return configurationItemValues; - } - - public String[] getConfigurationItemNames() { - return configurationItemNames; - } - - public int getTextBaseAddress() { - return configurationItemValues[0]; - } - - public int getDataSegmentBaseAddress() { - return configurationItemValues[1]; - } - - public int getExternBaseAddress() { - return configurationItemValues[2]; - } - - public int getGlobalPointer() { - return configurationItemValues[3]; - } - - public int getDataBaseAddress() { - return configurationItemValues[4]; - } - - public int getHeapBaseAddress() { - return configurationItemValues[5]; - } - - public int getStackPointer() { - return configurationItemValues[6]; - } - - public int getStackBaseAddress() { - return configurationItemValues[7]; - } - - public int getUserHighAddress() { - return configurationItemValues[8]; - } - - public int getKernelBaseAddress() { - return configurationItemValues[9]; - } - - public int getKernelTextBaseAddress() { - return configurationItemValues[10]; - } - - public int getExceptionHandlerAddress() { - return configurationItemValues[11]; - } - - public int getKernelDataBaseAddress() { - return configurationItemValues[12]; - } - - public int getMemoryMapBaseAddress() { - return configurationItemValues[13]; - } - - public int getKernelHighAddress () { - return configurationItemValues[14]; - } - - public int getDataSegmentLimitAddress() { - return configurationItemValues[15]; - } - - public int getTextLimitAddress() { - return configurationItemValues[16]; - } - - public int getKernelDataSegmentLimitAddress() { - return configurationItemValues[17]; - } - - public int getKernelTextLimitAddress() { - return configurationItemValues[18]; - } - - public int getStackLimitAddress() { - return configurationItemValues[19]; - } - - public int getMemoryMapLimitAddress() { - return configurationItemValues[20]; - } - - } \ No newline at end of file +public class MemoryConfiguration +{ + // Identifier is used for saving setting; name is used for display + private final String configurationIdentifier; + + private final String configurationName; + + private final String[] configurationItemNames; + + private final int[] configurationItemValues; + + + public MemoryConfiguration(String ident, String name, String[] items, int[] values) + { + this.configurationIdentifier = ident; + this.configurationName = name; + this.configurationItemNames = items; + this.configurationItemValues = values; + } + + public String getConfigurationIdentifier() + { + return configurationIdentifier; + } + + public String getConfigurationName() + { + return configurationName; + } + + public int[] getConfigurationItemValues() + { + return configurationItemValues; + } + + public String[] getConfigurationItemNames() + { + return configurationItemNames; + } + + public int getTextBaseAddress() + { + return configurationItemValues[0]; + } + + public int getDataSegmentBaseAddress() + { + return configurationItemValues[1]; + } + + public int getExternBaseAddress() + { + return configurationItemValues[2]; + } + + public int getGlobalPointer() + { + return configurationItemValues[3]; + } + + public int getDataBaseAddress() + { + return configurationItemValues[4]; + } + + public int getHeapBaseAddress() + { + return configurationItemValues[5]; + } + + public int getStackPointer() + { + return configurationItemValues[6]; + } + + public int getStackBaseAddress() + { + return configurationItemValues[7]; + } + + public int getUserHighAddress() + { + return configurationItemValues[8]; + } + + public int getKernelBaseAddress() + { + return configurationItemValues[9]; + } + + public int getKernelTextBaseAddress() + { + return configurationItemValues[10]; + } + + public int getExceptionHandlerAddress() + { + return configurationItemValues[11]; + } + + public int getKernelDataBaseAddress() + { + return configurationItemValues[12]; + } + + public int getMemoryMapBaseAddress() + { + return configurationItemValues[13]; + } + + public int getKernelHighAddress() + { + return configurationItemValues[14]; + } + + public int getDataSegmentLimitAddress() + { + return configurationItemValues[15]; + } + + public int getTextLimitAddress() + { + return configurationItemValues[16]; + } + + public int getKernelDataSegmentLimitAddress() + { + return configurationItemValues[17]; + } + + public int getKernelTextLimitAddress() + { + return configurationItemValues[18]; + } + + public int getStackLimitAddress() + { + return configurationItemValues[19]; + } + + public int getMemoryMapLimitAddress() + { + return configurationItemValues[20]; + } + +} diff --git a/src/main/java/mars/mips/hardware/MemoryConfigurations.java b/src/main/java/mars/mips/hardware/MemoryConfigurations.java index d16f11f..ccf8bb1 100644 --- a/src/main/java/mars/mips/hardware/MemoryConfigurations.java +++ b/src/main/java/mars/mips/hardware/MemoryConfigurations.java @@ -1,6 +1,9 @@ - package mars.mips.hardware; - import mars.Globals; - import java.util.*; +package mars.mips.hardware; + +import mars.Globals; + +import java.util.ArrayList; +import java.util.Iterator; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -31,191 +34,208 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Models the collection of MIPS memory configurations. - * The default configuration is based on SPIM. Starting with MARS 3.7, - * the configuration can be changed. - * - * @author Pete Sanderson + * Models the collection of MIPS memory configurations. The default configuration is based on SPIM. Starting with MARS + * 3.7, the configuration can be changed. + * + * @author Pete Sanderson * @version August 2009 */ - public class MemoryConfigurations { - - private static ArrayList configurations = null; - private static MemoryConfiguration defaultConfiguration; - private static MemoryConfiguration currentConfiguration; - - // Be careful, these arrays are parallel and position-sensitive. - // The getters in this and in MemoryConfiguration depend on this - // sequence. Should be refactored... The order comes from the - // original listed order in Memory.java, where most of these were - // "final" until Mars 3.7 and changeable memory configurations. - private static final String[] configurationItemNames = { - ".text base address", - "data segment base address", - ".extern base address", - "global pointer $gp", - ".data base address", - "heap base address", - "stack pointer $sp", - "stack base address", - "user space high address", - "kernel space base address", - ".ktext base address", - "exception handler address", - ".kdata base address", - "MMIO base address", - "kernel space high address", - "data segment limit address", - "text limit address", - "kernel data segment limit address", - "kernel text limit address", - "stack limit address", - "memory map limit address" - }; - - // Default configuration comes from SPIM - private static int[] defaultConfigurationItemValues = { - 0x00400000, // .text Base Address - 0x10000000, // Data Segment base address - 0x10000000, // .extern Base Address - 0x10008000, // Global Pointer $gp) - 0x10010000, // .data base Address - 0x10040000, // heap base address - 0x7fffeffc, // stack pointer $sp (from SPIM not MIPS) - 0x7ffffffc, // stack base address - 0x7fffffff, // highest address in user space - 0x80000000, // lowest address in kernel space - 0x80000000, // .ktext base address - 0x80000180, // exception handler address - 0x90000000, // .kdata base address - 0xffff0000, // MMIO base address - 0xffffffff, // highest address in kernel (and memory) - 0x7fffffff, // data segment limit address - 0x0ffffffc, // text limit address - 0xfffeffff, // kernel data segment limit address - 0x8ffffffc, // kernel text limit address - 0x10040000, // stack limit address - 0xffffffff // memory map limit address - }; - - // Compact allows 16 bit addressing, data segment starts at 0 - private static int[] dataBasedCompactConfigurationItemValues = { - 0x00003000, // .text Base Address - 0x00000000, // Data Segment base address - 0x00001000, // .extern Base Address - 0x00001800, // Global Pointer $gp) - 0x00000000, // .data base Address - 0x00002000, // heap base address - 0x00002ffc, // stack pointer $sp - 0x00002ffc, // stack base address - 0x00003fff, // highest address in user space - 0x00004000, // lowest address in kernel space - 0x00004000, // .ktext base address - 0x00004180, // exception handler address - 0x00005000, // .kdata base address - 0x00007f00, // MMIO base address - 0x00007fff, // highest address in kernel (and memory) - 0x00002fff, // data segment limit address - 0x00003ffc, // text limit address - 0x00007eff, // kernel data segment limit address - 0x00004ffc, // kernel text limit address - 0x00002000, // stack limit address - 0x00007fff // memory map limit address - }; - - // Compact allows 16 bit addressing, text segment starts at 0 - private static int[] textBasedCompactConfigurationItemValues = { - 0x00000000, // .text Base Address - 0x00001000, // Data Segment base address - 0x00001000, // .extern Base Address - 0x00001800, // Global Pointer $gp) - 0x00002000, // .data base Address - 0x00003000, // heap base address - 0x00003ffc, // stack pointer $sp - 0x00003ffc, // stack base address - 0x00003fff, // highest address in user space - 0x00004000, // lowest address in kernel space - 0x00004000, // .ktext base address - 0x00004180, // exception handler address - 0x00005000, // .kdata base address - 0x00007f00, // MMIO base address - 0x00007fff, // highest address in kernel (and memory) - 0x00003fff, // data segment limit address - 0x00000ffc, // text limit address - 0x00007eff, // kernel data segment limit address - 0x00004ffc, // kernel text limit address - 0x00003000, // stack limit address - 0x00007fff // memory map limit address - }; - - - - public MemoryConfigurations() { - - } - - - public static void buildConfigurationCollection() { - if (configurations == null) { +public class MemoryConfigurations +{ + + // Be careful, these arrays are parallel and position-sensitive. + // The getters in this and in MemoryConfiguration depend on this + // sequence. Should be refactored... The order comes from the + // original listed order in Memory.java, where most of these were + // "final" until Mars 3.7 and changeable memory configurations. + private static final String[] configurationItemNames = { + ".text base address", + "data segment base address", + ".extern base address", + "global pointer $gp", + ".data base address", + "heap base address", + "stack pointer $sp", + "stack base address", + "user space high address", + "kernel space base address", + ".ktext base address", + "exception handler address", + ".kdata base address", + "MMIO base address", + "kernel space high address", + "data segment limit address", + "text limit address", + "kernel data segment limit address", + "kernel text limit address", + "stack limit address", + "memory map limit address" + }; + + private static ArrayList configurations = null; + + private static MemoryConfiguration defaultConfiguration; + + private static MemoryConfiguration currentConfiguration; + + // Default configuration comes from SPIM + private static final int[] defaultConfigurationItemValues = { + 0x00400000, // .text Base Address + 0x10000000, // Data Segment base address + 0x10000000, // .extern Base Address + 0x10008000, // Global Pointer $gp) + 0x10010000, // .data base Address + 0x10040000, // heap base address + 0x7fffeffc, // stack pointer $sp (from SPIM not MIPS) + 0x7ffffffc, // stack base address + 0x7fffffff, // highest address in user space + 0x80000000, // lowest address in kernel space + 0x80000000, // .ktext base address + 0x80000180, // exception handler address + 0x90000000, // .kdata base address + 0xffff0000, // MMIO base address + 0xffffffff, // highest address in kernel (and memory) + 0x7fffffff, // data segment limit address + 0x0ffffffc, // text limit address + 0xfffeffff, // kernel data segment limit address + 0x8ffffffc, // kernel text limit address + 0x10040000, // stack limit address + 0xffffffff // memory map limit address + }; + + // Compact allows 16 bit addressing, data segment starts at 0 + private static final int[] dataBasedCompactConfigurationItemValues = { + 0x00003000, // .text Base Address + 0x00000000, // Data Segment base address + 0x00001000, // .extern Base Address + 0x00001800, // Global Pointer $gp) + 0x00000000, // .data base Address + 0x00002000, // heap base address + 0x00002ffc, // stack pointer $sp + 0x00002ffc, // stack base address + 0x00003fff, // highest address in user space + 0x00004000, // lowest address in kernel space + 0x00004000, // .ktext base address + 0x00004180, // exception handler address + 0x00005000, // .kdata base address + 0x00007f00, // MMIO base address + 0x00007fff, // highest address in kernel (and memory) + 0x00002fff, // data segment limit address + 0x00003ffc, // text limit address + 0x00007eff, // kernel data segment limit address + 0x00004ffc, // kernel text limit address + 0x00002000, // stack limit address + 0x00007fff // memory map limit address + }; + + // Compact allows 16 bit addressing, text segment starts at 0 + private static final int[] textBasedCompactConfigurationItemValues = { + 0x00000000, // .text Base Address + 0x00001000, // Data Segment base address + 0x00001000, // .extern Base Address + 0x00001800, // Global Pointer $gp) + 0x00002000, // .data base Address + 0x00003000, // heap base address + 0x00003ffc, // stack pointer $sp + 0x00003ffc, // stack base address + 0x00003fff, // highest address in user space + 0x00004000, // lowest address in kernel space + 0x00004000, // .ktext base address + 0x00004180, // exception handler address + 0x00005000, // .kdata base address + 0x00007f00, // MMIO base address + 0x00007fff, // highest address in kernel (and memory) + 0x00003fff, // data segment limit address + 0x00000ffc, // text limit address + 0x00007eff, // kernel data segment limit address + 0x00004ffc, // kernel text limit address + 0x00003000, // stack limit address + 0x00007fff // memory map limit address + }; + + + public MemoryConfigurations() + { + + } + + + public static void buildConfigurationCollection() + { + if (configurations == null) + { configurations = new ArrayList(); configurations.add(new MemoryConfiguration("Default", "Default", configurationItemNames, defaultConfigurationItemValues)); configurations.add(new MemoryConfiguration("CompactDataAtZero", "Compact, Data at Address 0", configurationItemNames, dataBasedCompactConfigurationItemValues)); configurations.add(new MemoryConfiguration("CompactTextAtZero", "Compact, Text at Address 0", configurationItemNames, textBasedCompactConfigurationItemValues)); defaultConfiguration = (MemoryConfiguration) configurations.get(0); currentConfiguration = defaultConfiguration; - // Get current config from settings - //String currentConfigurationIdentifier = Globals.getSettings().getMemoryConfiguration(); + // Get current config from settings + //String currentConfigurationIdentifier = Globals.getSettings().getMemoryConfiguration(); setCurrentConfiguration(getConfigurationByName(Globals.getSettings().getMemoryConfiguration())); - // Iterator configurationsIterator = getConfigurationsIterator(); - // while (configurationsIterator.hasNext()) { - // MemoryConfiguration config = (MemoryConfiguration)configurationsIterator.next(); - // if (currentConfigurationIdentifier.equals(config.getConfigurationIdentifier())) { - // setCurrentConfiguration(config); - // } - // } - } - } - - public static Iterator getConfigurationsIterator() { - if (configurations == null) { + // Iterator configurationsIterator = getConfigurationsIterator(); + // while (configurationsIterator.hasNext()) { + // MemoryConfiguration config = (MemoryConfiguration)configurationsIterator.next(); + // if (currentConfigurationIdentifier.equals(config.getConfigurationIdentifier())) { + // setCurrentConfiguration(config); + // } + // } + } + } + + public static Iterator getConfigurationsIterator() + { + if (configurations == null) + { buildConfigurationCollection(); - } - return configurations.iterator(); - - } - - public static MemoryConfiguration getConfigurationByName(String name) { - Iterator configurationsIterator = getConfigurationsIterator(); - while (configurationsIterator.hasNext()) { - MemoryConfiguration config = (MemoryConfiguration)configurationsIterator.next(); - if (name.equals(config.getConfigurationIdentifier())) { - return config; + } + return configurations.iterator(); + + } + + public static MemoryConfiguration getConfigurationByName(String name) + { + Iterator configurationsIterator = getConfigurationsIterator(); + while (configurationsIterator.hasNext()) + { + MemoryConfiguration config = (MemoryConfiguration) configurationsIterator.next(); + if (name.equals(config.getConfigurationIdentifier())) + { + return config; } - } - return null; - } - - - public static MemoryConfiguration getDefaultConfiguration() { - if (defaultConfiguration == null) { + } + return null; + } + + + public static MemoryConfiguration getDefaultConfiguration() + { + if (defaultConfiguration == null) + { buildConfigurationCollection(); - } - return defaultConfiguration; - } - - public static MemoryConfiguration getCurrentConfiguration() { - if (currentConfiguration == null) { + } + return defaultConfiguration; + } + + public static MemoryConfiguration getCurrentConfiguration() + { + if (currentConfiguration == null) + { buildConfigurationCollection(); - } - return currentConfiguration; - } - - public static boolean setCurrentConfiguration(MemoryConfiguration config) { - if (config == null) + } + return currentConfiguration; + } + + public static boolean setCurrentConfiguration(MemoryConfiguration config) + { + if (config == null) + { return false; - if (config != currentConfiguration) { + } + if (config != currentConfiguration) + { currentConfiguration = config; Globals.memory.clear(); RegisterFile.getUserRegister("$gp").changeResetValue(config.getGlobalPointer()); @@ -224,99 +244,120 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. RegisterFile.initializeProgramCounter(config.getTextBaseAddress()); RegisterFile.resetRegisters(); return true; - } - else { + } + else + { return false; - } - } - - - //// Use these to intialize Memory static variables at launch - - public static int getDefaultTextBaseAddress() { - return defaultConfigurationItemValues[0]; - } - - public static int getDefaultDataSegmentBaseAddress() { - return defaultConfigurationItemValues[1]; - } - - public static int getDefaultExternBaseAddress() { - return defaultConfigurationItemValues[2]; - } - - public static int getDefaultGlobalPointer() { - return defaultConfigurationItemValues[3]; - } - - public static int getDefaultDataBaseAddress() { - return defaultConfigurationItemValues[4]; - } - - public static int getDefaultHeapBaseAddress() { - return defaultConfigurationItemValues[5]; - } - - public static int getDefaultStackPointer() { - return defaultConfigurationItemValues[6]; - } - - public static int getDefaultStackBaseAddress() { - return defaultConfigurationItemValues[7]; - } - - public static int getDefaultUserHighAddress() { - return defaultConfigurationItemValues[8]; - } - - public static int getDefaultKernelBaseAddress() { - return defaultConfigurationItemValues[9]; - } - - public static int getDefaultKernelTextBaseAddress() { - return defaultConfigurationItemValues[10]; - } - - public static int getDefaultExceptionHandlerAddress() { - return defaultConfigurationItemValues[11]; - } - - public static int getDefaultKernelDataBaseAddress() { - return defaultConfigurationItemValues[12]; - } - - public static int getDefaultMemoryMapBaseAddress() { - return defaultConfigurationItemValues[13]; - } - - public static int getDefaultKernelHighAddress () { - return defaultConfigurationItemValues[14]; - } - - public int getDefaultDataSegmentLimitAddress() { - return defaultConfigurationItemValues[15]; - } - - public int getDefaultTextLimitAddress() { - return defaultConfigurationItemValues[16]; - } - - public int getDefaultKernelDataSegmentLimitAddress() { - return defaultConfigurationItemValues[17]; - } - - public int getDefaultKernelTextLimitAddress() { - return defaultConfigurationItemValues[18]; - } - - public int getDefaultStackLimitAddress() { - return defaultConfigurationItemValues[19]; - } - - public int getMemoryMapLimitAddress() { - return defaultConfigurationItemValues[20]; - } - - - - } \ No newline at end of file + } + } + + + //// Use these to intialize Memory static variables at launch + + public static int getDefaultTextBaseAddress() + { + return defaultConfigurationItemValues[0]; + } + + public static int getDefaultDataSegmentBaseAddress() + { + return defaultConfigurationItemValues[1]; + } + + public static int getDefaultExternBaseAddress() + { + return defaultConfigurationItemValues[2]; + } + + public static int getDefaultGlobalPointer() + { + return defaultConfigurationItemValues[3]; + } + + public static int getDefaultDataBaseAddress() + { + return defaultConfigurationItemValues[4]; + } + + public static int getDefaultHeapBaseAddress() + { + return defaultConfigurationItemValues[5]; + } + + public static int getDefaultStackPointer() + { + return defaultConfigurationItemValues[6]; + } + + public static int getDefaultStackBaseAddress() + { + return defaultConfigurationItemValues[7]; + } + + public static int getDefaultUserHighAddress() + { + return defaultConfigurationItemValues[8]; + } + + public static int getDefaultKernelBaseAddress() + { + return defaultConfigurationItemValues[9]; + } + + public static int getDefaultKernelTextBaseAddress() + { + return defaultConfigurationItemValues[10]; + } + + public static int getDefaultExceptionHandlerAddress() + { + return defaultConfigurationItemValues[11]; + } + + public static int getDefaultKernelDataBaseAddress() + { + return defaultConfigurationItemValues[12]; + } + + public static int getDefaultMemoryMapBaseAddress() + { + return defaultConfigurationItemValues[13]; + } + + public static int getDefaultKernelHighAddress() + { + return defaultConfigurationItemValues[14]; + } + + public int getDefaultDataSegmentLimitAddress() + { + return defaultConfigurationItemValues[15]; + } + + public int getDefaultTextLimitAddress() + { + return defaultConfigurationItemValues[16]; + } + + public int getDefaultKernelDataSegmentLimitAddress() + { + return defaultConfigurationItemValues[17]; + } + + public int getDefaultKernelTextLimitAddress() + { + return defaultConfigurationItemValues[18]; + } + + public int getDefaultStackLimitAddress() + { + return defaultConfigurationItemValues[19]; + } + + public int getMemoryMapLimitAddress() + { + return defaultConfigurationItemValues[20]; + } + + +} diff --git a/src/main/java/mars/mips/hardware/Register.java b/src/main/java/mars/mips/hardware/Register.java index 0f38707..80588ba 100644 --- a/src/main/java/mars/mips/hardware/Register.java +++ b/src/main/java/mars/mips/hardware/Register.java @@ -1,6 +1,6 @@ - package mars.mips.hardware; - import mars.*; - import java.util.*; +package mars.mips.hardware; + +import java.util.Observable; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -30,125 +30,145 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - /** - * Abstraction to represent a register of a MIPS Assembler. - * @author Jason Bumgarner, Jason Shrewsbury, Ben Sherman - * @version June 2003 - **/ - - public class Register extends Observable { - private String name; - private int number, resetValue; - // volatile should be enough to allow safe multi-threaded access - // w/o the use of synchronized methods. getValue and setValue - // are the only methods here used by the register collection - // (RegisterFile, Coprocessor0, Coprocessor1) methods. - private volatile int value; - - /** - * Creates a new register with specified name, number, and value. - * @param n The name of the register. - * @param num The number of the register. - * @param val The inital (and reset) value of the register. - */ - - public Register(String n, int num, int val){ - name= n; - number=num; - value= val; - resetValue = val; - } - - /** - * Returns the name of the Register. - * @return name The name of the Register. - */ - - public String getName(){ - return name; - } - - /** - * Returns the value of the Register. Observers are notified - * of the READ operation. - * @return value The value of the Register. - */ - - public synchronized int getValue(){ - notifyAnyObservers(AccessNotice.READ); - return value; - } +/** + * Abstraction to represent a register of a MIPS Assembler. + * + * @author Jason Bumgarner, Jason Shrewsbury, Ben Sherman + * @version June 2003 + **/ - - /** - * Returns the value of the Register. Observers are not notified. - * Added for release 3.8. - * @return value The value of the Register. - */ - - public synchronized int getValueNoNotify(){ - return value; - } - - - /** - * Returns the reset value of the Register. - * @return The reset (initial) value of the Register. - */ - - public int getResetValue(){ - return resetValue; - } - /** - * Returns the number of the Register. - * @return number The number of the Register. - */ - - public int getNumber(){ - return number; - } - - /** - * Sets the value of the register to the val passed to it. - * Observers are notified of the WRITE operation. - * @param val Value to set the Register to. - * @return previous value of register - */ - - public synchronized int setValue(int val){ - int old = value; - value = val; - notifyAnyObservers(AccessNotice.WRITE); - return old; - } - - /** - * Resets the value of the register to the value it was constructed with. - * Observers are not notified. - */ - - public synchronized void resetValue(){ - value = resetValue; - } - - /** - * Change the register's reset value; the value to which it will be - * set when resetValue() is called. - */ - - public synchronized void changeResetValue(int reset) { - resetValue = reset; - } - - // - // Method to notify any observers of register operation that has just occurred. - // - private void notifyAnyObservers(int type) { - if (this.countObservers() > 0){// && Globals.program != null) && Globals.program.inSteppedExecution()) { +public class Register extends Observable +{ + private final String name; + + private final int number; + + private int resetValue; + + // volatile should be enough to allow safe multi-threaded access + // w/o the use of synchronized methods. getValue and setValue + // are the only methods here used by the register collection + // (RegisterFile, Coprocessor0, Coprocessor1) methods. + private volatile int value; + + /** + * Creates a new register with specified name, number, and value. + * + * @param n The name of the register. + * @param num The number of the register. + * @param val The inital (and reset) value of the register. + */ + + public Register(String n, int num, int val) + { + name = n; + number = num; + value = val; + resetValue = val; + } + + /** + * Returns the name of the Register. + * + * @return name The name of the Register. + */ + + public String getName() + { + return name; + } + + /** + * Returns the value of the Register. Observers are notified of the READ operation. + * + * @return value The value of the Register. + */ + + public synchronized int getValue() + { + notifyAnyObservers(AccessNotice.READ); + return value; + } + + + /** + * Returns the value of the Register. Observers are not notified. Added for release 3.8. + * + * @return value The value of the Register. + */ + + public synchronized int getValueNoNotify() + { + return value; + } + + + /** + * Returns the reset value of the Register. + * + * @return The reset (initial) value of the Register. + */ + + public int getResetValue() + { + return resetValue; + } + + /** + * Returns the number of the Register. + * + * @return number The number of the Register. + */ + + public int getNumber() + { + return number; + } + + /** + * Sets the value of the register to the val passed to it. Observers are notified of the WRITE operation. + * + * @param val Value to set the Register to. + * @return previous value of register + */ + + public synchronized int setValue(int val) + { + int old = value; + value = val; + notifyAnyObservers(AccessNotice.WRITE); + return old; + } + + /** + * Resets the value of the register to the value it was constructed with. Observers are not notified. + */ + + public synchronized void resetValue() + { + value = resetValue; + } + + /** + * Change the register's reset value; the value to which it will be set when resetValue() is called. + */ + + public synchronized void changeResetValue(int reset) + { + resetValue = reset; + } + + // + // Method to notify any observers of register operation that has just occurred. + // + private void notifyAnyObservers(int type) + { + if (this.countObservers() > 0) + {// && Globals.program != null) && Globals.program.inSteppedExecution()) { this.setChanged(); this.notifyObservers(new RegisterAccessNotice(type, this.name)); - } - } - - - } + } + } + + +} diff --git a/src/main/java/mars/mips/hardware/RegisterAccessNotice.java b/src/main/java/mars/mips/hardware/RegisterAccessNotice.java index ff8f4c6..15b6225 100644 --- a/src/main/java/mars/mips/hardware/RegisterAccessNotice.java +++ b/src/main/java/mars/mips/hardware/RegisterAccessNotice.java @@ -29,31 +29,37 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Object provided to Observers of runtime access to MIPS register. - * Observer can get the access type (R/W) and register number. - * - * @author Pete Sanderson + * Object provided to Observers of runtime access to MIPS register. Observer can get the access type (R/W) and register + * number. + * + * @author Pete Sanderson * @version July 2005 */ -public class RegisterAccessNotice extends AccessNotice { - private String registerName; +public class RegisterAccessNotice extends AccessNotice +{ + private final String registerName; - /** Constructor will be called only within this package, so assume - * register number is in valid range. - */ - RegisterAccessNotice(int type, String registerName) { - super(type); - this.registerName = registerName; - } - /** Fetch the register number of register accessed. */ - public String getRegisterName() { - return registerName; - } - /** String representation indicates access type and which register */ - public String toString() { - return ((this.getAccessType()==AccessNotice.READ) ? "R " : "W ") + - "Reg " + registerName; - } - -} \ No newline at end of file + /** + * Constructor will be called only within this package, so assume register number is in valid range. + */ + RegisterAccessNotice(int type, String registerName) + { + super(type); + this.registerName = registerName; + } + + /** Fetch the register number of register accessed. */ + public String getRegisterName() + { + return registerName; + } + + /** String representation indicates access type and which register */ + public String toString() + { + return ((this.getAccessType() == AccessNotice.READ) ? "R " : "W ") + + "Reg " + registerName; + } + +} diff --git a/src/main/java/mars/mips/hardware/RegisterFile.java b/src/main/java/mars/mips/hardware/RegisterFile.java index cf2f837..54bf135 100644 --- a/src/main/java/mars/mips/hardware/RegisterFile.java +++ b/src/main/java/mars/mips/hardware/RegisterFile.java @@ -1,11 +1,11 @@ - package mars.mips.hardware; +package mars.mips.hardware; - import java.util.Observer; +import mars.Globals; +import mars.assembler.SymbolTable; +import mars.mips.instructions.Instruction; +import mars.util.Binary; - import mars.Globals; - import mars.assembler.SymbolTable; - import mars.mips.instructions.Instruction; - import mars.util.Binary; +import java.util.Observer; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -36,304 +36,360 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Represents the collection of MIPS registers. - * @author Jason Bumgarner, Jason Shrewsbury - * @version June 2003 - **/ + * Represents the collection of MIPS registers. + * + * @author Jason Bumgarner, Jason Shrewsbury + * @version June 2003 + **/ - public class RegisterFile { - - public static final int GLOBAL_POINTER_REGISTER = 28; - public static final int STACK_POINTER_REGISTER = 29; - - private static Register [] regFile = - { new Register("$zero", 0, 0), new Register("$at", 1, 0), - new Register("$v0", 2, 0),new Register("$v1", 3, 0), - new Register("$a0", 4, 0),new Register("$a1", 5, 0), - new Register("$a2", 6, 0),new Register("$a3", 7, 0), - new Register("$t0", 8, 0),new Register("$t1", 9, 0), - new Register("$t2", 10, 0),new Register("$t3", 11, 0), - new Register("$t4", 12, 0),new Register("$t5", 13, 0), - new Register("$t6", 14, 0),new Register("$t7", 15, 0), - new Register("$s0", 16, 0),new Register("$s1", 17, 0), - new Register("$s2", 18, 0),new Register("$s3", 19, 0), - new Register("$s4", 20, 0),new Register("$s5", 21, 0), - new Register("$s6", 22, 0),new Register("$s7", 23, 0), - new Register("$t8", 24, 0),new Register("$t9", 25, 0), - new Register("$k0", 26, 0),new Register("$k1", 27, 0), - new Register("$gp", GLOBAL_POINTER_REGISTER, Memory.globalPointer), - new Register("$sp", STACK_POINTER_REGISTER, Memory.stackPointer), - new Register("$fp", 30, 0),new Register("$ra", 31, 0) - }; - - private static Register programCounter= new Register("pc", 32, Memory.textBaseAddress); - private static Register hi= new Register("hi", 33, 0);//this is an internal register with arbitrary number - private static Register lo= new Register("lo", 34, 0);// this is an internal register with arbitrary number - - - /** - * Method for displaying the register values for debugging. - **/ - - public static void showRegisters(){ - for (int i=0; i< regFile.length; i++){ +public class RegisterFile +{ + + public static final int GLOBAL_POINTER_REGISTER = 28; + + public static final int STACK_POINTER_REGISTER = 29; + + private static final Register[] regFile = + {new Register("$zero", 0, 0), new Register("$at", 1, 0), + new Register("$v0", 2, 0), new Register("$v1", 3, 0), + new Register("$a0", 4, 0), new Register("$a1", 5, 0), + new Register("$a2", 6, 0), new Register("$a3", 7, 0), + new Register("$t0", 8, 0), new Register("$t1", 9, 0), + new Register("$t2", 10, 0), new Register("$t3", 11, 0), + new Register("$t4", 12, 0), new Register("$t5", 13, 0), + new Register("$t6", 14, 0), new Register("$t7", 15, 0), + new Register("$s0", 16, 0), new Register("$s1", 17, 0), + new Register("$s2", 18, 0), new Register("$s3", 19, 0), + new Register("$s4", 20, 0), new Register("$s5", 21, 0), + new Register("$s6", 22, 0), new Register("$s7", 23, 0), + new Register("$t8", 24, 0), new Register("$t9", 25, 0), + new Register("$k0", 26, 0), new Register("$k1", 27, 0), + new Register("$gp", GLOBAL_POINTER_REGISTER, Memory.globalPointer), + new Register("$sp", STACK_POINTER_REGISTER, Memory.stackPointer), + new Register("$fp", 30, 0), new Register("$ra", 31, 0) + }; + + private static final Register programCounter = new Register("pc", 32, Memory.textBaseAddress); + + private static final Register hi = new Register("hi", 33, 0);//this is an internal register with arbitrary number + + private static final Register lo = new Register("lo", 34, 0);// this is an internal register with arbitrary number + + + /** + * Method for displaying the register values for debugging. + **/ + + public static void showRegisters() + { + for (int i = 0; i < regFile.length; i++) + { System.out.println("Name: " + regFile[i].getName()); System.out.println("Number: " + regFile[i].getNumber()); - System.out.println("Value: " + regFile[i].getValue()); - System.out.println(""); - } - } - - - - /** - * This method updates the register value who's number is num. Also handles the lo and hi registers - * @param num Register to set the value of. - * @param val The desired value for the register. - **/ - - public static int updateRegister(int num, int val){ - int old = 0; - if(num == 0){ + System.out.println("Value: " + regFile[i].getValue()); + System.out.println(); + } + } + + + /** + * This method updates the register value who's number is num. Also handles the lo and hi registers + * + * @param num Register to set the value of. + * @param val The desired value for the register. + **/ + + public static int updateRegister(int num, int val) + { + int old = 0; + if (num == 0) + { //System.out.println("You can not change the value of the zero register."); - } - else { - for (int i=0; i< regFile.length; i++){ - if(regFile[i].getNumber()== num) { - old = (Globals.getSettings().getBackSteppingEnabled()) - ? Globals.program.getBackStepper().addRegisterFileRestore(num,regFile[i].setValue(val)) - : regFile[i].setValue(val); - break; - } + } + else + { + for (int i = 0; i < regFile.length; i++) + { + if (regFile[i].getNumber() == num) + { + old = (Globals.getSettings().getBackSteppingEnabled()) + ? Globals.program.getBackStepper().addRegisterFileRestore(num, regFile[i].setValue(val)) + : regFile[i].setValue(val); + break; + } } - } - if(num== 33){//updates the hi register + } + if (num == 33) + {//updates the hi register old = (Globals.getSettings().getBackSteppingEnabled()) - ? Globals.program.getBackStepper().addRegisterFileRestore(num,hi.setValue(val)) - : hi.setValue(val); - } - else if(num== 34){// updates the low register + ? Globals.program.getBackStepper().addRegisterFileRestore(num, hi.setValue(val)) + : hi.setValue(val); + } + else if (num == 34) + {// updates the low register old = (Globals.getSettings().getBackSteppingEnabled()) - ? Globals.program.getBackStepper().addRegisterFileRestore(num,lo.setValue(val)) - : lo.setValue(val); - } - return old; - } - - /** - * Sets the value of the register given to the value given. - * @param reg Name of register to set the value of. - * @param val The desired value for the register. - **/ - - public static void updateRegister(String reg, int val){ - if(reg.equals("zero")){ + ? Globals.program.getBackStepper().addRegisterFileRestore(num, lo.setValue(val)) + : lo.setValue(val); + } + return old; + } + + /** + * Sets the value of the register given to the value given. + * + * @param reg Name of register to set the value of. + * @param val The desired value for the register. + **/ + + public static void updateRegister(String reg, int val) + { + if (reg.equals("zero")) + { //System.out.println("You can not change the value of the zero register."); - } - else{ - for (int i=0; i< regFile.length; i++){ - if(regFile[i].getName().equals(reg)) { - updateRegister(i,val); - break; - } - }} - } - - /** - * Returns the value of the register who's number is num. - * @param num The register number. - * @return The value of the given register. - **/ - - public static int getValue(int num){ - if(num==33){ + } + else + { + for (int i = 0; i < regFile.length; i++) + { + if (regFile[i].getName().equals(reg)) + { + updateRegister(i, val); + break; + } + } + } + } + + /** + * Returns the value of the register who's number is num. + * + * @param num The register number. + * @return The value of the given register. + **/ + + public static int getValue(int num) + { + if (num == 33) + { return hi.getValue(); - } - else if(num==34){ + } + else if (num == 34) + { return lo.getValue(); - } - else + } + else + { return regFile[num].getValue(); - - } - - /** - * For getting the number representation of the register. - * @param n The string formatted register name to look for. - * @return The number of the register represented by the string - * or -1 if no match. - **/ - - public static int getNumber(String n){ - int j=-1; - for (int i=0; i< regFile.length; i++){ - if(regFile[i].getName().equals(n)) { - j= regFile[i].getNumber(); - break; + } + + } + + /** + * For getting the number representation of the register. + * + * @param n The string formatted register name to look for. + * @return The number of the register represented by the string or -1 if no match. + **/ + + public static int getNumber(String n) + { + int j = -1; + for (int i = 0; i < regFile.length; i++) + { + if (regFile[i].getName().equals(n)) + { + j = regFile[i].getNumber(); + break; } - } - return j; - } - - /** - * For returning the set of registers. - * @return The set of registers. - **/ - - public static Register[] getRegisters(){ - return regFile; - } - - /** - * Get register object corresponding to given name. If no match, return null. - * @param Rname The register name, either in $0 or $zero format. - * @return The register object,or null if not found. - **/ - - public static Register getUserRegister(String Rname) { - Register reg = null; - if (Rname.charAt(0) == '$') { - try { - // check for register number 0-31. - reg = regFile[Binary.stringToInt(Rname.substring(1))]; // KENV 1/6/05 + } + return j; + } + + /** + * For returning the set of registers. + * + * @return The set of registers. + **/ + + public static Register[] getRegisters() + { + return regFile; + } + + /** + * Get register object corresponding to given name. If no match, return null. + * + * @param Rname The register name, either in $0 or $zero format. + * @return The register object,or null if not found. + **/ + + public static Register getUserRegister(String Rname) + { + Register reg = null; + if (Rname.charAt(0) == '$') + { + try + { + // check for register number 0-31. + reg = regFile[Binary.stringToInt(Rname.substring(1))]; // KENV 1/6/05 } - catch (Exception e) { - // handles both NumberFormat and ArrayIndexOutOfBounds - // check for register mnemonic $zero thru $ra - reg = null; // just to be sure - // just do linear search; there aren't that many registers - for (int i=0; i < regFile.length; i++) { - if (Rname.equals(regFile[i].getName())) { + catch (Exception e) + { + // handles both NumberFormat and ArrayIndexOutOfBounds + // check for register mnemonic $zero thru $ra + reg = null; // just to be sure + // just do linear search; there aren't that many registers + for (int i = 0; i < regFile.length; i++) + { + if (Rname.equals(regFile[i].getName())) + { reg = regFile[i]; break; - } - } - } - } - return reg; - } - - /** - * For initializing the Program Counter. Do not use this to implement jumps and - * branches, as it will NOT record a backstep entry with the restore value. - * If you need backstepping capability, use setProgramCounter instead. - * @param value The value to set the Program Counter to. - **/ - - public static void initializeProgramCounter(int value){ - programCounter.setValue(value); - } - - /** - * Will initialize the Program Counter to either the default reset value, or the address - * associated with source program global label "main", if it exists as a text segment label - * and the global setting is set. - * @param startAtMain If true, will set program counter to address of statement labeled - * 'main' (or other defined start label) if defined. If not defined, or if parameter false, - * will set program counter to default reset value. - **/ - - public static void initializeProgramCounter(boolean startAtMain) { - int mainAddr = Globals.symbolTable.getAddress(SymbolTable.getStartLabel()); - if (startAtMain && mainAddr != SymbolTable.NOT_FOUND && (Memory.inTextSegment(mainAddr) || Memory.inKernelTextSegment(mainAddr))) { + } + } + } + } + return reg; + } + + /** + * For initializing the Program Counter. Do not use this to implement jumps and branches, as it will NOT record a + * backstep entry with the restore value. If you need backstepping capability, use setProgramCounter instead. + * + * @param value The value to set the Program Counter to. + **/ + + public static void initializeProgramCounter(int value) + { + programCounter.setValue(value); + } + + /** + * Will initialize the Program Counter to either the default reset value, or the address associated with source + * program global label "main", if it exists as a text segment label and the global setting is set. + * + * @param startAtMain If true, will set program counter to address of statement labeled 'main' (or other defined + * start label) if defined. If not defined, or if parameter false, will set program counter to default reset + * value. + **/ + + public static void initializeProgramCounter(boolean startAtMain) + { + int mainAddr = Globals.symbolTable.getAddress(SymbolTable.getStartLabel()); + if (startAtMain && mainAddr != SymbolTable.NOT_FOUND && (Memory.inTextSegment(mainAddr) || Memory.inKernelTextSegment(mainAddr))) + { initializeProgramCounter(mainAddr); - } - else { + } + else + { initializeProgramCounter(programCounter.getResetValue()); - } - } - - /** - * For setting the Program Counter. Note that ordinary PC update should be done using - * incrementPC() method. Use this only when processing jumps and branches. - * @param value The value to set the Program Counter to. - * @return previous PC value - **/ - - public static int setProgramCounter(int value){ - int old = programCounter.getValue(); - programCounter.setValue(value); - if (Globals.getSettings().getBackSteppingEnabled()) { + } + } + + /** + * For setting the Program Counter. Note that ordinary PC update should be done using incrementPC() method. Use + * this only when processing jumps and branches. + * + * @param value The value to set the Program Counter to. + * @return previous PC value + **/ + + public static int setProgramCounter(int value) + { + int old = programCounter.getValue(); + programCounter.setValue(value); + if (Globals.getSettings().getBackSteppingEnabled()) + { Globals.program.getBackStepper().addPCRestore(old); - } - return old; - } - - /** - * For returning the program counters value. - * @return The program counters value as an int. - **/ - - public static int getProgramCounter(){ - return programCounter.getValue(); - } - - /** - * Returns Register object for program counter. Use with caution. - * @return program counter's Register object. - */ - public static Register getProgramCounterRegister() { - return programCounter; - } - - /** - * For returning the program counter's initial (reset) value. - * @return The program counter's initial value - **/ - - public static int getInitialProgramCounter(){ - return programCounter.getResetValue(); - } - - /** - * Method to reinitialize the values of the registers. - * NOTE: Should not be called from command-mode MARS because this - * this method uses global settings from the registry. Command-mode must operate - * using only the command switches, not registry settings. It can be called - * from tools running stand-alone, and this is done in - * AbstractMarsToolAndApplication. - **/ - - public static void resetRegisters(){ - for(int i=0; i< regFile.length; i++){ + } + return old; + } + + /** + * For returning the program counters value. + * + * @return The program counters value as an int. + **/ + + public static int getProgramCounter() + { + return programCounter.getValue(); + } + + /** + * Returns Register object for program counter. Use with caution. + * + * @return program counter's Register object. + */ + public static Register getProgramCounterRegister() + { + return programCounter; + } + + /** + * For returning the program counter's initial (reset) value. + * + * @return The program counter's initial value + **/ + + public static int getInitialProgramCounter() + { + return programCounter.getResetValue(); + } + + /** + * Method to reinitialize the values of the registers. + * NOTE: Should not be called from command-mode MARS because this + * this method uses global settings from the registry. Command-mode must operate using only the command switches, + * not registry settings. It can be called from tools running stand-alone, and this is done in + * AbstractMarsToolAndApplication. + **/ + + public static void resetRegisters() + { + for (int i = 0; i < regFile.length; i++) + { regFile[i].resetValue(); - } - initializeProgramCounter(Globals .getSettings().getStartAtMain());// replaces "programCounter.resetValue()", DPS 3/3/09 - hi.resetValue(); - lo.resetValue(); - } - - /** - * Method to increment the Program counter in the general case (not a jump or branch). - **/ - - public static void incrementPC(){ - programCounter.setValue(programCounter.getValue() + Instruction.INSTRUCTION_LENGTH); - } - - /** - * Each individual register is a separate object and Observable. This handy method - * will add the given Observer to each one. Currently does not apply to Program - * Counter. - */ - public static void addRegistersObserver(Observer observer) { - for (int i=0; i + * The presence of an alternative "compact translation" can optimize code generation by assuming that data label + * addresses are 16 bits instead of 32 **/ - - public ExtendedInstruction(String example, String translation, String compactTranslation, String description) { - this.exampleFormat = example; - this.description = description; - this.mnemonic = this.extractOperator(example); - this.createExampleTokenList(); - this.translationStrings = buildTranslationList(translation); - this.compactTranslationStrings = buildTranslationList(compactTranslation); - } - + + public ExtendedInstruction(String example, String translation, String compactTranslation, String description) + { + this.exampleFormat = example; + this.description = description; + this.mnemonic = this.extractOperator(example); + this.createExampleTokenList(); + this.translationStrings = buildTranslationList(translation); + this.compactTranslationStrings = buildTranslationList(compactTranslation); + } + /** * Constructor for ExtendedInstruction. No compact translation is provided. - * + * * @param example A String containing example use of the MIPS extended instruction. - * @param translation Specifications for translating this instruction into a sequence - * of one or more MIPS basic instructions. + * @param translation Specifications for translating this instruction into a sequence of one or more MIPS basic + * instructions. * @param description a helpful description to be included on help requests **/ - - public ExtendedInstruction(String example, String translation, String description) { - this.exampleFormat = example; - this.description = description; - this.mnemonic = this.extractOperator(example); - this.createExampleTokenList(); - this.translationStrings = buildTranslationList(translation); - this.compactTranslationStrings = null; - } - - /** - * Constructor for ExtendedInstruction, where no instruction description or - * compact translation is provided. - * + + public ExtendedInstruction(String example, String translation, String description) + { + this.exampleFormat = example; + this.description = description; + this.mnemonic = this.extractOperator(example); + this.createExampleTokenList(); + this.translationStrings = buildTranslationList(translation); + this.compactTranslationStrings = null; + } + + /** + * Constructor for ExtendedInstruction, where no instruction description or compact translation is provided. + * * @param example A String containing example use of the MIPS extended instruction. - * @param translation Specifications for translating this instruction into a sequence - * of one or more MIPS basic instructions. + * @param translation Specifications for translating this instruction into a sequence of one or more MIPS basic + * instructions. **/ - - public ExtendedInstruction(String example, String translation) { - this(example, translation, ""); - } - - /** - * Get length in bytes that this extended instruction requires in its - * binary form. The answer depends on how many basic instructions it - * expands to. This may vary, if expansion includes a nop, depending on - * whether or not delayed branches are enabled. Each requires 4 bytes. - * @return int length in bytes of corresponding binary instruction(s). - */ - - public int getInstructionLength() { - return getInstructionLength(translationStrings); - } - - /** - * Get ArrayList of Strings that represent list of templates for - * basic instructions generated by this extended instruction. - * @return ArrayList of Strings. - */ - - public ArrayList getBasicIntructionTemplateList() { - return translationStrings; - } - - /** - * Get length in bytes that this extended instruction requires in its - * binary form if it includes an alternative expansion for compact - * memory (16 bit addressing) configuration. The answer depends on - * how many basic instructions it expands to. This may vary, if - * expansion includes a nop, depending on whether or not delayed - * branches are enabled. Each requires 4 bytes. - * @return int length in bytes of corresponding binary instruction(s). - * Returns 0 if an alternative expansion is not defined for this - * instruction. - */ - - public int getCompactInstructionLength() { - return getInstructionLength(compactTranslationStrings); - } - - - /** - * Determine whether or not this pseudo-instruction has a second - * translation optimized for 16 bit address space: a compact version. - */ - public boolean hasCompactTranslation() { - return compactTranslationStrings != null; - } + public ExtendedInstruction(String example, String translation) + { + this(example, translation, ""); + } - /** - * Get ArrayList of Strings that represent list of templates for - * basic instructions generated by the "compact" or 16-bit version - * of this extended instruction. - * @return ArrayList of Strings. Returns null if the instruction does not - * have a compact alternative. + * Given a basic instruction template and the list of tokens from an extended instruction statement, substitute + * operands from the token list appropriately into the template to generate the basic statement. Assumes the + * extended instruction statement has been translated from source form to basic assembly form (e.g. register + * mnemonics translated to corresponding register numbers). Operand format of source statement is already verified + * correct. Assume the template has correct number and positions of operands. Template is String with special + * markers. In the list below, n represents token position (1,2,3,etc) in source statement (operator is token 0, + * parentheses count but commas don't): + *

    + *
  • RGn means substitute register found in n'th token of source statement + *
  • NRn means substitute next higher register than the one in n'th token of source code + *
  • OPn means substitute n'th token of source code as is + *
  • LLn means substitute low order 16 bits from label address in source token n. + *
  • LLnU means substitute low order 16 bits (unsigned) from label address in source token n. + *
  • LLnPm (m=1,2,3,4) means substitute low order 16 bits from label address in source token n, after adding m. + *
  • LHn means substitute high order 16 bits from label address in source token n. Must add 1 if address bit 15 is + * 1. + *
  • LHnPm (m=1,2,3,4) means substitute high order 16 bits from label address in source token n, after adding m. + * Must then add 1 if bit 15 is 1. + *
  • VLn means substitute low order 16 bits from 32 bit value in source token n. + *
  • VLnU means substitute low order 16 bits (unsigned) from 32 bit value in source token n. + *
  • VLnPm (m=1,2,3,4) means substitute low order 16 bits from 32 bit value in source token n, after adding m to + * value. + *
  • VLnPmU (m=1,2,3,4) means substitute low order 16 bits (unsigned) from 32 bit value in source token n, after + * adding m to value. + *
  • VHLn means substitute high order 16 bits from 32 bit value in source token n. Use this if later combined + * with low order 16 bits using "ori $1,$1,VLn". See logical and branch operations. + *
  • VHn means substitute high order 16 bits from 32 bit value in source token n, then add 1 if value's bit 15 is + * 1. Use this only if later instruction uses VLn($1) to calculate 32 bit address. See loads and stores. + *
  • VHLnPm (m=1,2,3,4) means substitute high order 16 bits from 32 bit value in source token n, after adding m. + * See VHLn. + *
  • VHnPm (m=1,2,3,4) means substitute high order 16 bits from 32 bit value in source token n, after adding m. + * Must then add 1 if bit 15 is 1. See VHn. + *
  • LLP is similar to LLn, but is needed for "label+100000" address offset. Immediate is added before taking low + * order 16. + *
  • LLPU is similar to LLnU, but is needed for "label+100000" address offset. Immediate is added before taking + * low order 16 (unsigned). + *
  • LLPPm (m=1,2,3,4) is similar to LLP except m is added along with mmediate before taking low order 16. + *
  • LHPA is similar to LHn, but is needed for "label+100000" address offset. Immediate is added before taking + * high order 16. + *
  • LHPN is similar to LHPA, used only by "la" instruction. Address resolved by "ori" so do not add 1 if bit 15 + * is 1. + *
  • LHPAPm (m=1,2,3,4) is similar to LHPA except value m is added along with immediate before taking high order + * 16. + *
  • LHL means substitute high order 16 bits from label address in token 2 of "la" (load address) source + * statement. + *
  • LAB means substitute textual label from last token of source statement. Used for various branches. + *
  • S32 means substitute the result of subtracting the constant value in last token from 32. Used by "ror", + * "rol". + *
  • DBNOP means Delayed Branching NOP - generate a "nop" instruction but only if delayed branching is enabled. + * Added in 3.4.1 release. + *
  • BROFFnm means substitute n if delayed branching is NOT enabled otherwise substitute m. n and m are single + * digit numbers indicating constant branch offset (in words). Added in 3.4.1 release. + *
+ * + * @param template a String containing template for basic statement. + * @param tokenList a TokenList containing tokens from extended instruction. + * @return String representing basic assembler statement. */ - - public ArrayList getCompactBasicIntructionTemplateList() { - return compactTranslationStrings; - } - - /** - * Given a basic instruction template and the list of tokens from an extended - * instruction statement, substitute operands from the token list appropriately into the - * template to generate the basic statement. Assumes the extended instruction statement has - * been translated from source form to basic assembly form (e.g. register mnemonics - * translated to corresponding register numbers). - * Operand format of source statement is already verified correct. - * Assume the template has correct number and positions of operands. - * Template is String with special markers. In the list below, n represents token position (1,2,3,etc) - * in source statement (operator is token 0, parentheses count but commas don't): - *
    - *
  • RGn means substitute register found in n'th token of source statement - *
  • NRn means substitute next higher register than the one in n'th token of source code - *
  • OPn means substitute n'th token of source code as is - *
  • LLn means substitute low order 16 bits from label address in source token n. - *
  • LLnU means substitute low order 16 bits (unsigned) from label address in source token n. - *
  • LLnPm (m=1,2,3,4) means substitute low order 16 bits from label address in source token n, after adding m. - *
  • LHn means substitute high order 16 bits from label address in source token n. Must add 1 if address bit 15 is 1. - *
  • LHnPm (m=1,2,3,4) means substitute high order 16 bits from label address in source token n, after adding m. Must then add 1 if bit 15 is 1. - *
  • VLn means substitute low order 16 bits from 32 bit value in source token n. - *
  • VLnU means substitute low order 16 bits (unsigned) from 32 bit value in source token n. - *
  • VLnPm (m=1,2,3,4) means substitute low order 16 bits from 32 bit value in source token n, after adding m to value. - *
  • VLnPmU (m=1,2,3,4) means substitute low order 16 bits (unsigned) from 32 bit value in source token n, after adding m to value. - *
  • VHLn means substitute high order 16 bits from 32 bit value in source token n. Use this if later combined with low order 16 bits using "ori $1,$1,VLn". See logical and branch operations. - *
  • VHn means substitute high order 16 bits from 32 bit value in source token n, then add 1 if value's bit 15 is 1. Use this only if later instruction uses VLn($1) to calculate 32 bit address. See loads and stores. - *
  • VHLnPm (m=1,2,3,4) means substitute high order 16 bits from 32 bit value in source token n, after adding m. See VHLn. - *
  • VHnPm (m=1,2,3,4) means substitute high order 16 bits from 32 bit value in source token n, after adding m. Must then add 1 if bit 15 is 1. See VHn. - *
  • LLP is similar to LLn, but is needed for "label+100000" address offset. Immediate is added before taking low order 16. - *
  • LLPU is similar to LLnU, but is needed for "label+100000" address offset. Immediate is added before taking low order 16 (unsigned). - *
  • LLPPm (m=1,2,3,4) is similar to LLP except m is added along with mmediate before taking low order 16. - *
  • LHPA is similar to LHn, but is needed for "label+100000" address offset. Immediate is added before taking high order 16. - *
  • LHPN is similar to LHPA, used only by "la" instruction. Address resolved by "ori" so do not add 1 if bit 15 is 1. - *
  • LHPAPm (m=1,2,3,4) is similar to LHPA except value m is added along with immediate before taking high order 16. - *
  • LHL means substitute high order 16 bits from label address in token 2 of "la" (load address) source statement. - *
  • LAB means substitute textual label from last token of source statement. Used for various branches. - *
  • S32 means substitute the result of subtracting the constant value in last token from 32. Used by "ror", "rol". - *
  • DBNOP means Delayed Branching NOP - generate a "nop" instruction but only if delayed branching is enabled. Added in 3.4.1 release. - *
  • BROFFnm means substitute n if delayed branching is NOT enabled otherwise substitute m. n and m are single digit numbers indicating constant branch offset (in words). Added in 3.4.1 release. - *
- * @param template a String containing template for basic statement. - * @param tokenList a TokenList containing tokens from extended instruction. - * @return String representing basic assembler statement. - */ - - public static String makeTemplateSubstitutions(MIPSprogram program, String template, TokenList theTokenList) { - String instruction = template; - int index; - // Added 22 Jan 2008 by DPS. The DBNOP template means to generate a "nop" instruction if delayed branching - // is enabled and generate no instruction otherwise. - - //This is the goal, but it leads to a cascade of - // additional changes, so for now I will generate "nop" in either case, then come back to it for the - // next major release. - if (instruction.indexOf("DBNOP")>=0) { + + public static String makeTemplateSubstitutions(MIPSprogram program, String template, TokenList theTokenList) + { + String instruction = template; + int index; + // Added 22 Jan 2008 by DPS. The DBNOP template means to generate a "nop" instruction if delayed branching + // is enabled and generate no instruction otherwise. + + //This is the goal, but it leads to a cascade of + // additional changes, so for now I will generate "nop" in either case, then come back to it for the + // next major release. + if (instruction.indexOf("DBNOP") >= 0) + { return Globals.getSettings().getDelayedBranchingEnabled() ? "nop" : ""; - } - // substitute first operand token for template's RG1 or OP1, second for RG2 or OP2, etc - for (int op=1; op=0) { - // Label, last operand, has already been translated to address by symtab lookup - String label = theTokenList.get(op).getValue(); - int addr = 0; - int add = instruction.charAt(index+4)-48; // extract the digit following P - try { - addr = Binary.stringToInt(label) + add; // KENV 1/6/05 - } - catch (NumberFormatException e) { - // this won't happen... - } - // If bit 15 is 1, that means lower 16 bits will become a negative offset! To - // compensate if that is the case, we need to add 1 to the high 16 bits. - int extra = Binary.bitValue(addr,15); - instruction = substitute(instruction,"LH"+op+"P"+add,String.valueOf((addr >> 16)+extra)); + } + // substitute first operand token for template's RG1 or OP1, second for RG2 or OP2, etc + for (int op = 1; op < theTokenList.size(); op++) + { + instruction = substitute(instruction, "RG" + op, theTokenList.get(op).getValue()); + instruction = substitute(instruction, "OP" + op, theTokenList.get(op).getValue()); + // substitute upper 16 bits of label address, after adding singe digit constant following P + if ((index = instruction.indexOf("LH" + op + "P")) >= 0) + { + // Label, last operand, has already been translated to address by symtab lookup + String label = theTokenList.get(op).getValue(); + int addr = 0; + int add = instruction.charAt(index + 4) - 48; // extract the digit following P + try + { + addr = Binary.stringToInt(label) + add; // KENV 1/6/05 + } + catch (NumberFormatException e) + { + // this won't happen... + } + // If bit 15 is 1, that means lower 16 bits will become a negative offset! To + // compensate if that is the case, we need to add 1 to the high 16 bits. + int extra = Binary.bitValue(addr, 15); + instruction = substitute(instruction, "LH" + op + "P" + add, String.valueOf((addr >> 16) + extra)); } - // substitute upper 16 bits of label address - // NOTE: form LHnPm will not match here since it is discovered and substituted above. - if (instruction.indexOf("LH"+op)>=0) { - // Label, last operand, has already been translated to address by symtab lookup - String label = theTokenList.get(op).getValue(); - int addr = 0; - try { - addr = Binary.stringToInt(label); // KENV 1/6/05 - } - catch (NumberFormatException e) { - // this won't happen... - } - // If bit 15 is 1, that means lower 16 bits will become a negative offset! To - // compensate if that is the case, we need to add 1 to the high 16 bits. - int extra = Binary.bitValue(addr,15); - instruction = substitute(instruction,"LH"+op,String.valueOf((addr >> 16)+extra)); + // substitute upper 16 bits of label address + // NOTE: form LHnPm will not match here since it is discovered and substituted above. + if (instruction.indexOf("LH" + op) >= 0) + { + // Label, last operand, has already been translated to address by symtab lookup + String label = theTokenList.get(op).getValue(); + int addr = 0; + try + { + addr = Binary.stringToInt(label); // KENV 1/6/05 + } + catch (NumberFormatException e) + { + // this won't happen... + } + // If bit 15 is 1, that means lower 16 bits will become a negative offset! To + // compensate if that is the case, we need to add 1 to the high 16 bits. + int extra = Binary.bitValue(addr, 15); + instruction = substitute(instruction, "LH" + op, String.valueOf((addr >> 16) + extra)); } - // substitute lower 16 bits of label address, after adding single digit that follows P - if ((index=instruction.indexOf("LL"+op+"P"))>=0) { - // label has already been translated to address by symtab lookup. - String label = theTokenList.get(op).getValue(); - int addr = 0; - int add = instruction.charAt(index+4)-48; // digit that follows P - try { - addr = Binary.stringToInt(label) + add; // KENV 1/6/05 - } - catch (NumberFormatException e) { - // this won't happen... - } - instruction = substitute(instruction,"LL"+op+"P"+add,String.valueOf(addr << 16 >> 16));//addr & 0xffff)); - } - // substitute lower 16 bits of label address. - // NOTE: form LLnPm will not match here since it is discovered and substituted above. - if ((index=instruction.indexOf("LL"+op))>=0) { - // label has already been translated to address by symtab lookup. - String label = theTokenList.get(op).getValue(); - int addr = 0; - try { - addr = Binary.stringToInt(label); // KENV 1/6/05 - } - catch (NumberFormatException e) { - // this won't happen... - } - if ((instruction.length() > index+3) && (instruction.charAt(index+3) == 'U')) { - instruction = substitute(instruction,"LL"+op+"U",String.valueOf(addr & 0xffff)); - } - else { - instruction = substitute(instruction,"LL"+op,String.valueOf(addr << 16 >> 16));//addr & 0xffff)); - } + // substitute lower 16 bits of label address, after adding single digit that follows P + if ((index = instruction.indexOf("LL" + op + "P")) >= 0) + { + // label has already been translated to address by symtab lookup. + String label = theTokenList.get(op).getValue(); + int addr = 0; + int add = instruction.charAt(index + 4) - 48; // digit that follows P + try + { + addr = Binary.stringToInt(label) + add; // KENV 1/6/05 + } + catch (NumberFormatException e) + { + // this won't happen... + } + instruction = substitute(instruction, "LL" + op + "P" + add, String.valueOf(addr << 16 >> 16));//addr & 0xffff)); } - // Substitute upper 16 bits of value after adding 1,2,3,4, (any single digit) - // Added by DPS on 22 Jan 2008 to fix "ble" and "bgt" bug [do not adjust for bit 15==1] - if ((index=instruction.indexOf("VHL"+op+"P"))>=0) { - String value = theTokenList.get(op).getValue(); - int val = 0; - int add = instruction.charAt(index+5)-'0'; // amount to add: 1,2,3,4 (any single digit) - try { // KENV 1/6/05 - val = Binary.stringToInt(value) + add; - } - catch (NumberFormatException e) { - // this won't happen... - } - instruction = substitute(instruction,"VHL"+op+"P"+add,String.valueOf(val >> 16)); - } - // substitute upper 16 bits of value after adding 1,2,3,4, then adjust - // if necessary, if resulting bit 15 is 1 (see "extra" below) - if ((index=instruction.indexOf("VH"+op+"P"))>=0) { - String value = theTokenList.get(op).getValue(); - int val = 0; - int add = instruction.charAt(index+4)-'0'; // amount to add: 1,2,3,4 (any single digit) - try { // KENV 1/6/05 - val = Binary.stringToInt(value) + add; - } - catch (NumberFormatException e) { - // this won't happen... - } - // If bit 15 is 1, that means lower 16 bits will become a negative offset! To - // compensate if that is the case, we need to add 1 to the high 16 bits. - int extra = Binary.bitValue(val,15); - instruction = substitute(instruction,"VH"+op+"P"+add,String.valueOf((val >> 16)+extra)); + // substitute lower 16 bits of label address. + // NOTE: form LLnPm will not match here since it is discovered and substituted above. + if ((index = instruction.indexOf("LL" + op)) >= 0) + { + // label has already been translated to address by symtab lookup. + String label = theTokenList.get(op).getValue(); + int addr = 0; + try + { + addr = Binary.stringToInt(label); // KENV 1/6/05 + } + catch (NumberFormatException e) + { + // this won't happen... + } + if ((instruction.length() > index + 3) && (instruction.charAt(index + 3) == 'U')) + { + instruction = substitute(instruction, "LL" + op + "U", String.valueOf(addr & 0xffff)); + } + else + { + instruction = substitute(instruction, "LL" + op, String.valueOf(addr << 16 >> 16));//addr & 0xffff)); + } } - // substitute upper 16 bits of value, adjusted if necessary (see "extra" below) - // NOTE: if VHnPm appears it will not match here; already substituted by code above - if (instruction.indexOf("VH"+op)>=0) { - String value = theTokenList.get(op).getValue(); - int val = 0; - try { - val = Binary.stringToInt(value); // KENV 1/6/05 - } - catch (NumberFormatException e) { - // this won't happen... - } - // If bit 15 is 1, that means lower 16 bits will become a negative offset! To - // compensate if that is the case, we need to add 1 to the high 16 bits. - int extra = Binary.bitValue(val,15); - instruction = substitute(instruction,"VH"+op,String.valueOf((val >> 16)+extra)); - } - // substitute lower 16 bits of value after adding specified amount (1,2,3,4) - if ((index=instruction.indexOf("VL"+op+"P"))>=0) { - String value = theTokenList.get(op).getValue(); - int val = 0; - int add = instruction.charAt(index+4)-'0'; // P is followed by 1,2,3,4(any single digit OK) - try { // KENV 1/6/05 - val = Binary.stringToInt(value) + add; - } - catch (NumberFormatException e) { - // this won't happen... - } - if ((instruction.length() > index+5) && (instruction.charAt(index+5) == 'U')) { - instruction = substitute(instruction,"VL"+op+"P"+add+"U",String.valueOf(val & 0xffff)); - } - else { - instruction = substitute(instruction,"VL"+op+"P"+add,String.valueOf(val << 16 >> 16));//val & 0xffff)); - } + // Substitute upper 16 bits of value after adding 1,2,3,4, (any single digit) + // Added by DPS on 22 Jan 2008 to fix "ble" and "bgt" bug [do not adjust for bit 15==1] + if ((index = instruction.indexOf("VHL" + op + "P")) >= 0) + { + String value = theTokenList.get(op).getValue(); + int val = 0; + int add = instruction.charAt(index + 5) - '0'; // amount to add: 1,2,3,4 (any single digit) + try + { // KENV 1/6/05 + val = Binary.stringToInt(value) + add; + } + catch (NumberFormatException e) + { + // this won't happen... + } + instruction = substitute(instruction, "VHL" + op + "P" + add, String.valueOf(val >> 16)); } - // substitute lower 16 bits of value. NOTE: VLnPm already substituted by above code. - if ((index=instruction.indexOf("VL"+op))>=0) { - String value = theTokenList.get(op).getValue(); - int val = 0; - try { - val = Binary.stringToInt(value); // KENV 1/6/05 - } - catch (NumberFormatException e) { - // this won't happen... - } - if ((instruction.length() > index+3) && (instruction.charAt(index+3) == 'U')) { - instruction = substitute(instruction,"VL"+op+"U",String.valueOf(val & 0xffff)); - } - else { - instruction = substitute(instruction,"VL"+op,String.valueOf(val << 16 >> 16));//val & 0xffff)); - } + // substitute upper 16 bits of value after adding 1,2,3,4, then adjust + // if necessary, if resulting bit 15 is 1 (see "extra" below) + if ((index = instruction.indexOf("VH" + op + "P")) >= 0) + { + String value = theTokenList.get(op).getValue(); + int val = 0; + int add = instruction.charAt(index + 4) - '0'; // amount to add: 1,2,3,4 (any single digit) + try + { // KENV 1/6/05 + val = Binary.stringToInt(value) + add; + } + catch (NumberFormatException e) + { + // this won't happen... + } + // If bit 15 is 1, that means lower 16 bits will become a negative offset! To + // compensate if that is the case, we need to add 1 to the high 16 bits. + int extra = Binary.bitValue(val, 15); + instruction = substitute(instruction, "VH" + op + "P" + add, String.valueOf((val >> 16) + extra)); } - // substitute upper 16 bits of 32 bit value - if (instruction.indexOf("VHL"+op)>=0) { - // value has to be second operand token. - String value = theTokenList.get(op).getValue(); // has to be token 2 position - int val = 0; - try { - val = Binary.stringToInt(value); // KENV 1/6/05 - } - catch (NumberFormatException e) { - // this won't happen... - } - instruction = substitute(instruction,"VHL"+op,String.valueOf(val >> 16)); + // substitute upper 16 bits of value, adjusted if necessary (see "extra" below) + // NOTE: if VHnPm appears it will not match here; already substituted by code above + if (instruction.indexOf("VH" + op) >= 0) + { + String value = theTokenList.get(op).getValue(); + int val = 0; + try + { + val = Binary.stringToInt(value); // KENV 1/6/05 + } + catch (NumberFormatException e) + { + // this won't happen... + } + // If bit 15 is 1, that means lower 16 bits will become a negative offset! To + // compensate if that is the case, we need to add 1 to the high 16 bits. + int extra = Binary.bitValue(val, 15); + instruction = substitute(instruction, "VH" + op, String.valueOf((val >> 16) + extra)); } - } - // substitute upper 16 bits of label address for "la" - if (instruction.indexOf("LHL")>=0) { + // substitute lower 16 bits of value after adding specified amount (1,2,3,4) + if ((index = instruction.indexOf("VL" + op + "P")) >= 0) + { + String value = theTokenList.get(op).getValue(); + int val = 0; + int add = instruction.charAt(index + 4) - '0'; // P is followed by 1,2,3,4(any single digit OK) + try + { // KENV 1/6/05 + val = Binary.stringToInt(value) + add; + } + catch (NumberFormatException e) + { + // this won't happen... + } + if ((instruction.length() > index + 5) && (instruction.charAt(index + 5) == 'U')) + { + instruction = substitute(instruction, "VL" + op + "P" + add + "U", String.valueOf(val & 0xffff)); + } + else + { + instruction = substitute(instruction, "VL" + op + "P" + add, String.valueOf(val << 16 >> 16));//val & 0xffff)); + } + } + // substitute lower 16 bits of value. NOTE: VLnPm already substituted by above code. + if ((index = instruction.indexOf("VL" + op)) >= 0) + { + String value = theTokenList.get(op).getValue(); + int val = 0; + try + { + val = Binary.stringToInt(value); // KENV 1/6/05 + } + catch (NumberFormatException e) + { + // this won't happen... + } + if ((instruction.length() > index + 3) && (instruction.charAt(index + 3) == 'U')) + { + instruction = substitute(instruction, "VL" + op + "U", String.valueOf(val & 0xffff)); + } + else + { + instruction = substitute(instruction, "VL" + op, String.valueOf(val << 16 >> 16));//val & 0xffff)); + } + } + // substitute upper 16 bits of 32 bit value + if (instruction.indexOf("VHL" + op) >= 0) + { + // value has to be second operand token. + String value = theTokenList.get(op).getValue(); // has to be token 2 position + int val = 0; + try + { + val = Binary.stringToInt(value); // KENV 1/6/05 + } + catch (NumberFormatException e) + { + // this won't happen... + } + instruction = substitute(instruction, "VHL" + op, String.valueOf(val >> 16)); + } + } + // substitute upper 16 bits of label address for "la" + if (instruction.indexOf("LHL") >= 0) + { // Label has already been translated to address by symtab lookup String label = theTokenList.get(2).getValue(); // has to be token 2 position int addr = 0; - try { - addr = Binary.stringToInt(label); // KENV 1/6/05 - } - catch (NumberFormatException e) { - // this won't happen... - } - instruction = substitute(instruction,"LHL",String.valueOf(addr >> 16)); - } - - // substitute upper 16 bits of label address after adding the digit that follows "P" and - // also adding the immediate e.g. here+44($s0) - // Address will be resolved using addition, so need to add 1 to upper half if bit 15 is 1. - if ((index=instruction.indexOf("LHPAP"))>=0) { - // Label has already been translated to address by symtab lookup - String label = theTokenList.get(2).getValue(); // 2 is only possible token position - String addend= theTokenList.get(4).getValue(); // 4 is only possible token position - int addr = 0; - int add = instruction.charAt(index+5)-48; // extract digit following P - try { - addr = Binary.stringToInt(label) + // KENV 1/6/05 - Binary.stringToInt(addend) + add; - } - catch (NumberFormatException e) { - // this won't happen... - } - // If bit 15 is 1, that means lower 16 bits will become a negative offset! To - // compensate if that is the case, we need to add 1 to the high 16 bits. - int extra = Binary.bitValue(addr,15); - instruction = substitute(instruction,"LHPAP"+add,String.valueOf((addr >> 16) + extra)); - } - // substitute upper 16 bits of label address after adding constant e.g. here+4($s0) - // Address will be resolved using addition, so need to add 1 to upper half if bit 15 is 1. - // NOTE: format LHPAPm is recognized and substituted by the code above. - if (instruction.indexOf("LHPA")>=0) { - // Label has already been translated to address by symtab lookup - String label = theTokenList.get(2).getValue(); // 2 is only possible token position - String addend= theTokenList.get(4).getValue(); // 4 is only possible token position - int addr = 0; - try { - addr = Binary.stringToInt(label) + // KENV 1/6/05 - Binary.stringToInt(addend); - } - catch (NumberFormatException e) { - // this won't happen... - } - // If bit 15 is 1, that means lower 16 bits will become a negative offset! To - // compensate if that is the case, we need to add 1 to the high 16 bits. - int extra = Binary.bitValue(addr,15); - instruction = substitute(instruction,"LHPA",String.valueOf((addr >> 16) + extra)); - } - // substitute upper 16 bits of label address after adding constant e.g. here+4($s0) - // Address will be resolved using "ori", so DO NOT adjust upper 16 if bit 15 is 1. - // This only happens in the "la" (load address) instruction. - if (instruction.indexOf("LHPN")>=0) { - // Label has already been translated to address by symtab lookup - String label = theTokenList.get(2).getValue(); // 2 is only possible token position - String addend= theTokenList.get(4).getValue(); // 4 is only possible token position - int addr = 0; - try { - addr = Binary.stringToInt(label) + // KENV 1/6/05 - Binary.stringToInt(addend); - } - catch (NumberFormatException e) { - // this won't happen... - } - instruction = substitute(instruction,"LHPN",String.valueOf(addr >> 16)); - } - // substitute lower 16 bits of label address after adding immediate value e.g. here+44($s0) - // and also adding the digit following LLPP in the spec. - if ((index=instruction.indexOf("LLPP"))>=0) { - // label has already been translated to address by symtab lookup. - String label = theTokenList.get(2).getValue(); // 2 is only possible token position - String addend= theTokenList.get(4).getValue(); // 4 is only possible token position - int addr = 0; - int add = instruction.charAt(index+4)-48; // extract digit following P - try { - addr = Binary.stringToInt(label) + // KENV 1/6/05 - Binary.stringToInt(addend) + add; - - } - catch (NumberFormatException e) { - // this won't happen... - } - instruction = substitute(instruction,"LLPP"+add,String.valueOf(addr << 16 >> 16));//addr & 0xffff)); - } - // substitute lower 16 bits of label address after adding immediate value e.g. here+44($s0) - // NOTE: format LLPPm is recognized and substituted by the code above - if ((index=instruction.indexOf("LLP"))>=0) { - // label has already been translated to address by symtab lookup. - String label = theTokenList.get(2).getValue(); // 2 is only possible token position - String addend= theTokenList.get(4).getValue(); // 4 is only possible token position - int addr = 0; - try { - addr = Binary.stringToInt(label) + // KENV 1/6/05 - Binary.stringToInt(addend); - - } - catch (NumberFormatException e) { - // this won't happen... - } - if ((instruction.length() > index+3) && (instruction.charAt(index+3) == 'U')) { - instruction = substitute(instruction,"LLPU",String.valueOf(addr & 0xffff)); + try + { + addr = Binary.stringToInt(label); // KENV 1/6/05 } - else { - instruction = substitute(instruction,"LLP",String.valueOf(addr << 16 >> 16));//addr & 0xffff)); + catch (NumberFormatException e) + { + // this won't happen... } - } - // 23-Jan-2008 DPS. Substitute correct constant branch offset depending on whether or not - // delayed branching is enabled. BROFF is followed by 2 digits. The first is branch offset - // to substitute if delayed branching is DISABLED, second is offset if ENABLED. - if ((index=instruction.indexOf("BROFF"))>=0) { - try { - String disabled = instruction.substring(index+5, index+6); - String enabled = instruction.substring(index+6, index+7); - instruction = substitute(instruction,"BROFF"+disabled+enabled, - Globals.getSettings().getDelayedBranchingEnabled() ? enabled : disabled ); - } - catch (IndexOutOfBoundsException iooe) { - instruction = substitute(instruction,"BROFF", "BAD_PSEUDO_OP_SPEC"); - } - } - // substitute Next higher Register for registers in token list (for "mfc1.d","mtc1.d") - if (instruction.indexOf("NR")>=0) { - for (int op=1; op=0) { - instruction = substitute(instruction, "NR"+op,"$"+(regNumber+1)); - } - } - catch (NullPointerException e) { // not in RegisterFile, must be Coprocessor1 register - regNumber = Coprocessor1.getRegisterNumber(token); - if (regNumber>=0) { - instruction = substitute(instruction, "NR"+op,"$f"+(regNumber+1)); - } - } - } - } - - // substitute result of subtracting last token from 32 (rol and ror constant rotate amount) - if (instruction.indexOf("S32")>=0) { - String value = theTokenList.get(theTokenList.size()-1).getValue(); - int val = 0; - try { - val = Binary.stringToInt(value); // KENV 1/6/05 - } - catch (NumberFormatException e) { - // this won't happen... - } - instruction = substitute(instruction,"S32",Integer.toString(32-val)); - } + instruction = substitute(instruction, "LHL", String.valueOf(addr >> 16)); + } - // substitute label if necessary - if (instruction.indexOf("LAB")>=0) { - // label has to be last token. It has already been translated to address - // by symtab lookup, so I need to get the text label back so parseLine() won't puke. - String label = theTokenList.get(theTokenList.size()-1).getValue(); - Symbol sym = program.getLocalSymbolTable().getSymbolGivenAddressLocalOrGlobal(label); - if (sym!=null) { - // should never be null, since there would not be an address if label were not in symtab! - // DPS 9 Dec 2007: The "substitute()" method will substitute for ALL matches. Here - // we want to substitute only for the first match, for two reasons: (1) a statement - // can only contain one label reference, its last operand, and (2) If the user's label - // contains the substring "LAB", then substitute() will go into an infinite loop because - // it will keep matching the substituted string! - instruction = substituteFirst(instruction,"LAB",sym.getName()); + // substitute upper 16 bits of label address after adding the digit that follows "P" and + // also adding the immediate e.g. here+44($s0) + // Address will be resolved using addition, so need to add 1 to upper half if bit 15 is 1. + if ((index = instruction.indexOf("LHPAP")) >= 0) + { + // Label has already been translated to address by symtab lookup + String label = theTokenList.get(2).getValue(); // 2 is only possible token position + String addend = theTokenList.get(4).getValue(); // 4 is only possible token position + int addr = 0; + int add = instruction.charAt(index + 5) - 48; // extract digit following P + try + { + addr = Binary.stringToInt(label) + // KENV 1/6/05 + Binary.stringToInt(addend) + add; } - } - return instruction; - } - - // Performs a String substitution. Java 1.5 adds an overloaded String.replace method to - // do this directly but I wanted to stay 1.4 compatible. - // Modified 12 July 2006 to "substitute all occurances", not just the first. - private static String substitute(String original, String find, String replacement) { - if (original.indexOf(find)<0 || find.equals(replacement)) { + catch (NumberFormatException e) + { + // this won't happen... + } + // If bit 15 is 1, that means lower 16 bits will become a negative offset! To + // compensate if that is the case, we need to add 1 to the high 16 bits. + int extra = Binary.bitValue(addr, 15); + instruction = substitute(instruction, "LHPAP" + add, String.valueOf((addr >> 16) + extra)); + } + // substitute upper 16 bits of label address after adding constant e.g. here+4($s0) + // Address will be resolved using addition, so need to add 1 to upper half if bit 15 is 1. + // NOTE: format LHPAPm is recognized and substituted by the code above. + if (instruction.indexOf("LHPA") >= 0) + { + // Label has already been translated to address by symtab lookup + String label = theTokenList.get(2).getValue(); // 2 is only possible token position + String addend = theTokenList.get(4).getValue(); // 4 is only possible token position + int addr = 0; + try + { + addr = Binary.stringToInt(label) + // KENV 1/6/05 + Binary.stringToInt(addend); + } + catch (NumberFormatException e) + { + // this won't happen... + } + // If bit 15 is 1, that means lower 16 bits will become a negative offset! To + // compensate if that is the case, we need to add 1 to the high 16 bits. + int extra = Binary.bitValue(addr, 15); + instruction = substitute(instruction, "LHPA", String.valueOf((addr >> 16) + extra)); + } + // substitute upper 16 bits of label address after adding constant e.g. here+4($s0) + // Address will be resolved using "ori", so DO NOT adjust upper 16 if bit 15 is 1. + // This only happens in the "la" (load address) instruction. + if (instruction.indexOf("LHPN") >= 0) + { + // Label has already been translated to address by symtab lookup + String label = theTokenList.get(2).getValue(); // 2 is only possible token position + String addend = theTokenList.get(4).getValue(); // 4 is only possible token position + int addr = 0; + try + { + addr = Binary.stringToInt(label) + // KENV 1/6/05 + Binary.stringToInt(addend); + } + catch (NumberFormatException e) + { + // this won't happen... + } + instruction = substitute(instruction, "LHPN", String.valueOf(addr >> 16)); + } + // substitute lower 16 bits of label address after adding immediate value e.g. here+44($s0) + // and also adding the digit following LLPP in the spec. + if ((index = instruction.indexOf("LLPP")) >= 0) + { + // label has already been translated to address by symtab lookup. + String label = theTokenList.get(2).getValue(); // 2 is only possible token position + String addend = theTokenList.get(4).getValue(); // 4 is only possible token position + int addr = 0; + int add = instruction.charAt(index + 4) - 48; // extract digit following P + try + { + addr = Binary.stringToInt(label) + // KENV 1/6/05 + Binary.stringToInt(addend) + add; + + } + catch (NumberFormatException e) + { + // this won't happen... + } + instruction = substitute(instruction, "LLPP" + add, String.valueOf(addr << 16 >> 16));//addr & 0xffff)); + } + // substitute lower 16 bits of label address after adding immediate value e.g. here+44($s0) + // NOTE: format LLPPm is recognized and substituted by the code above + if ((index = instruction.indexOf("LLP")) >= 0) + { + // label has already been translated to address by symtab lookup. + String label = theTokenList.get(2).getValue(); // 2 is only possible token position + String addend = theTokenList.get(4).getValue(); // 4 is only possible token position + int addr = 0; + try + { + addr = Binary.stringToInt(label) + // KENV 1/6/05 + Binary.stringToInt(addend); + + } + catch (NumberFormatException e) + { + // this won't happen... + } + if ((instruction.length() > index + 3) && (instruction.charAt(index + 3) == 'U')) + { + instruction = substitute(instruction, "LLPU", String.valueOf(addr & 0xffff)); + } + else + { + instruction = substitute(instruction, "LLP", String.valueOf(addr << 16 >> 16));//addr & 0xffff)); + } + } + // 23-Jan-2008 DPS. Substitute correct constant branch offset depending on whether or not + // delayed branching is enabled. BROFF is followed by 2 digits. The first is branch offset + // to substitute if delayed branching is DISABLED, second is offset if ENABLED. + if ((index = instruction.indexOf("BROFF")) >= 0) + { + try + { + String disabled = instruction.substring(index + 5, index + 6); + String enabled = instruction.substring(index + 6, index + 7); + instruction = substitute(instruction, "BROFF" + disabled + enabled, + Globals.getSettings().getDelayedBranchingEnabled() ? enabled : disabled); + } + catch (IndexOutOfBoundsException iooe) + { + instruction = substitute(instruction, "BROFF", "BAD_PSEUDO_OP_SPEC"); + } + } + // substitute Next higher Register for registers in token list (for "mfc1.d","mtc1.d") + if (instruction.indexOf("NR") >= 0) + { + for (int op = 1; op < theTokenList.size(); op++) + { + String token = theTokenList.get(op).getValue(); + int regNumber; + try + { // if token is a RegisterFile register, substitute next higher register + regNumber = RegisterFile.getUserRegister(token).getNumber(); + if (regNumber >= 0) + { + instruction = substitute(instruction, "NR" + op, "$" + (regNumber + 1)); + } + } + catch (NullPointerException e) + { // not in RegisterFile, must be Coprocessor1 register + regNumber = Coprocessor1.getRegisterNumber(token); + if (regNumber >= 0) + { + instruction = substitute(instruction, "NR" + op, "$f" + (regNumber + 1)); + } + } + } + } + + // substitute result of subtracting last token from 32 (rol and ror constant rotate amount) + if (instruction.indexOf("S32") >= 0) + { + String value = theTokenList.get(theTokenList.size() - 1).getValue(); + int val = 0; + try + { + val = Binary.stringToInt(value); // KENV 1/6/05 + } + catch (NumberFormatException e) + { + // this won't happen... + } + instruction = substitute(instruction, "S32", Integer.toString(32 - val)); + } + + // substitute label if necessary + if (instruction.indexOf("LAB") >= 0) + { + // label has to be last token. It has already been translated to address + // by symtab lookup, so I need to get the text label back so parseLine() won't puke. + String label = theTokenList.get(theTokenList.size() - 1).getValue(); + Symbol sym = program.getLocalSymbolTable().getSymbolGivenAddressLocalOrGlobal(label); + if (sym != null) + { + // should never be null, since there would not be an address if label were not in symtab! + // DPS 9 Dec 2007: The "substitute()" method will substitute for ALL matches. Here + // we want to substitute only for the first match, for two reasons: (1) a statement + // can only contain one label reference, its last operand, and (2) If the user's label + // contains the substring "LAB", then substitute() will go into an infinite loop because + // it will keep matching the substituted string! + instruction = substituteFirst(instruction, "LAB", sym.getName()); + } + } + return instruction; + } + + // Performs a String substitution. Java 1.5 adds an overloaded String.replace method to + // do this directly but I wanted to stay 1.4 compatible. + // Modified 12 July 2006 to "substitute all occurances", not just the first. + private static String substitute(String original, String find, String replacement) + { + if (original.indexOf(find) < 0 || find.equals(replacement)) + { return original; // second condition prevents infinite loop below - } - int i; - String modified = original; - while ((i=modified.indexOf(find))>=0) { + } + int i; + String modified = original; + while ((i = modified.indexOf(find)) >= 0) + { modified = modified.substring(0, i) + replacement + modified.substring(i + find.length()); - } - return modified; - } - - // Performs a String substitution, but will only substitute for the first match. - // Java 1.5 adds an overloaded String.replace method to do this directly but I - // wanted to stay 1.4 compatible. - private static String substituteFirst(String original, String find, String replacement) { - if (original.indexOf(find)<0 || find.equals(replacement)) { + } + return modified; + } + + // Performs a String substitution, but will only substitute for the first match. + // Java 1.5 adds an overloaded String.replace method to do this directly but I + // wanted to stay 1.4 compatible. + private static String substituteFirst(String original, String find, String replacement) + { + if (original.indexOf(find) < 0 || find.equals(replacement)) + { return original; // second condition prevents infinite loop below - } - int i; - String modified = original; - if ((i=modified.indexOf(find))>=0) { + } + int i; + String modified = original; + if ((i = modified.indexOf(find)) >= 0) + { modified = modified.substring(0, i) + replacement + modified.substring(i + find.length()); - } - return modified; - } - - + } + return modified; + } + + /** + * Get length in bytes that this extended instruction requires in its binary form. The answer depends on how many + * basic instructions it expands to. This may vary, if expansion includes a nop, depending on whether or not + * delayed branches are enabled. Each requires 4 bytes. + * + * @return int length in bytes of corresponding binary instruction(s). + */ + + public int getInstructionLength() + { + return getInstructionLength(translationStrings); + } + + /** + * Get ArrayList of Strings that represent list of templates for basic instructions generated by this extended + * instruction. + * + * @return ArrayList of Strings. + */ + + public ArrayList getBasicIntructionTemplateList() + { + return translationStrings; + } + + /** + * Get length in bytes that this extended instruction requires in its binary form if it includes an alternative + * expansion for compact memory (16 bit addressing) configuration. The answer depends on how many basic instructions + * it expands to. This may vary, if expansion includes a nop, depending on whether or not delayed branches are + * enabled. Each requires 4 bytes. + * + * @return int length in bytes of corresponding binary instruction(s). Returns 0 if an alternative expansion is not + * defined for this instruction. + */ + + public int getCompactInstructionLength() + { + return getInstructionLength(compactTranslationStrings); + } + + /** + * Determine whether or not this pseudo-instruction has a second translation optimized for 16 bit address space: a + * compact version. + */ + public boolean hasCompactTranslation() + { + return compactTranslationStrings != null; + } + + /** + * Get ArrayList of Strings that represent list of templates for basic instructions generated by the "compact" or + * 16-bit version of this extended instruction. + * + * @return ArrayList of Strings. Returns null if the instruction does not have a compact alternative. + */ + + public ArrayList getCompactBasicIntructionTemplateList() + { + return compactTranslationStrings; + } + + // Takes list of basic instructions that this extended instruction // expands to, which is a string, and breaks out into separate // instructions. They are separated by '\n' character. - - private ArrayList buildTranslationList(String translation) { - if (translation == null || translation.length() == 0) { + + private ArrayList buildTranslationList(String translation) + { + if (translation == null || translation.length() == 0) + { return null; - } - ArrayList translationList = new ArrayList(); - StringTokenizer st = new StringTokenizer(translation,"\n"); - while (st.hasMoreTokens()) { + } + ArrayList translationList = new ArrayList(); + StringTokenizer st = new StringTokenizer(translation, "\n"); + while (st.hasMoreTokens()) + { translationList.add(st.nextToken()); - } - return translationList; - } - - - + } + return translationList; + } + + /* - * Get length in bytes that this extended instruction requires in its - * binary form. The answer depends on how many basic instructions it + * Get length in bytes that this extended instruction requires in its + * binary form. The answer depends on how many basic instructions it * expands to. This may vary, if expansion includes a nop, depending on * whether or not delayed branches are enabled. Each requires 4 bytes. * Returns length in bytes of corresponding binary instruction(s). * Returns 0 if the ArrayList is null or empty. - */ - private int getInstructionLength(ArrayList translationList) { - if (translationList == null || translationList.size() == 0) { + */ + private int getInstructionLength(ArrayList translationList) + { + if (translationList == null || translationList.size() == 0) + { return 0; - } - // If instruction template is DBNOP, that means generate a "nop" instruction but only - // if Delayed branching is enabled. Otherwise generate nothing. If generating nothing, - // then don't count the nop in the instruction length. DPS 23-Jan-2008 - int instructionCount = 0; - for (int i=0; i=0 && !Globals.getSettings().getDelayedBranchingEnabled()) - continue; + } + // If instruction template is DBNOP, that means generate a "nop" instruction but only + // if Delayed branching is enabled. Otherwise generate nothing. If generating nothing, + // then don't count the nop in the instruction length. DPS 23-Jan-2008 + int instructionCount = 0; + for (int i = 0; i < translationList.size(); i++) + { + if (((String) translationList.get(i)).indexOf("DBNOP") >= 0 && !Globals.getSettings().getDelayedBranchingEnabled()) + { + continue; + } instructionCount++; - } - return 4 * instructionCount; - } - - } \ No newline at end of file + } + return 4 * instructionCount; + } + +} diff --git a/src/main/java/mars/mips/instructions/Instruction.java b/src/main/java/mars/mips/instructions/Instruction.java index b73d41f..d6b69e3 100644 --- a/src/main/java/mars/mips/instructions/Instruction.java +++ b/src/main/java/mars/mips/instructions/Instruction.java @@ -1,7 +1,10 @@ package mars.mips.instructions; -import mars.assembler.*; -import mars.*; -import java.util.*; + +import mars.ProcessingException; +import mars.assembler.TokenList; +import mars.assembler.Tokenizer; + +import java.util.StringTokenizer; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,30 +36,37 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Base class to represent member of MIPS instruction set. - * + * * @author Pete Sanderson and Ken Vollmar * @version August 2003 */ -public abstract class Instruction { - /** - * Length in bytes of a machine instruction. MIPS is a RISC architecture - * so all instructions are the same length. Currently set to 4. - */ - public static final int INSTRUCTION_LENGTH = 4; - public static final int INSTRUCTION_LENGTH_BITS = 32; - /** Characters used in instruction mask to indicate bit positions - * for 'f'irst, 's'econd, and 't'hird operands. +public abstract class Instruction +{ + /** + * Length in bytes of a machine instruction. MIPS is a RISC architecture so all instructions are the same length. + * Currently set to 4. + */ + public static final int INSTRUCTION_LENGTH = 4; + + public static final int INSTRUCTION_LENGTH_BITS = 32; + + /** + * Characters used in instruction mask to indicate bit positions for 'f'irst, 's'econd, and 't'hird operands. **/ - public static char[] operandMask = { 'f', 's', 't'}; + public static char[] operandMask = {'f', 's', 't'}; + /** The instruction name. **/ - protected String mnemonic; - /** Example usage of this instruction. Is provided as subclass constructor argument. **/ - protected String exampleFormat; - /** Description of instruction for display to user **/ - protected String description; - /** List of tokens generated by tokenizing example usage (see exampleFormat). **/ - protected TokenList tokenList; + protected String mnemonic; + + /** Example usage of this instruction. Is provided as subclass constructor argument. **/ + protected String exampleFormat; + + /** Description of instruction for display to user **/ + protected String description; + + /** List of tokens generated by tokenizing example usage (see exampleFormat). **/ + protected TokenList tokenList; /** @@ -64,72 +74,83 @@ public abstract class Instruction { * * @return operation mnemonic (e.g. addi, sw) */ - public String getName() { + public String getName() + { return mnemonic; } /** - * Get string descriptor of instruction's format. This is an example MIPS - * assembler instruction usage which contains the operator and all operands. - * Operands are separated by commas, an operand that begins with a '$' - * represents a register, and an integer operand represents an immediate value - * or address. Here are two examples: "nor $1,$2,$3" and "sw $1,100($2)" + * Get string descriptor of instruction's format. This is an example MIPS assembler instruction usage which + * contains the operator and all operands. Operands are separated by commas, an operand that begins with a '$' + * represents a register, and an integer operand represents an immediate value or address. Here are two examples: + * "nor $1,$2,$3" and "sw $1,100($2)" * * @return String representing example instruction format. */ - public String getExampleFormat() { + public String getExampleFormat() + { return exampleFormat; } /** - * Get string describing the instruction. This is not used internally by - * MARS, but is for display to the user. + * Get string describing the instruction. This is not used internally by MARS, but is for display to the user. * * @return String describing the instruction. */ - public String getDescription() { + public String getDescription() + { return description; } - + /** - * Get TokenList corresponding to correct instruction syntax. - * For example, the instruction with format "sw $1,100($2)" yields token list + * Get TokenList corresponding to correct instruction syntax. For example, the instruction with format "sw + * $1,100($2)" yields token list * * * @return TokenList object representing correct instruction usage. */ - public TokenList getTokenList() { + public TokenList getTokenList() + { return tokenList; } - - + + /** - * Get length in bytes that this instruction requires in its binary form. - * Default is 4 (holds for all basic instructions), but can be overridden - * in subclass. + * Get length in bytes that this instruction requires in its binary form. Default is 4 (holds for all basic + * instructions), but can be overridden in subclass. + * * @return int length in bytes of corresponding binary instruction(s). */ - public int getInstructionLength() { + public int getInstructionLength() + { return INSTRUCTION_LENGTH; } - - /** Used by subclass constructors to extract operator mnemonic from the - instruction example. **/ - protected String extractOperator(String example) { + /** + * Used by subclass constructors to extract operator mnemonic from the instruction example. + **/ + + protected String extractOperator(String example) + { StringTokenizer st = new StringTokenizer(example, " ,\t"); return st.nextToken(); } - - /** Used to build a token list from the example instruction - provided as constructor argument. Parser uses this for syntax checking. **/ - protected void createExampleTokenList() { - try { + + /** + * Used to build a token list from the example instruction provided as constructor argument. Parser uses this for + * syntax checking. + **/ + protected void createExampleTokenList() + { + try + { tokenList = ((new Tokenizer()).tokenizeExampleInstruction(exampleFormat)); - } catch (ProcessingException pe) { - System.out.println("CONFIGURATION ERROR: Instruction example \""+exampleFormat+"\" contains invalid token(s)."); + } + catch (ProcessingException pe) + { + System.out.println("CONFIGURATION ERROR: Instruction example \"" + exampleFormat + "\" contains invalid token(s)."); } } } diff --git a/src/main/java/mars/mips/instructions/InstructionSet.java b/src/main/java/mars/mips/instructions/InstructionSet.java index 5b186a4..19b62f7 100644 --- a/src/main/java/mars/mips/instructions/InstructionSet.java +++ b/src/main/java/mars/mips/instructions/InstructionSet.java @@ -3,10 +3,7 @@ package mars.mips.instructions; import mars.Globals; import mars.ProcessingException; import mars.ProgramStatement; -import mars.mips.hardware.AddressErrorException; -import mars.mips.hardware.Coprocessor0; -import mars.mips.hardware.Coprocessor1; -import mars.mips.hardware.RegisterFile; +import mars.mips.hardware.*; import mars.mips.instructions.syscalls.Syscall; import mars.simulator.DelayedBranch; import mars.simulator.Exceptions; @@ -50,3315 +47,3497 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * The list of Instruction objects, each of which represents a MIPS instruction. - * The instruction may either be basic (translates into binary machine code) or - * extended (translates into sequence of one or more basic instructions). + * The list of Instruction objects, each of which represents a MIPS instruction. The instruction may either be basic + * (translates into binary machine code) or extended (translates into sequence of one or more basic instructions). * * @author Pete Sanderson and Ken Vollmar * @version August 2003-5 */ - public class InstructionSet - { - private ArrayList instructionList; - private ArrayList opcodeMatchMaps; - private SyscallLoader syscallLoader; +public class InstructionSet +{ + private final ArrayList instructionList; + + private ArrayList opcodeMatchMaps; + + private SyscallLoader syscallLoader; + /** * Creates a new InstructionSet object. */ - public InstructionSet() - { - instructionList = new ArrayList(); - - } + public InstructionSet() + { + instructionList = new ArrayList(); + + } + /** * Retrieve the current instruction set. */ - public ArrayList getInstructionList() - { - return instructionList; - - } + public ArrayList getInstructionList() + { + return instructionList; + + } + /** - * Adds all instructions to the set. A given extended instruction may have - * more than one Instruction object, depending on how many formats it can have. + * Adds all instructions to the set. A given extended instruction may have more than one Instruction object, + * depending on how many formats it can have. + * * @see Instruction * @see BasicInstruction * @see ExtendedInstruction */ - public void populate() - { + public void populate() + { /* Here is where the parade begins. Every instruction is added to the set here.*/ - + // //////////////////////////////////// BASIC INSTRUCTIONS START HERE //////////////////////////////// - - instructionList.add( - new BasicInstruction("nop", - "Null operation : machine code is all zeroes", + + instructionList.add( + new BasicInstruction("nop", + "Null operation : machine code is all zeroes", BasicInstructionFormat.R_FORMAT, "000000 00000 00000 00000 00000 000000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - // Hey I like this so far! - } - })); - instructionList.add( - new BasicInstruction("add $t1,$t2,$t3", - "Addition with overflow : set $t1 to ($t2 plus $t3)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Hey I like this so far! + } + })); + instructionList.add( + new BasicInstruction("add $t1,$t2,$t3", + "Addition with overflow : set $t1 to ($t2 plus $t3)", BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 100000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int add1 = RegisterFile.getValue(operands[1]); - int add2 = RegisterFile.getValue(operands[2]); - int sum = add1 + add2; - // overflow on A+B detected when A and B have same sign and A+B has other sign. - if ((add1 >= 0 && add2 >= 0 && sum < 0) - || (add1 < 0 && add2 < 0 && sum >= 0)) - { - throw new ProcessingException(statement, - "arithmetic overflow",Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION); - } - RegisterFile.updateRegister(operands[0], sum); - } - })); - instructionList.add( - new BasicInstruction("sub $t1,$t2,$t3", - "Subtraction with overflow : set $t1 to ($t2 minus $t3)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int add1 = RegisterFile.getValue(operands[1]); + int add2 = RegisterFile.getValue(operands[2]); + int sum = add1 + add2; + // overflow on A+B detected when A and B have same sign and A+B has other sign. + if ((add1 >= 0 && add2 >= 0 && sum < 0) + || (add1 < 0 && add2 < 0 && sum >= 0)) + { + throw new ProcessingException(statement, + "arithmetic overflow", Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION); + } + RegisterFile.updateRegister(operands[0], sum); + } + })); + instructionList.add( + new BasicInstruction("sub $t1,$t2,$t3", + "Subtraction with overflow : set $t1 to ($t2 minus $t3)", BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 100010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int sub1 = RegisterFile.getValue(operands[1]); - int sub2 = RegisterFile.getValue(operands[2]); - int dif = sub1 - sub2; - // overflow on A-B detected when A and B have opposite signs and A-B has B's sign - if ((sub1 >= 0 && sub2 < 0 && dif < 0) - || (sub1 < 0 && sub2 >= 0 && dif >= 0)) - { - throw new ProcessingException(statement, - "arithmetic overflow",Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION); - } - RegisterFile.updateRegister(operands[0], dif); - } - })); - instructionList.add( - new BasicInstruction("addi $t1,$t2,-100", - "Addition immediate with overflow : set $t1 to ($t2 plus signed 16-bit immediate)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int sub1 = RegisterFile.getValue(operands[1]); + int sub2 = RegisterFile.getValue(operands[2]); + int dif = sub1 - sub2; + // overflow on A-B detected when A and B have opposite signs and A-B has B's sign + if ((sub1 >= 0 && sub2 < 0 && dif < 0) + || (sub1 < 0 && sub2 >= 0 && dif >= 0)) + { + throw new ProcessingException(statement, + "arithmetic overflow", Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION); + } + RegisterFile.updateRegister(operands[0], dif); + } + })); + instructionList.add( + new BasicInstruction("addi $t1,$t2,-100", + "Addition immediate with overflow : set $t1 to ($t2 plus signed 16-bit immediate)", BasicInstructionFormat.I_FORMAT, "001000 sssss fffff tttttttttttttttt", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int add1 = RegisterFile.getValue(operands[1]); - int add2 = operands[2] << 16 >> 16; - int sum = add1 + add2; - // overflow on A+B detected when A and B have same sign and A+B has other sign. - if ((add1 >= 0 && add2 >= 0 && sum < 0) - || (add1 < 0 && add2 < 0 && sum >= 0)) - { - throw new ProcessingException(statement, - "arithmetic overflow",Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION); - } - RegisterFile.updateRegister(operands[0], sum); - } - })); - instructionList.add( - new BasicInstruction("addu $t1,$t2,$t3", - "Addition unsigned without overflow : set $t1 to ($t2 plus $t3), no overflow", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int add1 = RegisterFile.getValue(operands[1]); + int add2 = operands[2] << 16 >> 16; + int sum = add1 + add2; + // overflow on A+B detected when A and B have same sign and A+B has other sign. + if ((add1 >= 0 && add2 >= 0 && sum < 0) + || (add1 < 0 && add2 < 0 && sum >= 0)) + { + throw new ProcessingException(statement, + "arithmetic overflow", Exceptions.ARITHMETIC_OVERFLOW_EXCEPTION); + } + RegisterFile.updateRegister(operands[0], sum); + } + })); + instructionList.add( + new BasicInstruction("addu $t1,$t2,$t3", + "Addition unsigned without overflow : set $t1 to ($t2 plus $t3), no overflow", BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 100001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) - + RegisterFile.getValue(operands[2])); - } - })); - instructionList.add( - new BasicInstruction("subu $t1,$t2,$t3", - "Subtraction unsigned without overflow : set $t1 to ($t2 minus $t3), no overflow", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) + + RegisterFile.getValue(operands[2])); + } + })); + instructionList.add( + new BasicInstruction("subu $t1,$t2,$t3", + "Subtraction unsigned without overflow : set $t1 to ($t2 minus $t3), no overflow", BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 100011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) - - RegisterFile.getValue(operands[2])); - } - })); - instructionList.add( - new BasicInstruction("addiu $t1,$t2,-100", - "Addition immediate unsigned without overflow : set $t1 to ($t2 plus signed 16-bit immediate), no overflow", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) + - RegisterFile.getValue(operands[2])); + } + })); + instructionList.add( + new BasicInstruction("addiu $t1,$t2,-100", + "Addition immediate unsigned without overflow : set $t1 to ($t2 plus signed 16-bit immediate), no overflow", BasicInstructionFormat.I_FORMAT, "001001 sssss fffff tttttttttttttttt", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) - + (operands[2] << 16 >> 16)); - } - })); - instructionList.add( - new BasicInstruction("mult $t1,$t2", - "Multiplication : Set hi to high-order 32 bits, lo to low-order 32 bits of the product of $t1 and $t2 (use mfhi to access hi, mflo to access lo)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) + + (operands[2] << 16 >> 16)); + } + })); + instructionList.add( + new BasicInstruction("mult $t1,$t2", + "Multiplication : Set hi to high-order 32 bits, lo to low-order 32 bits of the product of $t1 and $t2 (use mfhi to access hi, mflo to access lo)", BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 011000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - long product = (long) RegisterFile.getValue(operands[0]) - * (long) RegisterFile.getValue(operands[1]); - // Register 33 is HIGH and 34 is LOW - RegisterFile.updateRegister(33, (int) (product >> 32)); - RegisterFile.updateRegister(34, (int) ((product << 32) >> 32)); - } - })); - instructionList.add( - new BasicInstruction("multu $t1,$t2", - "Multiplication unsigned : Set HI to high-order 32 bits, LO to low-order 32 bits of the product of unsigned $t1 and $t2 (use mfhi to access HI, mflo to access LO)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + long product = (long) RegisterFile.getValue(operands[0]) + * (long) RegisterFile.getValue(operands[1]); + // Register 33 is HIGH and 34 is LOW + RegisterFile.updateRegister(33, (int) (product >> 32)); + RegisterFile.updateRegister(34, (int) ((product << 32) >> 32)); + } + })); + instructionList.add( + new BasicInstruction("multu $t1,$t2", + "Multiplication unsigned : Set HI to high-order 32 bits, LO to low-order 32 bits of the product of unsigned $t1 and $t2 (use mfhi to access HI, mflo to access LO)", BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 011001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - long product = (((long) RegisterFile.getValue(operands[0]))<<32>>>32) - * (((long) RegisterFile.getValue(operands[1]))<<32>>>32); - // Register 33 is HIGH and 34 is LOW - RegisterFile.updateRegister(33, (int) (product >> 32)); - RegisterFile.updateRegister(34, (int) ((product << 32) >> 32)); - } - })); - instructionList.add( - new BasicInstruction("mul $t1,$t2,$t3", - "Multiplication without overflow : Set HI to high-order 32 bits, LO and $t1 to low-order 32 bits of the product of $t2 and $t3 (use mfhi to access HI, mflo to access LO)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + long product = (((long) RegisterFile.getValue(operands[0])) << 32 >>> 32) + * (((long) RegisterFile.getValue(operands[1])) << 32 >>> 32); + // Register 33 is HIGH and 34 is LOW + RegisterFile.updateRegister(33, (int) (product >> 32)); + RegisterFile.updateRegister(34, (int) ((product << 32) >> 32)); + } + })); + instructionList.add( + new BasicInstruction("mul $t1,$t2,$t3", + "Multiplication without overflow : Set HI to high-order 32 bits, LO and $t1 to low-order 32 bits of the product of $t2 and $t3 (use mfhi to access HI, mflo to access LO)", BasicInstructionFormat.R_FORMAT, "011100 sssss ttttt fffff 00000 000010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - long product = (long) RegisterFile.getValue(operands[1]) - * (long) RegisterFile.getValue(operands[2]); - RegisterFile.updateRegister(operands[0], - (int) ((product << 32) >> 32)); - // Register 33 is HIGH and 34 is LOW. Not required by MIPS; SPIM does it. - RegisterFile.updateRegister(33, (int) (product >> 32)); - RegisterFile.updateRegister(34, (int) ((product << 32) >> 32)); - } - })); - instructionList.add( - new BasicInstruction("madd $t1,$t2", - "Multiply add : Multiply $t1 by $t2 then increment HI by high-order 32 bits of product, increment LO by low-order 32 bits of product (use mfhi to access HI, mflo to access LO)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + long product = (long) RegisterFile.getValue(operands[1]) + * (long) RegisterFile.getValue(operands[2]); + RegisterFile.updateRegister(operands[0], + (int) ((product << 32) >> 32)); + // Register 33 is HIGH and 34 is LOW. Not required by MIPS; SPIM does it. + RegisterFile.updateRegister(33, (int) (product >> 32)); + RegisterFile.updateRegister(34, (int) ((product << 32) >> 32)); + } + })); + instructionList.add( + new BasicInstruction("madd $t1,$t2", + "Multiply add : Multiply $t1 by $t2 then increment HI by high-order 32 bits of product, increment LO by low-order 32 bits of product (use mfhi to access HI, mflo to access LO)", BasicInstructionFormat.R_FORMAT, "011100 fffff sssss 00000 00000 000000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - long product = (long) RegisterFile.getValue(operands[0]) - * (long) RegisterFile.getValue(operands[1]); - // Register 33 is HIGH and 34 is LOW. - long contentsHiLo = Binary.twoIntsToLong( - RegisterFile.getValue(33), RegisterFile.getValue(34)); - long sum = contentsHiLo + product; - RegisterFile.updateRegister(33, Binary.highOrderLongToInt(sum)); - RegisterFile.updateRegister(34, Binary.lowOrderLongToInt(sum)); - } - })); - instructionList.add( - new BasicInstruction("maddu $t1,$t2", - "Multiply add unsigned : Multiply $t1 by $t2 then increment HI by high-order 32 bits of product, increment LO by low-order 32 bits of product, unsigned (use mfhi to access HI, mflo to access LO)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + long product = (long) RegisterFile.getValue(operands[0]) + * (long) RegisterFile.getValue(operands[1]); + // Register 33 is HIGH and 34 is LOW. + long contentsHiLo = Binary.twoIntsToLong( + RegisterFile.getValue(33), RegisterFile.getValue(34)); + long sum = contentsHiLo + product; + RegisterFile.updateRegister(33, Binary.highOrderLongToInt(sum)); + RegisterFile.updateRegister(34, Binary.lowOrderLongToInt(sum)); + } + })); + instructionList.add( + new BasicInstruction("maddu $t1,$t2", + "Multiply add unsigned : Multiply $t1 by $t2 then increment HI by high-order 32 bits of product, increment LO by low-order 32 bits of product, unsigned (use mfhi to access HI, mflo to access LO)", BasicInstructionFormat.R_FORMAT, "011100 fffff sssss 00000 00000 000001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - long product = (((long) RegisterFile.getValue(operands[0]))<<32>>>32) - * (((long) RegisterFile.getValue(operands[1]))<<32>>>32); - // Register 33 is HIGH and 34 is LOW. - long contentsHiLo = Binary.twoIntsToLong( - RegisterFile.getValue(33), RegisterFile.getValue(34)); - long sum = contentsHiLo + product; - RegisterFile.updateRegister(33, Binary.highOrderLongToInt(sum)); - RegisterFile.updateRegister(34, Binary.lowOrderLongToInt(sum)); - } - })); - instructionList.add( - new BasicInstruction("msub $t1,$t2", - "Multiply subtract : Multiply $t1 by $t2 then decrement HI by high-order 32 bits of product, decrement LO by low-order 32 bits of product (use mfhi to access HI, mflo to access LO)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + long product = (((long) RegisterFile.getValue(operands[0])) << 32 >>> 32) + * (((long) RegisterFile.getValue(operands[1])) << 32 >>> 32); + // Register 33 is HIGH and 34 is LOW. + long contentsHiLo = Binary.twoIntsToLong( + RegisterFile.getValue(33), RegisterFile.getValue(34)); + long sum = contentsHiLo + product; + RegisterFile.updateRegister(33, Binary.highOrderLongToInt(sum)); + RegisterFile.updateRegister(34, Binary.lowOrderLongToInt(sum)); + } + })); + instructionList.add( + new BasicInstruction("msub $t1,$t2", + "Multiply subtract : Multiply $t1 by $t2 then decrement HI by high-order 32 bits of product, decrement LO by low-order 32 bits of product (use mfhi to access HI, mflo to access LO)", BasicInstructionFormat.R_FORMAT, "011100 fffff sssss 00000 00000 000100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - long product = (long) RegisterFile.getValue(operands[0]) - * (long) RegisterFile.getValue(operands[1]); - // Register 33 is HIGH and 34 is LOW. - long contentsHiLo = Binary.twoIntsToLong( - RegisterFile.getValue(33), RegisterFile.getValue(34)); - long diff = contentsHiLo - product; - RegisterFile.updateRegister(33, Binary.highOrderLongToInt(diff)); - RegisterFile.updateRegister(34, Binary.lowOrderLongToInt(diff)); - } - })); - instructionList.add( - new BasicInstruction("msubu $t1,$t2", - "Multiply subtract unsigned : Multiply $t1 by $t2 then decrement HI by high-order 32 bits of product, decement LO by low-order 32 bits of product, unsigned (use mfhi to access HI, mflo to access LO)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + long product = (long) RegisterFile.getValue(operands[0]) + * (long) RegisterFile.getValue(operands[1]); + // Register 33 is HIGH and 34 is LOW. + long contentsHiLo = Binary.twoIntsToLong( + RegisterFile.getValue(33), RegisterFile.getValue(34)); + long diff = contentsHiLo - product; + RegisterFile.updateRegister(33, Binary.highOrderLongToInt(diff)); + RegisterFile.updateRegister(34, Binary.lowOrderLongToInt(diff)); + } + })); + instructionList.add( + new BasicInstruction("msubu $t1,$t2", + "Multiply subtract unsigned : Multiply $t1 by $t2 then decrement HI by high-order 32 bits of product, decement LO by low-order 32 bits of product, unsigned (use mfhi to access HI, mflo to access LO)", BasicInstructionFormat.R_FORMAT, "011100 fffff sssss 00000 00000 000101", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - long product = (((long) RegisterFile.getValue(operands[0]))<<32>>>32) - * (((long) RegisterFile.getValue(operands[1]))<<32>>>32); - // Register 33 is HIGH and 34 is LOW. - long contentsHiLo = Binary.twoIntsToLong( - RegisterFile.getValue(33), RegisterFile.getValue(34)); - long diff = contentsHiLo - product; - RegisterFile.updateRegister(33, Binary.highOrderLongToInt(diff)); - RegisterFile.updateRegister(34, Binary.lowOrderLongToInt(diff)); - } - })); - instructionList.add( - new BasicInstruction("div $t1,$t2", - "Division with overflow : Divide $t1 by $t2 then set LO to quotient and HI to remainder (use mfhi to access HI, mflo to access LO)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + long product = (((long) RegisterFile.getValue(operands[0])) << 32 >>> 32) + * (((long) RegisterFile.getValue(operands[1])) << 32 >>> 32); + // Register 33 is HIGH and 34 is LOW. + long contentsHiLo = Binary.twoIntsToLong( + RegisterFile.getValue(33), RegisterFile.getValue(34)); + long diff = contentsHiLo - product; + RegisterFile.updateRegister(33, Binary.highOrderLongToInt(diff)); + RegisterFile.updateRegister(34, Binary.lowOrderLongToInt(diff)); + } + })); + instructionList.add( + new BasicInstruction("div $t1,$t2", + "Division with overflow : Divide $t1 by $t2 then set LO to quotient and HI to remainder (use mfhi to access HI, mflo to access LO)", BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 011010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[1]) == 0) - { - // Note: no exceptions and undefined results for zero div - // COD3 Appendix A says "with overflow" but MIPS 32 instruction set - // specification says "no arithmetic exception under any circumstances". - return; - } - - // Register 33 is HIGH and 34 is LOW - RegisterFile.updateRegister(33, - RegisterFile.getValue(operands[0]) - % RegisterFile.getValue(operands[1])); - RegisterFile.updateRegister(34, - RegisterFile.getValue(operands[0]) - / RegisterFile.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("divu $t1,$t2", - "Division unsigned without overflow : Divide unsigned $t1 by $t2 then set LO to quotient and HI to remainder (use mfhi to access HI, mflo to access LO)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[1]) == 0) + { + // Note: no exceptions and undefined results for zero div + // COD3 Appendix A says "with overflow" but MIPS 32 instruction set + // specification says "no arithmetic exception under any circumstances". + return; + } + + // Register 33 is HIGH and 34 is LOW + RegisterFile.updateRegister(33, + RegisterFile.getValue(operands[0]) + % RegisterFile.getValue(operands[1])); + RegisterFile.updateRegister(34, + RegisterFile.getValue(operands[0]) + / RegisterFile.getValue(operands[1])); + } + })); + instructionList.add( + new BasicInstruction("divu $t1,$t2", + "Division unsigned without overflow : Divide unsigned $t1 by $t2 then set LO to quotient and HI to remainder (use mfhi to access HI, mflo to access LO)", BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 011011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[1]) == 0) - { - // Note: no exceptions, and undefined results for zero divide - return; - } - long oper1 = ((long)RegisterFile.getValue(operands[0])) << 32 >>> 32; - long oper2 = ((long)RegisterFile.getValue(operands[1])) << 32 >>> 32; - // Register 33 is HIGH and 34 is LOW - RegisterFile.updateRegister(33, - (int) (((oper1 % oper2) << 32) >> 32)); - RegisterFile.updateRegister(34, - (int) (((oper1 / oper2) << 32) >> 32)); - } - })); - instructionList.add( - new BasicInstruction("mfhi $t1", - "Move from HI register : Set $t1 to contents of HI (see multiply and divide operations)", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[1]) == 0) + { + // Note: no exceptions, and undefined results for zero divide + return; + } + long oper1 = ((long) RegisterFile.getValue(operands[0])) << 32 >>> 32; + long oper2 = ((long) RegisterFile.getValue(operands[1])) << 32 >>> 32; + // Register 33 is HIGH and 34 is LOW + RegisterFile.updateRegister(33, + (int) (((oper1 % oper2) << 32) >> 32)); + RegisterFile.updateRegister(34, + (int) (((oper1 / oper2) << 32) >> 32)); + } + })); + instructionList.add( + new BasicInstruction("mfhi $t1", + "Move from HI register : Set $t1 to contents of HI (see multiply and divide operations)", + BasicInstructionFormat.R_FORMAT, "000000 00000 00000 fffff 00000 010000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(33)); - } - })); - instructionList.add( - new BasicInstruction("mflo $t1", - "Move from LO register : Set $t1 to contents of LO (see multiply and divide operations)", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(33)); + } + })); + instructionList.add( + new BasicInstruction("mflo $t1", + "Move from LO register : Set $t1 to contents of LO (see multiply and divide operations)", + BasicInstructionFormat.R_FORMAT, "000000 00000 00000 fffff 00000 010010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(34)); - } - })); - instructionList.add( - new BasicInstruction("mthi $t1", - "Move to HI registerr : Set HI to contents of $t1 (see multiply and divide operations)", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(34)); + } + })); + instructionList.add( + new BasicInstruction("mthi $t1", + "Move to HI registerr : Set HI to contents of $t1 (see multiply and divide operations)", + BasicInstructionFormat.R_FORMAT, "000000 fffff 00000 00000 00000 010001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(33, - RegisterFile.getValue(operands[0])); - } - })); - instructionList.add( - new BasicInstruction("mtlo $t1", - "Move to LO register : Set LO to contents of $t1 (see multiply and divide operations)", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(33, + RegisterFile.getValue(operands[0])); + } + })); + instructionList.add( + new BasicInstruction("mtlo $t1", + "Move to LO register : Set LO to contents of $t1 (see multiply and divide operations)", + BasicInstructionFormat.R_FORMAT, "000000 fffff 00000 00000 00000 010011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(34, - RegisterFile.getValue(operands[0])); - } - })); - instructionList.add( - new BasicInstruction("and $t1,$t2,$t3", - "Bitwise AND : Set $t1 to bitwise AND of $t2 and $t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(34, + RegisterFile.getValue(operands[0])); + } + })); + instructionList.add( + new BasicInstruction("and $t1,$t2,$t3", + "Bitwise AND : Set $t1 to bitwise AND of $t2 and $t3", BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 100100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) - & RegisterFile.getValue(operands[2])); - } - })); - instructionList.add( - new BasicInstruction("or $t1,$t2,$t3", - "Bitwise OR : Set $t1 to bitwise OR of $t2 and $t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) + & RegisterFile.getValue(operands[2])); + } + })); + instructionList.add( + new BasicInstruction("or $t1,$t2,$t3", + "Bitwise OR : Set $t1 to bitwise OR of $t2 and $t3", BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 100101", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) - | RegisterFile.getValue(operands[2])); - } - })); - instructionList.add( - new BasicInstruction("andi $t1,$t2,100", - "Bitwise AND immediate : Set $t1 to bitwise AND of $t2 and zero-extended 16-bit immediate", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) + | RegisterFile.getValue(operands[2])); + } + })); + instructionList.add( + new BasicInstruction("andi $t1,$t2,100", + "Bitwise AND immediate : Set $t1 to bitwise AND of $t2 and zero-extended 16-bit immediate", BasicInstructionFormat.I_FORMAT, "001100 sssss fffff tttttttttttttttt", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // ANDing with 0x0000FFFF zero-extends the immediate (high 16 bits always 0). - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) - & (operands[2] & 0x0000FFFF)); - } - })); - instructionList.add( - new BasicInstruction("ori $t1,$t2,100", - "Bitwise OR immediate : Set $t1 to bitwise OR of $t2 and zero-extended 16-bit immediate", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // ANDing with 0x0000FFFF zero-extends the immediate (high 16 bits always 0). + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) + & (operands[2] & 0x0000FFFF)); + } + })); + instructionList.add( + new BasicInstruction("ori $t1,$t2,100", + "Bitwise OR immediate : Set $t1 to bitwise OR of $t2 and zero-extended 16-bit immediate", BasicInstructionFormat.I_FORMAT, "001101 sssss fffff tttttttttttttttt", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // ANDing with 0x0000FFFF zero-extends the immediate (high 16 bits always 0). - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) - | (operands[2] & 0x0000FFFF)); - } - })); - instructionList.add( - new BasicInstruction("nor $t1,$t2,$t3", - "Bitwise NOR : Set $t1 to bitwise NOR of $t2 and $t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // ANDing with 0x0000FFFF zero-extends the immediate (high 16 bits always 0). + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) + | (operands[2] & 0x0000FFFF)); + } + })); + instructionList.add( + new BasicInstruction("nor $t1,$t2,$t3", + "Bitwise NOR : Set $t1 to bitwise NOR of $t2 and $t3", BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 100111", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - ~(RegisterFile.getValue(operands[1]) - | RegisterFile.getValue(operands[2]))); - } - })); - instructionList.add( - new BasicInstruction("xor $t1,$t2,$t3", - "Bitwise XOR (exclusive OR) : Set $t1 to bitwise XOR of $t2 and $t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + ~(RegisterFile.getValue(operands[1]) + | RegisterFile.getValue(operands[2]))); + } + })); + instructionList.add( + new BasicInstruction("xor $t1,$t2,$t3", + "Bitwise XOR (exclusive OR) : Set $t1 to bitwise XOR of $t2 and $t3", BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 100110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) - ^ RegisterFile.getValue(operands[2])); - } - })); - instructionList.add( - new BasicInstruction("xori $t1,$t2,100", - "Bitwise XOR immediate : Set $t1 to bitwise XOR of $t2 and zero-extended 16-bit immediate", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) + ^ RegisterFile.getValue(operands[2])); + } + })); + instructionList.add( + new BasicInstruction("xori $t1,$t2,100", + "Bitwise XOR immediate : Set $t1 to bitwise XOR of $t2 and zero-extended 16-bit immediate", BasicInstructionFormat.I_FORMAT, "001110 sssss fffff tttttttttttttttt", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // ANDing with 0x0000FFFF zero-extends the immediate (high 16 bits always 0). - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) - ^ (operands[2] & 0x0000FFFF)); - } - })); - instructionList.add( - new BasicInstruction("sll $t1,$t2,10", - "Shift left logical : Set $t1 to result of shifting $t2 left by number of bits specified by immediate", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // ANDing with 0x0000FFFF zero-extends the immediate (high 16 bits always 0). + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) + ^ (operands[2] & 0x0000FFFF)); + } + })); + instructionList.add( + new BasicInstruction("sll $t1,$t2,10", + "Shift left logical : Set $t1 to result of shifting $t2 left by number of bits specified by immediate", BasicInstructionFormat.R_FORMAT, "000000 00000 sssss fffff ttttt 000000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) << operands[2]); - } - })); - instructionList.add( - new BasicInstruction("sllv $t1,$t2,$t3", - "Shift left logical variable : Set $t1 to result of shifting $t2 left by number of bits specified by value in low-order 5 bits of $t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) << operands[2]); + } + })); + instructionList.add( + new BasicInstruction("sllv $t1,$t2,$t3", + "Shift left logical variable : Set $t1 to result of shifting $t2 left by number of bits specified by value in low-order 5 bits of $t3", BasicInstructionFormat.R_FORMAT, "000000 ttttt sssss fffff 00000 000100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // Mask all but low 5 bits of register containing shamt. - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) << - (RegisterFile.getValue(operands[2]) & 0x0000001F)); - } - })); - instructionList.add( - new BasicInstruction("srl $t1,$t2,10", - "Shift right logical : Set $t1 to result of shifting $t2 right by number of bits specified by immediate", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // Mask all but low 5 bits of register containing shamt. + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) << + (RegisterFile.getValue(operands[2]) & 0x0000001F)); + } + })); + instructionList.add( + new BasicInstruction("srl $t1,$t2,10", + "Shift right logical : Set $t1 to result of shifting $t2 right by number of bits specified by immediate", BasicInstructionFormat.R_FORMAT, "000000 00000 sssss fffff ttttt 000010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // must zero-fill, so use ">>>" instead of ">>". - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) >>> operands[2]); - } - })); - instructionList.add( - new BasicInstruction("sra $t1,$t2,10", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // must zero-fill, so use ">>>" instead of ">>". + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) >>> operands[2]); + } + })); + instructionList.add( + new BasicInstruction("sra $t1,$t2,10", "Shift right arithmetic : Set $t1 to result of sign-extended shifting $t2 right by number of bits specified by immediate", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 00000 sssss fffff ttttt 000011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // must sign-fill, so use ">>". - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) >> operands[2]); - } - })); - instructionList.add( - new BasicInstruction("srav $t1,$t2,$t3", - "Shift right arithmetic variable : Set $t1 to result of sign-extended shifting $t2 right by number of bits specified by value in low-order 5 bits of $t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // must sign-fill, so use ">>". + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) >> operands[2]); + } + })); + instructionList.add( + new BasicInstruction("srav $t1,$t2,$t3", + "Shift right arithmetic variable : Set $t1 to result of sign-extended shifting $t2 right by number of bits specified by value in low-order 5 bits of $t3", BasicInstructionFormat.R_FORMAT, "000000 ttttt sssss fffff 00000 000111", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // Mask all but low 5 bits of register containing shamt.Use ">>" to sign-fill. - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) >> - (RegisterFile.getValue(operands[2]) & 0x0000001F)); - } - })); - instructionList.add( - new BasicInstruction("srlv $t1,$t2,$t3", - "Shift right logical variable : Set $t1 to result of shifting $t2 right by number of bits specified by value in low-order 5 bits of $t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // Mask all but low 5 bits of register containing shamt.Use ">>" to sign-fill. + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) >> + (RegisterFile.getValue(operands[2]) & 0x0000001F)); + } + })); + instructionList.add( + new BasicInstruction("srlv $t1,$t2,$t3", + "Shift right logical variable : Set $t1 to result of shifting $t2 right by number of bits specified by value in low-order 5 bits of $t3", BasicInstructionFormat.R_FORMAT, "000000 ttttt sssss fffff 00000 000110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // Mask all but low 5 bits of register containing shamt.Use ">>>" to zero-fill. - RegisterFile.updateRegister(operands[0], - RegisterFile.getValue(operands[1]) >>> - (RegisterFile.getValue(operands[2]) & 0x0000001F)); - } - })); - instructionList.add( - new BasicInstruction("lw $t1,-100($t2)", - "Load word : Set $t1 to contents of effective memory word address", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // Mask all but low 5 bits of register containing shamt.Use ">>>" to zero-fill. + RegisterFile.updateRegister(operands[0], + RegisterFile.getValue(operands[1]) >>> + (RegisterFile.getValue(operands[2]) & 0x0000001F)); + } + })); + instructionList.add( + new BasicInstruction("lw $t1,-100($t2)", + "Load word : Set $t1 to contents of effective memory word address", BasicInstructionFormat.I_FORMAT, "100011 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - RegisterFile.updateRegister(operands[0], - Globals.memory.getWord( - RegisterFile.getValue(operands[2]) + operands[1])); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + RegisterFile.updateRegister(operands[0], + Globals.memory.getWord( + RegisterFile.getValue(operands[2]) + operands[1])); } - } - })); - instructionList.add( - new BasicInstruction("ll $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("ll $t1,-100($t2)", "Load linked : Paired with Store Conditional (sc) to perform atomic read-modify-write. Treated as equivalent to Load Word (lw) because MARS does not simulate multiple processors.", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "110000 ttttt fffff ssssssssssssssss", - // The ll (load link) command is supposed to be the front end of an atomic - // operation completed by sc (store conditional), with success or failure - // of the store depending on whether the memory block containing the - // loaded word is modified in the meantime by a different processor. - // Since MARS, like SPIM simulates only a single processor, the store - // conditional will always succeed so there is no need to do anything - // special here. In that case, ll is same as lw. And sc does the same - // thing as sw except in addition it writes 1 into the source register. + // The ll (load link) command is supposed to be the front end of an atomic + // operation completed by sc (store conditional), with success or failure + // of the store depending on whether the memory block containing the + // loaded word is modified in the meantime by a different processor. + // Since MARS, like SPIM simulates only a single processor, the store + // conditional will always succeed so there is no need to do anything + // special here. In that case, ll is same as lw. And sc does the same + // thing as sw except in addition it writes 1 into the source register. new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - RegisterFile.updateRegister(operands[0], - Globals.memory.getWord( - RegisterFile.getValue(operands[2]) + operands[1])); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + RegisterFile.updateRegister(operands[0], + Globals.memory.getWord( + RegisterFile.getValue(operands[2]) + operands[1])); } - } - })); - instructionList.add( - new BasicInstruction("lwl $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("lwl $t1,-100($t2)", "Load word left : Load from 1 to 4 bytes left-justified into $t1, starting with effective memory byte address and continuing through the low-order byte of its word", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "100010 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - int address = RegisterFile.getValue(operands[2]) + operands[1]; - int result = RegisterFile.getValue(operands[0]); - for (int i=0; i<=address % Globals.memory.WORD_LENGTH_BYTES; i++) { - result = Binary.setByte(result,3-i,Globals.memory.getByte(address-i)); - } - RegisterFile.updateRegister(operands[0], result); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + int address = RegisterFile.getValue(operands[2]) + operands[1]; + int result = RegisterFile.getValue(operands[0]); + for (int i = 0; i <= address % Memory.WORD_LENGTH_BYTES; i++) + { + result = Binary.setByte(result, 3 - i, Globals.memory.getByte(address - i)); + } + RegisterFile.updateRegister(operands[0], result); } - } - })); - instructionList.add( - new BasicInstruction("lwr $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("lwr $t1,-100($t2)", "Load word right : Load from 1 to 4 bytes right-justified into $t1, starting with effective memory byte address and continuing through the high-order byte of its word", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "100110 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - int address = RegisterFile.getValue(operands[2]) + operands[1]; - int result = RegisterFile.getValue(operands[0]); - for (int i=0; i<=3-(address % Globals.memory.WORD_LENGTH_BYTES); i++) { - result = Binary.setByte(result,i,Globals.memory.getByte(address+i)); - } - RegisterFile.updateRegister(operands[0], result); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + int address = RegisterFile.getValue(operands[2]) + operands[1]; + int result = RegisterFile.getValue(operands[0]); + for (int i = 0; i <= 3 - (address % Memory.WORD_LENGTH_BYTES); i++) + { + result = Binary.setByte(result, i, Globals.memory.getByte(address + i)); + } + RegisterFile.updateRegister(operands[0], result); } - } - })); - instructionList.add( - new BasicInstruction("sw $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("sw $t1,-100($t2)", "Store word : Store contents of $t1 into effective memory word address", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "101011 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - Globals.memory.setWord( - RegisterFile.getValue(operands[2]) + operands[1], - RegisterFile.getValue(operands[0])); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + Globals.memory.setWord( + RegisterFile.getValue(operands[2]) + operands[1], + RegisterFile.getValue(operands[0])); } - } - })); - instructionList.add( - new BasicInstruction("sc $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("sc $t1,-100($t2)", "Store conditional : Paired with Load Linked (ll) to perform atomic read-modify-write. Stores $t1 value into effective address, then sets $t1 to 1 for success. Always succeeds because MARS does not simulate multiple processors.", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "111000 ttttt fffff ssssssssssssssss", - // See comments with "ll" instruction above. "sc" is implemented - // like "sw", except that 1 is placed in the source register. + // See comments with "ll" instruction above. "sc" is implemented + // like "sw", except that 1 is placed in the source register. new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - Globals.memory.setWord( - RegisterFile.getValue(operands[2]) + operands[1], - RegisterFile.getValue(operands[0])); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + Globals.memory.setWord( + RegisterFile.getValue(operands[2]) + operands[1], + RegisterFile.getValue(operands[0])); } - RegisterFile.updateRegister(operands[0],1); // always succeeds - } - })); - instructionList.add( - new BasicInstruction("swl $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + RegisterFile.updateRegister(operands[0], 1); // always succeeds + } + })); + instructionList.add( + new BasicInstruction("swl $t1,-100($t2)", "Store word left : Store high-order 1 to 4 bytes of $t1 into memory, starting with effective byte address and continuing through the low-order byte of its word", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "101010 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - int address = RegisterFile.getValue(operands[2]) + operands[1]; - int source = RegisterFile.getValue(operands[0]); - for (int i=0; i<=address % Globals.memory.WORD_LENGTH_BYTES; i++) { - Globals.memory.setByte(address-i,Binary.getByte(source,3-i)); - } - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + int address = RegisterFile.getValue(operands[2]) + operands[1]; + int source = RegisterFile.getValue(operands[0]); + for (int i = 0; i <= address % Memory.WORD_LENGTH_BYTES; i++) + { + Globals.memory.setByte(address - i, Binary.getByte(source, 3 - i)); + } } - } - })); - instructionList.add( - new BasicInstruction("swr $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("swr $t1,-100($t2)", "Store word right : Store low-order 1 to 4 bytes of $t1 into memory, starting with high-order byte of word containing effective byte address and continuing through that byte address", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "101110 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - int address = RegisterFile.getValue(operands[2]) + operands[1]; - int source = RegisterFile.getValue(operands[0]); - for (int i=0; i<=3-(address % Globals.memory.WORD_LENGTH_BYTES); i++) { - Globals.memory.setByte(address+i,Binary.getByte(source,i)); - } - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + int address = RegisterFile.getValue(operands[2]) + operands[1]; + int source = RegisterFile.getValue(operands[0]); + for (int i = 0; i <= 3 - (address % Memory.WORD_LENGTH_BYTES); i++) + { + Globals.memory.setByte(address + i, Binary.getByte(source, i)); + } } - } - })); - instructionList.add( - new BasicInstruction("lui $t1,100", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("lui $t1,100", "Load upper immediate : Set high-order 16 bits of $t1 to 16-bit immediate and low-order 16 bits to 0", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "001111 00000 fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], operands[1] << 16); - } - })); - instructionList.add( - new BasicInstruction("beq $t1,$t2,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], operands[1] << 16); + } + })); + instructionList.add( + new BasicInstruction("beq $t1,$t2,label", "Branch if equal : Branch to statement at label's address if $t1 and $t2 are equal", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "000100 fffff sssss tttttttttttttttt", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - - if (RegisterFile.getValue(operands[0]) - == RegisterFile.getValue(operands[1])) - { - processBranch(operands[2]); - } - } - })); - instructionList.add( - new BasicInstruction("bne $t1,$t2,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + + if (RegisterFile.getValue(operands[0]) + == RegisterFile.getValue(operands[1])) + { + processBranch(operands[2]); + } + } + })); + instructionList.add( + new BasicInstruction("bne $t1,$t2,label", "Branch if not equal : Branch to statement at label's address if $t1 and $t2 are not equal", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "000101 fffff sssss tttttttttttttttt", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) - != RegisterFile.getValue(operands[1])) - { - processBranch(operands[2]); - } - } - })); - instructionList.add( - new BasicInstruction("bgez $t1,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) + != RegisterFile.getValue(operands[1])) + { + processBranch(operands[2]); + } + } + })); + instructionList.add( + new BasicInstruction("bgez $t1,label", "Branch if greater than or equal to zero : Branch to statement at label's address if $t1 is greater than or equal to zero", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "000001 fffff 00001 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) >= 0) - { - processBranch(operands[1]); - } - } - })); - instructionList.add( - new BasicInstruction("bgezal $t1,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) >= 0) + { + processBranch(operands[1]); + } + } + })); + instructionList.add( + new BasicInstruction("bgezal $t1,label", "Branch if greater then or equal to zero and link : If $t1 is greater than or equal to zero, then set $ra to the Program Counter and branch to statement at label's address", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "000001 fffff 10001 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) >= 0) - { // the "and link" part - processReturnAddress(31);//RegisterFile.updateRegister("$ra",RegisterFile.getProgramCounter()); - processBranch(operands[1]); - } - } - })); - instructionList.add( - new BasicInstruction("bgtz $t1,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) >= 0) + { // the "and link" part + processReturnAddress(31);//RegisterFile.updateRegister("$ra",RegisterFile.getProgramCounter()); + processBranch(operands[1]); + } + } + })); + instructionList.add( + new BasicInstruction("bgtz $t1,label", "Branch if greater than zero : Branch to statement at label's address if $t1 is greater than zero", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "000111 fffff 00000 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) > 0) - { - processBranch(operands[1]); - } - } - })); - instructionList.add( - new BasicInstruction("blez $t1,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) > 0) + { + processBranch(operands[1]); + } + } + })); + instructionList.add( + new BasicInstruction("blez $t1,label", "Branch if less than or equal to zero : Branch to statement at label's address if $t1 is less than or equal to zero", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "000110 fffff 00000 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) <= 0) - { - processBranch(operands[1]); - } - } - })); - instructionList.add( - new BasicInstruction("bltz $t1,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) <= 0) + { + processBranch(operands[1]); + } + } + })); + instructionList.add( + new BasicInstruction("bltz $t1,label", "Branch if less than zero : Branch to statement at label's address if $t1 is less than zero", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "000001 fffff 00000 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) < 0) - { - processBranch(operands[1]); - } - } - })); - instructionList.add( - new BasicInstruction("bltzal $t1,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) < 0) + { + processBranch(operands[1]); + } + } + })); + instructionList.add( + new BasicInstruction("bltzal $t1,label", "Branch if less than zero and link : If $t1 is less than or equal to zero, then set $ra to the Program Counter and branch to statement at label's address", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "000001 fffff 10000 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) < 0) - { // the "and link" part - processReturnAddress(31);//RegisterFile.updateRegister("$ra",RegisterFile.getProgramCounter()); - processBranch(operands[1]); - } - } - })); - instructionList.add( - new BasicInstruction("slt $t1,$t2,$t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) < 0) + { // the "and link" part + processReturnAddress(31);//RegisterFile.updateRegister("$ra",RegisterFile.getProgramCounter()); + processBranch(operands[1]); + } + } + })); + instructionList.add( + new BasicInstruction("slt $t1,$t2,$t3", "Set less than : If $t2 is less than $t3, then set $t1 to 1 else set $t1 to 0", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 101010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - (RegisterFile.getValue(operands[1]) - < RegisterFile.getValue(operands[2])) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + (RegisterFile.getValue(operands[1]) + < RegisterFile.getValue(operands[2])) ? 1 : 0); - } - })); - instructionList.add( - new BasicInstruction("sltu $t1,$t2,$t3", + } + })); + instructionList.add( + new BasicInstruction("sltu $t1,$t2,$t3", "Set less than unsigned : If $t2 is less than $t3 using unsigned comparision, then set $t1 to 1 else set $t1 to 0", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 101011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int first = RegisterFile.getValue(operands[1]); - int second = RegisterFile.getValue(operands[2]); - if (first >= 0 && second >= 0 || first < 0 && second < 0) - { - RegisterFile.updateRegister(operands[0], - (first < second) ? 1 : 0); - } - else - { - RegisterFile.updateRegister(operands[0], - (first >= 0) ? 1 : 0); - } - } - })); - instructionList.add( - new BasicInstruction("slti $t1,$t2,-100", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int first = RegisterFile.getValue(operands[1]); + int second = RegisterFile.getValue(operands[2]); + if (first >= 0 && second >= 0 || first < 0 && second < 0) + { + RegisterFile.updateRegister(operands[0], + (first < second) ? 1 : 0); + } + else + { + RegisterFile.updateRegister(operands[0], + (first >= 0) ? 1 : 0); + } + } + })); + instructionList.add( + new BasicInstruction("slti $t1,$t2,-100", "Set less than immediate : If $t2 is less than sign-extended 16-bit immediate, then set $t1 to 1 else set $t1 to 0", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "001010 sssss fffff tttttttttttttttt", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // 16 bit immediate value in operands[2] is sign-extended - RegisterFile.updateRegister(operands[0], - (RegisterFile.getValue(operands[1]) - < (operands[2] << 16 >> 16)) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // 16 bit immediate value in operands[2] is sign-extended + RegisterFile.updateRegister(operands[0], + (RegisterFile.getValue(operands[1]) + < (operands[2] << 16 >> 16)) ? 1 : 0); - } - })); - instructionList.add( - new BasicInstruction("sltiu $t1,$t2,-100", + } + })); + instructionList.add( + new BasicInstruction("sltiu $t1,$t2,-100", "Set less than immediate unsigned : If $t2 is less than sign-extended 16-bit immediate using unsigned comparison, then set $t1 to 1 else set $t1 to 0", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "001011 sssss fffff tttttttttttttttt", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int first = RegisterFile.getValue(operands[1]); - // 16 bit immediate value in operands[2] is sign-extended - int second = operands[2] << 16 >> 16; - if (first >= 0 && second >= 0 || first < 0 && second < 0) - { - RegisterFile.updateRegister(operands[0], - (first < second) ? 1 : 0); - } - else - { - RegisterFile.updateRegister(operands[0], - (first >= 0) ? 1 : 0); - } - } - })); - instructionList.add( - new BasicInstruction("movn $t1,$t2,$t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int first = RegisterFile.getValue(operands[1]); + // 16 bit immediate value in operands[2] is sign-extended + int second = operands[2] << 16 >> 16; + if (first >= 0 && second >= 0 || first < 0 && second < 0) + { + RegisterFile.updateRegister(operands[0], + (first < second) ? 1 : 0); + } + else + { + RegisterFile.updateRegister(operands[0], + (first >= 0) ? 1 : 0); + } + } + })); + instructionList.add( + new BasicInstruction("movn $t1,$t2,$t3", "Move conditional not zero : Set $t1 to $t2 if $t3 is not zero", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 001011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[2])!=0) - RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movz $t1,$t2,$t3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[2]) != 0) + { + RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movz $t1,$t2,$t3", "Move conditional zero : Set $t1 to $t2 if $t3 is zero", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 sssss ttttt fffff 00000 001010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[2])==0) - RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movf $t1,$t2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[2]) == 0) + { + RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movf $t1,$t2", "Move if FP condition flag 0 false : Set $t1 to $t2 if FPU (Coprocessor 1) condition flag 0 is false (zero)", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 sssss 000 00 fffff 00000 000001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(0)==0) - RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movf $t1,$t2,1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(0) == 0) + { + RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movf $t1,$t2,1", "Move if specified FP condition flag false : Set $t1 to $t2 if FPU (Coprocessor 1) condition flag specified by the immediate is false (zero)", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 sssss ttt 00 fffff 00000 000001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(operands[2])==0) - RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movt $t1,$t2", - "Move if FP condition flag 0 true : Set $t1 to $t2 if FPU (Coprocessor 1) condition flag 0 is true (one)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(operands[2]) == 0) + { + RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movt $t1,$t2", + "Move if FP condition flag 0 true : Set $t1 to $t2 if FPU (Coprocessor 1) condition flag 0 is true (one)", BasicInstructionFormat.R_FORMAT, "000000 sssss 000 01 fffff 00000 000001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(0)==1) - RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movt $t1,$t2,1", - "Move if specfied FP condition flag true : Set $t1 to $t2 if FPU (Coprocessor 1) condition flag specified by the immediate is true (one)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(0) == 1) + { + RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movt $t1,$t2,1", + "Move if specfied FP condition flag true : Set $t1 to $t2 if FPU (Coprocessor 1) condition flag specified by the immediate is true (one)", BasicInstructionFormat.R_FORMAT, "000000 sssss ttt 01 fffff 00000 000001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(operands[2])==1) - RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("break 100", - "Break execution with code : Terminate program execution with specified exception code", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(operands[2]) == 1) + { + RegisterFile.updateRegister(operands[0], RegisterFile.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("break 100", + "Break execution with code : Terminate program execution with specified exception code", + BasicInstructionFormat.R_FORMAT, "000000 ffffffffffffffffffff 001101", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { // At this time I don't have exception processing or trap handlers - // so will just halt execution with a message. - int[] operands = statement.getOperands(); - throw new ProcessingException(statement, "break instruction executed; code = "+ - operands[0]+".", Exceptions.BREAKPOINT_EXCEPTION); - } - })); - instructionList.add( - new BasicInstruction("break", - "Break execution : Terminate program execution with exception", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { // At this time I don't have exception processing or trap handlers + // so will just halt execution with a message. + int[] operands = statement.getOperands(); + throw new ProcessingException(statement, "break instruction executed; code = " + + operands[0] + ".", Exceptions.BREAKPOINT_EXCEPTION); + } + })); + instructionList.add( + new BasicInstruction("break", + "Break execution : Terminate program execution with exception", + BasicInstructionFormat.R_FORMAT, "000000 00000 00000 00000 00000 001101", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { // At this time I don't have exception processing or trap handlers - // so will just halt execution with a message. - throw new ProcessingException(statement, "break instruction executed; no code given.", - Exceptions.BREAKPOINT_EXCEPTION); - } - })); - instructionList.add( - new BasicInstruction("syscall", - "Issue a system call : Execute the system call specified by value in $v0", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { // At this time I don't have exception processing or trap handlers + // so will just halt execution with a message. + throw new ProcessingException(statement, "break instruction executed; no code given.", + Exceptions.BREAKPOINT_EXCEPTION); + } + })); + instructionList.add( + new BasicInstruction("syscall", + "Issue a system call : Execute the system call specified by value in $v0", + BasicInstructionFormat.R_FORMAT, "000000 00000 00000 00000 00000 001100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - findAndSimulateSyscall(RegisterFile.getValue(2),statement); - } - })); - instructionList.add( - new BasicInstruction("j target", - "Jump unconditionally : Jump to statement at target address", - BasicInstructionFormat.J_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + findAndSimulateSyscall(RegisterFile.getValue(2), statement); + } + })); + instructionList.add( + new BasicInstruction("j target", + "Jump unconditionally : Jump to statement at target address", + BasicInstructionFormat.J_FORMAT, "000010 ffffffffffffffffffffffffff", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - processJump( - ((RegisterFile.getProgramCounter() & 0xF0000000) - | (operands[0] << 2))); - } - })); - instructionList.add( - new BasicInstruction("jr $t1", - "Jump register unconditionally : Jump to statement whose address is in $t1", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + processJump( + ((RegisterFile.getProgramCounter() & 0xF0000000) + | (operands[0] << 2))); + } + })); + instructionList.add( + new BasicInstruction("jr $t1", + "Jump register unconditionally : Jump to statement whose address is in $t1", + BasicInstructionFormat.R_FORMAT, "000000 fffff 00000 00000 00000 001000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - processJump(RegisterFile.getValue(operands[0])); - } - })); - instructionList.add( - new BasicInstruction("jal target", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + processJump(RegisterFile.getValue(operands[0])); + } + })); + instructionList.add( + new BasicInstruction("jal target", "Jump and link : Set $ra to Program Counter (return address) then jump to statement at target address", - BasicInstructionFormat.J_FORMAT, + BasicInstructionFormat.J_FORMAT, "000011 ffffffffffffffffffffffffff", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - processReturnAddress(31);// RegisterFile.updateRegister(31, RegisterFile.getProgramCounter()); - processJump( - (RegisterFile.getProgramCounter() & 0xF0000000) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + processReturnAddress(31);// RegisterFile.updateRegister(31, RegisterFile.getProgramCounter()); + processJump( + (RegisterFile.getProgramCounter() & 0xF0000000) | (operands[0] << 2)); - } - })); - instructionList.add( - new BasicInstruction("jalr $t1,$t2", + } + })); + instructionList.add( + new BasicInstruction("jalr $t1,$t2", "Jump and link register : Set $t1 to Program Counter (return address) then jump to statement whose address is in $t2", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 sssss 00000 fffff 00000 001001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - processReturnAddress(operands[0]);//RegisterFile.updateRegister(operands[0], RegisterFile.getProgramCounter()); - processJump(RegisterFile.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("jalr $t1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + processReturnAddress(operands[0]);//RegisterFile.updateRegister(operands[0], RegisterFile.getProgramCounter()); + processJump(RegisterFile.getValue(operands[1])); + } + })); + instructionList.add( + new BasicInstruction("jalr $t1", "Jump and link register : Set $ra to Program Counter (return address) then jump to statement whose address is in $t1", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 fffff 00000 11111 00000 001001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - processReturnAddress(31);//RegisterFile.updateRegister(31, RegisterFile.getProgramCounter()); - processJump(RegisterFile.getValue(operands[0])); - } - })); - instructionList.add( - new BasicInstruction("lb $t1,-100($t2)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + processReturnAddress(31);//RegisterFile.updateRegister(31, RegisterFile.getProgramCounter()); + processJump(RegisterFile.getValue(operands[0])); + } + })); + instructionList.add( + new BasicInstruction("lb $t1,-100($t2)", "Load byte : Set $t1 to sign-extended 8-bit value from effective memory byte address", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "100000 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - RegisterFile.updateRegister(operands[0], - Globals.memory.getByte( - RegisterFile.getValue(operands[2]) - + (operands[1] << 16 >> 16)) - << 24 - >> 24); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + RegisterFile.updateRegister(operands[0], + Globals.memory.getByte( + RegisterFile.getValue(operands[2]) + + (operands[1] << 16 >> 16)) + << 24 + >> 24); } - } - })); - instructionList.add( - new BasicInstruction("lh $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("lh $t1,-100($t2)", "Load halfword : Set $t1 to sign-extended 16-bit value from effective memory halfword address", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "100001 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - RegisterFile.updateRegister(operands[0], - Globals.memory.getHalf( - RegisterFile.getValue(operands[2]) - + (operands[1] << 16 >> 16)) - << 16 - >> 16); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + RegisterFile.updateRegister(operands[0], + Globals.memory.getHalf( + RegisterFile.getValue(operands[2]) + + (operands[1] << 16 >> 16)) + << 16 + >> 16); } - } - })); - instructionList.add( - new BasicInstruction("lhu $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("lhu $t1,-100($t2)", "Load halfword unsigned : Set $t1 to zero-extended 16-bit value from effective memory halfword address", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "100101 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - // offset is sign-extended and loaded halfword value is zero-extended - RegisterFile.updateRegister(operands[0], - Globals.memory.getHalf( - RegisterFile.getValue(operands[2]) - + (operands[1] << 16 >> 16)) - & 0x0000ffff); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + // offset is sign-extended and loaded halfword value is zero-extended + RegisterFile.updateRegister(operands[0], + Globals.memory.getHalf( + RegisterFile.getValue(operands[2]) + + (operands[1] << 16 >> 16)) + & 0x0000ffff); } - } - })); - instructionList.add( - new BasicInstruction("lbu $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("lbu $t1,-100($t2)", "Load byte unsigned : Set $t1 to zero-extended 8-bit value from effective memory byte address", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "100100 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - RegisterFile.updateRegister(operands[0], - Globals.memory.getByte( - RegisterFile.getValue(operands[2]) - + (operands[1] << 16 >> 16)) - & 0x000000ff); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + RegisterFile.updateRegister(operands[0], + Globals.memory.getByte( + RegisterFile.getValue(operands[2]) + + (operands[1] << 16 >> 16)) + & 0x000000ff); } - } - })); - instructionList.add( - new BasicInstruction("sb $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("sb $t1,-100($t2)", "Store byte : Store the low-order 8 bits of $t1 into the effective memory byte address", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "101000 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - Globals.memory.setByte( - RegisterFile.getValue(operands[2]) - + (operands[1] << 16 >> 16), - RegisterFile.getValue(operands[0]) - & 0x000000ff); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + Globals.memory.setByte( + RegisterFile.getValue(operands[2]) + + (operands[1] << 16 >> 16), + RegisterFile.getValue(operands[0]) + & 0x000000ff); } - } - })); - instructionList.add( - new BasicInstruction("sh $t1,-100($t2)", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("sh $t1,-100($t2)", "Store halfword : Store the low-order 16 bits of $t1 into the effective memory halfword address", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "101001 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - Globals.memory.setHalf( - RegisterFile.getValue(operands[2]) - + (operands[1] << 16 >> 16), - RegisterFile.getValue(operands[0]) - & 0x0000ffff); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + Globals.memory.setHalf( + RegisterFile.getValue(operands[2]) + + (operands[1] << 16 >> 16), + RegisterFile.getValue(operands[0]) + & 0x0000ffff); } - } - })); - instructionList.add( - new BasicInstruction("clo $t1,$t2", - "Count number of leading ones : Set $t1 to the count of leading one bits in $t2 starting at most significant bit position", - BasicInstructionFormat.R_FORMAT, - // MIPS32 requires rd (first) operand to appear twice in machine code. - // It has to be same as rt (third) operand in machine code, but the - // source statement does not have or permit third operand. - // In the machine code, rd and rt are adjacent, but my mask - // substitution cannot handle adjacent placement of the same source - // operand (e.g. "... sssss fffff fffff ...") because it would interpret - // the mask to be the total length of both (10 bits). I could code it - // to have 3 operands then define a pseudo-instruction of two operands - // to translate into this, but then both would show up in instruction set - // list and I don't want that. So I will use the convention of Computer - // Organization and Design 3rd Edition, Appendix A, and code the rt bits - // as 0's. The generated code does not match SPIM and would not run - // on a real MIPS machine but since I am providing no means of storing - // the binary code that is not really an issue. + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("clo $t1,$t2", + "Count number of leading ones : Set $t1 to the count of leading one bits in $t2 starting at most significant bit position", + BasicInstructionFormat.R_FORMAT, + // MIPS32 requires rd (first) operand to appear twice in machine code. + // It has to be same as rt (third) operand in machine code, but the + // source statement does not have or permit third operand. + // In the machine code, rd and rt are adjacent, but my mask + // substitution cannot handle adjacent placement of the same source + // operand (e.g. "... sssss fffff fffff ...") because it would interpret + // the mask to be the total length of both (10 bits). I could code it + // to have 3 operands then define a pseudo-instruction of two operands + // to translate into this, but then both would show up in instruction set + // list and I don't want that. So I will use the convention of Computer + // Organization and Design 3rd Edition, Appendix A, and code the rt bits + // as 0's. The generated code does not match SPIM and would not run + // on a real MIPS machine but since I am providing no means of storing + // the binary code that is not really an issue. "011100 sssss 00000 fffff 00000 100001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int value = RegisterFile.getValue(operands[1]); - int leadingOnes = 0; - int bitPosition = 31; - while (Binary.bitValue(value,bitPosition)==1 && bitPosition>=0) { - leadingOnes++; - bitPosition--; - } - RegisterFile.updateRegister(operands[0], leadingOnes); - } - })); - instructionList.add( - new BasicInstruction("clz $t1,$t2", - "Count number of leading zeroes : Set $t1 to the count of leading zero bits in $t2 starting at most significant bit positio", - BasicInstructionFormat.R_FORMAT, - // See comments for "clo" instruction above. They apply here too. + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int value = RegisterFile.getValue(operands[1]); + int leadingOnes = 0; + int bitPosition = 31; + while (Binary.bitValue(value, bitPosition) == 1 && bitPosition >= 0) + { + leadingOnes++; + bitPosition--; + } + RegisterFile.updateRegister(operands[0], leadingOnes); + } + })); + instructionList.add( + new BasicInstruction("clz $t1,$t2", + "Count number of leading zeroes : Set $t1 to the count of leading zero bits in $t2 starting at most significant bit positio", + BasicInstructionFormat.R_FORMAT, + // See comments for "clo" instruction above. They apply here too. "011100 sssss 00000 fffff 00000 100000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int value = RegisterFile.getValue(operands[1]); - int leadingZeros = 0; - int bitPosition = 31; - while (Binary.bitValue(value,bitPosition)==0 && bitPosition>=0) { - leadingZeros++; - bitPosition--; - } - RegisterFile.updateRegister(operands[0], leadingZeros); - } - })); - instructionList.add( - new BasicInstruction("mfc0 $t1,$8", - "Move from Coprocessor 0 : Set $t1 to the value stored in Coprocessor 0 register $8", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int value = RegisterFile.getValue(operands[1]); + int leadingZeros = 0; + int bitPosition = 31; + while (Binary.bitValue(value, bitPosition) == 0 && bitPosition >= 0) + { + leadingZeros++; + bitPosition--; + } + RegisterFile.updateRegister(operands[0], leadingZeros); + } + })); + instructionList.add( + new BasicInstruction("mfc0 $t1,$8", + "Move from Coprocessor 0 : Set $t1 to the value stored in Coprocessor 0 register $8", + BasicInstructionFormat.R_FORMAT, "010000 00000 fffff sssss 00000 000000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], - Coprocessor0.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("mtc0 $t1,$8", - "Move to Coprocessor 0 : Set Coprocessor 0 register $8 to value stored in $t1", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], + Coprocessor0.getValue(operands[1])); + } + })); + instructionList.add( + new BasicInstruction("mtc0 $t1,$8", + "Move to Coprocessor 0 : Set Coprocessor 0 register $8 to value stored in $t1", + BasicInstructionFormat.R_FORMAT, "010000 00100 fffff sssss 00000 000000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - Coprocessor0.updateRegister(operands[1], - RegisterFile.getValue(operands[0])); - } - })); - + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + Coprocessor0.updateRegister(operands[1], + RegisterFile.getValue(operands[0])); + } + })); + /////////////////////// Floating Point Instructions Start Here //////////////// - instructionList.add( - new BasicInstruction("add.s $f0,$f1,$f3", - "Floating point addition single precision : Set $f0 to single-precision floating point value of $f1 plus $f3", - BasicInstructionFormat.R_FORMAT, + instructionList.add( + new BasicInstruction("add.s $f0,$f1,$f3", + "Floating point addition single precision : Set $f0 to single-precision floating point value of $f1 plus $f3", + BasicInstructionFormat.R_FORMAT, "010001 10000 ttttt sssss fffff 000000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float add1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - float add2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); - float sum = add1 + add2; - // overflow detected when sum is positive or negative infinity. + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float add1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + float add2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); + float sum = add1 + add2; + // overflow detected when sum is positive or negative infinity. /* if (sum == Float.NEGATIVE_INFINITY || sum == Float.POSITIVE_INFINITY) { throw new ProcessingException(statement,"arithmetic overflow"); } */ - Coprocessor1.updateRegister(operands[0], Float.floatToIntBits(sum)); - } - })); - instructionList.add( - new BasicInstruction("sub.s $f0,$f1,$f3", + Coprocessor1.updateRegister(operands[0], Float.floatToIntBits(sum)); + } + })); + instructionList.add( + new BasicInstruction("sub.s $f0,$f1,$f3", "Floating point subtraction single precision : Set $f0 to single-precision floating point value of $f1 minus $f3", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10000 ttttt sssss fffff 000001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float sub1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - float sub2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); - float diff = sub1 - sub2; - Coprocessor1.updateRegister(operands[0], Float.floatToIntBits(diff)); - } - })); - instructionList.add( - new BasicInstruction("mul.s $f0,$f1,$f3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float sub1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + float sub2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); + float diff = sub1 - sub2; + Coprocessor1.updateRegister(operands[0], Float.floatToIntBits(diff)); + } + })); + instructionList.add( + new BasicInstruction("mul.s $f0,$f1,$f3", "Floating point multiplication single precision : Set $f0 to single-precision floating point value of $f1 times $f3", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10000 ttttt sssss fffff 000010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float mul1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - float mul2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); - float prod = mul1 * mul2; - Coprocessor1.updateRegister(operands[0], Float.floatToIntBits(prod)); - } - })); - instructionList.add( - new BasicInstruction("div.s $f0,$f1,$f3", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float mul1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + float mul2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); + float prod = mul1 * mul2; + Coprocessor1.updateRegister(operands[0], Float.floatToIntBits(prod)); + } + })); + instructionList.add( + new BasicInstruction("div.s $f0,$f1,$f3", "Floating point division single precision : Set $f0 to single-precision floating point value of $f1 divided by $f3", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10000 ttttt sssss fffff 000011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float div1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - float div2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); - float quot = div1 / div2; - Coprocessor1.updateRegister(operands[0], Float.floatToIntBits(quot)); - } - })); - instructionList.add( - new BasicInstruction("sqrt.s $f0,$f1", - "Square root single precision : Set $f0 to single-precision floating point square root of $f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float div1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + float div2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); + float quot = div1 / div2; + Coprocessor1.updateRegister(operands[0], Float.floatToIntBits(quot)); + } + })); + instructionList.add( + new BasicInstruction("sqrt.s $f0,$f1", + "Square root single precision : Set $f0 to single-precision floating point square root of $f1", BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 000100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float value = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - int floatSqrt = 0; - if (value < 0.0f) { - // This is subject to refinement later. Release 4.0 defines floor, ceil, trunc, round - // to act silently rather than raise Invalid Operation exception, so sqrt should do the - // same. An intermediate step would be to define a setting for FCSR Invalid Operation - // flag, but the best solution is to simulate the FCSR register itself. - // FCSR = Floating point unit Control and Status Register. DPS 10-Aug-2010 - floatSqrt = Float.floatToIntBits( Float.NaN); - //throw new ProcessingException(statement, "Invalid Operation: sqrt of negative number"); - } - else { - floatSqrt = Float.floatToIntBits( (float) Math.sqrt(value)); - } - Coprocessor1.updateRegister(operands[0], floatSqrt); - } - })); - instructionList.add( - new BasicInstruction("floor.w.s $f0,$f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float value = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + int floatSqrt = 0; + if (value < 0.0f) + { + // This is subject to refinement later. Release 4.0 defines floor, ceil, trunc, round + // to act silently rather than raise Invalid Operation exception, so sqrt should do the + // same. An intermediate step would be to define a setting for FCSR Invalid Operation + // flag, but the best solution is to simulate the FCSR register itself. + // FCSR = Floating point unit Control and Status Register. DPS 10-Aug-2010 + floatSqrt = Float.floatToIntBits(Float.NaN); + //throw new ProcessingException(statement, "Invalid Operation: sqrt of negative number"); + } + else + { + floatSqrt = Float.floatToIntBits((float) Math.sqrt(value)); + } + Coprocessor1.updateRegister(operands[0], floatSqrt); + } + })); + instructionList.add( + new BasicInstruction("floor.w.s $f0,$f1", "Floor single precision to word : Set $f0 to 32-bit integer floor of single-precision float in $f1", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 001111", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float floatValue = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - int floor = (int) Math.floor(floatValue); - // DPS 28-July-2010: Since MARS does not simulate the FSCR, I will take the default - // action of setting the result to 2^31-1, if the value is outside the 32 bit range. - if ( Float.isNaN(floatValue) - || Float.isInfinite(floatValue) - || floatValue < (float) Integer.MIN_VALUE - || floatValue > (float) Integer.MAX_VALUE ) { - floor = Integer.MAX_VALUE; - } - Coprocessor1.updateRegister(operands[0], floor); - } - })); - instructionList.add( - new BasicInstruction("ceil.w.s $f0,$f1", - "Ceiling single precision to word : Set $f0 to 32-bit integer ceiling of single-precision float in $f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float floatValue = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + int floor = (int) Math.floor(floatValue); + // DPS 28-July-2010: Since MARS does not simulate the FSCR, I will take the default + // action of setting the result to 2^31-1, if the value is outside the 32 bit range. + if (Float.isNaN(floatValue) + || Float.isInfinite(floatValue) + || floatValue < (float) Integer.MIN_VALUE + || floatValue > (float) Integer.MAX_VALUE) + { + floor = Integer.MAX_VALUE; + } + Coprocessor1.updateRegister(operands[0], floor); + } + })); + instructionList.add( + new BasicInstruction("ceil.w.s $f0,$f1", + "Ceiling single precision to word : Set $f0 to 32-bit integer ceiling of single-precision float in $f1", BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 001110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float floatValue = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - int ceiling = (int) Math.ceil(floatValue); - // DPS 28-July-2010: Since MARS does not simulate the FSCR, I will take the default - // action of setting the result to 2^31-1, if the value is outside the 32 bit range. - if ( Float.isNaN(floatValue) - || Float.isInfinite(floatValue) - || floatValue < (float) Integer.MIN_VALUE - || floatValue > (float) Integer.MAX_VALUE ) { - ceiling = Integer.MAX_VALUE; - } - Coprocessor1.updateRegister(operands[0], ceiling); - } - })); - instructionList.add( - new BasicInstruction("round.w.s $f0,$f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float floatValue = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + int ceiling = (int) Math.ceil(floatValue); + // DPS 28-July-2010: Since MARS does not simulate the FSCR, I will take the default + // action of setting the result to 2^31-1, if the value is outside the 32 bit range. + if (Float.isNaN(floatValue) + || Float.isInfinite(floatValue) + || floatValue < (float) Integer.MIN_VALUE + || floatValue > (float) Integer.MAX_VALUE) + { + ceiling = Integer.MAX_VALUE; + } + Coprocessor1.updateRegister(operands[0], ceiling); + } + })); + instructionList.add( + new BasicInstruction("round.w.s $f0,$f1", "Round single precision to word : Set $f0 to 32-bit integer round of single-precision float in $f1", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 001100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { // MIPS32 documentation (and IEEE 754) states that round rounds to the nearest but when - // both are equally near it rounds to the even one! SPIM rounds -4.5, -5.5, - // 4.5 and 5.5 to (-4, -5, 5, 6). Curiously, it rounds -5.1 to -4 and -5.6 to -5. - // Until MARS 3.5, I used Math.round, which rounds to nearest but when both are - // equal it rounds toward positive infinity. With Release 3.5, I painstakingly - // carry out the MIPS and IEEE 754 standard. - int[] operands = statement.getOperands(); - float floatValue = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - int below=0, above=0, round = Math.round(floatValue); - // According to MIPS32 spec, if any of these conditions is true, set - // Invalid Operation in the FCSR (Floating point Control/Status Register) and - // set result to be 2^31-1. MARS does not implement this register (as of release 3.4.1). - // It also mentions the "Invalid Operation Enable bit" in FCSR, that, if set, results - // in immediate exception instead of default value. - if ( Float.isNaN(floatValue) - || Float.isInfinite(floatValue) - || floatValue < (float) Integer.MIN_VALUE - || floatValue > (float) Integer.MAX_VALUE ) { - round = Integer.MAX_VALUE; - } - else { - Float floatObj = floatValue; - // If we are EXACTLY in the middle, then round to even! To determine this, - // find next higher integer and next lower integer, then see if distances - // are exactly equal. - if (floatValue < 0.0F) { - above = floatObj.intValue(); // truncates - below = above - 1; - } - else { - below = floatObj.intValue(); // truncates - above = below + 1; + { + public void simulate(ProgramStatement statement) throws ProcessingException + { // MIPS32 documentation (and IEEE 754) states that round rounds to the nearest but when + // both are equally near it rounds to the even one! SPIM rounds -4.5, -5.5, + // 4.5 and 5.5 to (-4, -5, 5, 6). Curiously, it rounds -5.1 to -4 and -5.6 to -5. + // Until MARS 3.5, I used Math.round, which rounds to nearest but when both are + // equal it rounds toward positive infinity. With Release 3.5, I painstakingly + // carry out the MIPS and IEEE 754 standard. + int[] operands = statement.getOperands(); + float floatValue = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + int below = 0, above = 0, round = Math.round(floatValue); + // According to MIPS32 spec, if any of these conditions is true, set + // Invalid Operation in the FCSR (Floating point Control/Status Register) and + // set result to be 2^31-1. MARS does not implement this register (as of release 3.4.1). + // It also mentions the "Invalid Operation Enable bit" in FCSR, that, if set, results + // in immediate exception instead of default value. + if (Float.isNaN(floatValue) + || Float.isInfinite(floatValue) + || floatValue < (float) Integer.MIN_VALUE + || floatValue > (float) Integer.MAX_VALUE) + { + round = Integer.MAX_VALUE; } - if (floatValue - below == above - floatValue) { // exactly in the middle? - round = (above%2 == 0) ? above : below; + else + { + Float floatObj = floatValue; + // If we are EXACTLY in the middle, then round to even! To determine this, + // find next higher integer and next lower integer, then see if distances + // are exactly equal. + if (floatValue < 0.0F) + { + above = floatObj.intValue(); // truncates + below = above - 1; + } + else + { + below = floatObj.intValue(); // truncates + above = below + 1; + } + if (floatValue - below == above - floatValue) + { // exactly in the middle? + round = (above % 2 == 0) ? above : below; + } } - } - Coprocessor1.updateRegister(operands[0], round); - } - })); - instructionList.add( - new BasicInstruction("trunc.w.s $f0,$f1", + Coprocessor1.updateRegister(operands[0], round); + } + })); + instructionList.add( + new BasicInstruction("trunc.w.s $f0,$f1", "Truncate single precision to word : Set $f0 to 32-bit integer truncation of single-precision float in $f1", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 001101", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float floatValue = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - int truncate = (int) floatValue;// Typecasting will round toward zero, the correct action - // DPS 28-July-2010: Since MARS does not simulate the FSCR, I will take the default - // action of setting the result to 2^31-1, if the value is outside the 32 bit range. - if ( Float.isNaN(floatValue) - || Float.isInfinite(floatValue) - || floatValue < (float) Integer.MIN_VALUE - || floatValue > (float) Integer.MAX_VALUE ) { - truncate = Integer.MAX_VALUE; - } - Coprocessor1.updateRegister(operands[0], truncate); - } - })); - instructionList.add( - new BasicInstruction("add.d $f2,$f4,$f6", - "Floating point addition double precision : Set $f2 to double-precision floating point value of $f4 plus $f6", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float floatValue = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + int truncate = (int) floatValue;// Typecasting will round toward zero, the correct action + // DPS 28-July-2010: Since MARS does not simulate the FSCR, I will take the default + // action of setting the result to 2^31-1, if the value is outside the 32 bit range. + if (Float.isNaN(floatValue) + || Float.isInfinite(floatValue) + || floatValue < (float) Integer.MIN_VALUE + || floatValue > (float) Integer.MAX_VALUE) + { + truncate = Integer.MAX_VALUE; + } + Coprocessor1.updateRegister(operands[0], truncate); + } + })); + instructionList.add( + new BasicInstruction("add.d $f2,$f4,$f6", + "Floating point addition double precision : Set $f2 to double-precision floating point value of $f4 plus $f6", + BasicInstructionFormat.R_FORMAT, "010001 10001 ttttt sssss fffff 000000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1 || operands[2]%2==1) { - throw new ProcessingException(statement, "all registers must be even-numbered"); - } - double add1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - double add2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[2]+1),Coprocessor1.getValue(operands[2]))); - double sum = add1 + add2; - long longSum = Double.doubleToLongBits(sum); - Coprocessor1.updateRegister(operands[0]+1, Binary.highOrderLongToInt(longSum)); - Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longSum)); - } - })); - instructionList.add( - new BasicInstruction("sub.d $f2,$f4,$f6", - "Floating point subtraction double precision : Set $f2 to double-precision floating point value of $f4 minus $f6", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1 || operands[2] % 2 == 1) + { + throw new ProcessingException(statement, "all registers must be even-numbered"); + } + double add1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + double add2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[2] + 1), Coprocessor1.getValue(operands[2]))); + double sum = add1 + add2; + long longSum = Double.doubleToLongBits(sum); + Coprocessor1.updateRegister(operands[0] + 1, Binary.highOrderLongToInt(longSum)); + Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longSum)); + } + })); + instructionList.add( + new BasicInstruction("sub.d $f2,$f4,$f6", + "Floating point subtraction double precision : Set $f2 to double-precision floating point value of $f4 minus $f6", BasicInstructionFormat.R_FORMAT, "010001 10001 ttttt sssss fffff 000001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1 || operands[2]%2==1) { - throw new ProcessingException(statement, "all registers must be even-numbered"); - } - double sub1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - double sub2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[2]+1),Coprocessor1.getValue(operands[2]))); - double diff = sub1 - sub2; - long longDiff = Double.doubleToLongBits(diff); - Coprocessor1.updateRegister(operands[0]+1, Binary.highOrderLongToInt(longDiff)); - Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longDiff)); - } - })); - instructionList.add( - new BasicInstruction("mul.d $f2,$f4,$f6", - "Floating point multiplication double precision : Set $f2 to double-precision floating point value of $f4 times $f6", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1 || operands[2] % 2 == 1) + { + throw new ProcessingException(statement, "all registers must be even-numbered"); + } + double sub1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + double sub2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[2] + 1), Coprocessor1.getValue(operands[2]))); + double diff = sub1 - sub2; + long longDiff = Double.doubleToLongBits(diff); + Coprocessor1.updateRegister(operands[0] + 1, Binary.highOrderLongToInt(longDiff)); + Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longDiff)); + } + })); + instructionList.add( + new BasicInstruction("mul.d $f2,$f4,$f6", + "Floating point multiplication double precision : Set $f2 to double-precision floating point value of $f4 times $f6", BasicInstructionFormat.R_FORMAT, "010001 10001 ttttt sssss fffff 000010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1 || operands[2]%2==1) { - throw new ProcessingException(statement, "all registers must be even-numbered"); - } - double mul1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - double mul2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[2]+1),Coprocessor1.getValue(operands[2]))); - double prod = mul1 * mul2; - long longProd = Double.doubleToLongBits(prod); - Coprocessor1.updateRegister(operands[0]+1, Binary.highOrderLongToInt(longProd)); - Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longProd)); - } - })); - instructionList.add( - new BasicInstruction("div.d $f2,$f4,$f6", - "Floating point division double precision : Set $f2 to double-precision floating point value of $f4 divided by $f6", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1 || operands[2] % 2 == 1) + { + throw new ProcessingException(statement, "all registers must be even-numbered"); + } + double mul1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + double mul2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[2] + 1), Coprocessor1.getValue(operands[2]))); + double prod = mul1 * mul2; + long longProd = Double.doubleToLongBits(prod); + Coprocessor1.updateRegister(operands[0] + 1, Binary.highOrderLongToInt(longProd)); + Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longProd)); + } + })); + instructionList.add( + new BasicInstruction("div.d $f2,$f4,$f6", + "Floating point division double precision : Set $f2 to double-precision floating point value of $f4 divided by $f6", BasicInstructionFormat.R_FORMAT, "010001 10001 ttttt sssss fffff 000011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1 || operands[2]%2==1) { - throw new ProcessingException(statement, "all registers must be even-numbered"); - } - double div1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - double div2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[2]+1),Coprocessor1.getValue(operands[2]))); - double quot = div1 / div2; - long longQuot = Double.doubleToLongBits(quot); - Coprocessor1.updateRegister(operands[0]+1, Binary.highOrderLongToInt(longQuot)); - Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longQuot)); - } - })); - instructionList.add( - new BasicInstruction("sqrt.d $f2,$f4", - "Square root double precision : Set $f2 to double-precision floating point square root of $f4", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1 || operands[2] % 2 == 1) + { + throw new ProcessingException(statement, "all registers must be even-numbered"); + } + double div1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + double div2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[2] + 1), Coprocessor1.getValue(operands[2]))); + double quot = div1 / div2; + long longQuot = Double.doubleToLongBits(quot); + Coprocessor1.updateRegister(operands[0] + 1, Binary.highOrderLongToInt(longQuot)); + Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longQuot)); + } + })); + instructionList.add( + new BasicInstruction("sqrt.d $f2,$f4", + "Square root double precision : Set $f2 to double-precision floating point square root of $f4", BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 000100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1 || operands[2]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - double value = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - long longSqrt = 0; - if (value < 0.0) { - // This is subject to refinement later. Release 4.0 defines floor, ceil, trunc, round - // to act silently rather than raise Invalid Operation exception, so sqrt should do the - // same. An intermediate step would be to define a setting for FCSR Invalid Operation - // flag, but the best solution is to simulate the FCSR register itself. - // FCSR = Floating point unit Control and Status Register. DPS 10-Aug-2010 - longSqrt = Double.doubleToLongBits(Double.NaN); - //throw new ProcessingException(statement, "Invalid Operation: sqrt of negative number"); - } - else { - longSqrt = Double.doubleToLongBits(Math.sqrt(value)); - } - Coprocessor1.updateRegister(operands[0]+1, Binary.highOrderLongToInt(longSqrt)); - Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longSqrt)); - } - })); - instructionList.add( - new BasicInstruction("floor.w.d $f1,$f2", - "Floor double precision to word : Set $f1 to 32-bit integer floor of double-precision float in $f2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1 || operands[2] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + double value = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + long longSqrt = 0; + if (value < 0.0) + { + // This is subject to refinement later. Release 4.0 defines floor, ceil, trunc, round + // to act silently rather than raise Invalid Operation exception, so sqrt should do the + // same. An intermediate step would be to define a setting for FCSR Invalid Operation + // flag, but the best solution is to simulate the FCSR register itself. + // FCSR = Floating point unit Control and Status Register. DPS 10-Aug-2010 + longSqrt = Double.doubleToLongBits(Double.NaN); + //throw new ProcessingException(statement, "Invalid Operation: sqrt of negative number"); + } + else + { + longSqrt = Double.doubleToLongBits(Math.sqrt(value)); + } + Coprocessor1.updateRegister(operands[0] + 1, Binary.highOrderLongToInt(longSqrt)); + Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(longSqrt)); + } + })); + instructionList.add( + new BasicInstruction("floor.w.d $f1,$f2", + "Floor double precision to word : Set $f1 to 32-bit integer floor of double-precision float in $f2", BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 001111", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[1]%2==1) { - throw new ProcessingException(statement, "second register must be even-numbered"); - } - double doubleValue = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - // DPS 27-July-2010: Since MARS does not simulate the FSCR, I will take the default - // action of setting the result to 2^31-1, if the value is outside the 32 bit range. - int floor = (int) Math.floor(doubleValue); - if ( Double.isNaN(doubleValue) - || Double.isInfinite(doubleValue) - || doubleValue < (double) Integer.MIN_VALUE - || doubleValue > (double) Integer.MAX_VALUE ) { - floor = Integer.MAX_VALUE; - } - Coprocessor1.updateRegister(operands[0], floor); - } - })); - instructionList.add( - new BasicInstruction("ceil.w.d $f1,$f2", - "Ceiling double precision to word : Set $f1 to 32-bit integer ceiling of double-precision float in $f2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "second register must be even-numbered"); + } + double doubleValue = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + // DPS 27-July-2010: Since MARS does not simulate the FSCR, I will take the default + // action of setting the result to 2^31-1, if the value is outside the 32 bit range. + int floor = (int) Math.floor(doubleValue); + if (Double.isNaN(doubleValue) + || Double.isInfinite(doubleValue) + || doubleValue < (double) Integer.MIN_VALUE + || doubleValue > (double) Integer.MAX_VALUE) + { + floor = Integer.MAX_VALUE; + } + Coprocessor1.updateRegister(operands[0], floor); + } + })); + instructionList.add( + new BasicInstruction("ceil.w.d $f1,$f2", + "Ceiling double precision to word : Set $f1 to 32-bit integer ceiling of double-precision float in $f2", BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 001110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[1]%2==1) { - throw new ProcessingException(statement, "second register must be even-numbered"); - } - double doubleValue = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - // DPS 27-July-2010: Since MARS does not simulate the FSCR, I will take the default - // action of setting the result to 2^31-1, if the value is outside the 32 bit range. - int ceiling = (int) Math.ceil(doubleValue); - if ( Double.isNaN(doubleValue) - || Double.isInfinite(doubleValue) - || doubleValue < (double) Integer.MIN_VALUE - || doubleValue > (double) Integer.MAX_VALUE ) { - ceiling = Integer.MAX_VALUE; - } - Coprocessor1.updateRegister(operands[0], ceiling); - } - })); - instructionList.add( - new BasicInstruction("round.w.d $f1,$f2", - "Round double precision to word : Set $f1 to 32-bit integer round of double-precision float in $f2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "second register must be even-numbered"); + } + double doubleValue = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + // DPS 27-July-2010: Since MARS does not simulate the FSCR, I will take the default + // action of setting the result to 2^31-1, if the value is outside the 32 bit range. + int ceiling = (int) Math.ceil(doubleValue); + if (Double.isNaN(doubleValue) + || Double.isInfinite(doubleValue) + || doubleValue < (double) Integer.MIN_VALUE + || doubleValue > (double) Integer.MAX_VALUE) + { + ceiling = Integer.MAX_VALUE; + } + Coprocessor1.updateRegister(operands[0], ceiling); + } + })); + instructionList.add( + new BasicInstruction("round.w.d $f1,$f2", + "Round double precision to word : Set $f1 to 32-bit integer round of double-precision float in $f2", BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 001100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { // See comments in round.w.s above, concerning MIPS and IEEE 754 standard. - // Until MARS 3.5, I used Math.round, which rounds to nearest but when both are - // equal it rounds toward positive infinity. With Release 3.5, I painstakingly - // carry out the MIPS and IEEE 754 standard (round to nearest/even). - int[] operands = statement.getOperands(); - if (operands[1]%2==1) { - throw new ProcessingException(statement, "second register must be even-numbered"); - } - double doubleValue = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - int below=0, above=0; - int round = (int) Math.round(doubleValue); - // See comments in round.w.s above concerning FSCR... - if ( Double.isNaN(doubleValue) - || Double.isInfinite(doubleValue) - || doubleValue < (double) Integer.MIN_VALUE - || doubleValue > (double) Integer.MAX_VALUE ) { - round = Integer.MAX_VALUE; - } - else { - Double doubleObj = doubleValue; - // If we are EXACTLY in the middle, then round to even! To determine this, - // find next higher integer and next lower integer, then see if distances - // are exactly equal. - if (doubleValue < 0.0) { - above = doubleObj.intValue(); // truncates - below = above - 1; - } - else { - below = doubleObj.intValue(); // truncates - above = below + 1; + { + public void simulate(ProgramStatement statement) throws ProcessingException + { // See comments in round.w.s above, concerning MIPS and IEEE 754 standard. + // Until MARS 3.5, I used Math.round, which rounds to nearest but when both are + // equal it rounds toward positive infinity. With Release 3.5, I painstakingly + // carry out the MIPS and IEEE 754 standard (round to nearest/even). + int[] operands = statement.getOperands(); + if (operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "second register must be even-numbered"); } - if (doubleValue - below == above - doubleValue) { // exactly in the middle? - round = (above%2 == 0) ? above : below; + double doubleValue = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + int below = 0, above = 0; + int round = (int) Math.round(doubleValue); + // See comments in round.w.s above concerning FSCR... + if (Double.isNaN(doubleValue) + || Double.isInfinite(doubleValue) + || doubleValue < (double) Integer.MIN_VALUE + || doubleValue > (double) Integer.MAX_VALUE) + { + round = Integer.MAX_VALUE; } - } - Coprocessor1.updateRegister(operands[0], round); - } - })); - instructionList.add( - new BasicInstruction("trunc.w.d $f1,$f2", - "Truncate double precision to word : Set $f1 to 32-bit integer truncation of double-precision float in $f2", + else + { + Double doubleObj = doubleValue; + // If we are EXACTLY in the middle, then round to even! To determine this, + // find next higher integer and next lower integer, then see if distances + // are exactly equal. + if (doubleValue < 0.0) + { + above = doubleObj.intValue(); // truncates + below = above - 1; + } + else + { + below = doubleObj.intValue(); // truncates + above = below + 1; + } + if (doubleValue - below == above - doubleValue) + { // exactly in the middle? + round = (above % 2 == 0) ? above : below; + } + } + Coprocessor1.updateRegister(operands[0], round); + } + })); + instructionList.add( + new BasicInstruction("trunc.w.d $f1,$f2", + "Truncate double precision to word : Set $f1 to 32-bit integer truncation of double-precision float in $f2", BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 001101", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[1]%2==1) { - throw new ProcessingException(statement, "second register must be even-numbered"); - } - double doubleValue = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - // DPS 27-July-2010: Since MARS does not simulate the FSCR, I will take the default - // action of setting the result to 2^31-1, if the value is outside the 32 bit range. - int truncate = (int) doubleValue; // Typecasting will round toward zero, the correct action. - if ( Double.isNaN(doubleValue) - || Double.isInfinite(doubleValue) - || doubleValue < (double) Integer.MIN_VALUE - || doubleValue > (double) Integer.MAX_VALUE ) { - truncate = Integer.MAX_VALUE; - } - Coprocessor1.updateRegister(operands[0], truncate); - } - })); - instructionList.add( - new BasicInstruction("bc1t label", - "Branch if FP condition flag 0 true (BC1T, not BCLT) : If Coprocessor 1 condition flag 0 is true (one) then branch to statement at label's address", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "second register must be even-numbered"); + } + double doubleValue = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + // DPS 27-July-2010: Since MARS does not simulate the FSCR, I will take the default + // action of setting the result to 2^31-1, if the value is outside the 32 bit range. + int truncate = (int) doubleValue; // Typecasting will round toward zero, the correct action. + if (Double.isNaN(doubleValue) + || Double.isInfinite(doubleValue) + || doubleValue < (double) Integer.MIN_VALUE + || doubleValue > (double) Integer.MAX_VALUE) + { + truncate = Integer.MAX_VALUE; + } + Coprocessor1.updateRegister(operands[0], truncate); + } + })); + instructionList.add( + new BasicInstruction("bc1t label", + "Branch if FP condition flag 0 true (BC1T, not BCLT) : If Coprocessor 1 condition flag 0 is true (one) then branch to statement at label's address", BasicInstructionFormat.I_BRANCH_FORMAT, "010001 01000 00001 ffffffffffffffff", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(0)==1) - { - processBranch(operands[0]); - } - } - })); - instructionList.add( - new BasicInstruction("bc1t 1,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(0) == 1) + { + processBranch(operands[0]); + } + } + })); + instructionList.add( + new BasicInstruction("bc1t 1,label", "Branch if specified FP condition flag true (BC1T, not BCLT) : If Coprocessor 1 condition flag specified by immediate is true (one) then branch to statement at label's address", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "010001 01000 fff 01 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(operands[0])==1) - { - processBranch(operands[1]); - } - } - })); - instructionList.add( - new BasicInstruction("bc1f label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(operands[0]) == 1) + { + processBranch(operands[1]); + } + } + })); + instructionList.add( + new BasicInstruction("bc1f label", "Branch if FP condition flag 0 false (BC1F, not BCLF) : If Coprocessor 1 condition flag 0 is false (zero) then branch to statement at label's address", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "010001 01000 00000 ffffffffffffffff", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(0)==0) - { - processBranch(operands[0]); - } - - } - })); - instructionList.add( - new BasicInstruction("bc1f 1,label", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(0) == 0) + { + processBranch(operands[0]); + } + + } + })); + instructionList.add( + new BasicInstruction("bc1f 1,label", "Branch if specified FP condition flag false (BC1F, not BCLF) : If Coprocessor 1 condition flag specified by immediate is false (zero) then branch to statement at label's address", - BasicInstructionFormat.I_BRANCH_FORMAT, + BasicInstructionFormat.I_BRANCH_FORMAT, "010001 01000 fff 00 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(operands[0])==0) - { - processBranch(operands[1]); - } - - } - })); - instructionList.add( - new BasicInstruction("c.eq.s $f0,$f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(operands[0]) == 0) + { + processBranch(operands[1]); + } + + } + })); + instructionList.add( + new BasicInstruction("c.eq.s $f0,$f1", "Compare equal single precision : If $f0 is equal to $f1, set Coprocessor 1 condition flag 0 true else set it false", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10000 sssss fffff 00000 110010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[0])); - float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - if (op1 == op2) - Coprocessor1.setConditionFlag(0); - else - Coprocessor1.clearConditionFlag(0); - } - })); - instructionList.add( - new BasicInstruction("c.eq.s 1,$f0,$f1", - "Compare equal single precision : If $f0 is equal to $f1, set Coprocessor 1 condition flag specied by immediate to true else set it to false", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[0])); + float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + if (op1 == op2) + { + Coprocessor1.setConditionFlag(0); + } + else + { + Coprocessor1.clearConditionFlag(0); + } + } + })); + instructionList.add( + new BasicInstruction("c.eq.s 1,$f0,$f1", + "Compare equal single precision : If $f0 is equal to $f1, set Coprocessor 1 condition flag specied by immediate to true else set it to false", + BasicInstructionFormat.R_FORMAT, "010001 10000 ttttt sssss fff 00 11 0010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); - if (op1 == op2) - Coprocessor1.setConditionFlag(operands[0]); - else - Coprocessor1.clearConditionFlag(operands[0]); - } - })); - instructionList.add( - new BasicInstruction("c.le.s $f0,$f1", - "Compare less or equal single precision : If $f0 is less than or equal to $f1, set Coprocessor 1 condition flag 0 true else set it false", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); + if (op1 == op2) + { + Coprocessor1.setConditionFlag(operands[0]); + } + else + { + Coprocessor1.clearConditionFlag(operands[0]); + } + } + })); + instructionList.add( + new BasicInstruction("c.le.s $f0,$f1", + "Compare less or equal single precision : If $f0 is less than or equal to $f1, set Coprocessor 1 condition flag 0 true else set it false", BasicInstructionFormat.R_FORMAT, "010001 10000 sssss fffff 00000 111110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[0])); - float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - if (op1 <= op2) - Coprocessor1.setConditionFlag(0); - else - Coprocessor1.clearConditionFlag(0); - } - })); - instructionList.add( - new BasicInstruction("c.le.s 1,$f0,$f1", - "Compare less or equal single precision : If $f0 is less than or equal to $f1, set Coprocessor 1 condition flag specified by immediate to true else set it to false", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[0])); + float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + if (op1 <= op2) + { + Coprocessor1.setConditionFlag(0); + } + else + { + Coprocessor1.clearConditionFlag(0); + } + } + })); + instructionList.add( + new BasicInstruction("c.le.s 1,$f0,$f1", + "Compare less or equal single precision : If $f0 is less than or equal to $f1, set Coprocessor 1 condition flag specified by immediate to true else set it to false", BasicInstructionFormat.R_FORMAT, "010001 10000 ttttt sssss fff 00 111110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); - if (op1 <= op2) - Coprocessor1.setConditionFlag(operands[0]); - else - Coprocessor1.clearConditionFlag(operands[0]); - } - })); - instructionList.add( - new BasicInstruction("c.lt.s $f0,$f1", - "Compare less than single precision : If $f0 is less than $f1, set Coprocessor 1 condition flag 0 true else set it false", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); + if (op1 <= op2) + { + Coprocessor1.setConditionFlag(operands[0]); + } + else + { + Coprocessor1.clearConditionFlag(operands[0]); + } + } + })); + instructionList.add( + new BasicInstruction("c.lt.s $f0,$f1", + "Compare less than single precision : If $f0 is less than $f1, set Coprocessor 1 condition flag 0 true else set it false", BasicInstructionFormat.R_FORMAT, "010001 10000 sssss fffff 00000 111100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[0])); - float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - if (op1 < op2) - Coprocessor1.setConditionFlag(0); - else - Coprocessor1.clearConditionFlag(0); - } - })); - instructionList.add( - new BasicInstruction("c.lt.s 1,$f0,$f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[0])); + float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + if (op1 < op2) + { + Coprocessor1.setConditionFlag(0); + } + else + { + Coprocessor1.clearConditionFlag(0); + } + } + })); + instructionList.add( + new BasicInstruction("c.lt.s 1,$f0,$f1", "Compare less than single precision : If $f0 is less than $f1, set Coprocessor 1 condition flag specified by immediate to true else set it to false", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10000 ttttt sssss fff 00 111100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); - float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); - if (op1 < op2) - Coprocessor1.setConditionFlag(operands[0]); - else - Coprocessor1.clearConditionFlag(operands[0]); - } - })); - instructionList.add( - new BasicInstruction("c.eq.d $f2,$f4", - "Compare equal double precision : If $f2 is equal to $f4 (double-precision), set Coprocessor 1 condition flag 0 true else set it false", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + float op1 = Float.intBitsToFloat(Coprocessor1.getValue(operands[1])); + float op2 = Float.intBitsToFloat(Coprocessor1.getValue(operands[2])); + if (op1 < op2) + { + Coprocessor1.setConditionFlag(operands[0]); + } + else + { + Coprocessor1.clearConditionFlag(operands[0]); + } + } + })); + instructionList.add( + new BasicInstruction("c.eq.d $f2,$f4", + "Compare equal double precision : If $f2 is equal to $f4 (double-precision), set Coprocessor 1 condition flag 0 true else set it false", BasicInstructionFormat.R_FORMAT, "010001 10001 sssss fffff 00000 110010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[0]+1),Coprocessor1.getValue(operands[0]))); - double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - if (op1 == op2) - Coprocessor1.setConditionFlag(0); - else - Coprocessor1.clearConditionFlag(0); - } - })); - instructionList.add( - new BasicInstruction("c.eq.d 1,$f2,$f4", - "Compare equal double precision : If $f2 is equal to $f4 (double-precision), set Coprocessor 1 condition flag specified by immediate to true else set it to false", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[0] + 1), Coprocessor1.getValue(operands[0]))); + double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + if (op1 == op2) + { + Coprocessor1.setConditionFlag(0); + } + else + { + Coprocessor1.clearConditionFlag(0); + } + } + })); + instructionList.add( + new BasicInstruction("c.eq.d 1,$f2,$f4", + "Compare equal double precision : If $f2 is equal to $f4 (double-precision), set Coprocessor 1 condition flag specified by immediate to true else set it to false", BasicInstructionFormat.R_FORMAT, "010001 10001 ttttt sssss fff 00 110010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[1]%2==1 || operands[2]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[2]+1),Coprocessor1.getValue(operands[2]))); - if (op1 == op2) - Coprocessor1.setConditionFlag(operands[0]); - else - Coprocessor1.clearConditionFlag(operands[0]); - } - })); - instructionList.add( - new BasicInstruction("c.le.d $f2,$f4", - "Compare less or equal double precision : If $f2 is less than or equal to $f4 (double-precision), set Coprocessor 1 condition flag 0 true else set it false", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[1] % 2 == 1 || operands[2] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[2] + 1), Coprocessor1.getValue(operands[2]))); + if (op1 == op2) + { + Coprocessor1.setConditionFlag(operands[0]); + } + else + { + Coprocessor1.clearConditionFlag(operands[0]); + } + } + })); + instructionList.add( + new BasicInstruction("c.le.d $f2,$f4", + "Compare less or equal double precision : If $f2 is less than or equal to $f4 (double-precision), set Coprocessor 1 condition flag 0 true else set it false", BasicInstructionFormat.R_FORMAT, "010001 10001 sssss fffff 00000 111110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[0]+1),Coprocessor1.getValue(operands[0]))); - double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - if (op1 <= op2) - Coprocessor1.setConditionFlag(0); - else - Coprocessor1.clearConditionFlag(0); - } - })); - instructionList.add( - new BasicInstruction("c.le.d 1,$f2,$f4", - "Compare less or equal double precision : If $f2 is less than or equal to $f4 (double-precision), set Coprocessor 1 condition flag specfied by immediate true else set it false", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[0] + 1), Coprocessor1.getValue(operands[0]))); + double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + if (op1 <= op2) + { + Coprocessor1.setConditionFlag(0); + } + else + { + Coprocessor1.clearConditionFlag(0); + } + } + })); + instructionList.add( + new BasicInstruction("c.le.d 1,$f2,$f4", + "Compare less or equal double precision : If $f2 is less than or equal to $f4 (double-precision), set Coprocessor 1 condition flag specfied by immediate true else set it false", BasicInstructionFormat.R_FORMAT, "010001 10001 ttttt sssss fff 00 111110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[1]%2==1 || operands[2]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[2]+1),Coprocessor1.getValue(operands[2]))); - if (op1 <= op2) - Coprocessor1.setConditionFlag(operands[0]); - else - Coprocessor1.clearConditionFlag(operands[0]); - } - })); - instructionList.add( - new BasicInstruction("c.lt.d $f2,$f4", - "Compare less than double precision : If $f2 is less than $f4 (double-precision), set Coprocessor 1 condition flag 0 true else set it false", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[1] % 2 == 1 || operands[2] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[2] + 1), Coprocessor1.getValue(operands[2]))); + if (op1 <= op2) + { + Coprocessor1.setConditionFlag(operands[0]); + } + else + { + Coprocessor1.clearConditionFlag(operands[0]); + } + } + })); + instructionList.add( + new BasicInstruction("c.lt.d $f2,$f4", + "Compare less than double precision : If $f2 is less than $f4 (double-precision), set Coprocessor 1 condition flag 0 true else set it false", BasicInstructionFormat.R_FORMAT, "010001 10001 sssss fffff 00000 111100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[0]+1),Coprocessor1.getValue(operands[0]))); - double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - if (op1 < op2) - Coprocessor1.setConditionFlag(0); - else - Coprocessor1.clearConditionFlag(0); - } - })); - instructionList.add( - new BasicInstruction("c.lt.d 1,$f2,$f4", - "Compare less than double precision : If $f2 is less than $f4 (double-precision), set Coprocessor 1 condition flag specified by immediate to true else set it to false", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[0] + 1), Coprocessor1.getValue(operands[0]))); + double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + if (op1 < op2) + { + Coprocessor1.setConditionFlag(0); + } + else + { + Coprocessor1.clearConditionFlag(0); + } + } + })); + instructionList.add( + new BasicInstruction("c.lt.d 1,$f2,$f4", + "Compare less than double precision : If $f2 is less than $f4 (double-precision), set Coprocessor 1 condition flag specified by immediate to true else set it to false", BasicInstructionFormat.R_FORMAT, "010001 10001 ttttt sssss fff 00 111100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[1]%2==1 || operands[2]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[2]+1),Coprocessor1.getValue(operands[2]))); - if (op1 < op2) - Coprocessor1.setConditionFlag(operands[0]); - else - Coprocessor1.clearConditionFlag(operands[0]); - } - })); - instructionList.add( - new BasicInstruction("abs.s $f0,$f1", - "Floating point absolute value single precision : Set $f0 to absolute value of $f1, single precision", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[1] % 2 == 1 || operands[2] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + double op1 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + double op2 = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[2] + 1), Coprocessor1.getValue(operands[2]))); + if (op1 < op2) + { + Coprocessor1.setConditionFlag(operands[0]); + } + else + { + Coprocessor1.clearConditionFlag(operands[0]); + } + } + })); + instructionList.add( + new BasicInstruction("abs.s $f0,$f1", + "Floating point absolute value single precision : Set $f0 to absolute value of $f1, single precision", BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 000101", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // I need only clear the high order bit! - Coprocessor1.updateRegister(operands[0], - Coprocessor1.getValue(operands[1]) & Integer.MAX_VALUE); - } - })); - instructionList.add( - new BasicInstruction("abs.d $f2,$f4", - "Floating point absolute value double precision : Set $f2 to absolute value of $f4, double precision", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // I need only clear the high order bit! + Coprocessor1.updateRegister(operands[0], + Coprocessor1.getValue(operands[1]) & Integer.MAX_VALUE); + } + })); + instructionList.add( + new BasicInstruction("abs.d $f2,$f4", + "Floating point absolute value double precision : Set $f2 to absolute value of $f4, double precision", BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 000101", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - // I need only clear the high order bit of high word register! - Coprocessor1.updateRegister(operands[0]+1, - Coprocessor1.getValue(operands[1]+1) & Integer.MAX_VALUE); - Coprocessor1.updateRegister(operands[0], - Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("cvt.d.s $f2,$f1", - "Convert from single precision to double precision : Set $f2 to double precision equivalent of single precision value in $f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + // I need only clear the high order bit of high word register! + Coprocessor1.updateRegister(operands[0] + 1, + Coprocessor1.getValue(operands[1] + 1) & Integer.MAX_VALUE); + Coprocessor1.updateRegister(operands[0], + Coprocessor1.getValue(operands[1])); + } + })); + instructionList.add( + new BasicInstruction("cvt.d.s $f2,$f1", + "Convert from single precision to double precision : Set $f2 to double precision equivalent of single precision value in $f1", BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 100001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1) { - throw new ProcessingException(statement, "first register must be even-numbered"); - } - // convert single precision in $f1 to double stored in $f2 - long result = Double.doubleToLongBits( - (double)Float.intBitsToFloat(Coprocessor1.getValue(operands[1]))); - Coprocessor1.updateRegister(operands[0]+1, Binary.highOrderLongToInt(result)); - Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(result)); - } - })); - instructionList.add( - new BasicInstruction("cvt.d.w $f2,$f1", - "Convert from word to double precision : Set $f2 to double precision equivalent of 32-bit integer value in $f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1) + { + throw new ProcessingException(statement, "first register must be even-numbered"); + } + // convert single precision in $f1 to double stored in $f2 + long result = Double.doubleToLongBits( + Float.intBitsToFloat(Coprocessor1.getValue(operands[1]))); + Coprocessor1.updateRegister(operands[0] + 1, Binary.highOrderLongToInt(result)); + Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(result)); + } + })); + instructionList.add( + new BasicInstruction("cvt.d.w $f2,$f1", + "Convert from word to double precision : Set $f2 to double precision equivalent of 32-bit integer value in $f1", BasicInstructionFormat.R_FORMAT, "010001 10100 00000 sssss fffff 100001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1) { - throw new ProcessingException(statement, "first register must be even-numbered"); - } - // convert integer to double (interpret $f1 value as int?) - long result = Double.doubleToLongBits( - (double)Coprocessor1.getValue(operands[1])); - Coprocessor1.updateRegister(operands[0]+1, Binary.highOrderLongToInt(result)); - Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(result)); - } - })); - instructionList.add( - new BasicInstruction("cvt.s.d $f1,$f2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1) + { + throw new ProcessingException(statement, "first register must be even-numbered"); + } + // convert integer to double (interpret $f1 value as int?) + long result = Double.doubleToLongBits( + Coprocessor1.getValue(operands[1])); + Coprocessor1.updateRegister(operands[0] + 1, Binary.highOrderLongToInt(result)); + Coprocessor1.updateRegister(operands[0], Binary.lowOrderLongToInt(result)); + } + })); + instructionList.add( + new BasicInstruction("cvt.s.d $f1,$f2", "Convert from double precision to single precision : Set $f1 to single precision equivalent of double precision value in $f2", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 100000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // convert double precision in $f2 to single stored in $f1 - if (operands[1]%2==1) { - throw new ProcessingException(statement, "second register must be even-numbered"); - } - double val = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - Coprocessor1.updateRegister(operands[0], Float.floatToIntBits((float)val)); - } - })); - instructionList.add( - new BasicInstruction("cvt.s.w $f0,$f1", - "Convert from word to single precision : Set $f0 to single precision equivalent of 32-bit integer value in $f2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // convert double precision in $f2 to single stored in $f1 + if (operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "second register must be even-numbered"); + } + double val = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + Coprocessor1.updateRegister(operands[0], Float.floatToIntBits((float) val)); + } + })); + instructionList.add( + new BasicInstruction("cvt.s.w $f0,$f1", + "Convert from word to single precision : Set $f0 to single precision equivalent of 32-bit integer value in $f2", BasicInstructionFormat.R_FORMAT, "010001 10100 00000 sssss fffff 100000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // convert integer to single (interpret $f1 value as int?) - Coprocessor1.updateRegister(operands[0], - Float.floatToIntBits((float)Coprocessor1.getValue(operands[1]))); - } - })); - instructionList.add( - new BasicInstruction("cvt.w.d $f1,$f2", - "Convert from double precision to word : Set $f1 to 32-bit integer equivalent of double precision value in $f2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // convert integer to single (interpret $f1 value as int?) + Coprocessor1.updateRegister(operands[0], + Float.floatToIntBits((float) Coprocessor1.getValue(operands[1]))); + } + })); + instructionList.add( + new BasicInstruction("cvt.w.d $f1,$f2", + "Convert from double precision to word : Set $f1 to 32-bit integer equivalent of double precision value in $f2", BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 100100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // convert double precision in $f2 to integer stored in $f1 - if (operands[1]%2==1) { - throw new ProcessingException(statement, "second register must be even-numbered"); - } - double val = Double.longBitsToDouble(Binary.twoIntsToLong( - Coprocessor1.getValue(operands[1]+1),Coprocessor1.getValue(operands[1]))); - Coprocessor1.updateRegister(operands[0], (int) val); - } - })); - instructionList.add( - new BasicInstruction("cvt.w.s $f0,$f1", - "Convert from single precision to word : Set $f0 to 32-bit integer equivalent of single precision value in $f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // convert double precision in $f2 to integer stored in $f1 + if (operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "second register must be even-numbered"); + } + double val = Double.longBitsToDouble(Binary.twoIntsToLong( + Coprocessor1.getValue(operands[1] + 1), Coprocessor1.getValue(operands[1]))); + Coprocessor1.updateRegister(operands[0], (int) val); + } + })); + instructionList.add( + new BasicInstruction("cvt.w.s $f0,$f1", + "Convert from single precision to word : Set $f0 to 32-bit integer equivalent of single precision value in $f1", BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 100100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - // convert single precision in $f1 to integer stored in $f0 - Coprocessor1.updateRegister(operands[0], - (int)Float.intBitsToFloat(Coprocessor1.getValue(operands[1]))); - } - })); - instructionList.add( - new BasicInstruction("mov.d $f2,$f4", - "Move floating point double precision : Set double precision $f2 to double precision value in $f4", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + // convert single precision in $f1 to integer stored in $f0 + Coprocessor1.updateRegister(operands[0], + (int) Float.intBitsToFloat(Coprocessor1.getValue(operands[1]))); + } + })); + instructionList.add( + new BasicInstruction("mov.d $f2,$f4", + "Move floating point double precision : Set double precision $f2 to double precision value in $f4", BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 000110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - Coprocessor1.updateRegister(operands[0]+1, Coprocessor1.getValue(operands[1]+1)); - } - })); - instructionList.add( - new BasicInstruction("movf.d $f2,$f4", - "Move floating point double precision : If condition flag 0 false, set double precision $f2 to double precision value in $f4", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + Coprocessor1.updateRegister(operands[0] + 1, Coprocessor1.getValue(operands[1] + 1)); + } + })); + instructionList.add( + new BasicInstruction("movf.d $f2,$f4", + "Move floating point double precision : If condition flag 0 false, set double precision $f2 to double precision value in $f4", BasicInstructionFormat.R_FORMAT, "010001 10001 000 00 sssss fffff 010001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - if (Coprocessor1.getConditionFlag(0)==0) { - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - Coprocessor1.updateRegister(operands[0]+1, Coprocessor1.getValue(operands[1]+1)); - } - } - })); - instructionList.add( - new BasicInstruction("movf.d $f2,$f4,1", - "Move floating point double precision : If condition flag specified by immediate is false, set double precision $f2 to double precision value in $f4", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + if (Coprocessor1.getConditionFlag(0) == 0) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + Coprocessor1.updateRegister(operands[0] + 1, Coprocessor1.getValue(operands[1] + 1)); + } + } + })); + instructionList.add( + new BasicInstruction("movf.d $f2,$f4,1", + "Move floating point double precision : If condition flag specified by immediate is false, set double precision $f2 to double precision value in $f4", BasicInstructionFormat.R_FORMAT, "010001 10001 ttt 00 sssss fffff 010001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - if (Coprocessor1.getConditionFlag(operands[2])==0) { - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - Coprocessor1.updateRegister(operands[0]+1, Coprocessor1.getValue(operands[1]+1)); - } - } - })); - instructionList.add( - new BasicInstruction("movt.d $f2,$f4", - "Move floating point double precision : If condition flag 0 true, set double precision $f2 to double precision value in $f4", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + if (Coprocessor1.getConditionFlag(operands[2]) == 0) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + Coprocessor1.updateRegister(operands[0] + 1, Coprocessor1.getValue(operands[1] + 1)); + } + } + })); + instructionList.add( + new BasicInstruction("movt.d $f2,$f4", + "Move floating point double precision : If condition flag 0 true, set double precision $f2 to double precision value in $f4", BasicInstructionFormat.R_FORMAT, "010001 10001 000 01 sssss fffff 010001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - if (Coprocessor1.getConditionFlag(0)==1) { - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - Coprocessor1.updateRegister(operands[0]+1, Coprocessor1.getValue(operands[1]+1)); - } - } - })); - instructionList.add( - new BasicInstruction("movt.d $f2,$f4,1", - "Move floating point double precision : If condition flag specified by immediate is true, set double precision $f2 to double precision value in $f4e", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + if (Coprocessor1.getConditionFlag(0) == 1) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + Coprocessor1.updateRegister(operands[0] + 1, Coprocessor1.getValue(operands[1] + 1)); + } + } + })); + instructionList.add( + new BasicInstruction("movt.d $f2,$f4,1", + "Move floating point double precision : If condition flag specified by immediate is true, set double precision $f2 to double precision value in $f4e", BasicInstructionFormat.R_FORMAT, "010001 10001 ttt 01 sssss fffff 010001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - if (Coprocessor1.getConditionFlag(operands[2])==1) { - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - Coprocessor1.updateRegister(operands[0]+1, Coprocessor1.getValue(operands[1]+1)); - } - } - })); - instructionList.add( - new BasicInstruction("movn.d $f2,$f4,$t3", - "Move floating point double precision : If $t3 is not zero, set double precision $f2 to double precision value in $f4", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + if (Coprocessor1.getConditionFlag(operands[2]) == 1) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + Coprocessor1.updateRegister(operands[0] + 1, Coprocessor1.getValue(operands[1] + 1)); + } + } + })); + instructionList.add( + new BasicInstruction("movn.d $f2,$f4,$t3", + "Move floating point double precision : If $t3 is not zero, set double precision $f2 to double precision value in $f4", BasicInstructionFormat.R_FORMAT, "010001 10001 ttttt sssss fffff 010011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - if (RegisterFile.getValue(operands[2])!=0) { - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - Coprocessor1.updateRegister(operands[0]+1, Coprocessor1.getValue(operands[1]+1)); - } - } - })); - instructionList.add( - new BasicInstruction("movz.d $f2,$f4,$t3", - "Move floating point double precision : If $t3 is zero, set double precision $f2 to double precision value in $f4", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + if (RegisterFile.getValue(operands[2]) != 0) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + Coprocessor1.updateRegister(operands[0] + 1, Coprocessor1.getValue(operands[1] + 1)); + } + } + })); + instructionList.add( + new BasicInstruction("movz.d $f2,$f4,$t3", + "Move floating point double precision : If $t3 is zero, set double precision $f2 to double precision value in $f4", BasicInstructionFormat.R_FORMAT, "010001 10001 ttttt sssss fffff 010010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - if (RegisterFile.getValue(operands[2])==0) { - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - Coprocessor1.updateRegister(operands[0]+1, Coprocessor1.getValue(operands[1]+1)); - } - } - })); - instructionList.add( - new BasicInstruction("mov.s $f0,$f1", - "Move floating point single precision : Set single precision $f0 to single precision value in $f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + if (RegisterFile.getValue(operands[2]) == 0) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + Coprocessor1.updateRegister(operands[0] + 1, Coprocessor1.getValue(operands[1] + 1)); + } + } + })); + instructionList.add( + new BasicInstruction("mov.s $f0,$f1", + "Move floating point single precision : Set single precision $f0 to single precision value in $f1", BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 000110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movf.s $f0,$f1", - "Move floating point single precision : If condition flag 0 is false, set single precision $f0 to single precision value in $f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + } + })); + instructionList.add( + new BasicInstruction("movf.s $f0,$f1", + "Move floating point single precision : If condition flag 0 is false, set single precision $f0 to single precision value in $f1", BasicInstructionFormat.R_FORMAT, "010001 10000 000 00 sssss fffff 010001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(0)==0) - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movf.s $f0,$f1,1", - "Move floating point single precision : If condition flag specified by immediate is false, set single precision $f0 to single precision value in $f1e", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(0) == 0) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movf.s $f0,$f1,1", + "Move floating point single precision : If condition flag specified by immediate is false, set single precision $f0 to single precision value in $f1e", BasicInstructionFormat.R_FORMAT, "010001 10000 ttt 00 sssss fffff 010001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(operands[2])==0) - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movt.s $f0,$f1", - "Move floating point single precision : If condition flag 0 is true, set single precision $f0 to single precision value in $f1e", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(operands[2]) == 0) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movt.s $f0,$f1", + "Move floating point single precision : If condition flag 0 is true, set single precision $f0 to single precision value in $f1e", BasicInstructionFormat.R_FORMAT, "010001 10000 000 01 sssss fffff 010001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(0)==1) - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movt.s $f0,$f1,1", - "Move floating point single precision : If condition flag specified by immediate is true, set single precision $f0 to single precision value in $f1e", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(0) == 1) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movt.s $f0,$f1,1", + "Move floating point single precision : If condition flag specified by immediate is true, set single precision $f0 to single precision value in $f1e", BasicInstructionFormat.R_FORMAT, "010001 10000 ttt 01 sssss fffff 010001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (Coprocessor1.getConditionFlag(operands[2])==1) - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movn.s $f0,$f1,$t3", - "Move floating point single precision : If $t3 is not zero, set single precision $f0 to single precision value in $f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (Coprocessor1.getConditionFlag(operands[2]) == 1) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movn.s $f0,$f1,$t3", + "Move floating point single precision : If $t3 is not zero, set single precision $f0 to single precision value in $f1", BasicInstructionFormat.R_FORMAT, "010001 10000 ttttt sssss fffff 010011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[2])!=0) - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("movz.s $f0,$f1,$t3", - "Move floating point single precision : If $t3 is zero, set single precision $f0 to single precision value in $f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[2]) != 0) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("movz.s $f0,$f1,$t3", + "Move floating point single precision : If $t3 is zero, set single precision $f0 to single precision value in $f1", BasicInstructionFormat.R_FORMAT, "010001 10000 ttttt sssss fffff 010010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[2])==0) - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("mfc1 $t1,$f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[2]) == 0) + { + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + } + } + })); + instructionList.add( + new BasicInstruction("mfc1 $t1,$f1", "Move from Coprocessor 1 (FPU) : Set $t1 to value in Coprocessor 1 register $f1", - BasicInstructionFormat.R_FORMAT, - "010001 00000 fffff sssss 00000 000000", + BasicInstructionFormat.R_FORMAT, + "010001 00000 fffff sssss 00000 000000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - RegisterFile.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("mtc1 $t1,$f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + RegisterFile.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + } + })); + instructionList.add( + new BasicInstruction("mtc1 $t1,$f1", "Move to Coprocessor 1 (FPU) : Set Coprocessor 1 register $f1 to value in $t1", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 00100 fffff sssss 00000 000000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - Coprocessor1.updateRegister(operands[1], RegisterFile.getValue(operands[0])); - } - })); - instructionList.add( - new BasicInstruction("neg.d $f2,$f4", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + Coprocessor1.updateRegister(operands[1], RegisterFile.getValue(operands[0])); + } + })); + instructionList.add( + new BasicInstruction("neg.d $f2,$f4", "Floating point negate double precision : Set double precision $f2 to negation of double precision value in $f4", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10001 00000 sssss fffff 000111", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1 || operands[1]%2==1) { - throw new ProcessingException(statement, "both registers must be even-numbered"); - } - // flip the sign bit of the second register (high order word) of the pair - int value = Coprocessor1.getValue(operands[1]+1); - Coprocessor1.updateRegister(operands[0]+1, - ((value < 0) ? (value & Integer.MAX_VALUE) : (value | Integer.MIN_VALUE))); - Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); - } - })); - instructionList.add( - new BasicInstruction("neg.s $f0,$f1", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1 || operands[1] % 2 == 1) + { + throw new ProcessingException(statement, "both registers must be even-numbered"); + } + // flip the sign bit of the second register (high order word) of the pair + int value = Coprocessor1.getValue(operands[1] + 1); + Coprocessor1.updateRegister(operands[0] + 1, + ((value < 0) ? (value & Integer.MAX_VALUE) : (value | Integer.MIN_VALUE))); + Coprocessor1.updateRegister(operands[0], Coprocessor1.getValue(operands[1])); + } + })); + instructionList.add( + new BasicInstruction("neg.s $f0,$f1", "Floating point negate single precision : Set single precision $f0 to negation of single precision value in $f1", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "010001 10000 00000 sssss fffff 000111", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int value = Coprocessor1.getValue(operands[1]); - // flip the sign bit - Coprocessor1.updateRegister(operands[0], - ((value < 0) ? (value & Integer.MAX_VALUE) : (value | Integer.MIN_VALUE))); - } - })); - instructionList.add( - new BasicInstruction("lwc1 $f1,-100($t2)", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int value = Coprocessor1.getValue(operands[1]); + // flip the sign bit + Coprocessor1.updateRegister(operands[0], + ((value < 0) ? (value & Integer.MAX_VALUE) : (value | Integer.MIN_VALUE))); + } + })); + instructionList.add( + new BasicInstruction("lwc1 $f1,-100($t2)", "Load word into Coprocessor 1 (FPU) : Set $f1 to 32-bit value from effective memory word address", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "110001 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - Coprocessor1.updateRegister(operands[0], - Globals.memory.getWord( - RegisterFile.getValue(operands[2]) + operands[1])); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + Coprocessor1.updateRegister(operands[0], + Globals.memory.getWord( + RegisterFile.getValue(operands[2]) + operands[1])); } - } - })); - instructionList.add(// no printed reference, got opcode from SPIM - new BasicInstruction("ldc1 $f2,-100($t2)", - "Load double word Coprocessor 1 (FPU)) : Set $f2 to 64-bit value from effective memory doubleword address", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add(// no printed reference, got opcode from SPIM + new BasicInstruction("ldc1 $f2,-100($t2)", + "Load double word Coprocessor 1 (FPU)) : Set $f2 to 64-bit value from effective memory doubleword address", BasicInstructionFormat.I_FORMAT, "110101 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1) { - throw new ProcessingException(statement, "first register must be even-numbered"); - } - // IF statement added by DPS 13-July-2011. - if (!Globals.memory.doublewordAligned(RegisterFile.getValue(operands[2]) + operands[1])) { - throw new ProcessingException(statement, - new AddressErrorException("address not aligned on doubleword boundary ", - Exceptions.ADDRESS_EXCEPTION_LOAD, RegisterFile.getValue(operands[2]) + operands[1])); - } - - try - { - Coprocessor1.updateRegister(operands[0], - Globals.memory.getWord( - RegisterFile.getValue(operands[2]) + operands[1])); - Coprocessor1.updateRegister(operands[0]+1, - Globals.memory.getWord( - RegisterFile.getValue(operands[2]) + operands[1] + 4)); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1) { - throw new ProcessingException(statement, e); + throw new ProcessingException(statement, "first register must be even-numbered"); } - } - })); - instructionList.add( - new BasicInstruction("swc1 $f1,-100($t2)", - "Store word from Coprocesor 1 (FPU) : Store 32 bit value in $f1 to effective memory word address", + // IF statement added by DPS 13-July-2011. + if (!Memory.doublewordAligned(RegisterFile.getValue(operands[2]) + operands[1])) + { + throw new ProcessingException(statement, + new AddressErrorException("address not aligned on doubleword boundary ", + Exceptions.ADDRESS_EXCEPTION_LOAD, RegisterFile.getValue(operands[2]) + operands[1])); + } + + try + { + Coprocessor1.updateRegister(operands[0], + Globals.memory.getWord( + RegisterFile.getValue(operands[2]) + operands[1])); + Coprocessor1.updateRegister(operands[0] + 1, + Globals.memory.getWord( + RegisterFile.getValue(operands[2]) + operands[1] + 4)); + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( + new BasicInstruction("swc1 $f1,-100($t2)", + "Store word from Coprocesor 1 (FPU) : Store 32 bit value in $f1 to effective memory word address", BasicInstructionFormat.I_FORMAT, "111001 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - try - { - Globals.memory.setWord( - RegisterFile.getValue(operands[2]) + operands[1], - Coprocessor1.getValue(operands[0])); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + try { - throw new ProcessingException(statement, e); + Globals.memory.setWord( + RegisterFile.getValue(operands[2]) + operands[1], + Coprocessor1.getValue(operands[0])); } - } - })); - instructionList.add( // no printed reference, got opcode from SPIM - new BasicInstruction("sdc1 $f2,-100($t2)", - "Store double word from Coprocessor 1 (FPU)) : Store 64 bit value in $f2 to effective memory doubleword address", + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + instructionList.add( // no printed reference, got opcode from SPIM + new BasicInstruction("sdc1 $f2,-100($t2)", + "Store double word from Coprocessor 1 (FPU)) : Store 64 bit value in $f2 to effective memory doubleword address", BasicInstructionFormat.I_FORMAT, "111101 ttttt fffff ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (operands[0]%2==1) { - throw new ProcessingException(statement, "first register must be even-numbered"); - } - // IF statement added by DPS 13-July-2011. - if (!Globals.memory.doublewordAligned(RegisterFile.getValue(operands[2]) + operands[1])) { - throw new ProcessingException(statement, - new AddressErrorException("address not aligned on doubleword boundary ", - Exceptions.ADDRESS_EXCEPTION_STORE, RegisterFile.getValue(operands[2]) + operands[1])); - } - try - { - Globals.memory.setWord( - RegisterFile.getValue(operands[2]) + operands[1], - Coprocessor1.getValue(operands[0])); - Globals.memory.setWord( - RegisterFile.getValue(operands[2]) + operands[1] + 4, - Coprocessor1.getValue(operands[0]+1)); - } - catch (AddressErrorException e) + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (operands[0] % 2 == 1) { - throw new ProcessingException(statement, e); + throw new ProcessingException(statement, "first register must be even-numbered"); } - } - })); - //////////////////////////// THE TRAP INSTRUCTIONS & ERET //////////////////////////// - instructionList.add( - new BasicInstruction("teq $t1,$t2", + // IF statement added by DPS 13-July-2011. + if (!Memory.doublewordAligned(RegisterFile.getValue(operands[2]) + operands[1])) + { + throw new ProcessingException(statement, + new AddressErrorException("address not aligned on doubleword boundary ", + Exceptions.ADDRESS_EXCEPTION_STORE, RegisterFile.getValue(operands[2]) + operands[1])); + } + try + { + Globals.memory.setWord( + RegisterFile.getValue(operands[2]) + operands[1], + Coprocessor1.getValue(operands[0])); + Globals.memory.setWord( + RegisterFile.getValue(operands[2]) + operands[1] + 4, + Coprocessor1.getValue(operands[0] + 1)); + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } + })); + //////////////////////////// THE TRAP INSTRUCTIONS & ERET //////////////////////////// + instructionList.add( + new BasicInstruction("teq $t1,$t2", "Trap if equal : Trap if $t1 is equal to $t2", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 110100", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) == RegisterFile.getValue(operands[1])) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("teqi $t1,-100", - "Trap if equal to immediate : Trap if $t1 is equal to sign-extended 16 bit immediate", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) == RegisterFile.getValue(operands[1])) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("teqi $t1,-100", + "Trap if equal to immediate : Trap if $t1 is equal to sign-extended 16 bit immediate", BasicInstructionFormat.I_FORMAT, "000001 fffff 01100 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) == (operands[1] << 16 >> 16)) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tne $t1,$t2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) == (operands[1] << 16 >> 16)) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tne $t1,$t2", "Trap if not equal : Trap if $t1 is not equal to $t2", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 110110", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) != RegisterFile.getValue(operands[1])) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tnei $t1,-100", - "Trap if not equal to immediate : Trap if $t1 is not equal to sign-extended 16 bit immediate", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) != RegisterFile.getValue(operands[1])) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tnei $t1,-100", + "Trap if not equal to immediate : Trap if $t1 is not equal to sign-extended 16 bit immediate", BasicInstructionFormat.I_FORMAT, "000001 fffff 01110 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) != (operands[1] << 16 >> 16)) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tge $t1,$t2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) != (operands[1] << 16 >> 16)) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tge $t1,$t2", "Trap if greater or equal : Trap if $t1 is greater than or equal to $t2", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 110000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) >= RegisterFile.getValue(operands[1])) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tgeu $t1,$t2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) >= RegisterFile.getValue(operands[1])) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tgeu $t1,$t2", "Trap if greater or equal unsigned : Trap if $t1 is greater than or equal to $t2 using unsigned comparision", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 110001", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int first = RegisterFile.getValue(operands[0]); - int second = RegisterFile.getValue(operands[1]); - // if signs same, do straight compare; if signs differ & first negative then first greater else second - if ((first >= 0 && second >= 0 || first < 0 && second < 0) ? (first >= second) : (first < 0) ) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tgei $t1,-100", - "Trap if greater than or equal to immediate : Trap if $t1 greater than or equal to sign-extended 16 bit immediate", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int first = RegisterFile.getValue(operands[0]); + int second = RegisterFile.getValue(operands[1]); + // if signs same, do straight compare; if signs differ & first negative then first greater else second + if ((first >= 0 && second >= 0 || first < 0 && second < 0) ? (first >= second) : (first < 0)) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tgei $t1,-100", + "Trap if greater than or equal to immediate : Trap if $t1 greater than or equal to sign-extended 16 bit immediate", BasicInstructionFormat.I_FORMAT, "000001 fffff 01000 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) >= (operands[1] << 16 >> 16)) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tgeiu $t1,-100", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) >= (operands[1] << 16 >> 16)) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tgeiu $t1,-100", "Trap if greater or equal to immediate unsigned : Trap if $t1 greater than or equal to sign-extended 16 bit immediate, unsigned comparison", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "000001 fffff 01001 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int first = RegisterFile.getValue(operands[0]); - // 16 bit immediate value in operands[1] is sign-extended - int second = operands[1] << 16 >> 16; - // if signs same, do straight compare; if signs differ & first negative then first greater else second - if ((first >= 0 && second >= 0 || first < 0 && second < 0) ? (first >= second) : (first < 0) ) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tlt $t1,$t2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int first = RegisterFile.getValue(operands[0]); + // 16 bit immediate value in operands[1] is sign-extended + int second = operands[1] << 16 >> 16; + // if signs same, do straight compare; if signs differ & first negative then first greater else second + if ((first >= 0 && second >= 0 || first < 0 && second < 0) ? (first >= second) : (first < 0)) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tlt $t1,$t2", "Trap if less than: Trap if $t1 less than $t2", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 110010", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) < RegisterFile.getValue(operands[1])) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tltu $t1,$t2", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) < RegisterFile.getValue(operands[1])) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tltu $t1,$t2", "Trap if less than unsigned : Trap if $t1 less than $t2, unsigned comparison", - BasicInstructionFormat.R_FORMAT, + BasicInstructionFormat.R_FORMAT, "000000 fffff sssss 00000 00000 110011", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int first = RegisterFile.getValue(operands[0]); - int second = RegisterFile.getValue(operands[1]); - // if signs same, do straight compare; if signs differ & first positive then first is less else second - if ((first >= 0 && second >= 0 || first < 0 && second < 0) ? (first < second) : (first >= 0) ) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tlti $t1,-100", - "Trap if less than immediate : Trap if $t1 less than sign-extended 16-bit immediate", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int first = RegisterFile.getValue(operands[0]); + int second = RegisterFile.getValue(operands[1]); + // if signs same, do straight compare; if signs differ & first positive then first is less else second + if ((first >= 0 && second >= 0 || first < 0 && second < 0) ? (first < second) : (first >= 0)) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tlti $t1,-100", + "Trap if less than immediate : Trap if $t1 less than sign-extended 16-bit immediate", BasicInstructionFormat.I_FORMAT, "000001 fffff 01010 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - if (RegisterFile.getValue(operands[0]) < (operands[1] << 16 >> 16)) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("tltiu $t1,-100", + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + if (RegisterFile.getValue(operands[0]) < (operands[1] << 16 >> 16)) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("tltiu $t1,-100", "Trap if less than immediate unsigned : Trap if $t1 less than sign-extended 16-bit immediate, unsigned comparison", - BasicInstructionFormat.I_FORMAT, + BasicInstructionFormat.I_FORMAT, "000001 fffff 01011 ssssssssssssssss", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - int[] operands = statement.getOperands(); - int first = RegisterFile.getValue(operands[0]); - // 16 bit immediate value in operands[1] is sign-extended - int second = operands[1] << 16 >> 16; - // if signs same, do straight compare; if signs differ & first positive then first is less else second - if ((first >= 0 && second >= 0 || first < 0 && second < 0) ? (first < second) : (first >= 0) ) - { - throw new ProcessingException(statement, - "trap",Exceptions.TRAP_EXCEPTION); - } - } - })); - instructionList.add( - new BasicInstruction("eret", - "Exception return : Set Program Counter to Coprocessor 0 EPC register value, set Coprocessor Status register bit 1 (exception level) to zero", - BasicInstructionFormat.R_FORMAT, + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + int[] operands = statement.getOperands(); + int first = RegisterFile.getValue(operands[0]); + // 16 bit immediate value in operands[1] is sign-extended + int second = operands[1] << 16 >> 16; + // if signs same, do straight compare; if signs differ & first positive then first is less else second + if ((first >= 0 && second >= 0 || first < 0 && second < 0) ? (first < second) : (first >= 0)) + { + throw new ProcessingException(statement, + "trap", Exceptions.TRAP_EXCEPTION); + } + } + })); + instructionList.add( + new BasicInstruction("eret", + "Exception return : Set Program Counter to Coprocessor 0 EPC register value, set Coprocessor Status register bit 1 (exception level) to zero", + BasicInstructionFormat.R_FORMAT, "010000 1 0000000000000000000 011000", new SimulationCode() - { - public void simulate(ProgramStatement statement) throws ProcessingException - { - // set EXL bit (bit 1) in Status register to 0 and set PC to EPC - Coprocessor0.updateRegister(Coprocessor0.STATUS, - Binary.clearBit(Coprocessor0.getValue(Coprocessor0.STATUS), Coprocessor0.EXCEPTION_LEVEL)); - RegisterFile.setProgramCounter(Coprocessor0.getValue(Coprocessor0.EPC)); - } - })); - + { + public void simulate(ProgramStatement statement) throws ProcessingException + { + // set EXL bit (bit 1) in Status register to 0 and set PC to EPC + Coprocessor0.updateRegister(Coprocessor0.STATUS, + Binary.clearBit(Coprocessor0.getValue(Coprocessor0.STATUS), Coprocessor0.EXCEPTION_LEVEL)); + RegisterFile.setProgramCounter(Coprocessor0.getValue(Coprocessor0.EPC)); + } + })); + ////////////// READ PSEUDO-INSTRUCTION SPECS FROM DATA FILE AND ADD ////////////////////// - addPseudoInstructions(); - + addPseudoInstructions(); + ////////////// GET AND CREATE LIST OF SYSCALL FUNCTION OBJECTS //////////////////// - syscallLoader = new SyscallLoader(); - syscallLoader.loadSyscalls(); - + syscallLoader = new SyscallLoader(); + syscallLoader.loadSyscalls(); + // Initialization step. Create token list for each instruction example. This is // used by parser to determine user program correct syntax. - for (int i = 0; i < instructionList.size(); i++) - { + for (int i = 0; i < instructionList.size(); i++) + { Instruction inst = (Instruction) instructionList.get(i); inst.createExampleTokenList(); - } + } - HashMap maskMap = new HashMap(); - ArrayList matchMaps = new ArrayList(); - for (int i = 0; i < instructionList.size(); i++) { - Object rawInstr = instructionList.get(i); - if (rawInstr instanceof BasicInstruction) { - BasicInstruction basic = (BasicInstruction) rawInstr; - Integer mask = Integer.valueOf(basic.getOpcodeMask()); - Integer match = Integer.valueOf(basic.getOpcodeMatch()); - HashMap matchMap = (HashMap) maskMap.get(mask); - if (matchMap == null) { - matchMap = new HashMap(); - maskMap.put(mask, matchMap); - matchMaps.add(new MatchMap(mask, matchMap)); - } - matchMap.put(match, basic); - } - } - Collections.sort(matchMaps); - this.opcodeMatchMaps = matchMaps; - } + HashMap maskMap = new HashMap(); + ArrayList matchMaps = new ArrayList(); + for (int i = 0; i < instructionList.size(); i++) + { + Object rawInstr = instructionList.get(i); + if (rawInstr instanceof BasicInstruction) + { + BasicInstruction basic = (BasicInstruction) rawInstr; + Integer mask = Integer.valueOf(basic.getOpcodeMask()); + Integer match = Integer.valueOf(basic.getOpcodeMatch()); + HashMap matchMap = (HashMap) maskMap.get(mask); + if (matchMap == null) + { + matchMap = new HashMap(); + maskMap.put(mask, matchMap); + matchMaps.add(new MatchMap(mask, matchMap)); + } + matchMap.put(match, basic); + } + } + Collections.sort(matchMaps); + this.opcodeMatchMaps = matchMaps; + } + + public BasicInstruction findByBinaryCode(int binaryInstr) + { + ArrayList matchMaps = this.opcodeMatchMaps; + for (int i = 0; i < matchMaps.size(); i++) + { + MatchMap map = (MatchMap) matchMaps.get(i); + BasicInstruction ret = map.find(binaryInstr); + if (ret != null) + { + return ret; + } + } + return null; + } - public BasicInstruction findByBinaryCode(int binaryInstr) { - ArrayList matchMaps = this.opcodeMatchMaps; - for (int i = 0; i < matchMaps.size(); i++) { - MatchMap map = (MatchMap) matchMaps.get(i); - BasicInstruction ret = map.find(binaryInstr); - if (ret != null) return ret; - } - return null; - } - /* METHOD TO ADD PSEUDO-INSTRUCTIONS - */ - - private void addPseudoInstructions() - { - InputStream is = null; - BufferedReader in = null; - try - { + */ + + private void addPseudoInstructions() + { + InputStream is = null; + BufferedReader in = null; + try + { // leading "/" prevents package name being prepended to filepath. is = this.getClass().getResourceAsStream("/PseudoOps.txt"); in = new BufferedReader(new InputStreamReader(is)); - } - catch (NullPointerException e) - { - System.out.println( - "Error: MIPS pseudo-instruction file PseudoOps.txt not found."); - System.exit(0); - } - try - { + } + catch (NullPointerException e) + { + System.out.println( + "Error: MIPS pseudo-instruction file PseudoOps.txt not found."); + System.exit(0); + } + try + { String line, pseudoOp, template, firstTemplate, token; String description; StringTokenizer tokenizer; - while ((line = in.readLine()) != null) { + while ((line = in.readLine()) != null) + { // skip over: comment lines, empty lines, lines starting with blank. - if (!line.startsWith("#") && !line.startsWith(" ") - && line.length() > 0) { - description = ""; - tokenizer = new StringTokenizer(line, "\t"); - pseudoOp = tokenizer.nextToken(); - template = ""; - firstTemplate = null; - while (tokenizer.hasMoreTokens()) { - token = tokenizer.nextToken(); - if (token.startsWith("#")) { - // Optional description must be last token in the line. - description = token.substring(1); - break; - } - if (token.startsWith("COMPACT")) { - // has second template for Compact (16-bit) memory config -- added DPS 3 Aug 2009 - firstTemplate = template; - template = ""; - continue; - } - template = template + token; - if (tokenizer.hasMoreTokens()) { - template = template + "\n"; - } - } - ExtendedInstruction inst = (firstTemplate == null) - ? new ExtendedInstruction(pseudoOp, template, description) - : new ExtendedInstruction(pseudoOp, firstTemplate, template, description); - instructionList.add(inst); - //if (firstTemplate != null) System.out.println("\npseudoOp: "+pseudoOp+"\ndefault template:\n"+firstTemplate+"\ncompact template:\n"+template); - } + if (!line.startsWith("#") && !line.startsWith(" ") + && line.length() > 0) + { + description = ""; + tokenizer = new StringTokenizer(line, "\t"); + pseudoOp = tokenizer.nextToken(); + template = ""; + firstTemplate = null; + while (tokenizer.hasMoreTokens()) + { + token = tokenizer.nextToken(); + if (token.startsWith("#")) + { + // Optional description must be last token in the line. + description = token.substring(1); + break; + } + if (token.startsWith("COMPACT")) + { + // has second template for Compact (16-bit) memory config -- added DPS 3 Aug 2009 + firstTemplate = template; + template = ""; + continue; + } + template = template + token; + if (tokenizer.hasMoreTokens()) + { + template = template + "\n"; + } + } + ExtendedInstruction inst = (firstTemplate == null) + ? new ExtendedInstruction(pseudoOp, template, description) + : new ExtendedInstruction(pseudoOp, firstTemplate, template, description); + instructionList.add(inst); + //if (firstTemplate != null) System.out.println("\npseudoOp: "+pseudoOp+"\ndefault template:\n"+firstTemplate+"\ncompact template:\n"+template); + } } in.close(); - } - catch (IOException ioe) - { - System.out.println( - "Internal Error: MIPS pseudo-instructions could not be loaded."); - System.exit(0); - } - catch (Exception ioe) - { - System.out.println( - "Error: Invalid MIPS pseudo-instruction specification."); - System.exit(0); - } - - } - + } + catch (IOException ioe) + { + System.out.println( + "Internal Error: MIPS pseudo-instructions could not be loaded."); + System.exit(0); + } + catch (Exception ioe) + { + System.out.println( + "Error: Invalid MIPS pseudo-instruction specification."); + System.exit(0); + } + + } + /** - * Given an operator mnemonic, will return the corresponding Instruction object(s) - * from the instruction set. Uses straight linear search technique. - * @param name operator mnemonic (e.g. addi, sw,...) - * @return list of corresponding Instruction object(s), or null if not found. + * Given an operator mnemonic, will return the corresponding Instruction object(s) from the instruction set. Uses + * straight linear search technique. + * + * @param name operator mnemonic (e.g. addi, sw,...) + * @return list of corresponding Instruction object(s), or null if not found. */ - public ArrayList matchOperator(String name) - { - ArrayList matchingInstructions = null; + public ArrayList matchOperator(String name) + { + ArrayList matchingInstructions = null; // Linear search for now.... - for (int i = 0; i < instructionList.size(); i++) - { + for (int i = 0; i < instructionList.size(); i++) + { if (((Instruction) instructionList.get(i)).getName().equalsIgnoreCase(name)) { - if (matchingInstructions == null) - matchingInstructions = new ArrayList(); - matchingInstructions.add(instructionList.get(i)); + if (matchingInstructions == null) + { + matchingInstructions = new ArrayList(); + } + matchingInstructions.add(instructionList.get(i)); } - } - return matchingInstructions; - } - - + } + return matchingInstructions; + } + + /** - * Given a string, will return the Instruction object(s) from the instruction - * set whose operator mnemonic prefix matches it. Case-insensitive. For example - * "s" will match "sw", "sh", "sb", etc. Uses straight linear search technique. - * @param name a string - * @return list of matching Instruction object(s), or null if none match. + * Given a string, will return the Instruction object(s) from the instruction set whose operator mnemonic prefix + * matches it. Case-insensitive. For example "s" will match "sw", "sh", "sb", etc. Uses straight linear search + * technique. + * + * @param name a string + * @return list of matching Instruction object(s), or null if none match. */ - public ArrayList prefixMatchOperator(String name) - { - ArrayList matchingInstructions = null; + public ArrayList prefixMatchOperator(String name) + { + ArrayList matchingInstructions = null; // Linear search for now.... - if (name != null) { + if (name != null) + { for (int i = 0; i < instructionList.size(); i++) { - if (((Instruction) instructionList.get(i)).getName().toLowerCase().startsWith(name.toLowerCase())) - { - if (matchingInstructions == null) - matchingInstructions = new ArrayList(); - matchingInstructions.add(instructionList.get(i)); - } + if (((Instruction) instructionList.get(i)).getName().toLowerCase().startsWith(name.toLowerCase())) + { + if (matchingInstructions == null) + { + matchingInstructions = new ArrayList(); + } + matchingInstructions.add(instructionList.get(i)); + } } - } - return matchingInstructions; - } - - /* - * Method to find and invoke a syscall given its service number. Each syscall - * function is represented by an object in an array list. Each object is of - * a class that implements Syscall or extends AbstractSyscall. - */ - - private void findAndSimulateSyscall(int number, ProgramStatement statement) - throws ProcessingException { - Syscall service = syscallLoader.findSyscall(number); - if (service != null) { + } + return matchingInstructions; + } + + /* + * Method to find and invoke a syscall given its service number. Each syscall + * function is represented by an object in an array list. Each object is of + * a class that implements Syscall or extends AbstractSyscall. + */ + + private void findAndSimulateSyscall(int number, ProgramStatement statement) + throws ProcessingException + { + Syscall service = syscallLoader.findSyscall(number); + if (service != null) + { service.simulate(statement); return; - } - throw new ProcessingException(statement, - "invalid or unimplemented syscall service: " + - number + " ", Exceptions.SYSCALL_EXCEPTION); - } - - /* - * Method to process a successful branch condition. DO NOT USE WITH JUMP - * INSTRUCTIONS! The branch operand is a relative displacement in words - * whereas the jump operand is an absolute address in bytes. - * - * The parameter is displacement operand from instruction. - * - * Handles delayed branching if that setting is enabled. - */ - // 4 January 2008 DPS: The subtraction of 4 bytes (instruction length) after - // the shift has been removed. It is left in as commented-out code below. - // This has the effect of always branching as if delayed branching is enabled, - // even if it isn't. This mod must work in conjunction with - // ProgramStatement.java, buildBasicStatementFromBasicInstruction() method near - // the bottom (currently line 194, heavily commented). - - private void processBranch(int displacement) { - if (Globals.getSettings().getDelayedBranchingEnabled()) { + } + throw new ProcessingException(statement, + "invalid or unimplemented syscall service: " + + number + " ", Exceptions.SYSCALL_EXCEPTION); + } + + /* + * Method to process a successful branch condition. DO NOT USE WITH JUMP + * INSTRUCTIONS! The branch operand is a relative displacement in words + * whereas the jump operand is an absolute address in bytes. + * + * The parameter is displacement operand from instruction. + * + * Handles delayed branching if that setting is enabled. + */ + // 4 January 2008 DPS: The subtraction of 4 bytes (instruction length) after + // the shift has been removed. It is left in as commented-out code below. + // This has the effect of always branching as if delayed branching is enabled, + // even if it isn't. This mod must work in conjunction with + // ProgramStatement.java, buildBasicStatementFromBasicInstruction() method near + // the bottom (currently line 194, heavily commented). + + private void processBranch(int displacement) + { + if (Globals.getSettings().getDelayedBranchingEnabled()) + { // Register the branch target address (absolute byte address). DelayedBranch.register(RegisterFile.getProgramCounter() + (displacement << 2)); - } - else { + } + else + { // Decrement needed because PC has already been incremented RegisterFile.setProgramCounter( RegisterFile.getProgramCounter() - + (displacement << 2)); // - Instruction.INSTRUCTION_LENGTH); - } - } - - /* - * Method to process a jump. DO NOT USE WITH BRANCH INSTRUCTIONS! - * The branch operand is a relative displacement in words - * whereas the jump operand is an absolute address in bytes. - * - * The parameter is jump target absolute byte address. - * - * Handles delayed branching if that setting is enabled. - */ - - private void processJump(int targetAddress) { - if (Globals.getSettings().getDelayedBranchingEnabled()) { + + (displacement << 2)); // - Instruction.INSTRUCTION_LENGTH); + } + } + + /* + * Method to process a jump. DO NOT USE WITH BRANCH INSTRUCTIONS! + * The branch operand is a relative displacement in words + * whereas the jump operand is an absolute address in bytes. + * + * The parameter is jump target absolute byte address. + * + * Handles delayed branching if that setting is enabled. + */ + + private void processJump(int targetAddress) + { + if (Globals.getSettings().getDelayedBranchingEnabled()) + { DelayedBranch.register(targetAddress); - } - else { + } + else + { RegisterFile.setProgramCounter(targetAddress); - } - } - - /* - * Method to process storing of a return address in the given - * register. This is used only by the "and link" - * instructions: jal, jalr, bltzal, bgezal. If delayed branching - * setting is off, the return address is the address of the - * next instruction (e.g. the current PC value). If on, the - * return address is the instruction following that, to skip over - * the delay slot. - * - * The parameter is register number to receive the return address. - */ - - private void processReturnAddress(int register) { - RegisterFile.updateRegister(register, RegisterFile.getProgramCounter() + - ((Globals.getSettings().getDelayedBranchingEnabled()) ? - Instruction.INSTRUCTION_LENGTH : 0) ); - } + } + } - private static class MatchMap implements Comparable { - private int mask; - private int maskLength; // number of 1 bits in mask - private HashMap matchMap; + /* + * Method to process storing of a return address in the given + * register. This is used only by the "and link" + * instructions: jal, jalr, bltzal, bgezal. If delayed branching + * setting is off, the return address is the address of the + * next instruction (e.g. the current PC value). If on, the + * return address is the instruction following that, to skip over + * the delay slot. + * + * The parameter is register number to receive the return address. + */ - public MatchMap(int mask, HashMap matchMap) { - this.mask = mask; - this.matchMap = matchMap; + private void processReturnAddress(int register) + { + RegisterFile.updateRegister(register, RegisterFile.getProgramCounter() + + ((Globals.getSettings().getDelayedBranchingEnabled()) ? + Instruction.INSTRUCTION_LENGTH : 0)); + } - int k = 0; - int n = mask; - while (n != 0) { - k++; - n &= n - 1; - } - this.maskLength = k; - } + private static class MatchMap implements Comparable + { + private final int mask; - public boolean equals(Object o) { - return o instanceof MatchMap && mask == ((MatchMap) o).mask; - } + private final int maskLength; // number of 1 bits in mask - public int compareTo(Object other) { - MatchMap o = (MatchMap) other; - int d = o.maskLength - this.maskLength; - if (d == 0) d = this.mask - o.mask; - return d; - } + private final HashMap matchMap; - public BasicInstruction find(int instr) { - int match = Integer.valueOf(instr & mask); - return (BasicInstruction) matchMap.get(match); - } - } - } + public MatchMap(int mask, HashMap matchMap) + { + this.mask = mask; + this.matchMap = matchMap; + + int k = 0; + int n = mask; + while (n != 0) + { + k++; + n &= n - 1; + } + this.maskLength = k; + } + + public boolean equals(Object o) + { + return o instanceof MatchMap && mask == ((MatchMap) o).mask; + } + + public int compareTo(Object other) + { + MatchMap o = (MatchMap) other; + int d = o.maskLength - this.maskLength; + if (d == 0) + { + d = this.mask - o.mask; + } + return d; + } + + public BasicInstruction find(int instr) + { + int match = Integer.valueOf(instr & mask); + return (BasicInstruction) matchMap.get(match); + } + } +} diff --git a/src/main/java/mars/mips/instructions/SimulationCode.java b/src/main/java/mars/mips/instructions/SimulationCode.java index 9e2646c..4be4644 100644 --- a/src/main/java/mars/mips/instructions/SimulationCode.java +++ b/src/main/java/mars/mips/instructions/SimulationCode.java @@ -1,5 +1,7 @@ package mars.mips.instructions; -import mars.*; + +import mars.ProcessingException; +import mars.ProgramStatement; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -30,23 +32,22 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Interface to represent the method for simulating the execution of a specific MIPS basic - * instruction. It will be implemented by the anonymous class created in the last - * argument to the BasicInstruction constructor. - * - * @author Pete Sanderson + * Interface to represent the method for simulating the execution of a specific MIPS basic instruction. It will be + * implemented by the anonymous class created in the last argument to the BasicInstruction constructor. + * + * @author Pete Sanderson * @version August 2003 - * */ -public interface SimulationCode { +public interface SimulationCode +{ /** * Method to simulate the execution of a specific MIPS basic instruction. - * - * @param statement A ProgramStatement representing the MIPS instruction to simulate. + * + * @param statement A ProgramStatement representing the MIPS instruction to simulate. * @throws ProcessingException This is a run-time exception generated during simulation. **/ - - public void simulate(ProgramStatement statement) throws ProcessingException; + + void simulate(ProgramStatement statement) throws ProcessingException; } diff --git a/src/main/java/mars/mips/instructions/SyscallLoader.java b/src/main/java/mars/mips/instructions/SyscallLoader.java index e9d30f4..a6e7b3d 100644 --- a/src/main/java/mars/mips/instructions/SyscallLoader.java +++ b/src/main/java/mars/mips/instructions/SyscallLoader.java @@ -1,8 +1,12 @@ - package mars.mips.instructions; - import mars.mips.instructions.syscalls.*; - import mars.*; - import mars.util.*; - import java.util.*; +package mars.mips.instructions; + +import mars.Globals; +import mars.mips.instructions.syscalls.Syscall; +import mars.mips.instructions.syscalls.SyscallNumberOverride; +import mars.util.FilenameFinder; + +import java.util.ArrayList; +import java.util.HashMap; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,138 +36,166 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /****************************************************************************/ - /* This class provides functionality to bring external Syscall definitions - * into MARS. This permits anyone with knowledge of the Mars public interfaces, - * in particular of the Memory and Register classes, to write custom MIPS syscall - * functions. This is adapted from the ToolLoader class, which is in turn adapted - * from Bret Barker's GameServer class from the book "Developing Games In Java". + +/****************************************************************************/ +/* This class provides functionality to bring external Syscall definitions + * into MARS. This permits anyone with knowledge of the Mars public interfaces, + * in particular of the Memory and Register classes, to write custom MIPS syscall + * functions. This is adapted from the ToolLoader class, which is in turn adapted + * from Bret Barker's GameServer class from the book "Developing Games In Java". + */ + +class SyscallLoader +{ + + private static final String CLASS_PREFIX = "mars.mips.instructions.syscalls."; + + private static final String SYSCALLS_DIRECTORY_PATH = "mars/mips/instructions/syscalls"; + + private static final String SYSCALL_INTERFACE = "Syscall.class"; + + private static final String SYSCALL_ABSTRACT = "AbstractSyscall.class"; + + private static final String CLASS_EXTENSION = "class"; + + private ArrayList syscallList; + + /* + * Dynamically loads Syscalls into an ArrayList. This method is adapted from + * the loadGameControllers() method in Bret Barker's GameServer class. + * Barker (bret@hypefiend.com) is co-author of the book "Developing Games + * in Java". Also see the "loadMarsTools()" method from ToolLoader class. */ - - class SyscallLoader { - - private static final String CLASS_PREFIX = "mars.mips.instructions.syscalls."; - private static final String SYSCALLS_DIRECTORY_PATH = "mars/mips/instructions/syscalls"; - private static final String SYSCALL_INTERFACE = "Syscall.class"; - private static final String SYSCALL_ABSTRACT = "AbstractSyscall.class"; - private static final String CLASS_EXTENSION = "class"; - - private ArrayList syscallList; - - /* - * Dynamically loads Syscalls into an ArrayList. This method is adapted from - * the loadGameControllers() method in Bret Barker's GameServer class. - * Barker (bret@hypefiend.com) is co-author of the book "Developing Games - * in Java". Also see the "loadMarsTools()" method from ToolLoader class. - */ - void loadSyscalls() { - syscallList = new ArrayList(); - // grab all class files in the same directory as Syscall - ArrayList candidates = FilenameFinder.getFilenameList(this.getClass( ).getClassLoader(), - SYSCALLS_DIRECTORY_PATH, CLASS_EXTENSION); - HashMap syscalls = new HashMap(); - for( int i = 0; i < candidates.size(); i++) { - String file = (String) candidates.get(i); - // Do not add class if already encountered (happens if run in MARS development directory) - if (syscalls.containsKey(file)) { - continue; - } else { - syscalls.put(file,file); - } + void loadSyscalls() + { + syscallList = new ArrayList(); + // grab all class files in the same directory as Syscall + ArrayList candidates = FilenameFinder.getFilenameList(this.getClass().getClassLoader(), + SYSCALLS_DIRECTORY_PATH, CLASS_EXTENSION); + HashMap syscalls = new HashMap(); + for (int i = 0; i < candidates.size(); i++) + { + String file = (String) candidates.get(i); + // Do not add class if already encountered (happens if run in MARS development directory) + if (syscalls.containsKey(file)) + { + continue; + } + else + { + syscalls.put(file, file); + } if ((!file.equals(SYSCALL_INTERFACE)) && - (!file.equals(SYSCALL_ABSTRACT)) ) { - try { - // grab the class, make sure it implements Syscall, instantiate, add to list - String syscallClassName = CLASS_PREFIX+file.substring(0, file.indexOf(CLASS_EXTENSION)-1); - Class clas = Class.forName(syscallClassName); - if (!Syscall.class.isAssignableFrom(clas)) { - continue; - } - Syscall syscall = (Syscall) clas.newInstance(); - if (findSyscall(syscall.getNumber()) == null) { - syscallList.add(syscall); - } - else { - throw new Exception("Duplicate service number: "+syscall.getNumber()+ - " already registered to "+ + (!file.equals(SYSCALL_ABSTRACT))) + { + try + { + // grab the class, make sure it implements Syscall, instantiate, add to list + String syscallClassName = CLASS_PREFIX + file.substring(0, file.indexOf(CLASS_EXTENSION) - 1); + Class clas = Class.forName(syscallClassName); + if (!Syscall.class.isAssignableFrom(clas)) + { + continue; + } + Syscall syscall = (Syscall) clas.newInstance(); + if (findSyscall(syscall.getNumber()) == null) + { + syscallList.add(syscall); + } + else + { + throw new Exception("Duplicate service number: " + syscall.getNumber() + + " already registered to " + findSyscall(syscall.getNumber()).getName()); - } - } - catch (Exception e) { - System.out.println("Error instantiating Syscall from file " + file + ": "+e); - System.exit(0); - } + } + } + catch (Exception e) + { + System.out.println("Error instantiating Syscall from file " + file + ": " + e); + System.exit(0); + } } - } - syscallList = processSyscallNumberOverrides(syscallList); - return; - } - - // Will get any syscall number override specifications from MARS config file and - // process them. This will alter syscallList entry for affected names. - private ArrayList processSyscallNumberOverrides(ArrayList syscallList) { - ArrayList overrides = new Globals().getSyscallOverrides(); - SyscallNumberOverride override; - Syscall syscall; - for (int index=0; index < overrides.size(); index++) { + } + syscallList = processSyscallNumberOverrides(syscallList); + } + + // Will get any syscall number override specifications from MARS config file and + // process them. This will alter syscallList entry for affected names. + private ArrayList processSyscallNumberOverrides(ArrayList syscallList) + { + ArrayList overrides = new Globals().getSyscallOverrides(); + SyscallNumberOverride override; + Syscall syscall; + for (int index = 0; index < overrides.size(); index++) + { override = (SyscallNumberOverride) overrides.get(index); - boolean match = false; - for (int i=0; i < syscallList.size(); i++) { - syscall = (Syscall) syscallList.get(i); - if (override.getName().equals(syscall.getName())) { - // we have a match to service name, assign new number - syscall.setNumber(override.getNumber()); - match = true; - } + boolean match = false; + for (int i = 0; i < syscallList.size(); i++) + { + syscall = (Syscall) syscallList.get(i); + if (override.getName().equals(syscall.getName())) + { + // we have a match to service name, assign new number + syscall.setNumber(override.getNumber()); + match = true; + } } - if (!match) { - System.out.println("Error: syscall name '"+override.getName()+ - "' in config file does not match any name in syscall list"); - System.exit(0); + if (!match) + { + System.out.println("Error: syscall name '" + override.getName() + + "' in config file does not match any name in syscall list"); + System.exit(0); } - } - // Wait until end to check for duplicate numbers. To do so earlier - // would disallow for instance the exchange of numbers between two - // services. This is N-squared operation but N is small. - // This will also detect duplicates that accidently occur from addition - // of a new Syscall subclass to the collection, even if the config file - // does not contain any overrides. - Syscall syscallA, syscallB; - boolean duplicates = false; - for (int i = 0; i < syscallList.size(); i++) { - syscallA = (Syscall)syscallList.get(i); - for (int j = i+1; j < syscallList.size(); j++) { - syscallB = (Syscall)syscallList.get(j); - if ( syscallA.getNumber() == syscallB.getNumber()) { - System.out.println("Error: syscalls "+syscallA.getName()+" and "+ - syscallB.getName()+" are both assigned same number "+syscallA.getNumber()); - duplicates = true; - } + } + // Wait until end to check for duplicate numbers. To do so earlier + // would disallow for instance the exchange of numbers between two + // services. This is N-squared operation but N is small. + // This will also detect duplicates that accidently occur from addition + // of a new Syscall subclass to the collection, even if the config file + // does not contain any overrides. + Syscall syscallA, syscallB; + boolean duplicates = false; + for (int i = 0; i < syscallList.size(); i++) + { + syscallA = (Syscall) syscallList.get(i); + for (int j = i + 1; j < syscallList.size(); j++) + { + syscallB = (Syscall) syscallList.get(j); + if (syscallA.getNumber() == syscallB.getNumber()) + { + System.out.println("Error: syscalls " + syscallA.getName() + " and " + + syscallB.getName() + " are both assigned same number " + syscallA.getNumber()); + duplicates = true; + } } - } - if (duplicates) { + } + if (duplicates) + { System.exit(0); - } - return syscallList; - } - - /* - * Method to find Syscall object associated with given service number. - * Returns null if no associated object found. - */ - Syscall findSyscall(int number) { - // linear search is OK since number of syscalls is small. - Syscall service, match = null; - if (syscallList==null) { + } + return syscallList; + } + + /* + * Method to find Syscall object associated with given service number. + * Returns null if no associated object found. + */ + Syscall findSyscall(int number) + { + // linear search is OK since number of syscalls is small. + Syscall service, match = null; + if (syscallList == null) + { loadSyscalls(); - } - for (int index=0; index < syscallList.size(); index++) { + } + for (int index = 0; index < syscallList.size(); index++) + { service = (Syscall) syscallList.get(index); - if (service.getNumber() == number) { - match = service; + if (service.getNumber() == number) + { + match = service; } - } - return match; - } - } + } + return match; + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/AbstractSyscall.java b/src/main/java/mars/mips/instructions/syscalls/AbstractSyscall.java index 9090e8e..52c739c 100644 --- a/src/main/java/mars/mips/instructions/syscalls/AbstractSyscall.java +++ b/src/main/java/mars/mips/instructions/syscalls/AbstractSyscall.java @@ -1,5 +1,7 @@ - package mars.mips.instructions.syscalls; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -29,66 +31,72 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Abstract class that a MIPS syscall system service may extend. A qualifying service - * must be a class in the mars.mips.instructions.syscalls package that - * implements the Syscall interface, must be compiled into a .class file, - * and its .class file must be in the same folder as Syscall.class. - * Mars will detect a qualifying syscall upon startup, create an instance - * using its no-argument constructor and add it to its syscall list. - * When its service is invoked at runtime ("syscall" instruction - * with its service number stored in register $v0), its simulate() - * method will be invoked. - * +/** + * Abstract class that a MIPS syscall system service may extend. A qualifying service must be a class in the + * mars.mips.instructions.syscalls package that implements the Syscall interface, must be compiled into a .class file, + * and its .class file must be in the same folder as Syscall.class. Mars will detect a qualifying syscall upon startup, + * create an instance using its no-argument constructor and add it to its syscall list. When its service is invoked at + * runtime ("syscall" instruction with its service number stored in register $v0), its simulate() method will be + * invoked. */ - - public abstract class AbstractSyscall implements Syscall { - private int serviceNumber; - private String serviceName; - - /** - * Constructor is provided so subclass may initialize instance variables. - * @param number default assigned service number - * @param name service name which may be used for reference independent of number - */ - public AbstractSyscall(int number, String name) { - serviceNumber = number; - serviceName = name; - } - - /** - * Return the name you have chosen for this syscall. This can be used by a MARS - * user to refer to the service when choosing to override its default service - * number in the configuration file. - * @return service name as a string - */ - public String getName() { - return serviceName; - } - - /** - * Set the service number. This is provided to allow MARS implementer or user - * to override the default service number. - * @param num specified service number to override the default. - */ - public void setNumber(int num) { - serviceNumber = num; - } - - /** - * Return the assigned service number. This is the number the MIPS programmer - * must store into $v0 before issuing the SYSCALL instruction. - * @return assigned service number - */ - public int getNumber() { - return serviceNumber; - } - - /** - * Performs syscall function. It will be invoked when the service is invoked - * at simulation time. Service is identified by value stored in $v0. - * @param statement ProgramStatement object for this syscall instruction. - */ - public abstract void simulate(ProgramStatement statement) - throws ProcessingException; - } \ No newline at end of file + +public abstract class AbstractSyscall implements Syscall +{ + private int serviceNumber; + + private final String serviceName; + + /** + * Constructor is provided so subclass may initialize instance variables. + * + * @param number default assigned service number + * @param name service name which may be used for reference independent of number + */ + public AbstractSyscall(int number, String name) + { + serviceNumber = number; + serviceName = name; + } + + /** + * Return the name you have chosen for this syscall. This can be used by a MARS user to refer to the service when + * choosing to override its default service number in the configuration file. + * + * @return service name as a string + */ + public String getName() + { + return serviceName; + } + + /** + * Return the assigned service number. This is the number the MIPS programmer must store into $v0 before issuing + * the SYSCALL instruction. + * + * @return assigned service number + */ + public int getNumber() + { + return serviceNumber; + } + + /** + * Set the service number. This is provided to allow MARS implementer or user to override the default service + * number. + * + * @param num specified service number to override the default. + */ + public void setNumber(int num) + { + serviceNumber = num; + } + + /** + * Performs syscall function. It will be invoked when the service is invoked at simulation time. Service is + * identified by value stored in $v0. + * + * @param statement ProgramStatement object for this syscall instruction. + */ + public abstract void simulate(ProgramStatement statement) + throws ProcessingException; +} diff --git a/src/main/java/mars/mips/instructions/syscalls/RandomStreams.java b/src/main/java/mars/mips/instructions/syscalls/RandomStreams.java index a127081..a9dbab6 100644 --- a/src/main/java/mars/mips/instructions/syscalls/RandomStreams.java +++ b/src/main/java/mars/mips/instructions/syscalls/RandomStreams.java @@ -1,5 +1,6 @@ - package mars.mips.instructions.syscalls; - import java.util.HashMap; +package mars.mips.instructions.syscalls; + +import java.util.HashMap; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -30,14 +31,16 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * This small class serves only to hold a static HashMap for storing - * random number generators for use by all the random number generator - * syscalls. +/** + * This small class serves only to hold a static HashMap for storing random number generators for use by all the random + * number generator syscalls. */ - - public class RandomStreams { - /** Collection of pseudorandom number streams available for use in Rand-type syscalls. - * The streams are by default not seeded. */ - static final HashMap randomStreams = new HashMap(); - } \ No newline at end of file + +public class RandomStreams +{ + /** + * Collection of pseudorandom number streams available for use in Rand-type syscalls. The streams are by default not + * seeded. + */ + static final HashMap randomStreams = new HashMap(); +} diff --git a/src/main/java/mars/mips/instructions/syscalls/Syscall.java b/src/main/java/mars/mips/instructions/syscalls/Syscall.java index b8beaab..aba8cb8 100644 --- a/src/main/java/mars/mips/instructions/syscalls/Syscall.java +++ b/src/main/java/mars/mips/instructions/syscalls/Syscall.java @@ -1,5 +1,7 @@ - package mars.mips.instructions.syscalls; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -30,47 +32,47 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Interface for any MIPS syscall system service. A qualifying service - * must be a class in the mars.mips.instructions.syscalls package that - * implements the Syscall interface, must be compiled into a .class file, - * and its .class file must be in the same folder as Syscall.class. - * Mars will detect a qualifying syscall upon startup, create an instance - * using its no-argument constructor and add it to its syscall list. - * When its service is invoked at runtime ("syscall" instruction - * with its service number stored in register $v0), its simulate() - * method will be invoked. - * +/** + * Interface for any MIPS syscall system service. A qualifying service must be a class in the + * mars.mips.instructions.syscalls package that implements the Syscall interface, must be compiled into a .class file, + * and its .class file must be in the same folder as Syscall.class. Mars will detect a qualifying syscall upon startup, + * create an instance using its no-argument constructor and add it to its syscall list. When its service is invoked at + * runtime ("syscall" instruction with its service number stored in register $v0), its simulate() method will be + * invoked. */ - - public interface Syscall { - /** - * Return a name you have chosen for this syscall. This can be used by a MARS - * user to refer to the service when choosing to override its default service - * number in the configuration file. - * @return service name as a string - */ - public abstract String getName(); - - /** - * Set the service number. This is provided to allow MARS implementer or user - * to override the default service number. - * @param num specified service number to override the default. - */ - public abstract void setNumber(int num); - - /** - * Return the assigned service number. This is the number the MIPS programmer - * must store into $v0 before issuing the SYSCALL instruction. - * @return assigned service number - */ - public abstract int getNumber(); - - /** - * Performs syscall function. It will be invoked when the service is invoked - * at simulation time. Service is identified by value stored in $v0. - * @param statement ProgramStatement for this syscall statement. - */ - public abstract void simulate(ProgramStatement statement) - throws ProcessingException; - } \ No newline at end of file + +public interface Syscall +{ + /** + * Return a name you have chosen for this syscall. This can be used by a MARS user to refer to the service when + * choosing to override its default service number in the configuration file. + * + * @return service name as a string + */ + String getName(); + + /** + * Return the assigned service number. This is the number the MIPS programmer must store into $v0 before issuing + * the SYSCALL instruction. + * + * @return assigned service number + */ + int getNumber(); + + /** + * Set the service number. This is provided to allow MARS implementer or user to override the default service + * number. + * + * @param num specified service number to override the default. + */ + void setNumber(int num); + + /** + * Performs syscall function. It will be invoked when the service is invoked at simulation time. Service is + * identified by value stored in $v0. + * + * @param statement ProgramStatement for this syscall statement. + */ + void simulate(ProgramStatement statement) + throws ProcessingException; +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallClose.java b/src/main/java/mars/mips/instructions/syscalls/SyscallClose.java index f751f12..20b4d17 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallClose.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallClose.java @@ -1,7 +1,9 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.util.SystemIO; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,24 +34,25 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to close file descriptor given in $a0. - * */ - - public class SyscallClose extends AbstractSyscall { - /** - * Build an instance of the Close syscall. Default service number - * is 16 and name is "Close". - */ - public SyscallClose() { - super(16, "Close"); - } - - /** - * Performs syscall function to close file descriptor given in $a0. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - SystemIO.closeFile(RegisterFile.getValue(4)); - } - } \ No newline at end of file + +public class SyscallClose extends AbstractSyscall +{ + /** + * Build an instance of the Close syscall. Default service number is 16 and name is "Close". + */ + public SyscallClose() + { + super(16, "Close"); + } + + /** + * Performs syscall function to close file descriptor given in $a0. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + SystemIO.closeFile(RegisterFile.getValue(4)); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallConfirmDialog.java b/src/main/java/mars/mips/instructions/syscalls/SyscallConfirmDialog.java index ef615b1..b670e91 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallConfirmDialog.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallConfirmDialog.java @@ -1,9 +1,12 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; -import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,52 +38,54 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to display a message to user. - * */ - public class SyscallConfirmDialog extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallConfirmDialog() { - super(50, "ConfirmDialog"); - } +public class SyscallConfirmDialog extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallConfirmDialog() + { + super(50, "ConfirmDialog"); + } - /** - * System call to display a message to user. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: $a0 = address of null-terminated string that is the message to user - // Output: $a0 contains value of user-chosen option - // 0: Yes - // 1: No - // 2: Cancel + /** + * System call to display a message to user. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: $a0 = address of null-terminated string that is the message to user + // Output: $a0 contains value of user-chosen option + // 0: Yes + // 1: No + // 2: Cancel - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); - } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } - // update register $a0 with the value from showConfirmDialog. - // showConfirmDialog returns an int with one of three possible values: - // 0 ---> meaning Yes - // 1 ---> meaning No - // 2 ---> meaning Cancel - RegisterFile.updateRegister(4, JOptionPane.showConfirmDialog(null, message) ); + // update register $a0 with the value from showConfirmDialog. + // showConfirmDialog returns an int with one of three possible values: + // 0 ---> meaning Yes + // 1 ---> meaning No + // 2 ---> meaning Cancel + RegisterFile.updateRegister(4, JOptionPane.showConfirmDialog(null, message)); - } + } - } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallExit.java b/src/main/java/mars/mips/instructions/syscalls/SyscallExit.java index 2f50240..761b549 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallExit.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallExit.java @@ -1,6 +1,7 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,24 +32,25 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to exit the MIPS program. - * */ - - public class SyscallExit extends AbstractSyscall { - /** - * Build an instance of the Exit syscall. Default service number - * is 10 and name is "Exit". - */ - public SyscallExit() { - super(10, "Exit"); - } - - /** - * Performs syscall function to exit the MIPS program. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - throw new ProcessingException(); // empty exception list. - } - } \ No newline at end of file + +public class SyscallExit extends AbstractSyscall +{ + /** + * Build an instance of the Exit syscall. Default service number is 10 and name is "Exit". + */ + public SyscallExit() + { + super(10, "Exit"); + } + + /** + * Performs syscall function to exit the MIPS program. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + throw new ProcessingException(); // empty exception list. + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallExit2.java b/src/main/java/mars/mips/instructions/syscalls/SyscallExit2.java index caa684c..57a2086 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallExit2.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallExit2.java @@ -1,7 +1,9 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.*; - import mars.mips.hardware.*; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,29 +34,30 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to exit the MIPS program with return value given in $a0. Ignored if running from GUI. - * */ - - public class SyscallExit2 extends AbstractSyscall { - /** - * Build an instance of the Exit2 syscall. Default service number - * is 17 and name is "Exit2". - */ - public SyscallExit2() { - super(17, "Exit2"); - } - - /** - * Performs syscall function to exit the MIPS program with return value given in $a0. - * If running in command mode, MARS will exit with that value. If running under GUI, - * return value is ignored. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - if (Globals.getGui()==null) { + +public class SyscallExit2 extends AbstractSyscall +{ + /** + * Build an instance of the Exit2 syscall. Default service number is 17 and name is "Exit2". + */ + public SyscallExit2() + { + super(17, "Exit2"); + } + + /** + * Performs syscall function to exit the MIPS program with return value given in $a0. If running in command mode, + * MARS will exit with that value. If running under GUI, return value is ignored. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + if (Globals.getGui() == null) + { Globals.exitCode = RegisterFile.getValue(4); - } - throw new ProcessingException(); // empty error list - } - } \ No newline at end of file + } + throw new ProcessingException(); // empty error list + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogDouble.java b/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogDouble.java index 1ab950e..4b267ea 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogDouble.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogDouble.java @@ -1,9 +1,15 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; -import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.InvalidRegisterAccessException; +import mars.mips.hardware.RegisterFile; +import mars.simulator.Exceptions; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,98 +41,100 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to input data. - * */ - public class SyscallInputDialogDouble extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallInputDialogDouble() { - super(53, "InputDialogDouble"); - } +public class SyscallInputDialogDouble extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallInputDialogDouble() + { + super(53, "InputDialogDouble"); + } - /** - * System call to input data. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: $a0 = address of null-terminated string that is the message to user - // Outputs: - // $f0 and $f1 contains value of double read. $f1 contains high order word of the double. - // $a1 contains status value - // 0: valid input data, correctly parsed - // -1: input data cannot be correctly parsed - // -2: Cancel was chosen - // -3: OK was chosen but no data had been input into field + /** + * System call to input data. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: $a0 = address of null-terminated string that is the message to user + // Outputs: + // $f0 and $f1 contains value of double read. $f1 contains high order word of the double. + // $a1 contains status value + // 0: valid input data, correctly parsed + // -1: input data cannot be correctly parsed + // -2: Cancel was chosen + // -3: OK was chosen but no data had been input into field - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } - } - catch (AddressErrorException e) + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + + // Values returned by Java's InputDialog: + // A null return value means that "Cancel" was chosen rather than OK. + // An empty string returned (that is, inputValue.length() of zero) + // means that OK was chosen but no string was input. + String inputValue = null; + inputValue = JOptionPane.showInputDialog(message); + + try + { + Coprocessor1.setRegisterPairToDouble(0, 0.0); // set $f0 to zero + if (inputValue == null) // Cancel was chosen { - throw new ProcessingException(statement, e); + RegisterFile.updateRegister(5, -2); // set $a1 to -2 flag + } + else if (inputValue.length() == 0) // OK was chosen but there was no input + { + RegisterFile.updateRegister(5, -3); // set $a1 to -3 flag + } + else + { + double doubleValue = Double.parseDouble(inputValue); + + // Successful parse of valid input data + Coprocessor1.setRegisterPairToDouble(0, doubleValue); // set $f0 to input data + RegisterFile.updateRegister(5, 0); // set $a1 to valid flag + } - // Values returned by Java's InputDialog: - // A null return value means that "Cancel" was chosen rather than OK. - // An empty string returned (that is, inputValue.length() of zero) - // means that OK was chosen but no string was input. - String inputValue = null; - inputValue = JOptionPane.showInputDialog(message); - - try - { - Coprocessor1.setRegisterPairToDouble(0, 0.0); // set $f0 to zero - if (inputValue == null) // Cancel was chosen - { - RegisterFile.updateRegister(5, -2 ); // set $a1 to -2 flag - } - else if (inputValue.length() == 0) // OK was chosen but there was no input - { - RegisterFile.updateRegister(5, -3 ); // set $a1 to -3 flag - } - else - { - double doubleValue = Double.parseDouble(inputValue); + } // end try block - // Successful parse of valid input data - Coprocessor1.setRegisterPairToDouble(0, doubleValue); // set $f0 to input data - RegisterFile.updateRegister(5, 0 ); // set $a1 to valid flag - - } + catch (InvalidRegisterAccessException e) // register ID error in this method + { + RegisterFile.updateRegister(5, -1); // set $a1 to -1 flag + throw new ProcessingException(statement, + "invalid int reg. access during double input (syscall " + this.getNumber() + ")", + Exceptions.SYSCALL_EXCEPTION); + } - } // end try block - - catch (InvalidRegisterAccessException e) // register ID error in this method - { - RegisterFile.updateRegister(5, -1 ); // set $a1 to -1 flag - throw new ProcessingException(statement, - "invalid int reg. access during double input (syscall "+this.getNumber()+")", - Exceptions.SYSCALL_EXCEPTION); - } - - catch ( NumberFormatException e) // Unsuccessful parse of input data - { - RegisterFile.updateRegister(5, -1 ); // set $a1 to -1 flag + catch (NumberFormatException e) // Unsuccessful parse of input data + { + RegisterFile.updateRegister(5, -1); // set $a1 to -1 flag /* Don't throw exception because returning a status flag throw new ProcessingException(statement, "invalid float input (syscall "+this.getNumber()+")", Exceptions.SYSCALL_EXCEPTION); */ - } + } - } + } - } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogFloat.java b/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogFloat.java index 8904605..f68e575 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogFloat.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogFloat.java @@ -1,9 +1,13 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; -import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.RegisterFile; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,86 +39,88 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to input data. - * */ - public class SyscallInputDialogFloat extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallInputDialogFloat() { - super(52, "InputDialogFloat"); - } +public class SyscallInputDialogFloat extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallInputDialogFloat() + { + super(52, "InputDialogFloat"); + } - /** - * System call to input data. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: $a0 = address of null-terminated string that is the message to user - // Outputs: - // $f0 contains value of float read - // $a1 contains status value - // 0: valid input data, correctly parsed - // -1: input data cannot be correctly parsed - // -2: Cancel was chosen - // -3: OK was chosen but no data had been input into field + /** + * System call to input data. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: $a0 = address of null-terminated string that is the message to user + // Outputs: + // $f0 contains value of float read + // $a1 contains status value + // 0: valid input data, correctly parsed + // -1: input data cannot be correctly parsed + // -2: Cancel was chosen + // -3: OK was chosen but no data had been input into field - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } - } - catch (AddressErrorException e) + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + + // Values returned by Java's InputDialog: + // A null return value means that "Cancel" was chosen rather than OK. + // An empty string returned (that is, inputValue.length() of zero) + // means that OK was chosen but no string was input. + String inputValue = null; + inputValue = JOptionPane.showInputDialog(message); + + try + { + Coprocessor1.setRegisterToFloat(0, (float) 0.0); // set $f0 to zero + if (inputValue == null) // Cancel was chosen { - throw new ProcessingException(statement, e); + RegisterFile.updateRegister(5, -2); // set $a1 to -2 flag + } + else if (inputValue.length() == 0) // OK was chosen but there was no input + { + RegisterFile.updateRegister(5, -3); // set $a1 to -3 flag + } + else + { + + float floatValue = Float.parseFloat(inputValue); + + //System.out.println("SyscallInputDialogFloat: floatValue is " + floatValue); + + // Successful parse of valid input data + Coprocessor1.setRegisterToFloat(0, floatValue); // set $f0 to input data + RegisterFile.updateRegister(5, 0); // set $a1 to valid flag + } - // Values returned by Java's InputDialog: - // A null return value means that "Cancel" was chosen rather than OK. - // An empty string returned (that is, inputValue.length() of zero) - // means that OK was chosen but no string was input. - String inputValue = null; - inputValue = JOptionPane.showInputDialog(message); - - try - { - Coprocessor1.setRegisterToFloat(0, (float)0.0); // set $f0 to zero - if (inputValue == null) // Cancel was chosen - { - RegisterFile.updateRegister(5, -2 ); // set $a1 to -2 flag - } - else if (inputValue.length() == 0) // OK was chosen but there was no input - { - RegisterFile.updateRegister(5, -3 ); // set $a1 to -3 flag - } - else - { - - float floatValue = Float.parseFloat(inputValue); - - //System.out.println("SyscallInputDialogFloat: floatValue is " + floatValue); - - // Successful parse of valid input data - Coprocessor1.setRegisterToFloat(0, floatValue); // set $f0 to input data - RegisterFile.updateRegister(5, 0 ); // set $a1 to valid flag - - } - - } // end try block + } // end try block - catch ( NumberFormatException e) // Unsuccessful parse of input data - { - RegisterFile.updateRegister(5, -1 ); // set $a1 to -1 flag + catch (NumberFormatException e) // Unsuccessful parse of input data + { + RegisterFile.updateRegister(5, -1); // set $a1 to -1 flag /* Don't throw exception because returning a status flag throw new ProcessingException(statement, @@ -122,9 +128,9 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Exceptions.SYSCALL_EXCEPTION); */ - } + } - } + } - } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogInt.java b/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogInt.java index 605d149..f7a1490 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogInt.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogInt.java @@ -1,9 +1,12 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; -import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,85 +38,87 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to input data. - * */ - public class SyscallInputDialogInt extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallInputDialogInt() { - super(51, "InputDialogInt"); - } +public class SyscallInputDialogInt extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallInputDialogInt() + { + super(51, "InputDialogInt"); + } - /** - * System call to input data. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: $a0 = address of null-terminated string that is the message to user - // Outputs: - // $a0 contains value of int read - // $a1 contains status value - // 0: valid input data, correctly parsed - // -1: input data cannot be correctly parsed - // -2: Cancel was chosen - // -3: OK was chosen but no data had been input into field + /** + * System call to input data. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: $a0 = address of null-terminated string that is the message to user + // Outputs: + // $a0 contains value of int read + // $a1 contains status value + // 0: valid input data, correctly parsed + // -1: input data cannot be correctly parsed + // -2: Cancel was chosen + // -3: OK was chosen but no data had been input into field - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } - } - catch (AddressErrorException e) + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + + // Values returned by Java's InputDialog: + // A null return value means that "Cancel" was chosen rather than OK. + // An empty string returned (that is, inputValue.length() of zero) + // means that OK was chosen but no string was input. + String inputValue = null; + inputValue = JOptionPane.showInputDialog(message); + if (inputValue == null) // Cancel was chosen + { + RegisterFile.updateRegister(4, 0); // set $a0 to zero + RegisterFile.updateRegister(5, -2); // set $a1 to -2 flag + } + else if (inputValue.length() == 0) // OK was chosen but there was no input + { + RegisterFile.updateRegister(4, 0); // set $a0 to zero + RegisterFile.updateRegister(5, -3); // set $a1 to -3 flag + } + else + { + try { - throw new ProcessingException(statement, e); + int i = Integer.parseInt(inputValue); + + // Successful parse of valid input data + RegisterFile.updateRegister(4, i); // set $a0 to the data read + RegisterFile.updateRegister(5, 0); // set $a1 to valid flag + } + catch (NumberFormatException e) + { + // Unsuccessful parse of input data + RegisterFile.updateRegister(4, 0); // set $a0 to zero + RegisterFile.updateRegister(5, -1); // set $a1 to -1 flag + } - // Values returned by Java's InputDialog: - // A null return value means that "Cancel" was chosen rather than OK. - // An empty string returned (that is, inputValue.length() of zero) - // means that OK was chosen but no string was input. - String inputValue = null; - inputValue = JOptionPane.showInputDialog(message); - if (inputValue == null) // Cancel was chosen - { - RegisterFile.updateRegister(4, 0 ); // set $a0 to zero - RegisterFile.updateRegister(5, -2 ); // set $a1 to -2 flag - } - else if (inputValue.length() == 0) // OK was chosen but there was no input - { - RegisterFile.updateRegister(4, 0 ); // set $a0 to zero - RegisterFile.updateRegister(5, -3 ); // set $a1 to -3 flag - } - else - { - try - { - int i = Integer.parseInt(inputValue); - - // Successful parse of valid input data - RegisterFile.updateRegister(4, i ); // set $a0 to the data read - RegisterFile.updateRegister(5, 0 ); // set $a1 to valid flag - } - catch ( NumberFormatException e) - { - // Unsuccessful parse of input data - RegisterFile.updateRegister(4, 0 ); // set $a0 to zero - RegisterFile.updateRegister(5, -1 ); // set $a1 to -1 flag + } // end else - } - - } // end else + } - } - - } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogString.java b/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogString.java index 566ef63..6f287d7 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogString.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallInputDialogString.java @@ -1,9 +1,12 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; - import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,103 +38,105 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to input data. - * */ - public class SyscallInputDialogString extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallInputDialogString() { - super(54, "InputDialogString"); - } - - /** - * System call to input data. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: - // $a0 = address of null-terminated string that is the message to user - // $a1 = address of input buffer for the input string - // $a2 = maximum number of characters to read - // Outputs: - // $a1 contains status value - // 0: valid input data, correctly parsed - // -1: input data cannot be correctly parsed - // -2: Cancel was chosen - // -3: OK was chosen but no data had been input into field - - - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); // byteAddress of string is in $a0 - char ch[] = { ' '}; // Need an array to convert to String - try - { +public class SyscallInputDialogString extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallInputDialogString() + { + super(54, "InputDialogString"); + } + + /** + * System call to input data. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: + // $a0 = address of null-terminated string that is the message to user + // $a1 = address of input buffer for the input string + // $a2 = maximum number of characters to read + // Outputs: + // $a1 contains status value + // 0: valid input data, correctly parsed + // -1: input data cannot be correctly parsed + // -2: Cancel was chosen + // -3: OK was chosen but no data had been input into field + + + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); // byteAddress of string is in $a0 + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); - } - - // Values returned by Java's InputDialog: - // A null return value means that "Cancel" was chosen rather than OK. - // An empty string returned (that is, inputString.length() of zero) - // means that OK was chosen but no string was input. - String inputString = null; - inputString = JOptionPane.showInputDialog(message); - byteAddress = RegisterFile.getValue(5); // byteAddress of string is in $a1 - int maxLength = RegisterFile.getValue(6); // input buffer size for input string is in $a2 - - try - { + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + + // Values returned by Java's InputDialog: + // A null return value means that "Cancel" was chosen rather than OK. + // An empty string returned (that is, inputString.length() of zero) + // means that OK was chosen but no string was input. + String inputString = null; + inputString = JOptionPane.showInputDialog(message); + byteAddress = RegisterFile.getValue(5); // byteAddress of string is in $a1 + int maxLength = RegisterFile.getValue(6); // input buffer size for input string is in $a2 + + try + { if (inputString == null) // Cancel was chosen { - RegisterFile.updateRegister(5, -2 ); // set $a1 to -2 flag + RegisterFile.updateRegister(5, -2); // set $a1 to -2 flag } else if (inputString.length() == 0) // OK was chosen but there was no input { - RegisterFile.updateRegister(5, -3 ); // set $a1 to -3 flag + RegisterFile.updateRegister(5, -3); // set $a1 to -3 flag } else { - // The buffer will contain characters, a '\n' character, and the null character - // Copy the input data to buffer as space permits - for (int index = 0; (index < inputString.length()) && (index < maxLength - 1); index++) - { - Globals.memory.setByte(byteAddress + index, - inputString.charAt(index)); - } - if (inputString.length() < maxLength-1) - { - Globals.memory.setByte(byteAddress + (int)Math.min(inputString.length(), maxLength-2), '\n'); // newline at string end - } - Globals.memory.setByte(byteAddress + (int)Math.min((inputString.length()+1), maxLength-1), 0); // null char to end string - - if (inputString.length() > maxLength - 1) - { + // The buffer will contain characters, a '\n' character, and the null character + // Copy the input data to buffer as space permits + for (int index = 0; (index < inputString.length()) && (index < maxLength - 1); index++) + { + Globals.memory.setByte(byteAddress + index, + inputString.charAt(index)); + } + if (inputString.length() < maxLength - 1) + { + Globals.memory.setByte(byteAddress + Math.min(inputString.length(), maxLength - 2), '\n'); // newline at string end + } + Globals.memory.setByte(byteAddress + Math.min((inputString.length() + 1), maxLength - 1), 0); // null char to end string + + if (inputString.length() > maxLength - 1) + { // length of the input string exceeded the specified maximum - RegisterFile.updateRegister(5, -4 ); // set $a1 to -4 flag - } - else - { - RegisterFile.updateRegister(5, 0 ); // set $a1 to 0 flag - } + RegisterFile.updateRegister(5, -4); // set $a1 to -4 flag + } + else + { + RegisterFile.updateRegister(5, 0); // set $a1 to 0 flag + } } // end else - - } // end try - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); - } - - - } - - } + + } // end try + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + + + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialog.java b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialog.java index e93e203..8deb804 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialog.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialog.java @@ -1,9 +1,12 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; -import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,56 +38,61 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to display a message to user. - * */ - public class SyscallMessageDialog extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallMessageDialog() { - super(55, "MessageDialog"); - } +public class SyscallMessageDialog extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallMessageDialog() + { + super(55, "MessageDialog"); + } - /** - * System call to display a message to user. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: - // $a0 = address of null-terminated string that is the message to user - // $a1 = the type of the message to the user, which is one of: - // 1: error message - // 2: information message - // 3: warning message - // 4: question message - // other: plain message - // Output: none + /** + * System call to display a message to user. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: + // $a0 = address of null-terminated string that is the message to user + // $a1 = the type of the message to the user, which is one of: + // 1: error message + // 2: information message + // 3: warning message + // 4: question message + // other: plain message + // Output: none - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); - } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } - // Display the dialog. - int msgType = RegisterFile.getValue(5); - if (msgType < 0 || msgType > 3) msgType = -1; // See values in http://java.sun.com/j2se/1.5.0/docs/api/constant-values.html - JOptionPane.showMessageDialog(null, message, null, msgType ); - + // Display the dialog. + int msgType = RegisterFile.getValue(5); + if (msgType < 0 || msgType > 3) + { + msgType = -1; // See values in http://java.sun.com/j2se/1.5.0/docs/api/constant-values.html + } + JOptionPane.showMessageDialog(null, message, null, msgType); - } - } + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogDouble.java b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogDouble.java index 8f30655..9c06159 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogDouble.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogDouble.java @@ -1,9 +1,15 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; -import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.InvalidRegisterAccessException; +import mars.mips.hardware.RegisterFile; +import mars.simulator.Exceptions; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,62 +41,64 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to display a message to user. - * */ - public class SyscallMessageDialogDouble extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallMessageDialogDouble() { - super(58, "MessageDialogDouble"); - } +public class SyscallMessageDialogDouble extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallMessageDialogDouble() + { + super(58, "MessageDialogDouble"); + } - /** - * System call to display a message to user. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: - // $a0 = address of null-terminated string that is an information-type message to user - // $f12 = double value to display in string form after the first message - // Output: none + /** + * System call to display a message to user. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: + // $a0 = address of null-terminated string that is an information-type message to user + // $f12 = double value to display in string form after the first message + // Output: none - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); - } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } - // Display the dialog. - try - { + // Display the dialog. + try + { JOptionPane.showMessageDialog(null, - message + Double.toString( Coprocessor1.getDoubleFromRegisterPair("$f12") ), - null, - JOptionPane.INFORMATION_MESSAGE ); - } - - catch (InvalidRegisterAccessException e) // register ID error in this method - { - RegisterFile.updateRegister(5, -1 ); // set $a1 to -1 flag - throw new ProcessingException(statement, - "invalid int reg. access during double input (syscall "+this.getNumber()+")", - Exceptions.SYSCALL_EXCEPTION); - } + message + Coprocessor1.getDoubleFromRegisterPair("$f12"), + null, + JOptionPane.INFORMATION_MESSAGE); + } - } + catch (InvalidRegisterAccessException e) // register ID error in this method + { + RegisterFile.updateRegister(5, -1); // set $a1 to -1 flag + throw new ProcessingException(statement, + "invalid int reg. access during double input (syscall " + this.getNumber() + ")", + Exceptions.SYSCALL_EXCEPTION); + } - } + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogFloat.java b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogFloat.java index 68ea05c..7cf0353 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogFloat.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogFloat.java @@ -1,9 +1,13 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; -import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.RegisterFile; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,52 +39,54 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to display a message to user. - * */ - public class SyscallMessageDialogFloat extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallMessageDialogFloat() { - super(57, "MessageDialogFloat"); - } +public class SyscallMessageDialogFloat extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallMessageDialogFloat() + { + super(57, "MessageDialogFloat"); + } - /** - * System call to display a message to user. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: - // $a0 = address of null-terminated string that is an information-type message to user - // $f12 = float value to display in string form after the first message - // Output: none + /** + * System call to display a message to user. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: + // $a0 = address of null-terminated string that is an information-type message to user + // $f12 = float value to display in string form after the first message + // Output: none - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); - } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } - // Display the dialog. - JOptionPane.showMessageDialog(null, - message + Float.toString( Coprocessor1.getFloatFromRegister("$f12") ), - null, - JOptionPane.INFORMATION_MESSAGE ); - + // Display the dialog. + JOptionPane.showMessageDialog(null, + message + Coprocessor1.getFloatFromRegister("$f12"), + null, + JOptionPane.INFORMATION_MESSAGE); - } - } + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogInt.java b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogInt.java index 9159448..e504e14 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogInt.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogInt.java @@ -1,9 +1,12 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; -import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,52 +38,54 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to display a message to user. - * */ - public class SyscallMessageDialogInt extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallMessageDialogInt() { - super(56, "MessageDialogInt"); - } +public class SyscallMessageDialogInt extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallMessageDialogInt() + { + super(56, "MessageDialogInt"); + } - /** - * System call to display a message to user. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: - // $a0 = address of null-terminated string that is an information-type message to user - // $a1 = int value to display in string form after the first message - // Output: none + /** + * System call to display a message to user. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: + // $a0 = address of null-terminated string that is an information-type message to user + // $a1 = int value to display in string form after the first message + // Output: none - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); - } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } - // Display the dialog. - JOptionPane.showMessageDialog(null, - message + Integer.toString(RegisterFile.getValue(5)), - null, - JOptionPane.INFORMATION_MESSAGE ); - + // Display the dialog. + JOptionPane.showMessageDialog(null, + message + RegisterFile.getValue(5), + null, + JOptionPane.INFORMATION_MESSAGE); - } - } + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogString.java b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogString.java index 5262126..51429f2 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogString.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallMessageDialogString.java @@ -1,9 +1,12 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; -import javax.swing.JOptionPane; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; + +import javax.swing.*; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,70 +38,72 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to display a message to user. - * */ - public class SyscallMessageDialogString extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallMessageDialogString() { - super(59, "MessageDialogString"); - } +public class SyscallMessageDialogString extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallMessageDialogString() + { + super(59, "MessageDialogString"); + } - /** - * System call to display a message to user. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: - // $a0 = address of null-terminated string that is an information-type message to user - // $a1 = address of null-terminated string to display after the first message - // Output: none + /** + * System call to display a message to user. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: + // $a0 = address of null-terminated string that is an information-type message to user + // $a1 = address of null-terminated string to display after the first message + // Output: none - String message = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + String message = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message = message.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); - } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); + message = message.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } - String message2 = new String(); // = ""; - byteAddress = RegisterFile.getValue(5); - try - { + String message2 = ""; // = ""; + byteAddress = RegisterFile.getValue(5); + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - message2 = message2.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte(byteAddress); - } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); + message2 = message2.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte(byteAddress); } + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } - // Display the dialog. - JOptionPane.showMessageDialog(null, - message + message2, - null, - JOptionPane.INFORMATION_MESSAGE ); - + // Display the dialog. + JOptionPane.showMessageDialog(null, + message + message2, + null, + JOptionPane.INFORMATION_MESSAGE); - } - } + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallMidiOut.java b/src/main/java/mars/mips/instructions/syscalls/SyscallMidiOut.java index 8d01f3e..9b2b7fb 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallMidiOut.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallMidiOut.java @@ -1,9 +1,8 @@ - package mars.mips.instructions.syscalls; - - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; /* @@ -35,49 +34,59 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Service to output simulated MIDI tone to sound card. The call returns - * immediately upon generating the tone. By contrast, syscall 33 - * (MidiOutSync) does not return until tone duration has elapsed. + * Service to output simulated MIDI tone to sound card. The call returns immediately upon generating the tone. By + * contrast, syscall 33 (MidiOutSync) does not return until tone duration has elapsed. */ - public class SyscallMidiOut extends AbstractSyscall { - +public class SyscallMidiOut extends AbstractSyscall +{ + // Endpoints of ranges for the three "byte" parameters. The duration // parameter is limited at the high end only by the int range. - static final int rangeLowEnd = 0; - static final int rangeHighEnd = 127; - - /** - * Build an instance of the MIDI (simulated) out syscall. Default service number - * is 31 and name is "MidiOut". - */ - public SyscallMidiOut() { - super(31, "MidiOut"); - } - - /** - * Performs syscall function to send MIDI output to sound card. This requires - * four arguments in registers $a0 through $a3.
- * $a0 - pitch (note). Integer value from 0 to 127, with 60 being middle-C on a piano.
- * $a1 - duration. Integer value in milliseconds.
- * $a2 - instrument. Integer value from 0 to 127, with 0 being acoustic grand piano.
- * $a3 - volume. Integer value from 0 to 127.
- * Default values, in case any parameters are outside the above ranges, are $a0=60, $a1=1000, - * $a2=0, $a3=100.
- * See MARS documentation elsewhere or www.midi.org for more information. Note that the pitch, - * instrument and volume value ranges 0-127 are from javax.sound.midi; actual MIDI instruments - * use the range 1-128. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - int pitch = RegisterFile.getValue(4); // $a0 - int duration = RegisterFile.getValue(5); // $a1 - int instrument = RegisterFile.getValue(6); // $a2 - int volume = RegisterFile.getValue(7); // $a3 - if (pitch < rangeLowEnd || pitch > rangeHighEnd) pitch = ToneGenerator.DEFAULT_PITCH; - if (duration < 0) duration = ToneGenerator.DEFAULT_DURATION; - if (instrument < rangeLowEnd || instrument > rangeHighEnd) instrument = ToneGenerator.DEFAULT_INSTRUMENT; - if (volume < rangeLowEnd || volume > rangeHighEnd) volume = ToneGenerator.DEFAULT_VOLUME; - new ToneGenerator().generateTone( (byte) pitch, duration, (byte) instrument, (byte) volume); - } + static final int rangeLowEnd = 0; - } + static final int rangeHighEnd = 127; + + /** + * Build an instance of the MIDI (simulated) out syscall. Default service number is 31 and name is "MidiOut". + */ + public SyscallMidiOut() + { + super(31, "MidiOut"); + } + + /** + * Performs syscall function to send MIDI output to sound card. This requires four arguments in registers $a0 + * through $a3.
$a0 - pitch (note). Integer value from 0 to 127, with 60 being middle-C on a piano.
$a1 - + * duration. Integer value in milliseconds.
$a2 - instrument. Integer value from 0 to 127, with 0 being + * acoustic grand piano.
$a3 - volume. Integer value from 0 to 127.
Default values, in case any parameters + * are outside the above ranges, are $a0=60, $a1=1000, $a2=0, $a3=100.
See MARS documentation elsewhere or + * www.midi.org for more information. Note that the pitch, instrument and volume value ranges 0-127 are from + * javax.sound.midi; actual MIDI instruments use the range 1-128. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + int pitch = RegisterFile.getValue(4); // $a0 + int duration = RegisterFile.getValue(5); // $a1 + int instrument = RegisterFile.getValue(6); // $a2 + int volume = RegisterFile.getValue(7); // $a3 + if (pitch < rangeLowEnd || pitch > rangeHighEnd) + { + pitch = ToneGenerator.DEFAULT_PITCH; + } + if (duration < 0) + { + duration = ToneGenerator.DEFAULT_DURATION; + } + if (instrument < rangeLowEnd || instrument > rangeHighEnd) + { + instrument = ToneGenerator.DEFAULT_INSTRUMENT; + } + if (volume < rangeLowEnd || volume > rangeHighEnd) + { + volume = ToneGenerator.DEFAULT_VOLUME; + } + new ToneGenerator().generateTone((byte) pitch, duration, (byte) instrument, (byte) volume); + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallMidiOutSync.java b/src/main/java/mars/mips/instructions/syscalls/SyscallMidiOutSync.java index 28d350b..5ea4907 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallMidiOutSync.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallMidiOutSync.java @@ -1,9 +1,8 @@ - package mars.mips.instructions.syscalls; - - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; /* Copyright (c) 2003-2007, Pete Sanderson and Kenneth Vollmar @@ -43,53 +42,62 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Service to output simulated MIDI tone to sound card. The call does - * not return until the tone duration has elapsed. By contrast, syscall 31 - * (MidiOut) returns immediately upon generating the tone. - * +/** + * Service to output simulated MIDI tone to sound card. The call does not return until the tone duration has elapsed. + * By contrast, syscall 31 (MidiOut) returns immediately upon generating the tone. */ - - public class SyscallMidiOutSync extends AbstractSyscall { - + +public class SyscallMidiOutSync extends AbstractSyscall +{ + // Endpoints of ranges for the three "byte" parameters. The duration // parameter is limited at the high end only by the int range. - static final int rangeLowEnd = 0; - static final int rangeHighEnd = 127; - - /** - * Build an instance of the MIDI (simulated) out syscall. Default service number - * is 33 and name is "MidiOutSync". - */ - public SyscallMidiOutSync() { - super(33, "MidiOutSync"); - } - - /** - * Performs syscall function to send MIDI output to sound card. The syscall does not - * return until after the duration period ($a1) has elapsed. This requires - * four arguments in registers $a0 through $a3.
- * $a0 - pitch (note). Integer value from 0 to 127, with 60 being middle-C on a piano.
- * $a1 - duration. Integer value in milliseconds.
- * $a2 - instrument. Integer value from 0 to 127, with 0 being acoustic grand piano.
- * $a3 - volume. Integer value from 0 to 127.
- * Default values, in case any parameters are outside the above ranges, are $a0=60, $a1=1000, - * $a2=0, $a3=100.
- * See MARS documentation elsewhere or www.midi.org for more information. Note that the pitch, - * instrument and volume value ranges 0-127 are from javax.sound.midi; actual MIDI instruments - * use the range 1-128. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - int pitch = RegisterFile.getValue(4); // $a0 - int duration = RegisterFile.getValue(5); // $a1 - int instrument = RegisterFile.getValue(6); // $a2 - int volume = RegisterFile.getValue(7); // $a3 - if (pitch < rangeLowEnd || pitch > rangeHighEnd) pitch = ToneGenerator.DEFAULT_PITCH; - if (duration < 0) duration = ToneGenerator.DEFAULT_DURATION; - if (instrument < rangeLowEnd || instrument > rangeHighEnd) instrument = ToneGenerator.DEFAULT_INSTRUMENT; - if (volume < rangeLowEnd || volume > rangeHighEnd) volume = ToneGenerator.DEFAULT_VOLUME; - new ToneGenerator().generateToneSynchronously( (byte) pitch, duration, (byte) instrument, (byte) volume); - } - - } + static final int rangeLowEnd = 0; + + static final int rangeHighEnd = 127; + + /** + * Build an instance of the MIDI (simulated) out syscall. Default service number is 33 and name is "MidiOutSync". + */ + public SyscallMidiOutSync() + { + super(33, "MidiOutSync"); + } + + /** + * Performs syscall function to send MIDI output to sound card. The syscall does not return until after the + * duration period ($a1) has elapsed. This requires four arguments in registers $a0 through $a3.
$a0 - pitch + * (note). Integer value from 0 to 127, with 60 being middle-C on a piano.
$a1 - duration. Integer value in + * milliseconds.
$a2 - instrument. Integer value from 0 to 127, with 0 being acoustic grand piano.
$a3 - + * volume. Integer value from 0 to 127.
Default values, in case any parameters are outside the above ranges, + * are $a0=60, $a1=1000, $a2=0, $a3=100.
See MARS documentation elsewhere or www.midi.org for more information. + * Note that the pitch, instrument and volume value ranges 0-127 are from javax.sound.midi; actual MIDI instruments + * use the range 1-128. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + int pitch = RegisterFile.getValue(4); // $a0 + int duration = RegisterFile.getValue(5); // $a1 + int instrument = RegisterFile.getValue(6); // $a2 + int volume = RegisterFile.getValue(7); // $a3 + if (pitch < rangeLowEnd || pitch > rangeHighEnd) + { + pitch = ToneGenerator.DEFAULT_PITCH; + } + if (duration < 0) + { + duration = ToneGenerator.DEFAULT_DURATION; + } + if (instrument < rangeLowEnd || instrument > rangeHighEnd) + { + instrument = ToneGenerator.DEFAULT_INSTRUMENT; + } + if (volume < rangeLowEnd || volume > rangeHighEnd) + { + volume = ToneGenerator.DEFAULT_VOLUME; + } + new ToneGenerator().generateToneSynchronously((byte) pitch, duration, (byte) instrument, (byte) volume); + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallNumberOverride.java b/src/main/java/mars/mips/instructions/syscalls/SyscallNumberOverride.java index 8a13ad8..d34a539 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallNumberOverride.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallNumberOverride.java @@ -1,6 +1,5 @@ - package mars.mips.instructions.syscalls; - import java.util.*; - import mars.util.*; +package mars.mips.instructions.syscalls; + /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -30,55 +29,62 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Represents User override of default syscall number assignment. - * Such overrides are specified in the config.txt file read when - * MARS starts up. + * Represents User override of default syscall number assignment. Such overrides are specified in the config.txt file + * read when MARS starts up. */ - public class SyscallNumberOverride { - private String serviceName; - private int newServiceNumber; - - /** - * Constructor is called with two strings: service name and desired - * number. Will throw an exception is number is malformed, but does - * not check validity of the service name or number. - * @param serviceName a String containing syscall service mnemonic. - * @param value a String containing its reassigned syscall service number. - * If this number is previously assigned to a different syscall which does not - * also receive a new number, then an error for duplicate numbers will - * be issued at MARS launch. - */ - - public SyscallNumberOverride(String serviceName, String value) { - this.serviceName = serviceName; - try { +public class SyscallNumberOverride +{ + private final String serviceName; + + private int newServiceNumber; + + /** + * Constructor is called with two strings: service name and desired number. Will throw an exception is number is + * malformed, but does not check validity of the service name or number. + * + * @param serviceName a String containing syscall service mnemonic. + * @param value a String containing its reassigned syscall service number. If this number is previously assigned + * to a different syscall which does not also receive a new number, then an error for duplicate numbers will be + * issued at MARS launch. + */ + + public SyscallNumberOverride(String serviceName, String value) + { + this.serviceName = serviceName; + try + { this.newServiceNumber = Integer.parseInt(value.trim()); - } - catch (NumberFormatException e) { - System.out.println("Error processing Syscall number override: '"+value.trim()+"' is not a valid integer"); - System.exit(0); - } - } - - - /** - * Get the service name as a String. - * @return the service name - */ - public String getName() { - return serviceName; - } - - /** - * Get the new service number as an int. - * @return the service number - */ - public int getNumber() { - return newServiceNumber; - } - - } + } + catch (NumberFormatException e) + { + System.out.println("Error processing Syscall number override: '" + value.trim() + "' is not a valid integer"); + System.exit(0); + } + } + + + /** + * Get the service name as a String. + * + * @return the service name + */ + public String getName() + { + return serviceName; + } + + /** + * Get the new service number as an int. + * + * @return the service number + */ + public int getNumber() + { + return newServiceNumber; + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallOpen.java b/src/main/java/mars/mips/instructions/syscalls/SyscallOpen.java index febafec..24fe5c3 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallOpen.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallOpen.java @@ -1,8 +1,11 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; +import mars.util.SystemIO; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,70 +36,69 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Service to open file name specified by $a0. File descriptor returned in $v0. - * (this was changed from $a0 in MARS 3.7 for SPIM compatibility. The table - * in COD erroneously shows $a0). - * +/** + * Service to open file name specified by $a0. File descriptor returned in $v0. (this was changed from $a0 in MARS 3.7 + * for SPIM compatibility. The table in COD erroneously shows $a0). */ - - public class SyscallOpen extends AbstractSyscall { - /** - * Build an instance of the Open file syscall. Default service number - * is 13 and name is "Open". - */ - public SyscallOpen() { - super(13, "Open"); - } - - /** - * Performs syscall function to open file name specified by $a0. File descriptor returned - * in $v0. Only supported flags ($a1) are read-only (0), write-only (1) and - * write-append (9). write-only flag creates file if it does not exist, so it is technically - * write-create. write-append will start writing at end of existing file. - * Mode ($a2) is ignored. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // NOTE: with MARS 3.7, return changed from $a0 to $v0 and the terminology - // of 'flags' and 'mode' was corrected (they had been reversed). - // - // Arguments: $a0 = filename (string), $a1 = flags, $a2 = mode - // Result: file descriptor (in $v0) - // This code implements the flags: - // Read flag = 0 - // Write flag = 1 - // Read/Write NOT IMPLEMENTED - // Write/append flag = 9 - // This code implements the modes: - // NO MODES IMPLEMENTED -- MODE IS IGNORED - // Returns in $v0: a "file descriptor" in the range 0 to SystemIO.SYSCALL_MAXFILES-1, - // or -1 if error - String filename = new String(); // = ""; - int byteAddress = RegisterFile.getValue(4); - char ch[] = { ' '}; // Need an array to convert to String - try - { + +public class SyscallOpen extends AbstractSyscall +{ + /** + * Build an instance of the Open file syscall. Default service number is 13 and name is "Open". + */ + public SyscallOpen() + { + super(13, "Open"); + } + + /** + * Performs syscall function to open file name specified by $a0. File descriptor returned in $v0. Only supported + * flags ($a1) are read-only (0), write-only (1) and write-append (9). write-only flag creates file if it does not + * exist, so it is technically write-create. write-append will start writing at end of existing file. Mode ($a2) is + * ignored. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // NOTE: with MARS 3.7, return changed from $a0 to $v0 and the terminology + // of 'flags' and 'mode' was corrected (they had been reversed). + // + // Arguments: $a0 = filename (string), $a1 = flags, $a2 = mode + // Result: file descriptor (in $v0) + // This code implements the flags: + // Read flag = 0 + // Write flag = 1 + // Read/Write NOT IMPLEMENTED + // Write/append flag = 9 + // This code implements the modes: + // NO MODES IMPLEMENTED -- MODE IS IGNORED + // Returns in $v0: a "file descriptor" in the range 0 to SystemIO.SYSCALL_MAXFILES-1, + // or -1 if error + String filename = ""; // = ""; + int byteAddress = RegisterFile.getValue(4); + char[] ch = {' '}; // Need an array to convert to String + try + { ch[0] = (char) Globals.memory.getByte(byteAddress); while (ch[0] != 0) // only uses single location ch[0] { - filename = filename.concat(new String(ch)); // parameter to String constructor is a char[] array - byteAddress++; - ch[0] = (char) Globals.memory.getByte( - byteAddress); + filename = filename.concat(new String(ch)); // parameter to String constructor is a char[] array + byteAddress++; + ch[0] = (char) Globals.memory.getByte( + byteAddress); } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); - } - int retValue = SystemIO.openFile(filename, - RegisterFile.getValue(5)); - RegisterFile.updateRegister(2, retValue); // set returned fd value in register - - // GETTING RID OF PROCESSING EXCEPTION. IT IS THE RESPONSIBILITY OF THE - // USER PROGRAM TO CHECK FOR BAD FILE OPEN. MARS SHOULD NOT PRE-EMPTIVELY - // TERMINATE MIPS EXECUTION BECAUSE OF IT. Thanks to UCLA student - // Duy Truong for pointing this out. DPS 28-July-2009. + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + int retValue = SystemIO.openFile(filename, + RegisterFile.getValue(5)); + RegisterFile.updateRegister(2, retValue); // set returned fd value in register + + // GETTING RID OF PROCESSING EXCEPTION. IT IS THE RESPONSIBILITY OF THE + // USER PROGRAM TO CHECK FOR BAD FILE OPEN. MARS SHOULD NOT PRE-EMPTIVELY + // TERMINATE MIPS EXECUTION BECAUSE OF IT. Thanks to UCLA student + // Duy Truong for pointing this out. DPS 28-July-2009. /* if (retValue < 0) // some error in opening file { @@ -105,5 +107,5 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Exceptions.SYSCALL_EXCEPTION); } */ - } - } \ No newline at end of file + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintChar.java b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintChar.java index 5420a51..ceb4e97 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintChar.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintChar.java @@ -1,7 +1,9 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.util.SystemIO; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,28 +34,29 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to display character stored in $a0 on the console. - * */ - - public class SyscallPrintChar extends AbstractSyscall { - /** - * Build an instance of the Print Char syscall. Default service number - * is 11 and name is "PrintChar". - */ - public SyscallPrintChar() { - super(11, "PrintChar"); - } - - /** - * Performs syscall function to print on the console the character stored in $a0. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // mask off the lower byte of register $a0. - // Convert to a one-character string and use the string technique. - char t = (char) (RegisterFile.getValue(4) & 0x000000ff); - SystemIO.printString(new Character(t).toString()); - } - - } \ No newline at end of file + +public class SyscallPrintChar extends AbstractSyscall +{ + /** + * Build an instance of the Print Char syscall. Default service number is 11 and name is "PrintChar". + */ + public SyscallPrintChar() + { + super(11, "PrintChar"); + } + + /** + * Performs syscall function to print on the console the character stored in $a0. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // mask off the lower byte of register $a0. + // Convert to a one-character string and use the string technique. + char t = (char) (RegisterFile.getValue(4) & 0x000000ff); + SystemIO.printString(Character.toString(t)); + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintDouble.java b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintDouble.java index 144c73f..931ebc4 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintDouble.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintDouble.java @@ -1,7 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.Coprocessor1; +import mars.util.Binary; +import mars.util.SystemIO; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,27 +35,29 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Service to display double whose bits are stored in $f12 & $f13 onto the console. - * $f13 contains high order word of the double. +/** + * Service to display double whose bits are stored in $f12 & $f13 onto the console. $f13 contains high order word of the + * double. */ - - public class SyscallPrintDouble extends AbstractSyscall { - /** - * Build an instance of the Print Double syscall. Default service number - * is 3 and name is "PrintDouble". - */ - public SyscallPrintDouble() { - super(3, "PrintDouble"); - } - - /** - * Performs syscall function to print double whose bits are stored in $f12 & $f13. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Note: Higher numbered reg contains high order word so concat 13-12. - SystemIO.printString(new Double(Double.longBitsToDouble( - Binary.twoIntsToLong(Coprocessor1.getValue(13),Coprocessor1.getValue(12)) - )).toString()); - } - } \ No newline at end of file + +public class SyscallPrintDouble extends AbstractSyscall +{ + /** + * Build an instance of the Print Double syscall. Default service number is 3 and name is "PrintDouble". + */ + public SyscallPrintDouble() + { + super(3, "PrintDouble"); + } + + /** + * Performs syscall function to print double whose bits are stored in $f12 & $f13. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Note: Higher numbered reg contains high order word so concat 13-12. + SystemIO.printString(Double.toString(Double.longBitsToDouble( + Binary.twoIntsToLong(Coprocessor1.getValue(13), Coprocessor1.getValue(12)) + ))); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintFloat.java b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintFloat.java index 3c527bb..f347860 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintFloat.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintFloat.java @@ -34,24 +34,26 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to display on the console float whose bits are stored in $f12 */ - - public class SyscallPrintFloat extends AbstractSyscall { - /** - * Build an instance of the Print Float syscall. Default service number - * is 2 and name is "PrintFloat". - */ - public SyscallPrintFloat() { - super(2, "PrintFloat"); - } + +public class SyscallPrintFloat extends AbstractSyscall +{ + /** + * Build an instance of the Print Float syscall. Default service number is 2 and name is "PrintFloat". + */ + public SyscallPrintFloat() + { + super(2, "PrintFloat"); + } /** * Performs syscall function to display float whose bits are stored in $f12 */ - public void simulate(ProgramStatement statement) throws ProcessingException { + public void simulate(ProgramStatement statement) throws ProcessingException + { SystemIO.printString(Float.toString(Float.intBitsToFloat( - Coprocessor1.getValue(12)))); + Coprocessor1.getValue(12)))); } } diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintInt.java b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintInt.java index 58a34b2..93a2a20 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintInt.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintInt.java @@ -34,25 +34,26 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to display integer stored in $a0 on the console. - * */ - - public class SyscallPrintInt extends AbstractSyscall { - /** - * Build an instance of the Print Integer syscall. Default service number - * is 1 and name is "PrintInt". - */ - public SyscallPrintInt() { - super(1, "PrintInt"); - } - - /** - * Performs syscall function to print on the console the integer stored in $a0. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - SystemIO.printString( - Integer.toString(RegisterFile.getValue(4))); - } - } + +public class SyscallPrintInt extends AbstractSyscall +{ + /** + * Build an instance of the Print Integer syscall. Default service number is 1 and name is "PrintInt". + */ + public SyscallPrintInt() + { + super(1, "PrintInt"); + } + + /** + * Performs syscall function to print on the console the integer stored in $a0. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + SystemIO.printString( + Integer.toString(RegisterFile.getValue(4))); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntBinary.java b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntBinary.java index b28dc4e..a00cec9 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntBinary.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntBinary.java @@ -1,7 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.util.Binary; +import mars.util.SystemIO; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -32,24 +35,25 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to display integer stored in $a0 on the console. - * */ - - public class SyscallPrintIntBinary extends AbstractSyscall { - /** - * Build an instance of the Print Integer syscall. Default service number - * is 1 and name is "PrintInt". - */ - public SyscallPrintIntBinary() { - super(35, "PrintIntBinary"); - } - - /** - * Performs syscall function to print on the console the integer stored in $a0, in hexadecimal format. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - SystemIO.printString(Binary.intToBinaryString(RegisterFile.getValue(4))); - } - } \ No newline at end of file + +public class SyscallPrintIntBinary extends AbstractSyscall +{ + /** + * Build an instance of the Print Integer syscall. Default service number is 1 and name is "PrintInt". + */ + public SyscallPrintIntBinary() + { + super(35, "PrintIntBinary"); + } + + /** + * Performs syscall function to print on the console the integer stored in $a0, in hexadecimal format. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + SystemIO.printString(Binary.intToBinaryString(RegisterFile.getValue(4))); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntHex.java b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntHex.java index d738b18..c5033fc 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntHex.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntHex.java @@ -1,7 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.util.Binary; +import mars.util.SystemIO; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -32,24 +35,25 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to display integer stored in $a0 on the console. - * */ - - public class SyscallPrintIntHex extends AbstractSyscall { - /** - * Build an instance of the Print Integer syscall. Default service number - * is 1 and name is "PrintInt". - */ - public SyscallPrintIntHex() { - super(34, "PrintIntHex"); - } - - /** - * Performs syscall function to print on the console the integer stored in $a0, in hexadecimal format. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - SystemIO.printString(Binary.intToHexString(RegisterFile.getValue(4))); - } - } \ No newline at end of file + +public class SyscallPrintIntHex extends AbstractSyscall +{ + /** + * Build an instance of the Print Integer syscall. Default service number is 1 and name is "PrintInt". + */ + public SyscallPrintIntHex() + { + super(34, "PrintIntHex"); + } + + /** + * Performs syscall function to print on the console the integer stored in $a0, in hexadecimal format. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + SystemIO.printString(Binary.intToHexString(RegisterFile.getValue(4))); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntUnsigned.java b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntUnsigned.java index f26d4cd..7ee2fa2 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntUnsigned.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintIntUnsigned.java @@ -1,7 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.util.Binary; +import mars.util.SystemIO; /* Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar @@ -32,26 +35,27 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to display integer stored in $a0 on the console as unsigned decimal. - * */ - - public class SyscallPrintIntUnsigned extends AbstractSyscall { - /** - * Build an instance of the Print Integer Unsigned syscall. Default service number - * is 36 and name is "PrintIntUnsigned". - */ - public SyscallPrintIntUnsigned() { - super(36, "PrintIntUnsigned"); - } - - /** - * Performs syscall function to print on the console the integer stored in $a0. - * The value is treated as unsigned. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - SystemIO.printString( - Binary.unsignedIntToIntString(RegisterFile.getValue(4))); - } - } \ No newline at end of file + +public class SyscallPrintIntUnsigned extends AbstractSyscall +{ + /** + * Build an instance of the Print Integer Unsigned syscall. Default service number is 36 and name is + * "PrintIntUnsigned". + */ + public SyscallPrintIntUnsigned() + { + super(36, "PrintIntUnsigned"); + } + + /** + * Performs syscall function to print on the console the integer stored in $a0. The value is treated as unsigned. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + SystemIO.printString( + Binary.unsignedIntToIntString(RegisterFile.getValue(4))); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintString.java b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintString.java index 076377c..f625ed3 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallPrintString.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallPrintString.java @@ -1,7 +1,11 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; +import mars.util.SystemIO; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,39 +36,41 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Service to display string stored starting at address in $a0 onto the console. +/** + * Service to display string stored starting at address in $a0 onto the console. */ - - public class SyscallPrintString extends AbstractSyscall { - /** - * Build an instance of the Print String syscall. Default service number - * is 4 and name is "PrintString". - */ - public SyscallPrintString() { - super(4, "PrintString"); - } - - /** - * Performs syscall function to print string stored starting at address in $a0. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - int byteAddress = RegisterFile.getValue(4); - char ch = 0; - try - { + +public class SyscallPrintString extends AbstractSyscall +{ + /** + * Build an instance of the Print String syscall. Default service number is 4 and name is "PrintString". + */ + public SyscallPrintString() + { + super(4, "PrintString"); + } + + /** + * Performs syscall function to print string stored starting at address in $a0. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + int byteAddress = RegisterFile.getValue(4); + char ch = 0; + try + { ch = (char) Globals.memory.getByte(byteAddress); - // won't stop until NULL byte reached! + // won't stop until NULL byte reached! while (ch != 0) { - SystemIO.printString(new Character(ch).toString()); - byteAddress++; - ch = (char) Globals.memory.getByte(byteAddress); + SystemIO.printString(Character.toString(ch)); + byteAddress++; + ch = (char) Globals.memory.getByte(byteAddress); } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); - } - } - } \ No newline at end of file + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallRandDouble.java b/src/main/java/mars/mips/instructions/syscalls/SyscallRandDouble.java index f2e144c..627c632 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallRandDouble.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallRandDouble.java @@ -1,9 +1,13 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; - import java.util.Random; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.InvalidRegisterAccessException; +import mars.mips.hardware.RegisterFile; +import mars.simulator.Exceptions; + +import java.util.Random; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -36,40 +40,44 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to return a random floating point value. - * */ - public class SyscallRandDouble extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallRandDouble() { - super(44, "RandDouble"); - } - - /** - * System call to the random number generator. - * Return in $f0 the next pseudorandom, uniformly distributed double value between 0.0 and 1.0 - * from this random number generator's sequence. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: $a0 = index of pseudorandom number generator - // Return: $f0 = the next pseudorandom, uniformly distributed double value between 0.0 and 1.0 - // from this random number generator's sequence. - Integer index = new Integer(RegisterFile.getValue(4)); - Random stream = (Random) RandomStreams.randomStreams.get(index); - if (stream == null) { +public class SyscallRandDouble extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallRandDouble() + { + super(44, "RandDouble"); + } + + /** + * System call to the random number generator. Return in $f0 the next pseudorandom, uniformly distributed double + * value between 0.0 and 1.0 from this random number generator's sequence. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: $a0 = index of pseudorandom number generator + // Return: $f0 = the next pseudorandom, uniformly distributed double value between 0.0 and 1.0 + // from this random number generator's sequence. + Integer index = Integer.valueOf(RegisterFile.getValue(4)); + Random stream = (Random) RandomStreams.randomStreams.get(index); + if (stream == null) + { stream = new Random(); // create a non-seeded stream RandomStreams.randomStreams.put(index, stream); - } - try { - Coprocessor1.setRegisterPairToDouble(0, stream.nextDouble( )); - } - catch (InvalidRegisterAccessException e) { // register ID error in this method - throw new ProcessingException(statement, - "Internal error storing double to register (syscall "+this.getNumber()+")", - Exceptions.SYSCALL_EXCEPTION); - } - } - - } + } + try + { + Coprocessor1.setRegisterPairToDouble(0, stream.nextDouble()); + } + catch (InvalidRegisterAccessException e) + { // register ID error in this method + throw new ProcessingException(statement, + "Internal error storing double to register (syscall " + this.getNumber() + ")", + Exceptions.SYSCALL_EXCEPTION); + } + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallRandFloat.java b/src/main/java/mars/mips/instructions/syscalls/SyscallRandFloat.java index d8d28a4..b0f7100 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallRandFloat.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallRandFloat.java @@ -1,9 +1,11 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; - import java.util.Random; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.RegisterFile; + +import java.util.Random; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -36,32 +38,34 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to return a random floating point value. - * */ - public class SyscallRandFloat extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallRandFloat() { - super(43, "RandFloat"); - } - - /** - * System call to the random number generator. - * Return in $f0 the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 - * from this random number generator's sequence. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: $a0 = index of pseudorandom number generator - // Return: $f0 = the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 - // from this random number generator's sequence. - Integer index = new Integer(RegisterFile.getValue(4)); - Random stream = (Random) RandomStreams.randomStreams.get(index); - if (stream == null) { +public class SyscallRandFloat extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallRandFloat() + { + super(43, "RandFloat"); + } + + /** + * System call to the random number generator. Return in $f0 the next pseudorandom, uniformly distributed float + * value between 0.0 and 1.0 from this random number generator's sequence. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: $a0 = index of pseudorandom number generator + // Return: $f0 = the next pseudorandom, uniformly distributed float value between 0.0 and 1.0 + // from this random number generator's sequence. + Integer index = Integer.valueOf(RegisterFile.getValue(4)); + Random stream = (Random) RandomStreams.randomStreams.get(index); + if (stream == null) + { stream = new Random(); // create a non-seeded stream RandomStreams.randomStreams.put(index, stream); - } - Coprocessor1.setRegisterToFloat(0, stream.nextFloat( )); - } - } \ No newline at end of file + } + Coprocessor1.setRegisterToFloat(0, stream.nextFloat()); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallRandInt.java b/src/main/java/mars/mips/instructions/syscalls/SyscallRandInt.java index 6a7c6b9..a23bda1 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallRandInt.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallRandInt.java @@ -1,9 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; - import java.util.Random; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; + +import java.util.Random; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -36,32 +37,35 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * Service to return a random integer. - * */ - public class SyscallRandInt extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallRandInt() { - super(41, "RandInt"); - } - - /** - * System call to the random number generator. - * Return in $a0 the next pseudorandom, uniformly distributed int value from this random number generator's sequence. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: $a0 = index of pseudorandom number generator - // Return: $a0 = the next pseudorandom, uniformly distributed int value from this random number generator's sequence. - Integer index = new Integer(RegisterFile.getValue(4)); - Random stream = (Random) RandomStreams.randomStreams.get(index); - if (stream == null) { - stream = new Random(); // create a non-seeded stream - RandomStreams.randomStreams.put(index, stream); - } - RegisterFile.updateRegister(4, stream.nextInt() ); - } +public class SyscallRandInt extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallRandInt() + { + super(41, "RandInt"); + } - } + /** + * System call to the random number generator. Return in $a0 the next pseudorandom, uniformly distributed int value + * from this random number generator's sequence. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: $a0 = index of pseudorandom number generator + // Return: $a0 = the next pseudorandom, uniformly distributed int value from this random number generator's sequence. + Integer index = Integer.valueOf(RegisterFile.getValue(4)); + Random stream = (Random) RandomStreams.randomStreams.get(index); + if (stream == null) + { + stream = new Random(); // create a non-seeded stream + RandomStreams.randomStreams.put(index, stream); + } + RegisterFile.updateRegister(4, stream.nextInt()); + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallRandIntRange.java b/src/main/java/mars/mips/instructions/syscalls/SyscallRandIntRange.java index 5b89816..3f2c494 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallRandIntRange.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallRandIntRange.java @@ -1,9 +1,11 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; - import java.util.Random; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.simulator.Exceptions; + +import java.util.Random; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -34,44 +36,49 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to return a random integer in a specified range. - * */ - - public class SyscallRandIntRange extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallRandIntRange() { - super(42, "RandIntRange"); - } - - /** - * System call to the random number generator, with an upper range specified. - * Return in $a0 the next pseudorandom, uniformly distributed int value between 0 (inclusive) - * and the specified value (exclusive), drawn from this random number generator's sequence. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: - // $a0 = index of pseudorandom number generator - // $a1 = the upper bound of range of returned values. - // Return: $a0 = the next pseudorandom, uniformly distributed int value from this - // random number generator's sequence. - Integer index = new Integer(RegisterFile.getValue(4)); - Random stream = (Random) RandomStreams.randomStreams.get(index); - if (stream == null) { + +public class SyscallRandIntRange extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallRandIntRange() + { + super(42, "RandIntRange"); + } + + /** + * System call to the random number generator, with an upper range specified. Return in $a0 the next pseudorandom, + * uniformly distributed int value between 0 (inclusive) and the specified value (exclusive), drawn from this random + * number generator's sequence. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: + // $a0 = index of pseudorandom number generator + // $a1 = the upper bound of range of returned values. + // Return: $a0 = the next pseudorandom, uniformly distributed int value from this + // random number generator's sequence. + Integer index = Integer.valueOf(RegisterFile.getValue(4)); + Random stream = (Random) RandomStreams.randomStreams.get(index); + if (stream == null) + { stream = new Random(); // create a non-seeded stream RandomStreams.randomStreams.put(index, stream); - } - try { - RegisterFile.updateRegister(4, stream.nextInt( RegisterFile.getValue(5) ) ); - } - catch (IllegalArgumentException iae) { - throw new ProcessingException(statement, - "Upper bound of range cannot be negative (syscall "+this.getNumber()+")", - Exceptions.SYSCALL_EXCEPTION); - } - } - - } + } + try + { + RegisterFile.updateRegister(4, stream.nextInt(RegisterFile.getValue(5))); + } + catch (IllegalArgumentException iae) + { + throw new ProcessingException(statement, + "Upper bound of range cannot be negative (syscall " + this.getNumber() + ")", + Exceptions.SYSCALL_EXCEPTION); + } + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallRandSeed.java b/src/main/java/mars/mips/instructions/syscalls/SyscallRandSeed.java index 2fc7673..f6ad849 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallRandSeed.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallRandSeed.java @@ -1,9 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; - import java.util.Random; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; + +import java.util.Random; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -34,35 +35,40 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to set seed for the underlying Java pseudorandom number generator. No values are returned. - * */ - - public class SyscallRandSeed extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallRandSeed() { - super(40, "RandSeed"); - } - /** - * Set the seed of the underlying Java pseudorandom number generator. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Arguments: $a0 = index of pseudorandom number generator - // $a1 = seed for pseudorandom number generator. - // Result: No values are returned. Sets the seed of the underlying Java pseudorandom number generator. +public class SyscallRandSeed extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallRandSeed() + { + super(40, "RandSeed"); + } - Integer index = new Integer(RegisterFile.getValue(4)); - Random stream = (Random) RandomStreams.randomStreams.get(index); - if (stream == null) { - RandomStreams.randomStreams.put(index, new Random(RegisterFile.getValue(5))); - } else { - stream.setSeed(RegisterFile.getValue(5)); - } - } + /** + * Set the seed of the underlying Java pseudorandom number generator. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Arguments: $a0 = index of pseudorandom number generator + // $a1 = seed for pseudorandom number generator. + // Result: No values are returned. Sets the seed of the underlying Java pseudorandom number generator. - } + Integer index = Integer.valueOf(RegisterFile.getValue(4)); + Random stream = (Random) RandomStreams.randomStreams.get(index); + if (stream == null) + { + RandomStreams.randomStreams.put(index, new Random(RegisterFile.getValue(5))); + } + else + { + stream.setSeed(RegisterFile.getValue(5)); + } + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallRead.java b/src/main/java/mars/mips/instructions/syscalls/SyscallRead.java index 837fc43..78aa359 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallRead.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallRead.java @@ -1,8 +1,11 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; +import mars.util.SystemIO; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -33,42 +36,43 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Service to read from file descriptor given in $a0. $a1 specifies buffer - * and $a2 specifies length. Number of characters read is returned in $v0. - * (this was changed from $a0 in MARS 3.7 for SPIM compatibility. The table - * in COD erroneously shows $a0). * +/** + * Service to read from file descriptor given in $a0. $a1 specifies buffer and $a2 specifies length. Number of + * characters read is returned in $v0. (this was changed from $a0 in MARS 3.7 for SPIM compatibility. The table in COD + * erroneously shows $a0). * */ - - public class SyscallRead extends AbstractSyscall { - /** - * Build an instance of the Read file syscall. Default service number - * is 14 and name is "Read". - */ - public SyscallRead() { - super(14, "Read"); - } - - /** - * Performs syscall function to read from file descriptor given in $a0. $a1 specifies buffer - * and $a2 specifies length. Number of characters read is returned in $v0 (starting MARS 3.7). - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - int byteAddress = RegisterFile.getValue(5); // destination of characters read from file - byte b = 0; - int index = 0; - byte myBuffer[] = new byte[RegisterFile.getValue(6)]; // specified length - // Call to SystemIO.xxxx.read(xxx,xxx,xxx) returns actual length - int retLength = SystemIO.readFromFile( - RegisterFile.getValue(4), // fd - myBuffer, // buffer - RegisterFile.getValue(6)); // length - RegisterFile.updateRegister(2, retLength); // set returned value in register - // Getting rid of processing exception. It is the responsibility of the - // user program to check the syscall's return value. MARS should not - // re-emptively terminate MIPS execution because of it. Thanks to - // UCLA student Duy Truong for pointing this out. DPS 28-July-2009 +public class SyscallRead extends AbstractSyscall +{ + /** + * Build an instance of the Read file syscall. Default service number is 14 and name is "Read". + */ + public SyscallRead() + { + super(14, "Read"); + } + + /** + * Performs syscall function to read from file descriptor given in $a0. $a1 specifies buffer and $a2 specifies + * length. Number of characters read is returned in $v0 (starting MARS 3.7). + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + int byteAddress = RegisterFile.getValue(5); // destination of characters read from file + byte b = 0; + int index = 0; + byte[] myBuffer = new byte[RegisterFile.getValue(6)]; // specified length + // Call to SystemIO.xxxx.read(xxx,xxx,xxx) returns actual length + int retLength = SystemIO.readFromFile( + RegisterFile.getValue(4), // fd + myBuffer, // buffer + RegisterFile.getValue(6)); // length + RegisterFile.updateRegister(2, retLength); // set returned value in register + + // Getting rid of processing exception. It is the responsibility of the + // user program to check the syscall's return value. MARS should not + // re-emptively terminate MIPS execution because of it. Thanks to + // UCLA student Duy Truong for pointing this out. DPS 28-July-2009 /* if (retLength < 0) // some error in opening file { @@ -76,19 +80,19 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SystemIO.getFileErrorMessage()+" (syscall 14)", Exceptions.SYSCALL_EXCEPTION); } - */ - // copy bytes from returned buffer into MARS memory - try - { + */ + // copy bytes from returned buffer into MARS memory + try + { while (index < retLength) { - Globals.memory.setByte(byteAddress++, - myBuffer[index++]); + Globals.memory.setByte(byteAddress++, + myBuffer[index++]); } - } - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); - } - } - } \ No newline at end of file + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallReadChar.java b/src/main/java/mars/mips/instructions/syscalls/SyscallReadChar.java index b8b1281..41b265c 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallReadChar.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallReadChar.java @@ -1,8 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.*; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.simulator.Exceptions; +import mars.util.SystemIO; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -33,37 +35,38 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to read a character from input console into $a0. - * */ - - public class SyscallReadChar extends AbstractSyscall { - /** - * Build an instance of the Read Char syscall. Default service number - * is 12 and name is "ReadChar". - */ - public SyscallReadChar() { - super(12, "ReadChar"); - } - - /** - * Performs syscall function to read a character from input console into $a0 - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - int value = 0; - try - { + +public class SyscallReadChar extends AbstractSyscall +{ + /** + * Build an instance of the Read Char syscall. Default service number is 12 and name is "ReadChar". + */ + public SyscallReadChar() + { + super(12, "ReadChar"); + } + + /** + * Performs syscall function to read a character from input console into $a0 + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + int value = 0; + try + { value = SystemIO.readChar(this.getNumber()); - } - catch (IndexOutOfBoundsException e) // means null input - { - throw new ProcessingException(statement, - "invalid char input (syscall "+this.getNumber()+")", - Exceptions.SYSCALL_EXCEPTION); - } - // DPS 20 June 2008: changed from 4 ($a0) to 2 ($v0) - RegisterFile.updateRegister(2, value); - } - - } \ No newline at end of file + } + catch (IndexOutOfBoundsException e) // means null input + { + throw new ProcessingException(statement, + "invalid char input (syscall " + this.getNumber() + ")", + Exceptions.SYSCALL_EXCEPTION); + } + // DPS 20 June 2008: changed from 4 ($a0) to 2 ($v0) + RegisterFile.updateRegister(2, value); + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallReadDouble.java b/src/main/java/mars/mips/instructions/syscalls/SyscallReadDouble.java index 6af57aa..fc9e6d3 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallReadDouble.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallReadDouble.java @@ -1,8 +1,11 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.simulator.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.Coprocessor1; +import mars.simulator.Exceptions; +import mars.util.Binary; +import mars.util.SystemIO; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,38 +36,39 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Service to read the bits of console input double into $f0 and $f1. - * $f1 contains high order word of the double. +/** + * Service to read the bits of console input double into $f0 and $f1. $f1 contains high order word of the double. */ - - public class SyscallReadDouble extends AbstractSyscall { - /** - * Build an instance of the Read Double syscall. Default service number - * is 7 and name is "ReadDouble". - */ - public SyscallReadDouble() { - super(7, "ReadDouble"); - } - - /** - * Performs syscall function to read the bits of input double into $f0 and $f1. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Higher numbered reg contains high order word so order is $f1 - $f0. - double doubleValue = 0; - try - { + +public class SyscallReadDouble extends AbstractSyscall +{ + /** + * Build an instance of the Read Double syscall. Default service number is 7 and name is "ReadDouble". + */ + public SyscallReadDouble() + { + super(7, "ReadDouble"); + } + + /** + * Performs syscall function to read the bits of input double into $f0 and $f1. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Higher numbered reg contains high order word so order is $f1 - $f0. + double doubleValue = 0; + try + { doubleValue = SystemIO.readDouble(this.getNumber()); - } - catch (NumberFormatException e) - { - throw new ProcessingException(statement, - "invalid double input (syscall "+this.getNumber()+")", - Exceptions.SYSCALL_EXCEPTION); - } - long longValue = Double.doubleToRawLongBits(doubleValue); - Coprocessor1.updateRegister(1, Binary.highOrderLongToInt(longValue)); - Coprocessor1.updateRegister(0, Binary.lowOrderLongToInt(longValue)); - } - } \ No newline at end of file + } + catch (NumberFormatException e) + { + throw new ProcessingException(statement, + "invalid double input (syscall " + this.getNumber() + ")", + Exceptions.SYSCALL_EXCEPTION); + } + long longValue = Double.doubleToRawLongBits(doubleValue); + Coprocessor1.updateRegister(1, Binary.highOrderLongToInt(longValue)); + Coprocessor1.updateRegister(0, Binary.lowOrderLongToInt(longValue)); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallReadFloat.java b/src/main/java/mars/mips/instructions/syscalls/SyscallReadFloat.java index d7cc0a0..f4a83ef 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallReadFloat.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallReadFloat.java @@ -1,8 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.simulator.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.Coprocessor1; +import mars.simulator.Exceptions; +import mars.util.SystemIO; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,34 +35,36 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to read the bits of input float into $f0 */ - - public class SyscallReadFloat extends AbstractSyscall { - /** - * Build an instance of the Read Float syscall. Default service number - * is 6 and name is "ReadFloat". - */ - public SyscallReadFloat() { - super(6, "ReadFloat"); - } - - /** - * Performs syscall function to read the bits of input float into $f0 - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - float floatValue = 0; - try - { + +public class SyscallReadFloat extends AbstractSyscall +{ + /** + * Build an instance of the Read Float syscall. Default service number is 6 and name is "ReadFloat". + */ + public SyscallReadFloat() + { + super(6, "ReadFloat"); + } + + /** + * Performs syscall function to read the bits of input float into $f0 + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + float floatValue = 0; + try + { floatValue = SystemIO.readFloat(this.getNumber()); - } - catch (NumberFormatException e) - { - throw new ProcessingException(statement, - "invalid float input (syscall "+this.getNumber()+")", - Exceptions.SYSCALL_EXCEPTION); - } - Coprocessor1.updateRegister(0, Float.floatToRawIntBits(floatValue)); - } - } \ No newline at end of file + } + catch (NumberFormatException e) + { + throw new ProcessingException(statement, + "invalid float input (syscall " + this.getNumber() + ")", + Exceptions.SYSCALL_EXCEPTION); + } + Coprocessor1.updateRegister(0, Float.floatToRawIntBits(floatValue)); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallReadInt.java b/src/main/java/mars/mips/instructions/syscalls/SyscallReadInt.java index cebdef3..2bf0c54 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallReadInt.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallReadInt.java @@ -1,8 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.*; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.simulator.Exceptions; +import mars.util.SystemIO; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,36 +35,37 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to read an integer from input console into $v0. - * */ - - public class SyscallReadInt extends AbstractSyscall { - /** - * Build an instance of the Read Integer syscall. Default service number - * is 5 and name is "ReadInt". - */ - public SyscallReadInt() { - super(5, "ReadInt"); - } - - /** - * Performs syscall function to read an integer from input console into $v0 - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - int value = 0; - try - { + +public class SyscallReadInt extends AbstractSyscall +{ + /** + * Build an instance of the Read Integer syscall. Default service number is 5 and name is "ReadInt". + */ + public SyscallReadInt() + { + super(5, "ReadInt"); + } + + /** + * Performs syscall function to read an integer from input console into $v0 + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + int value = 0; + try + { value = SystemIO.readInteger(this.getNumber()); - } - catch (NumberFormatException e) - { - throw new ProcessingException(statement, - "invalid integer input (syscall "+this.getNumber()+")", - Exceptions.SYSCALL_EXCEPTION); - } - RegisterFile.updateRegister(2, value); - } - - } \ No newline at end of file + } + catch (NumberFormatException e) + { + throw new ProcessingException(statement, + "invalid integer input (syscall " + this.getNumber() + ")", + Exceptions.SYSCALL_EXCEPTION); + } + RegisterFile.updateRegister(2, value); + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallReadString.java b/src/main/java/mars/mips/instructions/syscalls/SyscallReadString.java index 0c7e773..2e63b45 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallReadString.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallReadString.java @@ -1,7 +1,11 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; +import mars.util.SystemIO; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,56 +36,60 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Service to read console input string into buffer starting at address in $a0. +/** + * Service to read console input string into buffer starting at address in $a0. */ - - public class SyscallReadString extends AbstractSyscall { - /** - * Build an instance of the Read String syscall. Default service number - * is 8 and name is "ReadString". - */ - public SyscallReadString() { - super(8, "ReadString"); - } - - /** - * Performs syscall function to read console input string into buffer starting at address in $a0. - * Follows semantics of UNIX 'fgets'. For specified length n, - * string can be no longer than n-1. If less than that, add - * newline to end. In either case, then pad with null byte. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - - String inputString = ""; - int buf = RegisterFile.getValue(4); // buf addr in $a0 - int maxLength = RegisterFile.getValue(5) - 1; // $a1 - boolean addNullByte = true; - // Guard against negative maxLength. DPS 13-July-2011 - if (maxLength < 0) - { + +public class SyscallReadString extends AbstractSyscall +{ + /** + * Build an instance of the Read String syscall. Default service number is 8 and name is "ReadString". + */ + public SyscallReadString() + { + super(8, "ReadString"); + } + + /** + * Performs syscall function to read console input string into buffer starting at address in $a0. Follows semantics + * of UNIX 'fgets'. For specified length n, string can be no longer than n-1. If less than that, add newline to + * end. In either case, then pad with null byte. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + + String inputString = ""; + int buf = RegisterFile.getValue(4); // buf addr in $a0 + int maxLength = RegisterFile.getValue(5) - 1; // $a1 + boolean addNullByte = true; + // Guard against negative maxLength. DPS 13-July-2011 + if (maxLength < 0) + { maxLength = 0; - addNullByte = false; - } - inputString = SystemIO.readString(this.getNumber(), maxLength); - int stringLength = Math.min(maxLength, inputString.length()); - try - { + addNullByte = false; + } + inputString = SystemIO.readString(this.getNumber(), maxLength); + int stringLength = Math.min(maxLength, inputString.length()); + try + { for (int index = 0; index < stringLength; index++) { - Globals.memory.setByte(buf + index, - inputString.charAt(index)); - } + Globals.memory.setByte(buf + index, + inputString.charAt(index)); + } if (stringLength < maxLength) { - Globals.memory.setByte(buf + stringLength, '\n'); - stringLength++; + Globals.memory.setByte(buf + stringLength, '\n'); + stringLength++; } - if (addNullByte) Globals.memory.setByte(buf + stringLength, 0); - } - catch (AddressErrorException e) + if (addNullByte) { - throw new ProcessingException(statement, e); + Globals.memory.setByte(buf + stringLength, 0); } - } - } \ No newline at end of file + } + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallSbrk.java b/src/main/java/mars/mips/instructions/syscalls/SyscallSbrk.java index 6152f66..a70020a 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallSbrk.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallSbrk.java @@ -1,8 +1,10 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.simulator.*; - import mars.mips.hardware.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.simulator.Exceptions; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,33 +35,36 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to allocate amount of heap memory specified in $a0, putting address into $v0. - * */ - - public class SyscallSbrk extends AbstractSyscall { - /** - * Build an instance of the Sbrk syscall. Default service number - * is 9 and name is "Sbrk". - */ - public SyscallSbrk() { - super(9, "Sbrk"); - } - - /** - * Performs syscall function to allocate amount of heap memory specified in $a0, putting address into $v0. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - int address = 0; - try { + +public class SyscallSbrk extends AbstractSyscall +{ + /** + * Build an instance of the Sbrk syscall. Default service number is 9 and name is "Sbrk". + */ + public SyscallSbrk() + { + super(9, "Sbrk"); + } + + /** + * Performs syscall function to allocate amount of heap memory specified in $a0, putting address into $v0. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + int address = 0; + try + { address = Globals.memory.allocateBytesFromHeap(RegisterFile.getValue(4)); - } - catch (IllegalArgumentException iae) { - throw new ProcessingException(statement, - iae.getMessage()+" (syscall "+this.getNumber()+")", - Exceptions.SYSCALL_EXCEPTION); - } - RegisterFile.updateRegister(2, address); - } - } \ No newline at end of file + } + catch (IllegalArgumentException iae) + { + throw new ProcessingException(statement, + iae.getMessage() + " (syscall " + this.getNumber() + ")", + Exceptions.SYSCALL_EXCEPTION); + } + RegisterFile.updateRegister(2, address); + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallSleep.java b/src/main/java/mars/mips/instructions/syscalls/SyscallSleep.java index 2bc832f..49874b2 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallSleep.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallSleep.java @@ -1,8 +1,8 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -34,34 +34,36 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** - * Service to cause the MARS Java thread to sleep for (at least) the specified number of milliseconds. - * This timing will not be precise as the Java implementation will add some overhead. - * + * Service to cause the MARS Java thread to sleep for (at least) the specified number of milliseconds. This timing will + * not be precise as the Java implementation will add some overhead. */ - public class SyscallSleep extends AbstractSyscall { - /** - * Build an instance of the syscall with its default service number and name. - */ - public SyscallSleep() { - super(32, "Sleep"); - } +public class SyscallSleep extends AbstractSyscall +{ + /** + * Build an instance of the syscall with its default service number and name. + */ + public SyscallSleep() + { + super(32, "Sleep"); + } - /** - * System call to cause the MARS Java thread to sleep for (at least) the specified number of milliseconds. - * This timing will not be precise as the Java implementation will add some overhead. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - // Input arguments: $a0 is the length of time to sleep in milliseconds. + /** + * System call to cause the MARS Java thread to sleep for (at least) the specified number of milliseconds. This + * timing will not be precise as the Java implementation will add some overhead. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + // Input arguments: $a0 is the length of time to sleep in milliseconds. - try - { - Thread.sleep(RegisterFile.getValue(4)); // units of milliseconds 1000 millisec = 1 sec. - } - catch (InterruptedException e) - { - return; // no exception handling - } - } + try + { + Thread.sleep(RegisterFile.getValue(4)); // units of milliseconds 1000 millisec = 1 sec. + } + catch (InterruptedException e) + { + // no exception handling + } + } - } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallTime.java b/src/main/java/mars/mips/instructions/syscalls/SyscallTime.java index 0f091dd..da486bc 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallTime.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallTime.java @@ -1,7 +1,9 @@ - package mars.mips.instructions.syscalls; - import mars.*; - import mars.util.*; - import mars.mips.hardware.*; +package mars.mips.instructions.syscalls; + +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.RegisterFile; +import mars.util.Binary; /* Copyright (c) 2003-2007, Pete Sanderson and Kenneth Vollmar @@ -32,28 +34,29 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** +/** * Service to read a character from input console into $a0. - * */ - - public class SyscallTime extends AbstractSyscall { - /** - * Build an instance of the Read Char syscall. Default service number - * is 12 and name is "ReadChar". - */ - public SyscallTime() { - super(30, "Time"); - } - - /** - * Performs syscall function to place current system time into $a0 (low order 32 bits) - * and $a1 (high order 32 bits). - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - long value = new java.util.Date().getTime(); - RegisterFile.updateRegister(4, Binary.lowOrderLongToInt(value)); // $a0 - RegisterFile.updateRegister(5, Binary.highOrderLongToInt(value)); // $a1 - } - - } \ No newline at end of file + +public class SyscallTime extends AbstractSyscall +{ + /** + * Build an instance of the Read Char syscall. Default service number is 12 and name is "ReadChar". + */ + public SyscallTime() + { + super(30, "Time"); + } + + /** + * Performs syscall function to place current system time into $a0 (low order 32 bits) and $a1 (high order 32 + * bits). + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + long value = new java.util.Date().getTime(); + RegisterFile.updateRegister(4, Binary.lowOrderLongToInt(value)); // $a0 + RegisterFile.updateRegister(5, Binary.highOrderLongToInt(value)); // $a1 + } + +} diff --git a/src/main/java/mars/mips/instructions/syscalls/SyscallWrite.java b/src/main/java/mars/mips/instructions/syscalls/SyscallWrite.java index 69e4519..f81a1ac 100644 --- a/src/main/java/mars/mips/instructions/syscalls/SyscallWrite.java +++ b/src/main/java/mars/mips/instructions/syscalls/SyscallWrite.java @@ -1,8 +1,11 @@ - package mars.mips.instructions.syscalls; - import mars.util.*; - import mars.mips.hardware.*; - import mars.simulator.*; - import mars.*; +package mars.mips.instructions.syscalls; + +import mars.Globals; +import mars.ProcessingException; +import mars.ProgramStatement; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.RegisterFile; +import mars.util.SystemIO; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -33,60 +36,60 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Service to write to file descriptor given in $a0. $a1 specifies buffer - * and $a2 specifies length. Number of characters written is returned in $v0 - * (this was changed from $a0 in MARS 3.7 for SPIM compatibility. The table - * in COD erroneously shows $a0). - * +/** + * Service to write to file descriptor given in $a0. $a1 specifies buffer and $a2 specifies length. Number of + * characters written is returned in $v0 (this was changed from $a0 in MARS 3.7 for SPIM compatibility. The table in + * COD erroneously shows $a0). */ - - public class SyscallWrite extends AbstractSyscall { - /** - * Build an instance of the Write file syscall. Default service number - * is 15 and name is "Write". - */ - public SyscallWrite() { - super(15, "Write"); - } - - /** - * Performs syscall function to write to file descriptor given in $a0. $a1 specifies buffer - * and $a2 specifies length. Number of characters written is returned in $v0, starting in MARS 3.7. - */ - public void simulate(ProgramStatement statement) throws ProcessingException { - int byteAddress = RegisterFile.getValue(5); // source of characters to write to file - byte b = 0; - int reqLength = RegisterFile.getValue(6); // user-requested length - int index = 0; - byte myBuffer[] = new byte[RegisterFile.getValue(6) + 1]; // specified length plus null termination - try - { - b = (byte) Globals.memory.getByte(byteAddress); - while (index < reqLength) // Stop at requested length. Null bytes are included. - // while (index < reqLength && b != 0) // Stop at requested length OR null byte - { - myBuffer[index++] = b; - byteAddress++; - b = (byte) Globals.memory.getByte(byteAddress); - } - - myBuffer[index] = 0; // Add string termination - } // end try - catch (AddressErrorException e) - { - throw new ProcessingException(statement, e); - } - int retValue = SystemIO.writeToFile( - RegisterFile.getValue(4), // fd - myBuffer, // buffer - RegisterFile.getValue(6)); // length - RegisterFile.updateRegister(2, retValue); // set returned value in register - // Getting rid of processing exception. It is the responsibility of the - // user program to check the syscall's return value. MARS should not - // re-emptively terminate MIPS execution because of it. Thanks to - // UCLA student Duy Truong for pointing this out. DPS 28-July-2009 +public class SyscallWrite extends AbstractSyscall +{ + /** + * Build an instance of the Write file syscall. Default service number is 15 and name is "Write". + */ + public SyscallWrite() + { + super(15, "Write"); + } + + /** + * Performs syscall function to write to file descriptor given in $a0. $a1 specifies buffer and $a2 specifies + * length. Number of characters written is returned in $v0, starting in MARS 3.7. + */ + public void simulate(ProgramStatement statement) throws ProcessingException + { + int byteAddress = RegisterFile.getValue(5); // source of characters to write to file + byte b = 0; + int reqLength = RegisterFile.getValue(6); // user-requested length + int index = 0; + byte[] myBuffer = new byte[RegisterFile.getValue(6) + 1]; // specified length plus null termination + try + { + b = (byte) Globals.memory.getByte(byteAddress); + while (index < reqLength) // Stop at requested length. Null bytes are included. + // while (index < reqLength && b != 0) // Stop at requested length OR null byte + { + myBuffer[index++] = b; + byteAddress++; + b = (byte) Globals.memory.getByte(byteAddress); + } + + myBuffer[index] = 0; // Add string termination + } // end try + catch (AddressErrorException e) + { + throw new ProcessingException(statement, e); + } + int retValue = SystemIO.writeToFile( + RegisterFile.getValue(4), // fd + myBuffer, // buffer + RegisterFile.getValue(6)); // length + RegisterFile.updateRegister(2, retValue); // set returned value in register + + // Getting rid of processing exception. It is the responsibility of the + // user program to check the syscall's return value. MARS should not + // re-emptively terminate MIPS execution because of it. Thanks to + // UCLA student Duy Truong for pointing this out. DPS 28-July-2009 /* if (retValue < 0) // some error in opening file { @@ -94,5 +97,5 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. SystemIO.getFileErrorMessage()); } */ - } - } \ No newline at end of file + } +} diff --git a/src/main/java/mars/mips/instructions/syscalls/ToneGenerator.java b/src/main/java/mars/mips/instructions/syscalls/ToneGenerator.java index 75dcd69..93f4df3 100644 --- a/src/main/java/mars/mips/instructions/syscalls/ToneGenerator.java +++ b/src/main/java/mars/mips/instructions/syscalls/ToneGenerator.java @@ -1,12 +1,11 @@ +package mars.mips.instructions.syscalls; - package mars.mips.instructions.syscalls; - - import javax.sound.midi.*; - import java.util.concurrent.locks.Lock; - import java.util.concurrent.locks.ReentrantLock; - import java.util.concurrent.Executor; - import java.util.concurrent.Executors; +import javax.sound.midi.*; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /* Copyright (c) 2003-2007, Pete Sanderson and Kenneth Vollmar @@ -36,248 +35,261 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - ///////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////////////////////////// - // - // The ToneGenerator and Tone classes were developed by Otterbein College - // student Tony Brock in July 2007. They simulate MIDI output through the - // computers soundcard using classes and methods of the javax.sound.midi - // package. - // - // Max Hailperin changed the interface of the - // ToneGenerator class 2009-10-19 in order to - // (1) provide a reliable way to wait for the completion of a - // synchronous tone, - // and while he was at it, - // (2) improve the efficiency of asynchronous tones by using a thread - // pool executor, and - // (3) simplify the interface by removing all the unused versions - // that provided default values for various parameters - ///////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////// - - - /* - * Creates a Tone object and passes it to a thread to "play" it using MIDI. - */ - class ToneGenerator { - - /** - * The default pitch value for the tone: 60 / middle C. - */ - public final static byte DEFAULT_PITCH = 60; - - /** - * The default duration of the tone: 1000 milliseconds. - */ - public final static int DEFAULT_DURATION = 1000; - - /** - * The default instrument of the tone: 0 / piano. - */ - public final static byte DEFAULT_INSTRUMENT = 0; - - /** - * The default volume of the tone: 100 (of 127). - */ - public final static byte DEFAULT_VOLUME = 100; - private static Executor threadPool = Executors.newCachedThreadPool(); - - /** - * Produces a Tone with the specified pitch, duration, and instrument, - * and volume. - * - * @param pitch the desired pitch in semitones - 0-127 where 60 is - * middle C. - * @param duration the desired duration in milliseconds. - * @param instrument the desired instrument (or patch) represented - * by a positive byte value (0-127). See the general - * MIDI instrument patch map for more instruments associated with - * each value. - * @param volume the desired volume of the initial attack of the - * Tone (MIDI velocity) represented by a positive byte value (0-127). - */ - public void generateTone(byte pitch, int duration, - byte instrument, byte volume) { - Runnable tone = new Tone(pitch, duration, instrument, volume); - threadPool.execute(tone); - } +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// The ToneGenerator and Tone classes were developed by Otterbein College +// student Tony Brock in July 2007. They simulate MIDI output through the +// computers soundcard using classes and methods of the javax.sound.midi +// package. +// +// Max Hailperin changed the interface of the +// ToneGenerator class 2009-10-19 in order to +// (1) provide a reliable way to wait for the completion of a +// synchronous tone, +// and while he was at it, +// (2) improve the efficiency of asynchronous tones by using a thread +// pool executor, and +// (3) simplify the interface by removing all the unused versions +// that provided default values for various parameters +///////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////// - /** - * Produces a Tone with the specified pitch, duration, and instrument, - * and volume, waiting for it to finish playing. - * - * @param pitch the desired pitch in semitones - 0-127 where 60 is - * middle C. - * @param duration the desired duration in milliseconds. - * @param instrument the desired instrument (or patch) represented - * by a positive byte value (0-127). See the general - * MIDI instrument patch map for more instruments associated with - * each value. - * @param volume the desired volume of the initial attack of the - * Tone (MIDI velocity) represented by a positive byte value (0-127). - */ - public void generateToneSynchronously(byte pitch, int duration, - byte instrument, byte volume) { - Runnable tone = new Tone(pitch, duration, instrument, volume); - tone.run(); - } - - } - - - /** - * Contains important variables for a MIDI Tone: pitch, duration - * instrument (patch), and volume. The tone can be passed to a thread - * and will be played using MIDI. - */ - class Tone implements Runnable { - - /** - * Tempo of the tone is in milliseconds: 1000 beats per second. - */ - - public final static int TEMPO = 1000; - /** - * The default MIDI channel of the tone: 0 (channel 1). - */ - public final static int DEFAULT_CHANNEL = 0; - - private byte pitch; - private int duration; - private byte instrument; - private byte volume; - - /** - * Instantiates a new Tone object, initializing the tone's pitch, - * duration, instrument (patch), and volume. - * - * @param pitch the pitch in semitones. Pitch is represented by - * a positive byte value - 0-127 where 60 is middle C. - * @param duration the duration of the tone in milliseconds. - * @param instrument a positive byte value (0-127) which represents - * the instrument (or patch) of the tone. See the general - * MIDI instrument patch map for more instruments associated with - * each value. - * @param volume a positive byte value (0-127) which represents the - * volume of the initial attack of the note (MIDI velocity). 127 being - * loud, and 0 being silent. - */ - public Tone(byte pitch, int duration, byte instrument, byte volume) { - this.pitch = pitch; - this.duration = duration; - this.instrument = instrument; - this.volume = volume; - } - - /** - * Plays the tone. - */ - public void run() { - playTone(); - } - - /* The following lock and the code which locks and unlocks it - * around the opening of the Sequencer were added 2009-10-19 by - * Max Hailperin in order to work around a - * bug in Sun's JDK which causes crashing if two threads race: - * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6888117 . - * This routinely manifested native-code crashes when tones - * were played asynchronously, on dual-core machines with Sun's - * JDK (but not on one core or with OpenJDK). Even when tones - * were played only synchronously, crashes sometimes occurred. - * This is likely due to the fact that Thread.sleep was used - * for synchronization, a role it cannot reliably serve. In - * any case, this one lock seems to make all the crashes go - * away, and the sleeps are being eliminated (since they can - * cause other, less severe, problems), so that case should be - * double covered. */ - private static Lock openLock = new ReentrantLock(); +/* + * Creates a Tone object and passes it to a thread to "play" it using MIDI. + */ +class ToneGenerator +{ + + /** + * The default pitch value for the tone: 60 / middle C. + */ + public final static byte DEFAULT_PITCH = 60; + + /** + * The default duration of the tone: 1000 milliseconds. + */ + public final static int DEFAULT_DURATION = 1000; + + /** + * The default instrument of the tone: 0 / piano. + */ + public final static byte DEFAULT_INSTRUMENT = 0; + + /** + * The default volume of the tone: 100 (of 127). + */ + public final static byte DEFAULT_VOLUME = 100; + + private static final Executor threadPool = Executors.newCachedThreadPool(); + + /** + * Produces a Tone with the specified pitch, duration, and instrument, and volume. + * + * @param pitch the desired pitch in semitones - 0-127 where 60 is middle C. + * @param duration the desired duration in milliseconds. + * @param instrument the desired instrument (or patch) represented by a positive byte value (0-127). See the general MIDI instrument patch map for + * more instruments associated with each value. + * @param volume the desired volume of the initial attack of the Tone (MIDI velocity) represented by a positive + * byte value (0-127). + */ + public void generateTone(byte pitch, int duration, + byte instrument, byte volume) + { + Runnable tone = new Tone(pitch, duration, instrument, volume); + threadPool.execute(tone); + } + + /** + * Produces a Tone with the specified pitch, duration, and instrument, and volume, waiting for it to finish + * playing. + * + * @param pitch the desired pitch in semitones - 0-127 where 60 is middle C. + * @param duration the desired duration in milliseconds. + * @param instrument the desired instrument (or patch) represented by a positive byte value (0-127). See the general MIDI instrument patch map for + * more instruments associated with each value. + * @param volume the desired volume of the initial attack of the Tone (MIDI velocity) represented by a positive + * byte value (0-127). + */ + public void generateToneSynchronously(byte pitch, int duration, + byte instrument, byte volume) + { + Runnable tone = new Tone(pitch, duration, instrument, volume); + tone.run(); + } + +} + + +/** + * Contains important variables for a MIDI Tone: pitch, duration instrument (patch), and volume. The tone can be passed + * to a thread and will be played using MIDI. + */ +class Tone implements Runnable +{ + + /** + * Tempo of the tone is in milliseconds: 1000 beats per second. + */ + + public final static int TEMPO = 1000; + + /** + * The default MIDI channel of the tone: 0 (channel 1). + */ + public final static int DEFAULT_CHANNEL = 0; + + private static final Lock openLock = new ReentrantLock(); + + private final byte pitch; + + private final int duration; + + private final byte instrument; + + private final byte volume; + + /** + * Instantiates a new Tone object, initializing the tone's pitch, duration, instrument (patch), and volume. + * + * @param pitch the pitch in semitones. Pitch is represented by a positive byte value - 0-127 where 60 is + * middle C. + * @param duration the duration of the tone in milliseconds. + * @param instrument a positive byte value (0-127) which represents the instrument (or patch) of the tone. See + * the general MIDI instrument patch + * map for more instruments associated with each value. + * @param volume a positive byte value (0-127) which represents the volume of the initial attack of the note + * (MIDI velocity). 127 being loud, and 0 being silent. + */ + public Tone(byte pitch, int duration, byte instrument, byte volume) + { + this.pitch = pitch; + this.duration = duration; + this.instrument = instrument; + this.volume = volume; + } + + /* The following lock and the code which locks and unlocks it + * around the opening of the Sequencer were added 2009-10-19 by + * Max Hailperin in order to work around a + * bug in Sun's JDK which causes crashing if two threads race: + * http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6888117 . + * This routinely manifested native-code crashes when tones + * were played asynchronously, on dual-core machines with Sun's + * JDK (but not on one core or with OpenJDK). Even when tones + * were played only synchronously, crashes sometimes occurred. + * This is likely due to the fact that Thread.sleep was used + * for synchronization, a role it cannot reliably serve. In + * any case, this one lock seems to make all the crashes go + * away, and the sleeps are being eliminated (since they can + * cause other, less severe, problems), so that case should be + * double covered. */ + + /** + * Plays the tone. + */ + public void run() + { + playTone(); + } + + private void playTone() + { + + try + { + Sequencer player = null; + openLock.lock(); + try + { + player = MidiSystem.getSequencer(); + player.open(); + } + finally + { + openLock.unlock(); + } - private void playTone() { - - try { - Sequencer player = null; - openLock.lock(); - try { - player = MidiSystem.getSequencer(); - player.open(); - } finally { - openLock.unlock(); - } - Sequence seq = new Sequence(Sequence.PPQ, 1); player.setTempoInMPQ(TEMPO); - Track t = seq.createTrack(); - + Track t = seq.createTrack(); + //select instrument ShortMessage inst = new ShortMessage(); inst.setMessage(ShortMessage.PROGRAM_CHANGE, DEFAULT_CHANNEL, instrument, 0); MidiEvent instChange = new MidiEvent(inst, 0); t.add(instChange); - + ShortMessage on = new ShortMessage(); on.setMessage(ShortMessage.NOTE_ON, DEFAULT_CHANNEL, pitch, volume); MidiEvent noteOn = new MidiEvent(on, 0); t.add(noteOn); - + ShortMessage off = new ShortMessage(); off.setMessage(ShortMessage.NOTE_OFF, DEFAULT_CHANNEL, pitch, volume); MidiEvent noteOff = new MidiEvent(off, duration); t.add(noteOff); - + player.setSequence(seq); - /* The EndOfTrackListener was added 2009-10-19 by Max - * Hailperin so that its - * awaitEndOfTrack method could be used as a more reliable - * replacement for Thread.sleep. (Given that the tone - * might not start playing right away, the sleep could end - * before the tone, clipping off the end of the tone.) */ - EndOfTrackListener eot = new EndOfTrackListener(); - player.addMetaEventListener(eot); - + /* The EndOfTrackListener was added 2009-10-19 by Max + * Hailperin so that its + * awaitEndOfTrack method could be used as a more reliable + * replacement for Thread.sleep. (Given that the tone + * might not start playing right away, the sleep could end + * before the tone, clipping off the end of the tone.) */ + EndOfTrackListener eot = new EndOfTrackListener(); + player.addMetaEventListener(eot); + player.start(); - - try { - eot.awaitEndOfTrack(); - } - catch (InterruptedException ex) { - } - finally { - player.close(); + + try + { + eot.awaitEndOfTrack(); } - - } - catch (MidiUnavailableException mue) { - mue.printStackTrace(); - } - catch (InvalidMidiDataException imde) { - imde.printStackTrace(); + catch (InterruptedException ex) + { + } + finally + { + player.close(); } - } - } -class EndOfTrackListener implements javax.sound.midi.MetaEventListener { - - private boolean endedYet = false; - - public synchronized void meta(javax.sound.midi.MetaMessage m){ - if(m.getType() == 47){ - endedYet = true; - notifyAll(); - } - } - - public synchronized void awaitEndOfTrack() throws InterruptedException { - while(!endedYet){ - wait(); - } - } + } + catch (MidiUnavailableException mue) + { + mue.printStackTrace(); + } + catch (InvalidMidiDataException imde) + { + imde.printStackTrace(); + } + } +} + +class EndOfTrackListener implements javax.sound.midi.MetaEventListener +{ + + private boolean endedYet = false; + + public synchronized void meta(javax.sound.midi.MetaMessage m) + { + if (m.getType() == 47) + { + endedYet = true; + notifyAll(); + } + } + + public synchronized void awaitEndOfTrack() throws InterruptedException + { + while (!endedYet) + { + wait(); + } + } } diff --git a/src/main/java/mars/simulator/BackStepper.java b/src/main/java/mars/simulator/BackStepper.java index c75756d..8104f07 100644 --- a/src/main/java/mars/simulator/BackStepper.java +++ b/src/main/java/mars/simulator/BackStepper.java @@ -1,9 +1,11 @@ - package mars.simulator; - import mars.*; - import mars.venus.*; - import mars.mips.hardware.*; - import mars.mips.instructions.*; - import java.util.*; +package mars.simulator; + +import mars.Globals; +import mars.ProgramStatement; +import mars.mips.hardware.Coprocessor0; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.RegisterFile; +import mars.mips.instructions.Instruction; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -35,332 +37,387 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * 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; + +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 final 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 = 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(); + do + { + BackStep step = 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); + 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 == 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) { + } + } + + + /* 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) { + } + 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) { + 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 - } + 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 @@ -369,92 +426,109 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. " 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) { + } + } + + // ***************************************************************************** + // 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 final int capacity; + + private int size; + + private int top; + + private final 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 + * (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. * - * (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; +public class DelayedBranch +{ + // Class states. + private static final int CLEARED = 0; - // 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; - } - } + private static final int REGISTERED = 1; - /** - * 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 : - } - } + private static final int TRIGGERED = 2; - /** - * 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; - } + // Initially nothing is happening. - /** - * 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. - */ + private static int state = CLEARED; - 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. - */ + private static int branchTargetAddress = 0; - static boolean isTriggered() { - return state == TRIGGERED; - } + /** + * 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 \ No newline at end of file + /** + * 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 diff --git a/src/main/java/mars/simulator/Exceptions.java b/src/main/java/mars/simulator/Exceptions.java index f127a0e..4676508 100644 --- a/src/main/java/mars/simulator/Exceptions.java +++ b/src/main/java/mars/simulator/Exceptions.java @@ -1,7 +1,9 @@ package mars.simulator; -import mars.mips.hardware.*; -import mars.mips.instructions.*; -import mars.util.*; + +import mars.mips.hardware.Coprocessor0; +import mars.mips.hardware.RegisterFile; +import mars.mips.instructions.Instruction; +import mars.util.Binary; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,65 +35,75 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * 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)); - } +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. - /** - * 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); - } + public static final int EXTERNAL_INTERRUPT_DISPLAY = 0x00000080; // see comment above. -} // Exceptions \ No newline at end of file + 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 diff --git a/src/main/java/mars/simulator/ProgramArgumentList.java b/src/main/java/mars/simulator/ProgramArgumentList.java index bf6126a..460498f 100644 --- a/src/main/java/mars/simulator/ProgramArgumentList.java +++ b/src/main/java/mars/simulator/ProgramArgumentList.java @@ -1,12 +1,14 @@ - 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.*; +package mars.simulator; + +import mars.Globals; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Memory; +import mars.mips.hardware.Register; +import mars.mips.hardware.RegisterFile; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.StringTokenizer; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -35,173 +37,185 @@ 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). + * 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; - +public class ProgramArgumentList +{ + + ArrayList programArgumentList; + /** - * Constructor that parses string to produce list. Delimiters - * are the default Java StringTokenizer delimiters (space, tab, - * newline, return, formfeed) + * 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 + * @param args String containing delimiter-separated arguments */ - public ProgramArgumentList(String args) { - StringTokenizer st = new StringTokenizer(args); - programArgumentList = new ArrayList(st.countTokens()); - while (st.hasMoreTokens()) { + 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. + * 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= 0; j--) { - Globals.memory.set(highAddress, programArgument.charAt(j), 1); - highAddress--; - } - argStartAddress[i] = highAddress+1; + } + // 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; + 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; + 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 + // 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("$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; - } - - - - } + 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); + } + } + + +} diff --git a/src/main/java/mars/simulator/Simulator.java b/src/main/java/mars/simulator/Simulator.java index 9d5097b..e790912 100644 --- a/src/main/java/mars/simulator/Simulator.java +++ b/src/main/java/mars/simulator/Simulator.java @@ -1,12 +1,21 @@ - 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.*; +package mars.simulator; + +import mars.*; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Coprocessor0; +import mars.mips.hardware.Memory; +import mars.mips.hardware.RegisterFile; +import mars.mips.instructions.BasicInstruction; +import mars.util.Binary; +import mars.util.SystemIO; +import mars.venus.RunGoAction; +import mars.venus.RunSpeedPanel; +import mars.venus.RunStepAction; + +import javax.swing.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Observable; /* Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar @@ -35,191 +44,227 @@ 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) { +public class Simulator extends Observable +{ + // 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; + + /** 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; + + public static volatile int externalInterruptingDevice = NO_DEVICE; + + private static Simulator simulator = null; // Singleton object + + private static Runnable interactiveGUIUpdater = null; + + private SimThread simulatorThread; + + private final ArrayList stopListeners = new ArrayList(1); + + 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) { + } + } + + /** + * 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; + } + + /** + * 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 + if (done) + { + SystemIO.resetFiles(); // close any files opened in MIPS progra + } this.simulatorThread = null; - if (pe != null) { - throw pe; + 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) { + } + 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); + 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 stopListeners = new ArrayList(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); + } + } + + 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)); + } + + /* 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); + } + + /** + * 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 final MIPSprogram p; + + private final int pc; + + private final int maxSteps; + + private int[] breakPoints; + + private boolean done; + + private ProcessingException pe; + + private volatile boolean stop = false; + + private volatile AbstractAction stopper; + + private final 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; @@ -228,309 +273,354 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 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) { + } + + /** + * 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() { + } + + + /** + * 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); + // 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 + + 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); - } + 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 Boolean.valueOf(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. - // ********************************************************************* - + + // ******************* 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); - } + + 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); } - } - }// 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); - } + 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 Boolean.valueOf(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 Boolean.valueOf(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) + { + this.constructReturnReason = PAUSE_OR_STOP; + this.done = false; + Simulator.getInstance().notifyObserversOfExecutionStop(maxSteps, pc); + return Boolean.valueOf(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 Boolean.valueOf(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 Boolean.valueOf(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 Boolean.valueOf(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(); + // 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. + // 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; + return Boolean.valueOf(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); - } - } + if (starterName.equals("Step")) + { + ((RunStepAction) starter).stepped(done, constructReturnReason, pe); } - 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(); + 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); + } + } + } + } + + } + + 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(); - } - } - - } + Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().highlightStepAtPC(); + } + } + +} diff --git a/src/main/java/mars/simulator/SimulatorNotice.java b/src/main/java/mars/simulator/SimulatorNotice.java index 9732b64..501b0e9 100644 --- a/src/main/java/mars/simulator/SimulatorNotice.java +++ b/src/main/java/mars/simulator/SimulatorNotice.java @@ -29,54 +29,68 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * 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 + * 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; - } +public class SimulatorNotice +{ + public static final int SIMULATOR_START = 0; - /** 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; - } -} \ No newline at end of file + public static final int SIMULATOR_STOP = 1; + + private final int action; + + private final int maxSteps; + + private final double runSpeed; + + private final int programCounter; + + /** + * 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; + } +} diff --git a/src/main/java/mars/simulator/SwingWorker.java b/src/main/java/mars/simulator/SwingWorker.java index babbb93..ea039b0 100644 --- a/src/main/java/mars/simulator/SwingWorker.java +++ b/src/main/java/mars/simulator/SwingWorker.java @@ -1,143 +1,177 @@ package mars.simulator; -import javax.swing.SwingUtilities; + +import javax.swing.*; /*----------------------------------------------------- * This file downloaded from the Sun Microsystems URL given below. * - * I will subclass it to create worker thread for running + * 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: - * + * 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. + *

+ * 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 { +public abstract class SwingWorker +{ private Object value; // see getValue(), setValue() - /** - * Class to maintain reference to current worker thread - * under separate synchronization control. + private final ThreadVar threadVar; + + /** + * Start a thread that will call the construct method and then exit. + * + * @param useSwing Set true if MARS is running from GUI, false otherwise. */ - private static class ThreadVar { - private Thread thread; - ThreadVar(Thread t) { thread = t; } - synchronized Thread get() { return thread; } - synchronized void clear() { thread = null; } + 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); } - private ThreadVar threadVar; - - /** - * Get the value produced by the worker thread, or null if it - * hasn't been constructed yet. + /** + * Get the value produced by the worker thread, or null if it hasn't been constructed yet. */ - protected synchronized Object getValue() { - return value; + protected synchronized Object getValue() + { + return value; } - /** - * Set the value produced by worker thread + /** + * Set the value produced by worker thread */ - private synchronized void setValue(Object x) { - value = x; + private synchronized void setValue(Object x) + { + value = x; } - /** - * Compute the value to be returned by the get method. + /** + * Compute the value to be returned by the get method. */ public abstract Object construct(); /** - * Called on the event dispatching thread (not on the worker thread) - * after the construct method has returned. + * Called on the event dispatching thread (not on the worker thread) after the construct method has + * returned. */ - public void finished() { + public void finished() + { } /** - * A new method that interrupts the worker thread. Call this method - * to force the worker to stop what it's doing. + * A new method that interrupts the worker thread. Call this method to force the worker to stop what it's doing. */ - public void interrupt() { + public void interrupt() + { Thread t = threadVar.get(); - if (t != null) { + if (t != null) + { t.interrupt(); } threadVar.clear(); } /** - * Return the value created by the construct 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 construct 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 construct method */ - public Object get() { - while (true) { + public Object get() + { + while (true) + { Thread t = threadVar.get(); - if (t == null) { + if (t == null) + { return getValue(); } - try { + try + { t.join(); } - catch (InterruptedException e) { + catch (InterruptedException e) + { Thread.currentThread().interrupt(); // propagate return null; } } } - - /** - * Start a thread that will call the construct 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() { + public void start() + { Thread t = threadVar.get(); - if (t != null) { + if (t != null) + { t.start(); } } + + /** + * 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; + } + } } diff --git a/src/main/java/mars/tools/AbstractMarsToolAndApplication.java b/src/main/java/mars/tools/AbstractMarsToolAndApplication.java index 41d2ae9..b575eba 100644 --- a/src/main/java/mars/tools/AbstractMarsToolAndApplication.java +++ b/src/main/java/mars/tools/AbstractMarsToolAndApplication.java @@ -1,15 +1,21 @@ - package mars.tools; - import javax.swing.*; - import javax.swing.border.*; - import javax.swing.filechooser.FileFilter; - import java.awt.*; - import java.awt.event.*; - import java.util.*; - import java.io.*; - import mars.*; - import mars.util.*; - import mars.tools.*; - import mars.mips.hardware.*; +package mars.tools; + +import mars.Globals; +import mars.MIPSprogram; +import mars.mips.hardware.*; +import mars.util.FilenameFinder; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; +import javax.swing.filechooser.FileFilter; +import java.awt.*; +import java.awt.event.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Observable; +import java.util.Observer; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -38,782 +44,876 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * An abstract class that provides generic components to facilitate implementation of - * a MarsTool and/or stand-alone Mars-based application. Provides default definitions - * of both the action() method required to implement MarsTool and the go() method - * conventionally used to launch a Mars-based stand-alone application. It also provides - * generic definitions for interactively controlling the application. The generic controls - * for MarsTools are 3 buttons: connect/disconnect to MIPS resource (memory and/or - * registers), reset, and close (exit). The generic controls for stand-alone Mars apps - * include: button that triggers a file open dialog, a text field to display status - * messages, the run-speed slider to control execution rate when running a MIPS program, - * a button that assembles and runs the current MIPS program, a button to interrupt - * the running MIPS program, a reset button, and an exit button. - * Pete Sanderson, 14 November 2006. - */ - public abstract class AbstractMarsToolAndApplication extends JFrame implements MarsTool, Observer { - protected boolean isBeingUsedAsAMarsTool = false; // can use to determine whether invoked as MarsTool or stand-alone. - protected AbstractMarsToolAndApplication thisMarsApp; - private JDialog dialog; // used only for MarsTool use. This is the pop-up dialog that appears when menu item selected. - protected Window theWindow; // highest level GUI component (a JFrame for app, a JDialog for MarsTool) - - // Major GUI components - JLabel headingLabel; - private String title; // descriptive title for title bar provided to constructor. - private String heading; // Text to be displayed in the top portion of the main window. - - // Some GUI settings - private EmptyBorder emptyBorder = new EmptyBorder(4,4,4,4); - private Color backgroundColor = Color.WHITE; - - - private int lowMemoryAddress = Memory.dataSegmentBaseAddress; - private int highMemoryAddress = Memory.stackBaseAddress; - // For MarsTool, is set true when "Connect" clicked, false when "Disconnect" clicked. - // For app, is set true when "Assemble and Run" clicked, false when program terminates. - private volatile boolean observing = false; - - // Several structures required for stand-alone use only (not MarsTool use) - private File mostRecentlyOpenedFile = null; - private Runnable interactiveGUIUpdater = new GUIUpdater(); - private MessageField operationStatusMessages; - private JButton openFileButton, assembleRunButton, stopButton; - private boolean multiFileAssemble = false; - - // Structure required for MarsTool use only (not stand-alone use). Want subclasses to have access. - protected ConnectButton connectButton; - - - /** - * Simple constructor - * @param title String containing title bar text - */ - protected AbstractMarsToolAndApplication(String title,String heading) { - thisMarsApp = this; - this.title = title; - this.heading = heading; - } - - ////////////////////////////////////////////////////////////////////////////////////// - ////////////////////////////// ABSTRACT METHODS /////////////////////////// - ////////////////////////////////////////////////////////////////////////////////////// - - /** - * Required MarsTool method to return Tool name. Must be defined by subclass. - * @return Tool name. MARS will display this in menu item. - */ - public abstract String getName(); - - /** - * Abstract method that must be instantiated by subclass to build the main display area - * of the GUI. It will be placed in the CENTER area of a BorderLayout. The title - * is in the NORTH area, and the controls are in the SOUTH area. - */ - protected abstract JComponent buildMainDisplayArea(); - - ////////////////////////////////////////////////////////////////////////////////////// - ///////////////////////////// METHODS WITH DEFAULT IMPLEMENTATIONS ////////////////// - ////////////////////////////////////////////////////////////////////////////////////// - - /** - * Run the simulator as stand-alone application. For this default implementation, - * the user-defined main display of the user interface is identical for both stand-alone - * and MARS Tools menu use, but the control buttons are different because the stand-alone - * must include a mechansim for controlling the opening, assembling, and executing of - * an underlying MIPS program. The generic controls include: a button that triggers a - * file open dialog, a text field to display status messages, the run-speed slider - * to control execution rate when running a MIPS program, a button that assembles and - * runs the current MIPS program, a reset button, and an exit button. - * This method calls 3 methods that can be defined/overriden in the subclass: initializePreGUI() - * for any special initialization that must be completed before building the user - * interface (e.g. data structures whose properties determine default GUI settings), - * initializePostGUI() for any special initialization that cannot be - * completed until after the building the user interface (e.g. data structure whose - * properties are determined by default GUI settings), and buildMainDisplayArea() - * to contain application-specific displays of parameters and results. - */ - public void go() { - theWindow = this; - this.isBeingUsedAsAMarsTool = false; - thisMarsApp.setTitle(this.title); - mars.Globals.initialize(true); - // assure the dialog goes away if user clicks the X - thisMarsApp.addWindowListener( - new WindowAdapter() { - public void windowClosing(WindowEvent e) { - performAppClosingDuties(); - } - }); - initializePreGUI(); - - JPanel contentPane = new JPanel(new BorderLayout(5,5)); - contentPane.setBorder(emptyBorder); - contentPane.setOpaque(true); - contentPane.add(buildHeadingArea(), BorderLayout.NORTH); - contentPane.add(buildMainDisplayArea(), BorderLayout.CENTER); - contentPane.add(buildButtonAreaStandAlone(), BorderLayout.SOUTH); - - thisMarsApp.setContentPane(contentPane); - thisMarsApp.pack(); - thisMarsApp.setLocationRelativeTo(null); // center on screen - thisMarsApp.setVisible(true); - initializePostGUI(); - } - - - /** - * Required MarsTool method to carry out Tool functions. It is invoked when MARS - * user selects this tool from the Tools menu. This default implementation provides - * generic definitions for interactively controlling the tool. The generic controls - * for MarsTools are 3 buttons: connect/disconnect to MIPS resource (memory and/or - * registers), reset, and close (exit). Like "go()" above, this default version - * calls 3 methods that can be defined/overriden in the subclass: initializePreGUI() - * for any special initialization that must be completed before building the user - * interface (e.g. data structures whose properties determine default GUI settings), - * initializePostGUI() for any special initialization that cannot be - * completed until after the building the user interface (e.g. data structure whose - * properties are determined by default GUI settings), and buildMainDisplayArea() - * to contain application-specific displays of parameters and results. - */ - - public void action() { - this.isBeingUsedAsAMarsTool = true; - dialog = new JDialog(Globals.getGui(), this.title); - // assure the dialog goes away if user clicks the X - dialog.addWindowListener( - new WindowAdapter() { - public void windowClosing(WindowEvent e) { - performToolClosingDuties(); - } - }); - theWindow = dialog; - initializePreGUI(); - JPanel contentPane = new JPanel(new BorderLayout(5,5)); - contentPane.setBorder(emptyBorder); - contentPane.setOpaque(true); - contentPane.add(buildHeadingArea(), BorderLayout.NORTH); - contentPane.add(buildMainDisplayArea(),BorderLayout.CENTER); - contentPane.add(buildButtonAreaMarsTool(), BorderLayout.SOUTH); - initializePostGUI(); - dialog.setContentPane(contentPane); - dialog.pack(); - dialog.setLocationRelativeTo(Globals.getGui()); - dialog.setVisible(true); - } - - - /** - * Method that will be called once just before the GUI is constructed in the go() and action() - * methods. Use it to initialize any data structures needed for the application whose values - * will be needed to determine the initial state of GUI components. By default it does nothing. - */ - protected void initializePreGUI() { - } - - /** - * Method that will be called once just after the GUI is constructed in the go() and action() - * methods. Use it to initialize data structures needed for the application whose values - * may depend on the initial state of GUI components. By default it does nothing. - */ - protected void initializePostGUI() { - } - - /** - * Method that will be called each time the default Reset button is clicked. - * Use it to reset any data structures and/or GUI components. By default it does nothing. - */ - protected void reset() { - } - - - /** - * Constructs GUI header as label with default positioning and font. May be overridden. - */ - protected JComponent buildHeadingArea() { - // OVERALL STRUCTURE OF MESSAGE (TOP) - headingLabel = new JLabel(); - Box headingPanel = Box.createHorizontalBox();//new JPanel(new BorderLayout()); - headingPanel.add(Box.createHorizontalGlue()); - headingPanel.add(headingLabel); - headingPanel.add(Box.createHorizontalGlue()); - // Details for heading area (top) - headingLabel.setText(heading); - headingLabel.setHorizontalTextPosition(JLabel.CENTER); - headingLabel.setFont(new Font(headingLabel.getFont().getFontName(),Font.PLAIN,18)); - return headingPanel; - } - - - - /** - * The MarsTool default set of controls has one row of 3 buttons. It includes a dual-purpose button to - * attach or detach simulator to MIPS memory, a button to reset the cache, and one to close the tool. - */ - protected JComponent buildButtonAreaMarsTool() { - Box buttonArea = Box.createHorizontalBox(); - TitledBorder tc =new TitledBorder("Tool Control"); - tc.setTitleJustification(TitledBorder.CENTER); - buttonArea.setBorder(tc); - connectButton = new ConnectButton(); - connectButton.setToolTipText("Control whether tool will respond to running MIPS program"); - connectButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (connectButton.isConnected()) { + +/** + * An abstract class that provides generic components to facilitate implementation of a MarsTool and/or stand-alone + * Mars-based application. Provides default definitions of both the action() method required to implement MarsTool and + * the go() method conventionally used to launch a Mars-based stand-alone application. It also provides generic + * definitions for interactively controlling the application. The generic controls for MarsTools are 3 buttons: + * connect/disconnect to MIPS resource (memory and/or registers), reset, and close (exit). The generic controls for + * stand-alone Mars apps include: button that triggers a file open dialog, a text field to display status messages, the + * run-speed slider to control execution rate when running a MIPS program, a button that assembles and runs the current + * MIPS program, a button to interrupt the running MIPS program, a reset button, and an exit button. Pete Sanderson, 14 + * November 2006. + */ +public abstract class AbstractMarsToolAndApplication extends JFrame implements MarsTool, Observer +{ + protected boolean isBeingUsedAsAMarsTool = false; // can use to determine whether invoked as MarsTool or stand-alone. + + protected AbstractMarsToolAndApplication thisMarsApp; + + protected Window theWindow; // highest level GUI component (a JFrame for app, a JDialog for MarsTool) + + // Structure required for MarsTool use only (not stand-alone use). Want subclasses to have access. + protected ConnectButton connectButton; + + // Major GUI components + JLabel headingLabel; + + private JDialog dialog; // used only for MarsTool use. This is the pop-up dialog that appears when menu item selected. + + private final String title; // descriptive title for title bar provided to constructor. + + private final String heading; // Text to be displayed in the top portion of the main window. + + // Some GUI settings + private final EmptyBorder emptyBorder = new EmptyBorder(4, 4, 4, 4); + + private final Color backgroundColor = Color.WHITE; + + private final int lowMemoryAddress = Memory.dataSegmentBaseAddress; + + private final int highMemoryAddress = Memory.stackBaseAddress; + + // For MarsTool, is set true when "Connect" clicked, false when "Disconnect" clicked. + // For app, is set true when "Assemble and Run" clicked, false when program terminates. + private volatile boolean observing = false; + + // Several structures required for stand-alone use only (not MarsTool use) + private File mostRecentlyOpenedFile = null; + + private final Runnable interactiveGUIUpdater = new GUIUpdater(); + + private MessageField operationStatusMessages; + + private JButton openFileButton, assembleRunButton, stopButton; + + private boolean multiFileAssemble = false; + + + /** + * Simple constructor + * + * @param title String containing title bar text + */ + protected AbstractMarsToolAndApplication(String title, String heading) + { + thisMarsApp = this; + this.title = title; + this.heading = heading; + } + + ////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////// ABSTRACT METHODS /////////////////////////// + ////////////////////////////////////////////////////////////////////////////////////// + + /** + * Required MarsTool method to return Tool name. Must be defined by subclass. + * + * @return Tool name. MARS will display this in menu item. + */ + public abstract String getName(); + + /** + * Abstract method that must be instantiated by subclass to build the main display area of the GUI. It will be + * placed in the CENTER area of a BorderLayout. The title is in the NORTH area, and the controls are in the SOUTH + * area. + */ + protected abstract JComponent buildMainDisplayArea(); + + ////////////////////////////////////////////////////////////////////////////////////// + ///////////////////////////// METHODS WITH DEFAULT IMPLEMENTATIONS ////////////////// + ////////////////////////////////////////////////////////////////////////////////////// + + /** + * Run the simulator as stand-alone application. For this default implementation, the user-defined main display of + * the user interface is identical for both stand-alone and MARS Tools menu use, but the control buttons are + * different because the stand-alone must include a mechansim for controlling the opening, assembling, and executing + * of an underlying MIPS program. The generic controls include: a button that triggers a file open dialog, a text + * field to display status messages, the run-speed slider to control execution rate when running a MIPS program, a + * button that assembles and runs the current MIPS program, a reset button, and an exit button. This method calls 3 + * methods that can be defined/overriden in the subclass: initializePreGUI() for any special initialization that + * must be completed before building the user interface (e.g. data structures whose properties determine default GUI + * settings), initializePostGUI() for any special initialization that cannot be completed until after the building + * the user interface (e.g. data structure whose properties are determined by default GUI settings), and + * buildMainDisplayArea() to contain application-specific displays of parameters and results. + */ + public void go() + { + theWindow = this; + this.isBeingUsedAsAMarsTool = false; + thisMarsApp.setTitle(this.title); + mars.Globals.initialize(true); + // assure the dialog goes away if user clicks the X + thisMarsApp.addWindowListener( + new WindowAdapter() + { + public void windowClosing(WindowEvent e) + { + performAppClosingDuties(); + } + }); + initializePreGUI(); + + JPanel contentPane = new JPanel(new BorderLayout(5, 5)); + contentPane.setBorder(emptyBorder); + contentPane.setOpaque(true); + contentPane.add(buildHeadingArea(), BorderLayout.NORTH); + contentPane.add(buildMainDisplayArea(), BorderLayout.CENTER); + contentPane.add(buildButtonAreaStandAlone(), BorderLayout.SOUTH); + + thisMarsApp.setContentPane(contentPane); + thisMarsApp.pack(); + thisMarsApp.setLocationRelativeTo(null); // center on screen + thisMarsApp.setVisible(true); + initializePostGUI(); + } + + + /** + * Required MarsTool method to carry out Tool functions. It is invoked when MARS user selects this tool from the + * Tools menu. This default implementation provides generic definitions for interactively controlling the tool. + * The generic controls for MarsTools are 3 buttons: connect/disconnect to MIPS resource (memory and/or registers), + * reset, and close (exit). Like "go()" above, this default version calls 3 methods that can be defined/overriden + * in the subclass: initializePreGUI() for any special initialization that must be completed before building the + * user interface (e.g. data structures whose properties determine default GUI settings), initializePostGUI() for + * any special initialization that cannot be completed until after the building the user interface (e.g. data + * structure whose properties are determined by default GUI settings), and buildMainDisplayArea() to contain + * application-specific displays of parameters and results. + */ + + public void action() + { + this.isBeingUsedAsAMarsTool = true; + dialog = new JDialog(Globals.getGui(), this.title); + // assure the dialog goes away if user clicks the X + dialog.addWindowListener( + new WindowAdapter() + { + public void windowClosing(WindowEvent e) + { + performToolClosingDuties(); + } + }); + theWindow = dialog; + initializePreGUI(); + JPanel contentPane = new JPanel(new BorderLayout(5, 5)); + contentPane.setBorder(emptyBorder); + contentPane.setOpaque(true); + contentPane.add(buildHeadingArea(), BorderLayout.NORTH); + contentPane.add(buildMainDisplayArea(), BorderLayout.CENTER); + contentPane.add(buildButtonAreaMarsTool(), BorderLayout.SOUTH); + initializePostGUI(); + dialog.setContentPane(contentPane); + dialog.pack(); + dialog.setLocationRelativeTo(Globals.getGui()); + dialog.setVisible(true); + } + + + /** + * Method that will be called once just before the GUI is constructed in the go() and action() methods. Use it to + * initialize any data structures needed for the application whose values will be needed to determine the initial + * state of GUI components. By default it does nothing. + */ + protected void initializePreGUI() + { + } + + /** + * Method that will be called once just after the GUI is constructed in the go() and action() methods. Use it to + * initialize data structures needed for the application whose values may depend on the initial state of GUI + * components. By default it does nothing. + */ + protected void initializePostGUI() + { + } + + /** + * Method that will be called each time the default Reset button is clicked. Use it to reset any data structures + * and/or GUI components. By default it does nothing. + */ + protected void reset() + { + } + + + /** + * Constructs GUI header as label with default positioning and font. May be overridden. + */ + protected JComponent buildHeadingArea() + { + // OVERALL STRUCTURE OF MESSAGE (TOP) + headingLabel = new JLabel(); + Box headingPanel = Box.createHorizontalBox();//new JPanel(new BorderLayout()); + headingPanel.add(Box.createHorizontalGlue()); + headingPanel.add(headingLabel); + headingPanel.add(Box.createHorizontalGlue()); + // Details for heading area (top) + headingLabel.setText(heading); + headingLabel.setHorizontalTextPosition(JLabel.CENTER); + headingLabel.setFont(new Font(headingLabel.getFont().getFontName(), Font.PLAIN, 18)); + return headingPanel; + } + + + /** + * The MarsTool default set of controls has one row of 3 buttons. It includes a dual-purpose button to attach or + * detach simulator to MIPS memory, a button to reset the cache, and one to close the tool. + */ + protected JComponent buildButtonAreaMarsTool() + { + Box buttonArea = Box.createHorizontalBox(); + TitledBorder tc = new TitledBorder("Tool Control"); + tc.setTitleJustification(TitledBorder.CENTER); + buttonArea.setBorder(tc); + connectButton = new ConnectButton(); + connectButton.setToolTipText("Control whether tool will respond to running MIPS program"); + connectButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + if (connectButton.isConnected()) + { connectButton.disconnect(); - } - else { + } + else + { connectButton.connect(); - } - } - }); - connectButton.addKeyListener(new EnterKeyListener(connectButton)); - - JButton resetButton = new JButton("Reset"); - resetButton.setToolTipText("Reset all counters and other structures"); - resetButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - reset(); - } - }); - resetButton.addKeyListener(new EnterKeyListener(resetButton)); - - JButton closeButton = new JButton("Close"); - closeButton.setToolTipText("Close (exit) this tool"); - closeButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - performToolClosingDuties(); - } - }); - closeButton.addKeyListener(new EnterKeyListener(closeButton)); - - // Add all the buttons... - buttonArea.add(connectButton); - buttonArea.add(Box.createHorizontalGlue()); - buttonArea.add(resetButton); - buttonArea.add(Box.createHorizontalGlue()); - JComponent helpComponent = getHelpComponent(); - if (helpComponent != null) { + } + } + }); + connectButton.addKeyListener(new EnterKeyListener(connectButton)); + + JButton resetButton = new JButton("Reset"); + resetButton.setToolTipText("Reset all counters and other structures"); + resetButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + reset(); + } + }); + resetButton.addKeyListener(new EnterKeyListener(resetButton)); + + JButton closeButton = new JButton("Close"); + closeButton.setToolTipText("Close (exit) this tool"); + closeButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + performToolClosingDuties(); + } + }); + closeButton.addKeyListener(new EnterKeyListener(closeButton)); + + // Add all the buttons... + buttonArea.add(connectButton); + buttonArea.add(Box.createHorizontalGlue()); + buttonArea.add(resetButton); + buttonArea.add(Box.createHorizontalGlue()); + JComponent helpComponent = getHelpComponent(); + if (helpComponent != null) + { buttonArea.add(helpComponent); buttonArea.add(Box.createHorizontalGlue()); - } - buttonArea.add(closeButton); - return buttonArea; - } - - /** - * The Mars stand-alone app default set of controls has two rows of controls. It includes a text field for - * displaying status messages, a button to trigger an open file dialog, the MARS run speed slider - * to control timed execution, a button to assemble and run the program, a reset button - * whose action is determined by the subclass reset() method, and an exit button. - */ - - protected JComponent buildButtonAreaStandAlone() { - // Overall structure of control area (two rows). - Box operationArea = Box.createVerticalBox(); - Box fileControlArea = Box.createHorizontalBox(); - Box buttonArea = Box.createHorizontalBox(); - operationArea.add(fileControlArea); - operationArea.add(Box.createVerticalStrut(5)); - operationArea.add(buttonArea); - TitledBorder ac =new TitledBorder("Application Control"); - ac.setTitleJustification(TitledBorder.CENTER); - operationArea.setBorder(ac); - - // Top row of controls consists of button to launch file open operation, - // text field to show filename, and run speed slider. - openFileButton = new JButton("Open MIPS program..."); - openFileButton.setToolTipText("Select MIPS program file to assemble and run"); - openFileButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - JFileChooser fileChooser = new JFileChooser(); - JCheckBox multiFileAssembleChoose = new JCheckBox("Assemble all in selected file's directory",multiFileAssemble); - multiFileAssembleChoose.setToolTipText("If checked, selected file will be assembled first and all other assembly files in directory will be assembled also."); - fileChooser.setAccessory(multiFileAssembleChoose); - if (mostRecentlyOpenedFile != null) { + } + buttonArea.add(closeButton); + return buttonArea; + } + + /** + * The Mars stand-alone app default set of controls has two rows of controls. It includes a text field for + * displaying status messages, a button to trigger an open file dialog, the MARS run speed slider to control timed + * execution, a button to assemble and run the program, a reset button whose action is determined by the subclass + * reset() method, and an exit button. + */ + + protected JComponent buildButtonAreaStandAlone() + { + // Overall structure of control area (two rows). + Box operationArea = Box.createVerticalBox(); + Box fileControlArea = Box.createHorizontalBox(); + Box buttonArea = Box.createHorizontalBox(); + operationArea.add(fileControlArea); + operationArea.add(Box.createVerticalStrut(5)); + operationArea.add(buttonArea); + TitledBorder ac = new TitledBorder("Application Control"); + ac.setTitleJustification(TitledBorder.CENTER); + operationArea.setBorder(ac); + + // Top row of controls consists of button to launch file open operation, + // text field to show filename, and run speed slider. + openFileButton = new JButton("Open MIPS program..."); + openFileButton.setToolTipText("Select MIPS program file to assemble and run"); + openFileButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + JFileChooser fileChooser = new JFileChooser(); + JCheckBox multiFileAssembleChoose = new JCheckBox("Assemble all in selected file's directory", multiFileAssemble); + multiFileAssembleChoose.setToolTipText("If checked, selected file will be assembled first and all other assembly files in directory will be assembled also."); + fileChooser.setAccessory(multiFileAssembleChoose); + if (mostRecentlyOpenedFile != null) + { fileChooser.setSelectedFile(mostRecentlyOpenedFile); - } - // DPS 13 June 2007. The next 4 lines add file filter to file chooser. - FileFilter defaultFileFilter = FilenameFinder.getFileFilter(Globals.fileExtensions, "Assembler Files", true); - fileChooser.addChoosableFileFilter(defaultFileFilter); - fileChooser.addChoosableFileFilter(fileChooser.getAcceptAllFileFilter()); - fileChooser.setFileFilter(defaultFileFilter); - - if (fileChooser.showOpenDialog(thisMarsApp) == JFileChooser.APPROVE_OPTION) { + } + // DPS 13 June 2007. The next 4 lines add file filter to file chooser. + FileFilter defaultFileFilter = FilenameFinder.getFileFilter(Globals.fileExtensions, "Assembler Files", true); + fileChooser.addChoosableFileFilter(defaultFileFilter); + fileChooser.addChoosableFileFilter(fileChooser.getAcceptAllFileFilter()); + fileChooser.setFileFilter(defaultFileFilter); + + if (fileChooser.showOpenDialog(thisMarsApp) == JFileChooser.APPROVE_OPTION) + { multiFileAssemble = multiFileAssembleChoose.isSelected(); File theFile = fileChooser.getSelectedFile(); - try { - theFile = theFile.getCanonicalFile(); - } - catch (IOException ioe) { - // nothing to do, theFile will keep current value - } + try + { + theFile = theFile.getCanonicalFile(); + } + catch (IOException ioe) + { + // nothing to do, theFile will keep current value + } String currentFilePath = theFile.getPath(); mostRecentlyOpenedFile = theFile; - operationStatusMessages.setText("File: "+currentFilePath); + operationStatusMessages.setText("File: " + currentFilePath); operationStatusMessages.setCaretPosition(0); assembleRunButton.setEnabled(true); - } - } - }); - openFileButton.addKeyListener(new EnterKeyListener(openFileButton)); - - operationStatusMessages = new MessageField("No file open."); - operationStatusMessages.setColumns(40); - operationStatusMessages.setMargin(new Insets(0, 3, 0, 3)); //(top, left, bottom, right) - operationStatusMessages.setBackground(backgroundColor); - operationStatusMessages.setFocusable(false); - operationStatusMessages.setToolTipText("Display operation status messages"); - - mars.venus.RunSpeedPanel speed = mars.venus.RunSpeedPanel.getInstance(); - - // Bottom row of controls consists of the three buttons defined here. - assembleRunButton = new JButton("Assemble and Run"); - assembleRunButton.setToolTipText("Assemble and run the currently selected MIPS program"); - assembleRunButton.setEnabled(false); - assembleRunButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - assembleRunButton.setEnabled(false); - openFileButton.setEnabled(false); - stopButton.setEnabled(true); - new Thread(new CreateAssembleRunMIPSprogram()).start(); - } - }); - assembleRunButton.addKeyListener(new EnterKeyListener(assembleRunButton)); - - stopButton = new JButton("Stop"); - stopButton.setToolTipText("Terminate MIPS program execution"); - stopButton.setEnabled(false); - stopButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - mars.simulator.Simulator.getInstance().stopExecution(null); - } - }); - stopButton.addKeyListener(new EnterKeyListener(stopButton)); - - JButton resetButton = new JButton("Reset"); - resetButton.setToolTipText("Reset all counters and other structures"); - resetButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - reset(); - } - }); - resetButton.addKeyListener(new EnterKeyListener(resetButton)); - - JButton closeButton = new JButton("Exit"); - closeButton.setToolTipText("Exit this application"); - closeButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - performAppClosingDuties(); - } - }); - closeButton.addKeyListener(new EnterKeyListener(closeButton)); - - - // Add top row of controls... - //fileControlArea.add(Box.createHorizontalStrut(5)); - - Box fileDisplayBox = Box.createVerticalBox(); - fileDisplayBox.add(Box.createVerticalStrut(8)); - fileDisplayBox.add(operationStatusMessages); - fileDisplayBox.add(Box.createVerticalStrut(8)); - fileControlArea.add(fileDisplayBox); - - fileControlArea.add(Box.createHorizontalGlue()); - fileControlArea.add(speed); - - // Add bottom row of buttons... - - buttonArea.add(openFileButton); - buttonArea.add(Box.createHorizontalGlue()); - buttonArea.add(assembleRunButton); - buttonArea.add(Box.createHorizontalGlue()); - buttonArea.add(stopButton); - buttonArea.add(Box.createHorizontalGlue()); - buttonArea.add(resetButton); - buttonArea.add(Box.createHorizontalGlue()); - JComponent helpComponent = getHelpComponent(); - if (helpComponent != null) { + } + } + }); + openFileButton.addKeyListener(new EnterKeyListener(openFileButton)); + + operationStatusMessages = new MessageField("No file open."); + operationStatusMessages.setColumns(40); + operationStatusMessages.setMargin(new Insets(0, 3, 0, 3)); //(top, left, bottom, right) + operationStatusMessages.setBackground(backgroundColor); + operationStatusMessages.setFocusable(false); + operationStatusMessages.setToolTipText("Display operation status messages"); + + mars.venus.RunSpeedPanel speed = mars.venus.RunSpeedPanel.getInstance(); + + // Bottom row of controls consists of the three buttons defined here. + assembleRunButton = new JButton("Assemble and Run"); + assembleRunButton.setToolTipText("Assemble and run the currently selected MIPS program"); + assembleRunButton.setEnabled(false); + assembleRunButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + assembleRunButton.setEnabled(false); + openFileButton.setEnabled(false); + stopButton.setEnabled(true); + new Thread(new CreateAssembleRunMIPSprogram()).start(); + } + }); + assembleRunButton.addKeyListener(new EnterKeyListener(assembleRunButton)); + + stopButton = new JButton("Stop"); + stopButton.setToolTipText("Terminate MIPS program execution"); + stopButton.setEnabled(false); + stopButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + mars.simulator.Simulator.getInstance().stopExecution(null); + } + }); + stopButton.addKeyListener(new EnterKeyListener(stopButton)); + + JButton resetButton = new JButton("Reset"); + resetButton.setToolTipText("Reset all counters and other structures"); + resetButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + reset(); + } + }); + resetButton.addKeyListener(new EnterKeyListener(resetButton)); + + JButton closeButton = new JButton("Exit"); + closeButton.setToolTipText("Exit this application"); + closeButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + performAppClosingDuties(); + } + }); + closeButton.addKeyListener(new EnterKeyListener(closeButton)); + + + // Add top row of controls... + //fileControlArea.add(Box.createHorizontalStrut(5)); + + Box fileDisplayBox = Box.createVerticalBox(); + fileDisplayBox.add(Box.createVerticalStrut(8)); + fileDisplayBox.add(operationStatusMessages); + fileDisplayBox.add(Box.createVerticalStrut(8)); + fileControlArea.add(fileDisplayBox); + + fileControlArea.add(Box.createHorizontalGlue()); + fileControlArea.add(speed); + + // Add bottom row of buttons... + + buttonArea.add(openFileButton); + buttonArea.add(Box.createHorizontalGlue()); + buttonArea.add(assembleRunButton); + buttonArea.add(Box.createHorizontalGlue()); + buttonArea.add(stopButton); + buttonArea.add(Box.createHorizontalGlue()); + buttonArea.add(resetButton); + buttonArea.add(Box.createHorizontalGlue()); + JComponent helpComponent = getHelpComponent(); + if (helpComponent != null) + { buttonArea.add(helpComponent); buttonArea.add(Box.createHorizontalGlue()); - } - buttonArea.add(closeButton); - return operationArea; - } - - ////////////////////////////////////////////////////////////////////////////////////// - // Rest of the methods. Some are used by stand-alone (JFrame-based) only, some are - // used by MarsTool (JDialog-based) only, others are used by both. - ////////////////////////////////////////////////////////////////////////////////////// - - /** - * Called when receiving notice of access to MIPS memory or registers. Default - * implementation of method required by Observer interface. This method will filter out - * notices originating from the MARS GUI or from direct user editing of memory or register - * displays. Only notices arising from MIPS program access are allowed in. - * It then calls two methods to be overridden by the subclass (since they do - * nothing by default): processMIPSUpdate() then updateDisplay(). - * @param resource the attached MIPS resource - * @param accessNotice AccessNotice information provided by the resource - */ - public void update(Observable resource, Object accessNotice) { - if (((AccessNotice)accessNotice).accessIsFromMIPS()) { - processMIPSUpdate(resource, (AccessNotice)accessNotice); + } + buttonArea.add(closeButton); + return operationArea; + } + + ////////////////////////////////////////////////////////////////////////////////////// + // Rest of the methods. Some are used by stand-alone (JFrame-based) only, some are + // used by MarsTool (JDialog-based) only, others are used by both. + ////////////////////////////////////////////////////////////////////////////////////// + + /** + * Called when receiving notice of access to MIPS memory or registers. Default implementation of method required by + * Observer interface. This method will filter out notices originating from the MARS GUI or from direct user + * editing of memory or register displays. Only notices arising from MIPS program access are allowed in. It then + * calls two methods to be overridden by the subclass (since they do nothing by default): processMIPSUpdate() then + * updateDisplay(). + * + * @param resource the attached MIPS resource + * @param accessNotice AccessNotice information provided by the resource + */ + public void update(Observable resource, Object accessNotice) + { + if (((AccessNotice) accessNotice).accessIsFromMIPS()) + { + processMIPSUpdate(resource, (AccessNotice) accessNotice); updateDisplay(); - } - } - - /** - * Override this method to process a received notice from MIPS Observable (memory or register) - * It will only be called if the notice was generated as the result of MIPS instruction execution. - * By default it does nothing. After this method is complete, the updateDisplay() method will be - * invoked automatically. - */ - protected void processMIPSUpdate(Observable resource, AccessNotice notice) { - } - - /** - * This method is called when tool/app is exited either through the close/exit button or the window's X box. - * Override it to perform any special housecleaning needed. By default it does nothing. - */ - protected void performSpecialClosingDuties() { - } - - - /** - * Add this app/tool as an Observer of desired MIPS Observables (memory and registers). - * By default, will add as an Observer of the entire Data Segment in memory. - * Override if you want something different. Note that the Memory methods to add an - * Observer to memory are flexible (you can register for a range of addresses) but - * may throw an AddressErrorException that you need to catch. - * This method is called whenever the default "Connect" button on a MarsTool or the - * default "Assemble and run" on a stand-alone Mars app is selected. The corresponding - * NOTE: if you do not want to register as an Observer of the entire data segment - * (starts at address 0x10000000) then override this to either do some alternative - * or nothing at all. This method is also overloaded to allow arbitrary memory - * subrange. - */ - - protected void addAsObserver() { - addAsObserver(lowMemoryAddress, highMemoryAddress); - } - - /** - * Add this app/tool as an Observer of the specified subrange of MIPS memory. Note - * that this method is not invoked automatically like the no-argument version, but - * if you use this method, you can still take advantage of provided default deleteAsObserver() - * since it will remove the app as a memory observer regardless of the subrange - * or number of subranges it is registered for. - * @param lowEnd low end of memory address range. - * @param highEnd high end of memory address range; must be >= lowEnd - */ - - protected void addAsObserver(int lowEnd, int highEnd) { - String errorMessage = "Error connecting to MIPS memory"; - try { - Globals.memory.addObserver(thisMarsApp,lowEnd, highEnd); - } - catch (AddressErrorException aee) { - if (this.isBeingUsedAsAMarsTool) { - headingLabel.setText(errorMessage); - } - else { - operationStatusMessages.displayTerminatingMessage(errorMessage); - } - } - } - - /** - * Add this app/tool as an Observer of the specified MIPS register. - */ - protected void addAsObserver(Register reg) { - if (reg != null) { + } + } + + /** + * Override this method to process a received notice from MIPS Observable (memory or register) It will only be + * called if the notice was generated as the result of MIPS instruction execution. By default it does nothing. After + * this method is complete, the updateDisplay() method will be invoked automatically. + */ + protected void processMIPSUpdate(Observable resource, AccessNotice notice) + { + } + + /** + * This method is called when tool/app is exited either through the close/exit button or the window's X box. + * Override it to perform any special housecleaning needed. By default it does nothing. + */ + protected void performSpecialClosingDuties() + { + } + + + /** + * Add this app/tool as an Observer of desired MIPS Observables (memory and registers). By default, will add as an + * Observer of the entire Data Segment in memory. Override if you want something different. Note that the Memory + * methods to add an Observer to memory are flexible (you can register for a range of addresses) but may throw an + * AddressErrorException that you need to catch. This method is called whenever the default "Connect" button on a + * MarsTool or the default "Assemble and run" on a stand-alone Mars app is selected. The corresponding NOTE: if you + * do not want to register as an Observer of the entire data segment (starts at address 0x10000000) then override + * this to either do some alternative or nothing at all. This method is also overloaded to allow arbitrary memory + * subrange. + */ + + protected void addAsObserver() + { + addAsObserver(lowMemoryAddress, highMemoryAddress); + } + + /** + * Add this app/tool as an Observer of the specified subrange of MIPS memory. Note that this method is not invoked + * automatically like the no-argument version, but if you use this method, you can still take advantage of provided + * default deleteAsObserver() since it will remove the app as a memory observer regardless of the subrange or number + * of subranges it is registered for. + * + * @param lowEnd low end of memory address range. + * @param highEnd high end of memory address range; must be >= lowEnd + */ + + protected void addAsObserver(int lowEnd, int highEnd) + { + String errorMessage = "Error connecting to MIPS memory"; + try + { + Globals.memory.addObserver(thisMarsApp, lowEnd, highEnd); + } + catch (AddressErrorException aee) + { + if (this.isBeingUsedAsAMarsTool) + { + headingLabel.setText(errorMessage); + } + else + { + operationStatusMessages.displayTerminatingMessage(errorMessage); + } + } + } + + /** + * Add this app/tool as an Observer of the specified MIPS register. + */ + protected void addAsObserver(Register reg) + { + if (reg != null) + { reg.addObserver(thisMarsApp); - } - } - - - /** - * Delete this app/tool as an Observer of MIPS Observables (memory and registers). - * By default, will delete as an Observer of memory. - * Override if you want something different. - * This method is called when the default "Disconnect" button on a MarsTool is selected or - * when the MIPS program execution triggered by the default "Assemble and run" on a stand-alone - * Mars app terminates (e.g. when the button is re-enabled). - */ - - protected void deleteAsObserver() { - Globals.memory.deleteObserver(thisMarsApp); - } - - /** - * Delete this app/tool as an Observer of the specified MIPS register - */ - - protected void deleteAsObserver(Register reg) { - if (reg != null) { + } + } + + + /** + * Delete this app/tool as an Observer of MIPS Observables (memory and registers). By default, will delete as an + * Observer of memory. Override if you want something different. This method is called when the default "Disconnect" + * button on a MarsTool is selected or when the MIPS program execution triggered by the default "Assemble and run" + * on a stand-alone Mars app terminates (e.g. when the button is re-enabled). + */ + + protected void deleteAsObserver() + { + Globals.memory.deleteObserver(thisMarsApp); + } + + /** + * Delete this app/tool as an Observer of the specified MIPS register + */ + + protected void deleteAsObserver(Register reg) + { + if (reg != null) + { reg.deleteObserver(thisMarsApp); - } - } - - /** - * Query method to let you know if the tool/app is (or could be) currently - * "observing" any MIPS resources. When running as a MarsTool, this - * will be true by default after clicking the "Connect to MIPS" button until "Disconnect - * from MIPS" is clicked. When running as a stand-alone app, this will be - * true by default after clicking the "Assemble and Run" button until until - * program execution has terminated either normally or by clicking the "Stop" - * button. The phrase "or could be" was added above because depending on how - * the tool/app operates, it may be possible to run the MIPS program without - * first registering as an Observer -- i.e. addAsObserver() is overridden and - * takes no action. - * @return true if tool/app is (or could be) currently active as an Observer. - */ - - protected boolean isObserving() { - return observing; - } - - /** - * Override this method to implement updating of GUI after each MIPS instruction is executed, - * while running in "timed" mode (user specifies execution speed on the slider control). - * Does nothing by default. - */ - protected void updateDisplay() { - } - - /** - * Override this method to provide a JComponent (probably a JButton) of your choice - * to be placed just left of the Close/Exit button. Its anticipated use is for a - * "help" button that launches a help message or dialog. But it can be any valid - * JComponent that doesn't mind co-existing among a bunch of JButtons. - */ - protected JComponent getHelpComponent() { - return null; - } - - ////////////////////////////////////////////////////////////////////////////////// - //////////////////// PRIVATE HELPER METHODS ////////////////////////////////// - ////////////////////////////////////////////////////////////////////////////////// - - // Closing duties for MarsTool only. - private void performToolClosingDuties() { - performSpecialClosingDuties(); - if (connectButton.isConnected()) { + } + } + + /** + * Query method to let you know if the tool/app is (or could be) currently "observing" any MIPS resources. When + * running as a MarsTool, this will be true by default after clicking the "Connect to MIPS" button until "Disconnect + * from MIPS" is clicked. When running as a stand-alone app, this will be true by default after clicking the + * "Assemble and Run" button until until program execution has terminated either normally or by clicking the "Stop" + * button. The phrase "or could be" was added above because depending on how the tool/app operates, it may be + * possible to run the MIPS program without first registering as an Observer -- i.e. addAsObserver() is overridden + * and takes no action. + * + * @return true if tool/app is (or could be) currently active as an Observer. + */ + + protected boolean isObserving() + { + return observing; + } + + /** + * Override this method to implement updating of GUI after each MIPS instruction is executed, while running in + * "timed" mode (user specifies execution speed on the slider control). Does nothing by default. + */ + protected void updateDisplay() + { + } + + /** + * Override this method to provide a JComponent (probably a JButton) of your choice to be placed just left of the + * Close/Exit button. Its anticipated use is for a "help" button that launches a help message or dialog. But it + * can be any valid JComponent that doesn't mind co-existing among a bunch of JButtons. + */ + protected JComponent getHelpComponent() + { + return null; + } + + ////////////////////////////////////////////////////////////////////////////////// + //////////////////// PRIVATE HELPER METHODS ////////////////////////////////// + ////////////////////////////////////////////////////////////////////////////////// + + // Closing duties for MarsTool only. + private void performToolClosingDuties() + { + performSpecialClosingDuties(); + if (connectButton.isConnected()) + { connectButton.disconnect(); - } - dialog.setVisible(false); - dialog.dispose(); - } - - // Closing duties for stand-alone application only. - private void performAppClosingDuties() { - performSpecialClosingDuties(); - thisMarsApp.setVisible(false); - System.exit(0); - } - - - ////////////////////////////////////////////////////////////////////////////////// - //////////////////// PRIVATE HELPER CLASSES ////////////////////////////////// - // Specialized inner classes. Either used by stand-alone (JFrame-based) only // - // or used by MarsTool (JDialog-based) only. // - ////////////////////////////////////////////////////////////////////////////////// - - ////////////////////////////////////////////////////////////////////// - // Little class for this dual-purpose button. It is used only by the MarsTool - // (not by the stand-alone app). - protected class ConnectButton extends JButton { - private static final String connectText = "Connect to MIPS"; - private static final String disconnectText = "Disconnect from MIPS"; - - public ConnectButton() { + } + dialog.setVisible(false); + dialog.dispose(); + } + + // Closing duties for stand-alone application only. + private void performAppClosingDuties() + { + performSpecialClosingDuties(); + thisMarsApp.setVisible(false); + System.exit(0); + } + + + ////////////////////////////////////////////////////////////////////////////////// + //////////////////// PRIVATE HELPER CLASSES ////////////////////////////////// + // Specialized inner classes. Either used by stand-alone (JFrame-based) only // + // or used by MarsTool (JDialog-based) only. // + ////////////////////////////////////////////////////////////////////////////////// + + ////////////////////////////////////////////////////////////////////// + // Little class for this dual-purpose button. It is used only by the MarsTool + // (not by the stand-alone app). + protected class ConnectButton extends JButton + { + private static final String connectText = "Connect to MIPS"; + + private static final String disconnectText = "Disconnect from MIPS"; + + public ConnectButton() + { super(); disconnect(); - } - - public void connect() { + } + + public void connect() + { observing = true; - synchronized (Globals.memoryAndRegistersLock) {// DPS 23 July 2008 - addAsObserver(); + synchronized (Globals.memoryAndRegistersLock) + {// DPS 23 July 2008 + addAsObserver(); } setText(disconnectText); - } - - public void disconnect() { - synchronized (Globals.memoryAndRegistersLock) {// DPS 23 July 2008 - deleteAsObserver(); + } + + public void disconnect() + { + synchronized (Globals.memoryAndRegistersLock) + {// DPS 23 July 2008 + deleteAsObserver(); } observing = false; setText(connectText); - } - - public boolean isConnected() { + } + + public boolean isConnected() + { return observing; - } - } - - - /////////////////////////////////////////////////////////////////////// - // Every control button will get one of these so when it has focus - // the Enter key can be used instead of a mouse click to perform - // its associated action. It will do nothing if no action listeners - // are attached to the button at the time of the call. Otherwise, - // it will call actionPerformed for the first action listener in the - // button's list. - protected class EnterKeyListener extends KeyAdapter { - AbstractButton myButton; - public EnterKeyListener(AbstractButton who) { + } + } + + + /////////////////////////////////////////////////////////////////////// + // Every control button will get one of these so when it has focus + // the Enter key can be used instead of a mouse click to perform + // its associated action. It will do nothing if no action listeners + // are attached to the button at the time of the call. Otherwise, + // it will call actionPerformed for the first action listener in the + // button's list. + protected class EnterKeyListener extends KeyAdapter + { + AbstractButton myButton; + + public EnterKeyListener(AbstractButton who) + { myButton = who; - } - public void keyPressed(KeyEvent e) { - if (e.getKeyChar()== KeyEvent.VK_ENTER) { - e.consume(); - try { - myButton.getActionListeners()[0].actionPerformed(new ActionEvent(myButton, 0, myButton.getText())); - } - catch (ArrayIndexOutOfBoundsException oob) { - // do nothing, since there is no action listener. - } + } + + public void keyPressed(KeyEvent e) + { + if (e.getKeyChar() == KeyEvent.VK_ENTER) + { + e.consume(); + try + { + myButton.getActionListeners()[0].actionPerformed(new ActionEvent(myButton, 0, myButton.getText())); + } + catch (ArrayIndexOutOfBoundsException oob) + { + // do nothing, since there is no action listener. + } } - } - } - - ///////////////////////////////////////////////////////////////////////////////// - // called when the Assemble and Run button is pressed. Used only by stand-alone app. - private class CreateAssembleRunMIPSprogram implements Runnable { - public void run() { + } + } + + ///////////////////////////////////////////////////////////////////////////////// + // called when the Assemble and Run button is pressed. Used only by stand-alone app. + private class CreateAssembleRunMIPSprogram implements Runnable + { + public void run() + { String noSupportForExceptionHandler = null; // no auto-loaded exception handlers. - // boolean extendedAssemblerEnabled = true; // In this context, no reason to constrain. - // boolean warningsAreErrors = false; // Ditto. - + // boolean extendedAssemblerEnabled = true; // In this context, no reason to constrain. + // boolean warningsAreErrors = false; // Ditto. + String exceptionHandler = null; if (Globals.getSettings().getExceptionHandlerEnabled() && - Globals.getSettings().getExceptionHandler() != null && - Globals.getSettings().getExceptionHandler().length() > 0) { - exceptionHandler = Globals.getSettings().getExceptionHandler(); + Globals.getSettings().getExceptionHandler() != null && + Globals.getSettings().getExceptionHandler().length() > 0) + { + exceptionHandler = Globals.getSettings().getExceptionHandler(); } - - Thread.currentThread().setPriority(Thread.NORM_PRIORITY-1); + + Thread.currentThread().setPriority(Thread.NORM_PRIORITY - 1); Thread.yield(); MIPSprogram program = new MIPSprogram(); mars.Globals.program = program; // Shouldn't have to do this... String fileToAssemble = mostRecentlyOpenedFile.getPath(); ArrayList filesToAssemble = null; - if (multiFileAssemble) {// setting (check box in file open dialog) calls for multiple file assembly - filesToAssemble = FilenameFinder.getFilenameList( - new File(fileToAssemble).getParent(), Globals.fileExtensions); - } - else { - filesToAssemble = new ArrayList(); - filesToAssemble.add(fileToAssemble); + if (multiFileAssemble) + {// setting (check box in file open dialog) calls for multiple file assembly + filesToAssemble = FilenameFinder.getFilenameList( + new File(fileToAssemble).getParent(), Globals.fileExtensions); + } + else + { + filesToAssemble = new ArrayList(); + filesToAssemble.add(fileToAssemble); } ArrayList programsToAssemble = null; - try { - operationStatusMessages.displayNonTerminatingMessage("Assembling "+fileToAssemble); - programsToAssemble = program.prepareFilesForAssembly(filesToAssemble, fileToAssemble, exceptionHandler); - } - catch (mars.ProcessingException pe) { - operationStatusMessages.displayTerminatingMessage("Error reading file(s): "+fileToAssemble); - return; - } - - try { - program.assemble(programsToAssemble, Globals.getSettings().getExtendedAssemblerEnabled(), Globals.getSettings().getWarningsAreErrors()); + try + { + operationStatusMessages.displayNonTerminatingMessage("Assembling " + fileToAssemble); + programsToAssemble = program.prepareFilesForAssembly(filesToAssemble, fileToAssemble, exceptionHandler); } - catch (mars.ProcessingException pe) { - operationStatusMessages.displayTerminatingMessage("Assembly Error: "+fileToAssemble); - return; - } - // Moved these three register resets from before the try block to after it. 17-Dec-09 DPS. + catch (mars.ProcessingException pe) + { + operationStatusMessages.displayTerminatingMessage("Error reading file(s): " + fileToAssemble); + return; + } + + try + { + program.assemble(programsToAssemble, Globals.getSettings().getExtendedAssemblerEnabled(), Globals.getSettings().getWarningsAreErrors()); + } + catch (mars.ProcessingException pe) + { + operationStatusMessages.displayTerminatingMessage("Assembly Error: " + fileToAssemble); + return; + } + // Moved these three register resets from before the try block to after it. 17-Dec-09 DPS. RegisterFile.resetRegisters(); Coprocessor1.resetRegisters(); Coprocessor0.resetRegisters(); - + addAsObserver(); observing = true; String terminatingMessage = "Normal termination: "; - try { - operationStatusMessages.displayNonTerminatingMessage("Running "+fileToAssemble); - program.simulate(-1); // unlimited steps + try + { + operationStatusMessages.displayNonTerminatingMessage("Running " + fileToAssemble); + program.simulate(-1); // unlimited steps } - catch (NullPointerException npe) { - // This will occur if program execution is interrupted by Stop button. - terminatingMessage = "User interrupt: "; - } - catch (mars.ProcessingException pe) { - terminatingMessage = "Runtime error: "; - } - finally { - deleteAsObserver(); - observing = false; - operationStatusMessages.displayTerminatingMessage(terminatingMessage+fileToAssemble); - } - return; - } - } - - - ////////////////////////////////////////////////////////////////////////// - // Class for text message field used to update operation status when - // assembling and running MIPS programs. - private class MessageField extends JTextField { - - public MessageField(String text) { + catch (NullPointerException npe) + { + // This will occur if program execution is interrupted by Stop button. + terminatingMessage = "User interrupt: "; + } + catch (mars.ProcessingException pe) + { + terminatingMessage = "Runtime error: "; + } + finally + { + deleteAsObserver(); + observing = false; + operationStatusMessages.displayTerminatingMessage(terminatingMessage + fileToAssemble); + } + } + } + + + ////////////////////////////////////////////////////////////////////////// + // Class for text message field used to update operation status when + // assembling and running MIPS programs. + private class MessageField extends JTextField + { + + public MessageField(String text) + { super(text); - } - - private void displayTerminatingMessage(String text) { + } + + private void displayTerminatingMessage(String text) + { displayMessage(text, true); - } - - private void displayNonTerminatingMessage(String text) { + } + + private void displayNonTerminatingMessage(String text) + { displayMessage(text, false); - } - - private void displayMessage(String text, boolean terminating) { - SwingUtilities.invokeLater(new MessageWriter(text, terminating)); - } - - ///////////////////////////////////////////////////////////////////////////////// - // Little inner-inner class to display processing error message on AWT thread. - // Used only by stand-alone app. - private class MessageWriter implements Runnable { - private String text; - private boolean terminatingMessage; - public MessageWriter(String text, boolean terminating) { - this.text = text; - this.terminatingMessage = terminating; + } + + private void displayMessage(String text, boolean terminating) + { + SwingUtilities.invokeLater(new MessageWriter(text, terminating)); + } + + ///////////////////////////////////////////////////////////////////////////////// + // Little inner-inner class to display processing error message on AWT thread. + // Used only by stand-alone app. + private class MessageWriter implements Runnable + { + private final String text; + + private final boolean terminatingMessage; + + public MessageWriter(String text, boolean terminating) + { + this.text = text; + this.terminatingMessage = terminating; } - public void run() { - if (text!=null) { - operationStatusMessages.setText(text); - operationStatusMessages.setCaretPosition(0); - } - if (terminatingMessage) { - assembleRunButton.setEnabled(true); - openFileButton.setEnabled(true); - stopButton.setEnabled(false); - } + + public void run() + { + if (text != null) + { + operationStatusMessages.setText(text); + operationStatusMessages.setCaretPosition(0); + } + if (terminatingMessage) + { + assembleRunButton.setEnabled(true); + openFileButton.setEnabled(true); + stopButton.setEnabled(false); + } } - } - } - - ////////////////////////////////////////////////////////////////////// - // For scheduling GUI update on timed runs...used only by stand-alone app. - private class GUIUpdater implements Runnable { - public void run() { + } + } + + ////////////////////////////////////////////////////////////////////// + // For scheduling GUI update on timed runs...used only by stand-alone app. + private class GUIUpdater implements Runnable + { + public void run() + { updateDisplay(); - } - } - - } \ No newline at end of file + } + } + +} diff --git a/src/main/java/mars/tools/BHTEntry.java b/src/main/java/mars/tools/BHTEntry.java index 36401b1..5098429 100644 --- a/src/main/java/mars/tools/BHTEntry.java +++ b/src/main/java/mars/tools/BHTEntry.java @@ -30,158 +30,182 @@ package mars.tools;//.bhtsim; /** * Represents a single entry of the Branch History Table. *

- * The entry holds the information about former branch predictions and outcomes. - * The number of past branch outcomes can be configured and is called the history. - * The semantics of the history of size n is as follows. - * The entry will change its prediction, if it mispredicts the branch n times in series. - * The prediction of the entry can be obtained by the {@link BHTEntry#getPrediction()} method. - * Feedback of taken or not taken branches is provided to the entry via the {@link BHTEntry#updatePrediction(boolean)} method. - * This causes the history and the prediction to be updated. + * The entry holds the information about former branch predictions and outcomes. The number of past branch outcomes can + * be configured and is called the history. The semantics of the history of size n is as follows. The entry will + * change its prediction, if it mispredicts the branch n times in series. The prediction of the entry can be + * obtained by the {@link BHTEntry#getPrediction()} method. Feedback of taken or not taken branches is provided to the + * entry via the {@link BHTEntry#updatePrediction(boolean)} method. This causes the history and the prediction to be + * updated. *

- * Additionally the entry keeps track about how many times the prediction was correct or incorrect. - * The statistics can be obtained by the methods {@link BHTEntry#getStatsPredCorrect()}, {@link BHTEntry#getStatsPredIncorrect()} and {@link BHTEntry#getStatsPredPrecision()}. - * + * Additionally the entry keeps track about how many times the prediction was correct or incorrect. The statistics can + * be obtained by the methods {@link BHTEntry#getStatsPredCorrect()}, {@link BHTEntry#getStatsPredIncorrect()} and + * {@link BHTEntry#getStatsPredPrecision()}. + * * @author ingo.kofler@itec.uni-klu.ac.at */ -public class BHTEntry { - - /** the history of the BHT entry. Each boolean value signals if the branch was taken or not. The value at index n-1 represents the most recent branch outcome. */ - private boolean m_history[]; +public class BHTEntry +{ - /** the current prediction */ - private boolean m_prediction; - - /** absolute number of incorrect predictions */ - private int m_incorrect; - - /** absolute number of correct predictions */ - private int m_correct; - - - /** - * Constructs a BHT entry with a given history size. - * - * The size of the history can only be set via the constructor and cannot be changed afterwards. - * - * @param historySize number of past branch outcomes to remember - * @param initVal the initial value of the entry (take or do not take) - */ - public BHTEntry(int historySize, boolean initVal) { - m_prediction = initVal; - m_history = new boolean[historySize]; - - for (int i=0; i < historySize; i++) { - m_history[i] = initVal; - } - m_correct = m_incorrect = 0; - } - - - /** - * Returns the branch prediction based on the history. - * - * @return true if prediction is to take the branch, false otherwise - */ - public boolean getPrediction() { - return m_prediction; - } - - - /** - * Updates the entry's history and prediction. - * This method provides feedback for a prediction. - * The history and the statistics are updated accordingly. - * Based on the updated history a new prediction is calculated - * - * @param branchTaken signals if the branch was taken (true) or not (false) - */ - public void updatePrediction(boolean branchTaken) { - - // update history - for (int i=0; i < m_history.length-1; i++) { - m_history[i] = m_history[i+1]; - } - m_history[m_history.length-1] = branchTaken; - - - // if the prediction was correct, update stats and keep prediction - if (branchTaken == m_prediction) { - m_correct ++; - } - else { - m_incorrect ++; - - // check if the prediction should change - boolean changePrediction = true; - - for (int i=0; i < m_history.length; i++) { - if (m_history[i] != branchTaken) - changePrediction = false; - } - - if (changePrediction) - m_prediction = !m_prediction; - - } - } - - - /** - * Get the absolute number of mispredictions. - * - * @return number of incorrect predictions (mispredictions) - */ - public int getStatsPredIncorrect() { - return m_incorrect; - } - - - /** - * Get the absolute number of correct predictions. - * - * @return number of correct predictions - */ - public int getStatsPredCorrect() { - return m_correct; - } - - - /** - * Get the percentage of correct predictions. - * - * @return the percentage of correct predictions - */ - public double getStatsPredPrecision() { - int sum = m_incorrect + m_correct; - return (sum==0) ? 0 : m_correct * 100.0 / sum; - } - - - /*** - * Builds a string representation of the BHT entry's history. - * The history is a sequence of flags that signal if the branch was taken (T) or not taken (NT). - * - * @return a string representation of the BHT entry's history - */ - public String getHistoryAsStr() { - String result = ""; - - for (int i=0; i0) result = result + ", "; - result += m_history[i] ? "T" : "NT"; - } - return result; - } - - - /*** - * Returns a string representation of the BHT entry's current prediction. - * The prediction can be either to TAKE or do NOT TAKE the branch. - * - * @return a string representation of the BHT entry's current prediction - */ - public String getPredictionAsStr() { - return m_prediction ? "TAKE" : "NOT TAKE"; - } + /** + * the history of the BHT entry. Each boolean value signals if the branch was taken or not. The value at index n-1 + * represents the most recent branch outcome. + */ + private final boolean[] m_history; + + /** the current prediction */ + private boolean m_prediction; + + /** absolute number of incorrect predictions */ + private int m_incorrect; + + /** absolute number of correct predictions */ + private int m_correct; + + + /** + * Constructs a BHT entry with a given history size. + *

+ * The size of the history can only be set via the constructor and cannot be changed afterwards. + * + * @param historySize number of past branch outcomes to remember + * @param initVal the initial value of the entry (take or do not take) + */ + public BHTEntry(int historySize, boolean initVal) + { + m_prediction = initVal; + m_history = new boolean[historySize]; + + for (int i = 0; i < historySize; i++) + { + m_history[i] = initVal; + } + m_correct = m_incorrect = 0; + } + + + /** + * Returns the branch prediction based on the history. + * + * @return true if prediction is to take the branch, false otherwise + */ + public boolean getPrediction() + { + return m_prediction; + } + + + /** + * Updates the entry's history and prediction. This method provides feedback for a prediction. The history and the + * statistics are updated accordingly. Based on the updated history a new prediction is calculated + * + * @param branchTaken signals if the branch was taken (true) or not (false) + */ + public void updatePrediction(boolean branchTaken) + { + + // update history + for (int i = 0; i < m_history.length - 1; i++) + { + m_history[i] = m_history[i + 1]; + } + m_history[m_history.length - 1] = branchTaken; + + + // if the prediction was correct, update stats and keep prediction + if (branchTaken == m_prediction) + { + m_correct++; + } + else + { + m_incorrect++; + + // check if the prediction should change + boolean changePrediction = true; + + for (int i = 0; i < m_history.length; i++) + { + if (m_history[i] != branchTaken) + { + changePrediction = false; + break; + } + } + + if (changePrediction) + { + m_prediction = !m_prediction; + } + + } + } + + + /** + * Get the absolute number of mispredictions. + * + * @return number of incorrect predictions (mispredictions) + */ + public int getStatsPredIncorrect() + { + return m_incorrect; + } + + + /** + * Get the absolute number of correct predictions. + * + * @return number of correct predictions + */ + public int getStatsPredCorrect() + { + return m_correct; + } + + + /** + * Get the percentage of correct predictions. + * + * @return the percentage of correct predictions + */ + public double getStatsPredPrecision() + { + int sum = m_incorrect + m_correct; + return (sum == 0) ? 0 : m_correct * 100.0 / sum; + } + + + /*** + * Builds a string representation of the BHT entry's history. + * The history is a sequence of flags that signal if the branch was taken (T) or not taken (NT). + * + * @return a string representation of the BHT entry's history + */ + public String getHistoryAsStr() + { + String result = ""; + + for (int i = 0; i < m_history.length; i++) + { + if (i > 0) + { + result = result + ", "; + } + result += m_history[i] ? "T" : "NT"; + } + return result; + } + + + /*** + * Returns a string representation of the BHT entry's current prediction. + * The prediction can be either to TAKE or do NOT TAKE the branch. + * + * @return a string representation of the BHT entry's current prediction + */ + public String getPredictionAsStr() + { + return m_prediction ? "TAKE" : "NOT TAKE"; + } } diff --git a/src/main/java/mars/tools/BHTSimGUI.java b/src/main/java/mars/tools/BHTSimGUI.java index 5b0e4aa..0cf3f61 100644 --- a/src/main/java/mars/tools/BHTSimGUI.java +++ b/src/main/java/mars/tools/BHTSimGUI.java @@ -27,29 +27,16 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package mars.tools;//.bhtsim; -import java.awt.BorderLayout; -import java.awt.Color; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; +import javax.swing.*; +import javax.swing.table.DefaultTableCellRenderer; +import java.awt.*; import java.text.DecimalFormat; import java.util.Vector; -import javax.swing.JComboBox; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.ListSelectionModel; -import javax.swing.SwingConstants; -import javax.swing.table.DefaultTableCellRenderer; - /** * Represents the GUI of the BHT Simulator Tool. *

- * + *

* The GUI consists of mainly four parts: *

    *
  • A configuration panel to select the number of entries and the history size @@ -57,289 +44,304 @@ import javax.swing.table.DefaultTableCellRenderer; *
  • A table representing the BHT with all entries and their internal state and statistics *
  • A log panel that summarizes the predictions in a textual form *
- * + * * @author ingo.kofler@itec.uni-klu.ac.at */ //@SuppressWarnings("serial") -public class BHTSimGUI extends JPanel { - - /** text field presenting the most recent branch instruction */ - private JTextField m_tfInstruction; - - /** text field representing the address of the most recent branch instruction */ - private JTextField m_tfAddress; - - /** text field representing the resulting BHT index of the branch instruction */ - private JTextField m_tfIndex; +public class BHTSimGUI extends JPanel +{ - /** combo box for selecting the number of BHT entries */ - private JComboBox m_cbBHTentries; - - /** combo box for selecting the history size */ - private JComboBox m_cbBHThistory; - - /** combo box for selecting the initial value */ - private JComboBox m_cbBHTinitVal; - - /** the table representing the BHT */ - private JTable m_tabBHT; - - /** text field for log output */ - private JTextArea m_taLog; - - /** constant for the color that highlights the current BHT entry */ - public final static Color COLOR_PREPREDICTION = Color.yellow; - - /** constant for the color to signal a correct prediction */ - public final static Color COLOR_PREDICTION_CORRECT = Color.green; - - /** constant for the color to signal a misprediction */ - public final static Color COLOR_PREDICTION_INCORRECT = Color.red; - - /** constant for the String representing "take the branch" */ - public final static String BHT_TAKE_BRANCH = "TAKE"; - - /** constant for the String representing "do not take the branch" */ - public final static String BHT_DO_NOT_TAKE_BRANCH = "NOT TAKE"; - - - - /** - * Creates the GUI components of the BHT Simulator - * The GUI is a subclass of JPanel which is integrated in the GUI of the MARS tool - */ - public BHTSimGUI() { - BorderLayout layout = new BorderLayout(); - layout.setVgap(10); - layout.setHgap(10); - setLayout(layout); - - m_tabBHT = createAndInitTable(); - - add(buildConfigPanel(), BorderLayout.NORTH); - add(buildInfoPanel(), BorderLayout.WEST); - add(new JScrollPane(m_tabBHT), BorderLayout.CENTER); - add(buildLogPanel(), BorderLayout.SOUTH); - } - - /** - * Creates and initializes the JTable representing the BHT. - * - * @return the JTable representing the BHT - */ - private JTable createAndInitTable() { - // create the table - JTable theTable = new JTable(); - - // create a default renderer for double values (percentage) - DefaultTableCellRenderer doubleRenderer = new DefaultTableCellRenderer() { - private DecimalFormat formatter = new DecimalFormat("##0.00"); - - public void setValue(Object value) { - setText((value == null) ? "" : formatter.format(value)); - } - }; - doubleRenderer.setHorizontalAlignment(SwingConstants.CENTER); - - // create a default renderer for all other values with center alignment - DefaultTableCellRenderer defRenderer = new DefaultTableCellRenderer(); - defRenderer.setHorizontalAlignment(SwingConstants.CENTER); - - theTable.setDefaultRenderer(Double.class, doubleRenderer); - theTable.setDefaultRenderer(Integer.class, defRenderer); - theTable.setDefaultRenderer(String.class, defRenderer); - - theTable.setSelectionBackground(BHTSimGUI.COLOR_PREPREDICTION); - theTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); - - return theTable; + /** constant for the color that highlights the current BHT entry */ + public final static Color COLOR_PREPREDICTION = Color.yellow; - } - - - /** - * Creates and initializes the panel holding the instruction, address and index text fields. - * - * @return the info panel - */ - private JPanel buildInfoPanel() { - m_tfInstruction = new JTextField(); - m_tfAddress = new JTextField(); - m_tfIndex = new JTextField(); - - m_tfInstruction.setColumns(10); - m_tfInstruction.setEditable(false); - m_tfInstruction.setHorizontalAlignment(JTextField.CENTER); - m_tfAddress.setColumns(10); - m_tfAddress.setEditable(false); - m_tfAddress.setHorizontalAlignment(JTextField.CENTER); - m_tfIndex.setColumns(10); - m_tfIndex.setEditable(false); - m_tfIndex.setHorizontalAlignment(JTextField.CENTER); - - JPanel panel = new JPanel(); - JPanel outerPanel = new JPanel(); - outerPanel.setLayout(new BorderLayout()); - - GridBagLayout gbl = new GridBagLayout(); - panel.setLayout(gbl); - - GridBagConstraints c = new GridBagConstraints(); - - c.insets = new Insets(5, 5, 2, 5); - c.gridx = 1; - c.gridy = 1; - - panel.add(new JLabel("Instruction"), c); - c.gridy++; - panel.add(m_tfInstruction, c); - c.gridy++; - panel.add(new JLabel("@ Address"), c); - c.gridy++; - panel.add(m_tfAddress, c); - c.gridy++; - panel.add(new JLabel("-> Index"), c); - c.gridy++; - panel.add(m_tfIndex, c); - - outerPanel.add(panel, BorderLayout.NORTH); - return outerPanel; - } - - - /** - * Creates and initializes the panel for the configuration of the tool - * The panel contains two combo boxes for selecting the number of BHT entries and the history size. - * - * @return a panel for the configuration - */ - private JPanel buildConfigPanel() { - JPanel panel = new JPanel(); - - Vector sizes = new Vector(); - sizes.add(new Integer(8)); - sizes.add(new Integer(16)); - sizes.add(new Integer(32)); - - Vector bits = new Vector(); - bits.add(new Integer(1)); - bits.add(new Integer(2)); - - Vector initVals = new Vector(); - initVals.add(BHTSimGUI.BHT_DO_NOT_TAKE_BRANCH); - initVals.add(BHTSimGUI.BHT_TAKE_BRANCH); - - m_cbBHTentries = new JComboBox(sizes); - m_cbBHThistory = new JComboBox(bits); - m_cbBHTinitVal = new JComboBox(initVals); - - panel.add(new JLabel("# of BHT entries")); - panel.add(m_cbBHTentries); - panel.add(new JLabel("BHT history size")); - panel.add(m_cbBHThistory); - panel.add(new JLabel("Initial value")); - panel.add(m_cbBHTinitVal); - - return panel; - } - - - /** - * Creates and initializes the panel containing the log text area. - * - * @return the panel for the logging output - */ - private JPanel buildLogPanel() { - JPanel panel = new JPanel(); - panel.setLayout(new BorderLayout()); - m_taLog = new JTextArea(); - m_taLog.setRows(6); - m_taLog.setEditable(false); + /** constant for the color to signal a correct prediction */ + public final static Color COLOR_PREDICTION_CORRECT = Color.green; - panel.add(new JLabel("Log"), BorderLayout.NORTH); - panel.add(new JScrollPane(m_taLog), BorderLayout.CENTER); - - return panel; - } - + /** constant for the color to signal a misprediction */ + public final static Color COLOR_PREDICTION_INCORRECT = Color.red; - /*** - * Returns the combo box for selecting the number of BHT entries. - * - * @return the reference to the combo box - */ - public JComboBox getCbBHTentries() { - return m_cbBHTentries; - } - - - /*** - * Returns the combo box for selecting the size of the BHT history. - * - * @return the reference to the combo box - */ - public JComboBox getCbBHThistory() { - return m_cbBHThistory; - } + /** constant for the String representing "take the branch" */ + public final static String BHT_TAKE_BRANCH = "TAKE"; - - /*** - * Returns the combo box for selecting the initial value of the BHT - * - * @return the reference to the combo box - */ - public JComboBox getCbBHTinitVal() { - return m_cbBHTinitVal; - } - - /*** - * Returns the table representing the BHT. - * - * @return the reference to the table - */ - public JTable getTabBHT() { - return m_tabBHT; - } + /** constant for the String representing "do not take the branch" */ + public final static String BHT_DO_NOT_TAKE_BRANCH = "NOT TAKE"; - - /*** - * Returns the text area for log purposes. - * - * @return the reference to the text area - */ - public JTextArea getTaLog() { - return m_taLog; - } - - - /*** - * Returns the text field for displaying the most recent branch instruction - * - * @return the reference to the text field - */ - public JTextField getTfInstruction() { - return m_tfInstruction; - } - - - /*** - * Returns the text field for displaying the address of the most recent branch instruction - * - * @return the reference to the text field - */ - public JTextField getTfAddress() { - return m_tfAddress; - } - - - /*** - * Returns the text field for displaying the corresponding index into the BHT - * - * @return the reference to the text field - */ - public JTextField getTfIndex() { - return m_tfIndex; - } + /** text field presenting the most recent branch instruction */ + private JTextField m_tfInstruction; + + /** text field representing the address of the most recent branch instruction */ + private JTextField m_tfAddress; + + /** text field representing the resulting BHT index of the branch instruction */ + private JTextField m_tfIndex; + + /** combo box for selecting the number of BHT entries */ + private JComboBox m_cbBHTentries; + + /** combo box for selecting the history size */ + private JComboBox m_cbBHThistory; + + /** combo box for selecting the initial value */ + private JComboBox m_cbBHTinitVal; + + /** the table representing the BHT */ + private final JTable m_tabBHT; + + /** text field for log output */ + private JTextArea m_taLog; + + + /** + * Creates the GUI components of the BHT Simulator The GUI is a subclass of JPanel which is integrated in the GUI of + * the MARS tool + */ + public BHTSimGUI() + { + BorderLayout layout = new BorderLayout(); + layout.setVgap(10); + layout.setHgap(10); + setLayout(layout); + + m_tabBHT = createAndInitTable(); + + add(buildConfigPanel(), BorderLayout.NORTH); + add(buildInfoPanel(), BorderLayout.WEST); + add(new JScrollPane(m_tabBHT), BorderLayout.CENTER); + add(buildLogPanel(), BorderLayout.SOUTH); + } + + /** + * Creates and initializes the JTable representing the BHT. + * + * @return the JTable representing the BHT + */ + private JTable createAndInitTable() + { + // create the table + JTable theTable = new JTable(); + + // create a default renderer for double values (percentage) + DefaultTableCellRenderer doubleRenderer = new DefaultTableCellRenderer() + { + private final DecimalFormat formatter = new DecimalFormat("##0.00"); + + public void setValue(Object value) + { + setText((value == null) ? "" : formatter.format(value)); + } + }; + doubleRenderer.setHorizontalAlignment(SwingConstants.CENTER); + + // create a default renderer for all other values with center alignment + DefaultTableCellRenderer defRenderer = new DefaultTableCellRenderer(); + defRenderer.setHorizontalAlignment(SwingConstants.CENTER); + + theTable.setDefaultRenderer(Double.class, doubleRenderer); + theTable.setDefaultRenderer(Integer.class, defRenderer); + theTable.setDefaultRenderer(String.class, defRenderer); + + theTable.setSelectionBackground(BHTSimGUI.COLOR_PREPREDICTION); + theTable.setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION); + + return theTable; + + } + + + /** + * Creates and initializes the panel holding the instruction, address and index text fields. + * + * @return the info panel + */ + private JPanel buildInfoPanel() + { + m_tfInstruction = new JTextField(); + m_tfAddress = new JTextField(); + m_tfIndex = new JTextField(); + + m_tfInstruction.setColumns(10); + m_tfInstruction.setEditable(false); + m_tfInstruction.setHorizontalAlignment(JTextField.CENTER); + m_tfAddress.setColumns(10); + m_tfAddress.setEditable(false); + m_tfAddress.setHorizontalAlignment(JTextField.CENTER); + m_tfIndex.setColumns(10); + m_tfIndex.setEditable(false); + m_tfIndex.setHorizontalAlignment(JTextField.CENTER); + + JPanel panel = new JPanel(); + JPanel outerPanel = new JPanel(); + outerPanel.setLayout(new BorderLayout()); + + GridBagLayout gbl = new GridBagLayout(); + panel.setLayout(gbl); + + GridBagConstraints c = new GridBagConstraints(); + + c.insets = new Insets(5, 5, 2, 5); + c.gridx = 1; + c.gridy = 1; + + panel.add(new JLabel("Instruction"), c); + c.gridy++; + panel.add(m_tfInstruction, c); + c.gridy++; + panel.add(new JLabel("@ Address"), c); + c.gridy++; + panel.add(m_tfAddress, c); + c.gridy++; + panel.add(new JLabel("-> Index"), c); + c.gridy++; + panel.add(m_tfIndex, c); + + outerPanel.add(panel, BorderLayout.NORTH); + return outerPanel; + } + + + /** + * Creates and initializes the panel for the configuration of the tool The panel contains two combo boxes for + * selecting the number of BHT entries and the history size. + * + * @return a panel for the configuration + */ + private JPanel buildConfigPanel() + { + JPanel panel = new JPanel(); + + Vector sizes = new Vector(); + sizes.add(Integer.valueOf(8)); + sizes.add(Integer.valueOf(16)); + sizes.add(Integer.valueOf(32)); + + Vector bits = new Vector(); + bits.add(Integer.valueOf(1)); + bits.add(Integer.valueOf(2)); + + Vector initVals = new Vector(); + initVals.add(BHTSimGUI.BHT_DO_NOT_TAKE_BRANCH); + initVals.add(BHTSimGUI.BHT_TAKE_BRANCH); + + m_cbBHTentries = new JComboBox(sizes); + m_cbBHThistory = new JComboBox(bits); + m_cbBHTinitVal = new JComboBox(initVals); + + panel.add(new JLabel("# of BHT entries")); + panel.add(m_cbBHTentries); + panel.add(new JLabel("BHT history size")); + panel.add(m_cbBHThistory); + panel.add(new JLabel("Initial value")); + panel.add(m_cbBHTinitVal); + + return panel; + } + + + /** + * Creates and initializes the panel containing the log text area. + * + * @return the panel for the logging output + */ + private JPanel buildLogPanel() + { + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + m_taLog = new JTextArea(); + m_taLog.setRows(6); + m_taLog.setEditable(false); + + panel.add(new JLabel("Log"), BorderLayout.NORTH); + panel.add(new JScrollPane(m_taLog), BorderLayout.CENTER); + + return panel; + } + + + /*** + * Returns the combo box for selecting the number of BHT entries. + * + * @return the reference to the combo box + */ + public JComboBox getCbBHTentries() + { + return m_cbBHTentries; + } + + + /*** + * Returns the combo box for selecting the size of the BHT history. + * + * @return the reference to the combo box + */ + public JComboBox getCbBHThistory() + { + return m_cbBHThistory; + } + + + /*** + * Returns the combo box for selecting the initial value of the BHT + * + * @return the reference to the combo box + */ + public JComboBox getCbBHTinitVal() + { + return m_cbBHTinitVal; + } + + /*** + * Returns the table representing the BHT. + * + * @return the reference to the table + */ + public JTable getTabBHT() + { + return m_tabBHT; + } + + + /*** + * Returns the text area for log purposes. + * + * @return the reference to the text area + */ + public JTextArea getTaLog() + { + return m_taLog; + } + + + /*** + * Returns the text field for displaying the most recent branch instruction + * + * @return the reference to the text field + */ + public JTextField getTfInstruction() + { + return m_tfInstruction; + } + + + /*** + * Returns the text field for displaying the address of the most recent branch instruction + * + * @return the reference to the text field + */ + public JTextField getTfAddress() + { + return m_tfAddress; + } + + + /*** + * Returns the text field for displaying the corresponding index into the BHT + * + * @return the reference to the text field + */ + public JTextField getTfIndex() + { + return m_tfIndex; + } } diff --git a/src/main/java/mars/tools/BHTSimulator.java b/src/main/java/mars/tools/BHTSimulator.java index 9130a0f..f702b7a 100644 --- a/src/main/java/mars/tools/BHTSimulator.java +++ b/src/main/java/mars/tools/BHTSimulator.java @@ -27,18 +27,13 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package mars.tools; +import mars.ProgramStatement; +import mars.mips.hardware.*; + +import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Observable; - -import javax.swing.JComponent; - -import mars.ProgramStatement; -import mars.mips.hardware.AccessNotice; -import mars.mips.hardware.AddressErrorException; -import mars.mips.hardware.Memory; -import mars.mips.hardware.MemoryAccessNotice; -import mars.mips.hardware.RegisterFile; //import mars.tools.bhtsim.BHTSimGUI; //import mars.tools.bhtsim.BHTableModel; @@ -46,342 +41,382 @@ import mars.mips.hardware.RegisterFile; /** * A MARS tool for simulating branch prediction with a Branch History Table (BHT) *

- * The simulation is based on observing the access to the instruction memory area (text segment). - * If a branch instruction is encountered, a prediction based on a BHT is performed. - * The outcome of the branch is compared with the prediction and the prediction is updated accordingly. - * Statistics about the correct and incorrect number of predictions can be obtained for each BHT entry. - * The number of entries in the BHT and the history that is considered for each prediction can be configured interactively. - * A change of the configuration however causes a re-initialization of the BHT. + * The simulation is based on observing the access to the instruction memory area (text segment). If a branch + * instruction is encountered, a prediction based on a BHT is performed. The outcome of the branch is compared with the + * prediction and the prediction is updated accordingly. Statistics about the correct and incorrect number of + * predictions can be obtained for each BHT entry. The number of entries in the BHT and the history that is considered + * for each prediction can be configured interactively. A change of the configuration however causes a re-initialization + * of the BHT. *

- * The tool can be used to show how branch prediction works in case of loops and how effective such simple methods are. + * The tool can be used to show how branch prediction works in case of loops and how effective such simple methods are. * In case of nested loops the difference of BHT with 1 or 2 Bit history can be explored and visualized. - * + * * @author ingo.kofler@itec.uni-klu.ac.at */ //@SuppressWarnings("serial") -public class BHTSimulator extends AbstractMarsToolAndApplication implements ActionListener { - - - /** constant for the default size of the BHT */ - public static final int BHT_DEFAULT_SIZE = 16; - - /** constant for the default history size */ - public static final int BHT_DEFAULT_HISTORY = 1; - - /** constant for the default inital value */ - public static final boolean BHT_DEFAULT_INITVAL = false; - - /** the name of the tool */ - public static final String BHT_NAME = "BHT Simulator"; - - /** the version of the tool */ - public static final String BHT_VERSION = "Version 1.0 (Ingo Kofler)"; - - /** the heading of the tool */ - public static final String BHT_HEADING = "Branch History Table Simulator"; - - /** the GUI of the BHT simulator */ - private BHTSimGUI m_gui; - - /** the model of the BHT */ - private BHTableModel m_bhtModel; - - /** state variable that indicates that the last instruction was a branch instruction (if address != 0) or not (address == 0) */ - private int m_pendingBranchInstAddress; - - /** state variable that signals if the last branch was taken */ - private boolean m_lastBranchTaken; - - - /** - * Creates a BHT Simulator with given name and heading. - */ - public BHTSimulator() { - super(BHTSimulator.BHT_NAME+", "+BHTSimulator.BHT_VERSION, BHTSimulator.BHT_HEADING); - } - - - /** - * Adds BHTSimulator as observer of the text segment. - */ - protected void addAsObserver() { - addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); - addAsObserver(RegisterFile.getProgramCounterRegister()); - } - - - /** - * Creates a GUI and initialize the GUI with the default values. - */ - protected JComponent buildMainDisplayArea() { - - m_gui = new BHTSimGUI(); - m_bhtModel = new BHTableModel(BHTSimulator.BHT_DEFAULT_SIZE, BHTSimulator.BHT_DEFAULT_HISTORY, BHT_DEFAULT_INITVAL); - - m_gui.getTabBHT().setModel(m_bhtModel); - m_gui.getCbBHThistory().setSelectedItem(new Integer(BHTSimulator.BHT_DEFAULT_HISTORY)); - m_gui.getCbBHTentries().setSelectedItem(new Integer(BHTSimulator.BHT_DEFAULT_SIZE)); - - m_gui.getCbBHTentries().addActionListener(this); - m_gui.getCbBHThistory().addActionListener(this); - m_gui.getCbBHTinitVal().addActionListener(this); - - return m_gui; - } - - - /** - * Returns the name of the tool. - * @return the tool's name as String - */ - public String getName() { - return BHTSimulator.BHT_NAME; - } +public class BHTSimulator extends AbstractMarsToolAndApplication implements ActionListener +{ - /** - * Performs a reset of the simulator. - * This causes the BHT to be reseted and the log messages to be cleared. - */ - protected void reset() { - resetSimulator(); - } - - - /** - * Handles the actions when selecting another value in one of the two combo boxes. - * Selecting a different BHT size or history causes a reset of the simulator. - */ - public void actionPerformed(ActionEvent event) { - // change of the BHT size or BHT bit configuration - // resets the simulator - if (event.getSource() == m_gui.getCbBHTentries() || event.getSource() == m_gui.getCbBHThistory() || event.getSource() == m_gui.getCbBHTinitVal()) { - resetSimulator(); - } - } - - - /** - * Resets the simulator by clearing the GUI elements and resetting the BHT. - */ - protected void resetSimulator() { - m_gui.getTfInstruction().setText(""); - m_gui.getTfAddress().setText(""); - m_gui.getTfIndex().setText(""); - m_gui.getTaLog().setText(""); - m_bhtModel.initBHT(((Integer)m_gui.getCbBHTentries().getSelectedItem()).intValue(), - ((Integer)m_gui.getCbBHThistory().getSelectedItem()).intValue(), - ((String)m_gui.getCbBHTinitVal().getSelectedItem()).equals(BHTSimGUI.BHT_TAKE_BRANCH)); - - m_pendingBranchInstAddress = 0; - m_lastBranchTaken = false; - } - - - /** - * Handles the execution branch instruction. - * This method is called each time a branch instruction is executed. - * Based on the address of the instruction the corresponding index into the BHT is calculated. - * The prediction is obtained from the BHT at the calculated index and is visualized appropriately. - * - * @param stmt the branch statement that is executed - */ - protected void handlePreBranchInst(ProgramStatement stmt) { - - String strStmt = stmt.getBasicAssemblyStatement(); - int address = stmt.getAddress(); - int idx = m_bhtModel.getIdxForAddress(address); - - // update the GUI - m_gui.getTfInstruction().setText(strStmt); - m_gui.getTfAddress().setText("0x" + Integer.toHexString(address)); - m_gui.getTfIndex().setText("" + idx); - - // mark the affected BHT row - m_gui.getTabBHT().setSelectionBackground(BHTSimGUI.COLOR_PREPREDICTION); - m_gui.getTabBHT().addRowSelectionInterval(idx, idx); - - // add output to log - m_gui.getTaLog().append("instruction " + strStmt + " at address 0x" + Integer.toHexString(address) + ", maps to index " + idx + "\n"); - m_gui.getTaLog().append("branches to address 0x" + BHTSimulator.extractBranchAddress(stmt) + "\n"); - m_gui.getTaLog().append("prediction is: " + (m_bhtModel.getPredictionAtIdx(idx) ? "take" : "do not take") + "...\n"); - m_gui.getTaLog().setCaretPosition(m_gui.getTaLog().getDocument().getLength()); - - } - - - /** - * Handles the execution of the branch instruction. - * The correctness of the prediction is visualized in both the table and the log message area. - * The BHT is updated based on the information if the branch instruction was taken or not. - * - * @param branchInstAddr the address of the branch instruction - * @param branchTaken the information if the branch is taken or not (determined in a step before) - */ - protected void handleExecBranchInst(int branchInstAddr, boolean branchTaken) { - - // determine the index in the BHT for the branch instruction - int idx = m_bhtModel.getIdxForAddress(branchInstAddr); - - // check if the prediction is correct - boolean correctPrediction = m_bhtModel.getPredictionAtIdx(idx) == branchTaken; - - m_gui.getTabBHT().setSelectionBackground(correctPrediction ? BHTSimGUI.COLOR_PREDICTION_CORRECT: BHTSimGUI.COLOR_PREDICTION_INCORRECT); - - // add some output at the log - m_gui.getTaLog().append("branch " + (branchTaken ? "taken" : "not taken") + ", prediction was " + ( correctPrediction ? "correct" : "incorrect") + "\n\n"); - m_gui.getTaLog().setCaretPosition(m_gui.getTaLog().getDocument().getLength()); - - // update the BHT -> causes refresh of the table - m_bhtModel.updatePredictionAtIdx(idx, branchTaken); - } - - - /** - * Determines if the instruction is a branch instruction or not. - * - * @param stmt the statement to investigate - * @return true, if stmt is a branch instruction, otherwise false - */ - protected static boolean isBranchInstruction(ProgramStatement stmt) { - - int opCode = stmt.getBinaryStatement() >>> (32-6); - int funct = stmt.getBinaryStatement() & 0x1F; - - if (opCode == 0x01) { - if (0x00 <= funct && funct <= 0x07) return true; // bltz, bgez, bltzl, bgezl - if (0x10 <= funct && funct <= 0x13) return true; // bltzal, bgezal, bltzall, bgczall - } + /** constant for the default size of the BHT */ + public static final int BHT_DEFAULT_SIZE = 16; - if (0x04 <= opCode && opCode <= 0x07) return true; // beq, bne, blez, bgtz - if (0x14 <= opCode && opCode <= 0x17) return true; // beql, bnel, blezl, bgtzl - - return false; - } - - - /** - * Checks if the branch instruction delivered as parameter will branch or not. - * - * @param stmt the branch instruction to be investigated - * @return true if the branch will be taken, otherwise false - */ - protected static boolean willBranch(ProgramStatement stmt) { - int opCode = stmt.getBinaryStatement() >>> (32-6); - int funct = stmt.getBinaryStatement() & 0x1F; - int rs = stmt.getBinaryStatement() >>> (32-6-5) & 0x1F; - int rt = stmt.getBinaryStatement() >>> (32-6-5-5) & 0x1F; - - int valRS = RegisterFile.getRegisters()[rs].getValue(); - int valRT = RegisterFile.getRegisters()[rt].getValue(); - - - if (opCode == 0x01) { - switch (funct) { - case 0x00: return valRS < 0; // bltz - case 0x01: return valRS >= 0; // bgez - case 0x02: return valRS < 0; // bltzl - case 0x03: return valRS >= 0; // bgezl - } - } + /** constant for the default history size */ + public static final int BHT_DEFAULT_HISTORY = 1; - switch (opCode) { - case 0x04: return valRS == valRT; - case 0x05: return valRS != valRT; - case 0x06: return valRS <= 0; - case 0x07: return valRS >= 0; - case 0x14: return valRS == valRT; - case 0x15: return valRS != valRT; - case 0x16: return valRS <= 0; - case 0x17: return valRS >= 0; - } - - return true; - } - - - /** - * Extracts the target address of the branch. - * - * In MIPS the target address is encoded as 16-bit value. - * The target address is encoded relative to the address of the instruction after the branch instruction - * - * @param stmt the branch instruction - * @return the address of the instruction that is executed if the branch is taken - */ - protected static int extractBranchAddress(ProgramStatement stmt) { - short offset = (short)(stmt.getBinaryStatement() & 0xFFFF); - return stmt.getAddress() + (offset<<2) + 4; - } - - - /** - * Callback for text segment access by the MIPS simulator. - * - * The method is called each time the text segment is accessed to fetch the next instruction. - * If the next instruction to execute was a branch instruction, the branch prediction is performed and visualized. - * In case the last instruction was a branch instruction, the outcome of the branch prediction is analyzed and visualized. - * - * @param resource the observed resource - * @param notice signals the type of access (memory, register etc.) - */ - protected void processMIPSUpdate(Observable resource, AccessNotice notice) { - - if (!notice.accessIsFromMIPS()) return; - - - if (notice.getAccessType() == AccessNotice.READ && notice instanceof MemoryAccessNotice) { - - // now it is safe to make a cast of the notice - MemoryAccessNotice memAccNotice = (MemoryAccessNotice) notice; - - try { - // access the statement in the text segment without notifying other tools etc. - ProgramStatement stmt = Memory.getInstance().getStatementNoNotify(memAccNotice.getAddress()); - - // necessary to handle possible null pointers at the end of the program - // (e.g., if the simulator tries to execute the next instruction after the last instruction in the text segment) - if (stmt != null) { - - boolean clearTextFields = true; - - // first, check if there's a pending branch to handle - if (m_pendingBranchInstAddress != 0) { - handleExecBranchInst(m_pendingBranchInstAddress, m_lastBranchTaken); - clearTextFields = false; - m_pendingBranchInstAddress = 0; - } - - - // if current instruction is branch instruction - if (BHTSimulator.isBranchInstruction(stmt)) { - handlePreBranchInst(stmt); - m_lastBranchTaken = willBranch(stmt); - m_pendingBranchInstAddress = stmt.getAddress(); - clearTextFields = false; - } + /** constant for the default inital value */ + public static final boolean BHT_DEFAULT_INITVAL = false; - - // clear text fields and selection - if (clearTextFields) { - m_gui.getTfInstruction().setText(""); - m_gui.getTfAddress().setText(""); - m_gui.getTfIndex().setText(""); - m_gui.getTabBHT().clearSelection(); - } - } - else { - // check if there's a pending branch to handle - if (m_pendingBranchInstAddress != 0) { - handleExecBranchInst(m_pendingBranchInstAddress, m_lastBranchTaken); - m_pendingBranchInstAddress = 0; - } - } - } - catch (AddressErrorException e) { - // silently ignore these exceptions - } - - } - } + /** the name of the tool */ + public static final String BHT_NAME = "BHT Simulator"; + + /** the version of the tool */ + public static final String BHT_VERSION = "Version 1.0 (Ingo Kofler)"; + + /** the heading of the tool */ + public static final String BHT_HEADING = "Branch History Table Simulator"; + + /** the GUI of the BHT simulator */ + private BHTSimGUI m_gui; + + /** the model of the BHT */ + private BHTableModel m_bhtModel; + + /** + * state variable that indicates that the last instruction was a branch instruction (if address != 0) or not + * (address == 0) + */ + private int m_pendingBranchInstAddress; + + /** state variable that signals if the last branch was taken */ + private boolean m_lastBranchTaken; + + + /** + * Creates a BHT Simulator with given name and heading. + */ + public BHTSimulator() + { + super(BHTSimulator.BHT_NAME + ", " + BHTSimulator.BHT_VERSION, BHTSimulator.BHT_HEADING); + } + + /** + * Determines if the instruction is a branch instruction or not. + * + * @param stmt the statement to investigate + * @return true, if stmt is a branch instruction, otherwise false + */ + protected static boolean isBranchInstruction(ProgramStatement stmt) + { + + int opCode = stmt.getBinaryStatement() >>> (32 - 6); + int funct = stmt.getBinaryStatement() & 0x1F; + + if (opCode == 0x01) + { + if (0x00 <= funct && funct <= 0x07) + { + return true; // bltz, bgez, bltzl, bgezl + } + if (0x10 <= funct && funct <= 0x13) + { + return true; // bltzal, bgezal, bltzall, bgczall + } + } + + if (0x04 <= opCode && opCode <= 0x07) + { + return true; // beq, bne, blez, bgtz + } + return 0x14 <= opCode && opCode <= 0x17; // beql, bnel, blezl, bgtzl + } + + /** + * Checks if the branch instruction delivered as parameter will branch or not. + * + * @param stmt the branch instruction to be investigated + * @return true if the branch will be taken, otherwise false + */ + protected static boolean willBranch(ProgramStatement stmt) + { + int opCode = stmt.getBinaryStatement() >>> (32 - 6); + int funct = stmt.getBinaryStatement() & 0x1F; + int rs = stmt.getBinaryStatement() >>> (32 - 6 - 5) & 0x1F; + int rt = stmt.getBinaryStatement() >>> (32 - 6 - 5 - 5) & 0x1F; + + int valRS = RegisterFile.getRegisters()[rs].getValue(); + int valRT = RegisterFile.getRegisters()[rt].getValue(); + + + if (opCode == 0x01) + { + switch (funct) + { + case 0x00: + return valRS < 0; // bltz + case 0x01: + return valRS >= 0; // bgez + case 0x02: + return valRS < 0; // bltzl + case 0x03: + return valRS >= 0; // bgezl + } + } + + switch (opCode) + { + case 0x04: + return valRS == valRT; + case 0x05: + return valRS != valRT; + case 0x06: + return valRS <= 0; + case 0x07: + return valRS >= 0; + case 0x14: + return valRS == valRT; + case 0x15: + return valRS != valRT; + case 0x16: + return valRS <= 0; + case 0x17: + return valRS >= 0; + } + + return true; + } + + /** + * Extracts the target address of the branch. + *

+ * In MIPS the target address is encoded as 16-bit value. The target address is encoded relative to the address of + * the instruction after the branch instruction + * + * @param stmt the branch instruction + * @return the address of the instruction that is executed if the branch is taken + */ + protected static int extractBranchAddress(ProgramStatement stmt) + { + short offset = (short) (stmt.getBinaryStatement() & 0xFFFF); + return stmt.getAddress() + (offset << 2) + 4; + } + + /** + * Adds BHTSimulator as observer of the text segment. + */ + protected void addAsObserver() + { + addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); + addAsObserver(RegisterFile.getProgramCounterRegister()); + } + + /** + * Creates a GUI and initialize the GUI with the default values. + */ + protected JComponent buildMainDisplayArea() + { + + m_gui = new BHTSimGUI(); + m_bhtModel = new BHTableModel(BHTSimulator.BHT_DEFAULT_SIZE, BHTSimulator.BHT_DEFAULT_HISTORY, BHT_DEFAULT_INITVAL); + + m_gui.getTabBHT().setModel(m_bhtModel); + m_gui.getCbBHThistory().setSelectedItem(Integer.valueOf(BHTSimulator.BHT_DEFAULT_HISTORY)); + m_gui.getCbBHTentries().setSelectedItem(Integer.valueOf(BHTSimulator.BHT_DEFAULT_SIZE)); + + m_gui.getCbBHTentries().addActionListener(this); + m_gui.getCbBHThistory().addActionListener(this); + m_gui.getCbBHTinitVal().addActionListener(this); + + return m_gui; + } + + /** + * Returns the name of the tool. + * + * @return the tool's name as String + */ + public String getName() + { + return BHTSimulator.BHT_NAME; + } + + /** + * Performs a reset of the simulator. This causes the BHT to be reseted and the log messages to be cleared. + */ + protected void reset() + { + resetSimulator(); + } + + /** + * Handles the actions when selecting another value in one of the two combo boxes. Selecting a different BHT size or + * history causes a reset of the simulator. + */ + public void actionPerformed(ActionEvent event) + { + // change of the BHT size or BHT bit configuration + // resets the simulator + if (event.getSource() == m_gui.getCbBHTentries() || event.getSource() == m_gui.getCbBHThistory() || event.getSource() == m_gui.getCbBHTinitVal()) + { + resetSimulator(); + } + } + + /** + * Resets the simulator by clearing the GUI elements and resetting the BHT. + */ + protected void resetSimulator() + { + m_gui.getTfInstruction().setText(""); + m_gui.getTfAddress().setText(""); + m_gui.getTfIndex().setText(""); + m_gui.getTaLog().setText(""); + m_bhtModel.initBHT(((Integer) m_gui.getCbBHTentries().getSelectedItem()).intValue(), + ((Integer) m_gui.getCbBHThistory().getSelectedItem()).intValue(), + m_gui.getCbBHTinitVal().getSelectedItem().equals(BHTSimGUI.BHT_TAKE_BRANCH)); + + m_pendingBranchInstAddress = 0; + m_lastBranchTaken = false; + } + + /** + * Handles the execution branch instruction. This method is called each time a branch instruction is executed. Based + * on the address of the instruction the corresponding index into the BHT is calculated. The prediction is obtained + * from the BHT at the calculated index and is visualized appropriately. + * + * @param stmt the branch statement that is executed + */ + protected void handlePreBranchInst(ProgramStatement stmt) + { + + String strStmt = stmt.getBasicAssemblyStatement(); + int address = stmt.getAddress(); + int idx = m_bhtModel.getIdxForAddress(address); + + // update the GUI + m_gui.getTfInstruction().setText(strStmt); + m_gui.getTfAddress().setText("0x" + Integer.toHexString(address)); + m_gui.getTfIndex().setText("" + idx); + + // mark the affected BHT row + m_gui.getTabBHT().setSelectionBackground(BHTSimGUI.COLOR_PREPREDICTION); + m_gui.getTabBHT().addRowSelectionInterval(idx, idx); + + // add output to log + m_gui.getTaLog().append("instruction " + strStmt + " at address 0x" + Integer.toHexString(address) + ", maps to index " + idx + "\n"); + m_gui.getTaLog().append("branches to address 0x" + BHTSimulator.extractBranchAddress(stmt) + "\n"); + m_gui.getTaLog().append("prediction is: " + (m_bhtModel.getPredictionAtIdx(idx) ? "take" : "do not take") + "...\n"); + m_gui.getTaLog().setCaretPosition(m_gui.getTaLog().getDocument().getLength()); + + } + + /** + * Handles the execution of the branch instruction. The correctness of the prediction is visualized in both the + * table and the log message area. The BHT is updated based on the information if the branch instruction was taken + * or not. + * + * @param branchInstAddr the address of the branch instruction + * @param branchTaken the information if the branch is taken or not (determined in a step before) + */ + protected void handleExecBranchInst(int branchInstAddr, boolean branchTaken) + { + + // determine the index in the BHT for the branch instruction + int idx = m_bhtModel.getIdxForAddress(branchInstAddr); + + // check if the prediction is correct + boolean correctPrediction = m_bhtModel.getPredictionAtIdx(idx) == branchTaken; + + m_gui.getTabBHT().setSelectionBackground(correctPrediction ? BHTSimGUI.COLOR_PREDICTION_CORRECT : BHTSimGUI.COLOR_PREDICTION_INCORRECT); + + // add some output at the log + m_gui.getTaLog().append("branch " + (branchTaken ? "taken" : "not taken") + ", prediction was " + (correctPrediction ? "correct" : "incorrect") + "\n\n"); + m_gui.getTaLog().setCaretPosition(m_gui.getTaLog().getDocument().getLength()); + + // update the BHT -> causes refresh of the table + m_bhtModel.updatePredictionAtIdx(idx, branchTaken); + } + + /** + * Callback for text segment access by the MIPS simulator. + *

+ * The method is called each time the text segment is accessed to fetch the next instruction. If the next + * instruction to execute was a branch instruction, the branch prediction is performed and visualized. In case the + * last instruction was a branch instruction, the outcome of the branch prediction is analyzed and visualized. + * + * @param resource the observed resource + * @param notice signals the type of access (memory, register etc.) + */ + protected void processMIPSUpdate(Observable resource, AccessNotice notice) + { + + if (!notice.accessIsFromMIPS()) + { + return; + } + + + if (notice.getAccessType() == AccessNotice.READ && notice instanceof MemoryAccessNotice) + { + + // now it is safe to make a cast of the notice + MemoryAccessNotice memAccNotice = (MemoryAccessNotice) notice; + + try + { + // access the statement in the text segment without notifying other tools etc. + ProgramStatement stmt = Memory.getInstance().getStatementNoNotify(memAccNotice.getAddress()); + + // necessary to handle possible null pointers at the end of the program + // (e.g., if the simulator tries to execute the next instruction after the last instruction in the text segment) + if (stmt != null) + { + + boolean clearTextFields = true; + + // first, check if there's a pending branch to handle + if (m_pendingBranchInstAddress != 0) + { + handleExecBranchInst(m_pendingBranchInstAddress, m_lastBranchTaken); + clearTextFields = false; + m_pendingBranchInstAddress = 0; + } + + + // if current instruction is branch instruction + if (BHTSimulator.isBranchInstruction(stmt)) + { + handlePreBranchInst(stmt); + m_lastBranchTaken = willBranch(stmt); + m_pendingBranchInstAddress = stmt.getAddress(); + clearTextFields = false; + } + + + // clear text fields and selection + if (clearTextFields) + { + m_gui.getTfInstruction().setText(""); + m_gui.getTfAddress().setText(""); + m_gui.getTfIndex().setText(""); + m_gui.getTabBHT().clearSelection(); + } + } + else + { + // check if there's a pending branch to handle + if (m_pendingBranchInstAddress != 0) + { + handleExecBranchInst(m_pendingBranchInstAddress, m_lastBranchTaken); + m_pendingBranchInstAddress = 0; + } + } + } + catch (AddressErrorException e) + { + // silently ignore these exceptions + } + + } + } } diff --git a/src/main/java/mars/tools/BHTableModel.java b/src/main/java/mars/tools/BHTableModel.java index 6e9b818..f9a24f9 100644 --- a/src/main/java/mars/tools/BHTableModel.java +++ b/src/main/java/mars/tools/BHTableModel.java @@ -27,18 +27,16 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. package mars.tools;//.bhtsim; -import java.util.Vector; - import javax.swing.table.AbstractTableModel; +import java.util.Vector; /** * Simulates the actual functionality of a Branch History Table (BHT). *

- * The BHT consists of a number of BHT entries which are used to perform branch prediction. - * The entries of the BHT are stored as a Vector of BHTEntry objects. - * The number of entries is configurable but has to be a power of 2. - * The history kept by each BHT entry is also configurable during run-time. - * A change of the configuration however causes a complete reset of the BHT. + * The BHT consists of a number of BHT entries which are used to perform branch prediction. The entries of the BHT are + * stored as a Vector of BHTEntry objects. The number of entries is configurable but has to be a power of 2. The history + * kept by each BHT entry is also configurable during run-time. A change of the configuration however causes a complete + * reset of the BHT. *

* The typical interaction is as follows: *

    @@ -49,190 +47,230 @@ import javax.swing.table.AbstractTableModel; *
*

* Additionally it serves as TableModel that can be directly used to render the state of the BHT in a JTable. - * Feedback provided to the BHT causes a change of the internal state and a repaint of the table(s) associated to this model. - * + * Feedback provided to the BHT causes a change of the internal state and a repaint of the table(s) associated to this model. + * * @author ingo.kofler@itec.uni-klu.ac.at */ //@SuppressWarnings("serial") -public class BHTableModel extends AbstractTableModel { +public class BHTableModel extends AbstractTableModel +{ - /** vector holding the entries of the BHT */ - private Vector m_entries; - - /** number of entries in the BHT */ - private int m_entryCnt; - - /** number of past branch events to remember */ - private int m_historySize; - - /** name of the table columns */ - private String m_columnNames[] = { "Index", "History", "Prediction", "Correct", "Incorrect", "Precision"}; - - /** type of the table columns */ - //@SuppressWarnings("unchecked") - private Class m_columnClasses[] = { Integer.class, String.class, String.class, Integer.class, Integer.class, Double.class}; + /** vector holding the entries of the BHT */ + private Vector m_entries; + + /** number of entries in the BHT */ + private int m_entryCnt; + + /** number of past branch events to remember */ + private int m_historySize; + + /** name of the table columns */ + private final String[] m_columnNames = {"Index", "History", "Prediction", "Correct", "Incorrect", "Precision"}; + + /** type of the table columns */ + //@SuppressWarnings("unchecked") + private final Class[] m_columnClasses = {Integer.class, String.class, String.class, Integer.class, Integer.class, Double.class}; + + + /** + * Constructs a new BHT with given number of entries and history size. + * + * @param numEntries number of entries in the BHT + * @param historySize size of the history (in bits/number of past branches) + */ + public BHTableModel(int numEntries, int historySize, boolean initVal) + { + initBHT(numEntries, historySize, initVal); + } + + + /** + * Returns the name of the i-th column of the table. Required by the TableModel interface. + * + * @param i the index of the column + * @return name of the i-th column + */ + public String getColumnName(int i) + { + if (i < 0 || i > m_columnNames.length) + { + throw new IllegalArgumentException("Illegal column index " + i + " (must be in range 0.." + (m_columnNames.length - 1) + ")"); + } - - /** - * Constructs a new BHT with given number of entries and history size. - * - * @param numEntries number of entries in the BHT - * @param historySize size of the history (in bits/number of past branches) - */ - public BHTableModel (int numEntries, int historySize, boolean initVal) { - initBHT(numEntries, historySize, initVal); - } - - - /** - * Returns the name of the i-th column of the table. - * Required by the TableModel interface. - * - * @param i the index of the column - * @return name of the i-th column - */ - public String getColumnName(int i) { - if (i < 0 || i > m_columnNames.length) - throw new IllegalArgumentException("Illegal column index " + i + " (must be in range 0.." + (m_columnNames.length-1) + ")"); - return m_columnNames[i]; } - - - /** - * Returns the class/type of the i-th column of the table. - * Required by the TableModel interface. - * - * @param i the index of the column - * @return class representing the type of the i-th column - */ - public Class getColumnClass(int i) { - if (i < 0 || i > m_columnClasses.length) - throw new IllegalArgumentException("Illegal column index " + i + " (must be in range 0.." + (m_columnClasses.length-1) + ")"); - - return m_columnClasses[i]; - } - - - /** - * Returns the number of columns. - * Required by the TableModel interface. - * - * @return currently the constant 6 - */ - public int getColumnCount() { - return 6; - } - - /** - * Returns the number of entries of the BHT. - * Required by the TableModel interface. - * - * @return number of rows / entries of the BHT - */ - public int getRowCount() { - return m_entryCnt; - } - - /** - * Returns the value of the cell at the given row and column - * Required by the TableModel interface. - * - * @param row the row index - * @param col the column index - * @return the value of the cell - */ - public Object getValueAt(int row, int col) { - - BHTEntry e = (BHTEntry) m_entries.elementAt(row); - if (e==null) return ""; - - if (col==0) return new Integer(row); - if (col==1) return e.getHistoryAsStr(); - if (col==2) return e.getPredictionAsStr(); - if (col==3) return new Integer(e.getStatsPredCorrect()); - if (col==4) return new Integer(e.getStatsPredIncorrect()); - if (col==5) return new Double(e.getStatsPredPrecision()); - - return ""; - } - - - /** - * Initializes the BHT with the given size and history. - * All previous data like the BHT entries' history and statistics will get lost. - * A refresh of the table that use this BHT as model will be triggered. - * - * @param numEntries number of entries in the BHT (has to be a power of 2) - * @param historySize size of the history to consider - * @param initVal initial value for each entry (true means take branch, false do not take branch) - */ - public void initBHT(int numEntries, int historySize, boolean initVal) { - - if (numEntries <= 0 || (numEntries & (numEntries-1)) != 0) - throw new IllegalArgumentException("Number of entries must be a positive power of 2."); - if (historySize < 1 || historySize > 2) - throw new IllegalArgumentException("Only history sizes of 1 or 2 supported."); - - m_entryCnt = numEntries; - m_historySize = historySize; - - m_entries = new Vector(); - - for (int i=0; i < m_entryCnt; i++) { - m_entries.add(new BHTEntry(m_historySize, initVal)); - } + /** + * Returns the class/type of the i-th column of the table. Required by the TableModel interface. + * + * @param i the index of the column + * @return class representing the type of the i-th column + */ + public Class getColumnClass(int i) + { + if (i < 0 || i > m_columnClasses.length) + { + throw new IllegalArgumentException("Illegal column index " + i + " (must be in range 0.." + (m_columnClasses.length - 1) + ")"); + } - // refresh the table(s) - fireTableStructureChanged(); - } - - - /** - * Returns the index into the BHT for a given branch instruction address. - * A simple direct mapping is used. - * - * @param address the address of the branch instruction - * @return the index into the BHT - */ - public int getIdxForAddress(int address) { - if (address < 0) - throw new IllegalArgumentException("No negative addresses supported"); - - return (address >> 2) % m_entryCnt; - } - - - /** - * Retrieve the prediction for the i-th BHT entry. - * - * @param index the index of the entry in the BHT - * @return the prediction to take (true) or do not take (false) the branch - */ - public boolean getPredictionAtIdx(int index) { - if (index < 0 || index > m_entryCnt) - throw new IllegalArgumentException("Only indexes in the range 0 to " + (m_entryCnt-1) + " allowed"); - - return ((BHTEntry) m_entries.elementAt(index)).getPrediction(); - } - - - /** - * Updates the BHT entry with the outcome of the branch instruction. - * This causes a change in the model and signals to update the connected table(s). - * - * @param index the index of the entry in the BHT - * @param branchTaken - */ - public void updatePredictionAtIdx(int index, boolean branchTaken) { - if (index < 0 || index > m_entryCnt) - throw new IllegalArgumentException("Only indexes in the range 0 to " + (m_entryCnt-1) + " allowed"); - - ((BHTEntry) m_entries.elementAt(index)).updatePrediction(branchTaken); - fireTableRowsUpdated(index, index); - } + return m_columnClasses[i]; + } + + + /** + * Returns the number of columns. Required by the TableModel interface. + * + * @return currently the constant 6 + */ + public int getColumnCount() + { + return 6; + } + + + /** + * Returns the number of entries of the BHT. Required by the TableModel interface. + * + * @return number of rows / entries of the BHT + */ + public int getRowCount() + { + return m_entryCnt; + } + + + /** + * Returns the value of the cell at the given row and column Required by the TableModel interface. + * + * @param row the row index + * @param col the column index + * @return the value of the cell + */ + public Object getValueAt(int row, int col) + { + + BHTEntry e = (BHTEntry) m_entries.elementAt(row); + if (e == null) + { + return ""; + } + + if (col == 0) + { + return Integer.valueOf(row); + } + if (col == 1) + { + return e.getHistoryAsStr(); + } + if (col == 2) + { + return e.getPredictionAsStr(); + } + if (col == 3) + { + return Integer.valueOf(e.getStatsPredCorrect()); + } + if (col == 4) + { + return Integer.valueOf(e.getStatsPredIncorrect()); + } + if (col == 5) + { + return new Double(e.getStatsPredPrecision()); + } + + return ""; + } + + + /** + * Initializes the BHT with the given size and history. All previous data like the BHT entries' history and + * statistics will get lost. A refresh of the table that use this BHT as model will be triggered. + * + * @param numEntries number of entries in the BHT (has to be a power of 2) + * @param historySize size of the history to consider + * @param initVal initial value for each entry (true means take branch, false do not take branch) + */ + public void initBHT(int numEntries, int historySize, boolean initVal) + { + + if (numEntries <= 0 || (numEntries & (numEntries - 1)) != 0) + { + throw new IllegalArgumentException("Number of entries must be a positive power of 2."); + } + if (historySize < 1 || historySize > 2) + { + throw new IllegalArgumentException("Only history sizes of 1 or 2 supported."); + } + + m_entryCnt = numEntries; + m_historySize = historySize; + + m_entries = new Vector(); + + for (int i = 0; i < m_entryCnt; i++) + { + m_entries.add(new BHTEntry(m_historySize, initVal)); + } + + // refresh the table(s) + fireTableStructureChanged(); + } + + + /** + * Returns the index into the BHT for a given branch instruction address. A simple direct mapping is used. + * + * @param address the address of the branch instruction + * @return the index into the BHT + */ + public int getIdxForAddress(int address) + { + if (address < 0) + { + throw new IllegalArgumentException("No negative addresses supported"); + } + + return (address >> 2) % m_entryCnt; + } + + + /** + * Retrieve the prediction for the i-th BHT entry. + * + * @param index the index of the entry in the BHT + * @return the prediction to take (true) or do not take (false) the branch + */ + public boolean getPredictionAtIdx(int index) + { + if (index < 0 || index > m_entryCnt) + { + throw new IllegalArgumentException("Only indexes in the range 0 to " + (m_entryCnt - 1) + " allowed"); + } + + return ((BHTEntry) m_entries.elementAt(index)).getPrediction(); + } + + + /** + * Updates the BHT entry with the outcome of the branch instruction. This causes a change in the model and signals + * to update the connected table(s). + * + * @param index the index of the entry in the BHT + * @param branchTaken + */ + public void updatePredictionAtIdx(int index, boolean branchTaken) + { + if (index < 0 || index > m_entryCnt) + { + throw new IllegalArgumentException("Only indexes in the range 0 to " + (m_entryCnt - 1) + " allowed"); + } + + ((BHTEntry) m_entries.elementAt(index)).updatePrediction(branchTaken); + fireTableRowsUpdated(index, index); + } } diff --git a/src/main/java/mars/tools/BitmapDisplay.java b/src/main/java/mars/tools/BitmapDisplay.java index 9272c72..5415793 100644 --- a/src/main/java/mars/tools/BitmapDisplay.java +++ b/src/main/java/mars/tools/BitmapDisplay.java @@ -1,12 +1,15 @@ - package mars.tools; - import javax.swing.*; - import javax.swing.border.*; - import javax.swing.event.*; - import java.awt.*; - import java.awt.event.*; - import java.util.*; - import mars.tools.*; - import mars.mips.hardware.*; +package mars.tools; + +import mars.mips.hardware.AccessNotice; +import mars.mips.hardware.Memory; +import mars.mips.hardware.MemoryAccessNotice; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Observable; /* Copyright (c) 2010-2011, Pete Sanderson and Kenneth Vollmar @@ -35,387 +38,438 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Bitmapp display simulator. It can be run either as a stand-alone Java application having - * access to the mars package, or through MARS as an item in its Tools menu. It makes - * maximum use of methods inherited from its abstract superclass AbstractMarsToolAndApplication. - * Pete Sanderson, verison 1.0, 23 December 2010. - */ - public class BitmapDisplay extends AbstractMarsToolAndApplication { - - private static String version = "Version 1.0"; - private static String heading = "Bitmap Display"; - - // Major GUI components - private JComboBox visualizationUnitPixelWidthSelector, visualizationUnitPixelHeightSelector, - visualizationPixelWidthSelector, visualizationPixelHeightSelector, displayBaseAddressSelector; - private Graphics drawingArea; - private JPanel canvas; - private JPanel results; - - // Some GUI settings - private EmptyBorder emptyBorder = new EmptyBorder(4,4,4,4); - private Font countFonts = new Font("Times", Font.BOLD,12); - private Color backgroundColor = Color.WHITE; - - // Values for Combo Boxes - - private final String[] visualizationUnitPixelWidthChoices = {"1","2","4","8","16","32"}; - private final int defaultVisualizationUnitPixelWidthIndex = 0; - private final String[] visualizationUnitPixelHeightChoices = {"1","2","4","8","16","32"}; - private final int defaultVisualizationUnitPixelHeightIndex = 0; - private final String[] displayAreaPixelWidthChoices = {"64","128","256","512","1024"}; - private final int defaultDisplayWidthIndex = 3; - private final String[] displayAreaPixelHeightChoices = {"64","128","256","512","1024"}; - private final int defaultDisplayHeightIndex = 2; - - // Values for display canvas. Note their initialization uses the identifiers just above. - - private int unitPixelWidth = Integer.parseInt(visualizationUnitPixelWidthChoices[defaultVisualizationUnitPixelWidthIndex]); - private int unitPixelHeight = Integer.parseInt(visualizationUnitPixelHeightChoices[defaultVisualizationUnitPixelHeightIndex]); - private int displayAreaWidthInPixels = Integer.parseInt(displayAreaPixelWidthChoices[defaultDisplayWidthIndex]); - private int displayAreaHeightInPixels = Integer.parseInt(displayAreaPixelHeightChoices[defaultDisplayHeightIndex]); - - - // The next four are initialized dynamically in initializeDisplayBaseChoices() - private String[] displayBaseAddressChoices; - private int[] displayBaseAddresses; - private int defaultBaseAddressIndex; - private int baseAddress; - - private Grid theGrid; - - /** - * Simple constructor, likely used to run a stand-alone bitmap display tool. - * @param title String containing title for title bar - * @param heading String containing text for heading shown in upper part of window. - */ - public BitmapDisplay(String title, String heading) { - super(title,heading); - } - - /** - * Simple constructor, likely used by the MARS Tools menu mechanism - */ - public BitmapDisplay() { - super ("Bitmap Display, "+version, heading); - } - - - /** - * Main provided for pure stand-alone use. Recommended stand-alone use is to write a - * driver program that instantiates a Bitmap object then invokes its go() method. - * "stand-alone" means it is not invoked from the MARS Tools menu. "Pure" means there - * is no driver program to invoke the application. - */ - public static void main(String[] args) { - new BitmapDisplay("Bitmap Display stand-alone, "+version,heading).go(); - } - - - /** - * Required MarsTool method to return Tool name. - * @return Tool name. MARS will display this in menu item. - */ - public String getName() { - return "Bitmap Display"; - } - - - /** - * Override the inherited method, which registers us as an Observer over the static data segment - * (starting address 0x10010000) only. This version will register us as observer over the - * the memory range as selected by the base address combo box and capacity of the visualization display - * (number of visualization elements times the number of memory words each one represents). - * It does so by calling the inherited 2-parameter overload of this method. - * If you use the inherited GUI buttons, this - * method is invoked when you click "Connect" button on MarsTool or the - * "Assemble and Run" button on a Mars-based app. - */ - protected void addAsObserver() { - int highAddress = baseAddress+theGrid.getRows()*theGrid.getColumns()*Memory.WORD_LENGTH_BYTES; - // Special case: baseAddress<0 means we're in kernel memory (0x80000000 and up) and most likely - // in memory map address space (0xffff0000 and up). In this case, we need to make sure the high address - // does not drop off the high end of 32 bit address space. Highest allowable word address is 0xfffffffc, - // which is interpreted in Java int as -4. - if (baseAddress < 0 && highAddress > -4) { + +/** + * Bitmapp display simulator. It can be run either as a stand-alone Java application having access to the mars package, + * or through MARS as an item in its Tools menu. It makes maximum use of methods inherited from its abstract superclass + * AbstractMarsToolAndApplication. Pete Sanderson, verison 1.0, 23 December 2010. + */ +public class BitmapDisplay extends AbstractMarsToolAndApplication +{ + + private static final String version = "Version 1.0"; + + private static final String heading = "Bitmap Display"; + + private final String[] visualizationUnitPixelWidthChoices = {"1", "2", "4", "8", "16", "32"}; + + private final int defaultVisualizationUnitPixelWidthIndex = 0; + + private final String[] visualizationUnitPixelHeightChoices = {"1", "2", "4", "8", "16", "32"}; + + private final int defaultVisualizationUnitPixelHeightIndex = 0; + + private final String[] displayAreaPixelWidthChoices = {"64", "128", "256", "512", "1024"}; + + private final int defaultDisplayWidthIndex = 3; + + private final String[] displayAreaPixelHeightChoices = {"64", "128", "256", "512", "1024"}; + + // Values for Combo Boxes + + private final int defaultDisplayHeightIndex = 2; + + // Major GUI components + private JComboBox visualizationUnitPixelWidthSelector, visualizationUnitPixelHeightSelector, + visualizationPixelWidthSelector, visualizationPixelHeightSelector, displayBaseAddressSelector; + + private Graphics drawingArea; + + private JPanel canvas; + + private JPanel results; + + // Some GUI settings + private final EmptyBorder emptyBorder = new EmptyBorder(4, 4, 4, 4); + + private final Font countFonts = new Font("Times", Font.BOLD, 12); + + private final Color backgroundColor = Color.WHITE; + + // Values for display canvas. Note their initialization uses the identifiers just above. + + private int unitPixelWidth = Integer.parseInt(visualizationUnitPixelWidthChoices[defaultVisualizationUnitPixelWidthIndex]); + + private int unitPixelHeight = Integer.parseInt(visualizationUnitPixelHeightChoices[defaultVisualizationUnitPixelHeightIndex]); + + private int displayAreaWidthInPixels = Integer.parseInt(displayAreaPixelWidthChoices[defaultDisplayWidthIndex]); + + private int displayAreaHeightInPixels = Integer.parseInt(displayAreaPixelHeightChoices[defaultDisplayHeightIndex]); + + + // The next four are initialized dynamically in initializeDisplayBaseChoices() + private String[] displayBaseAddressChoices; + + private int[] displayBaseAddresses; + + private int defaultBaseAddressIndex; + + private int baseAddress; + + private Grid theGrid; + + /** + * Simple constructor, likely used to run a stand-alone bitmap display tool. + * + * @param title String containing title for title bar + * @param heading String containing text for heading shown in upper part of window. + */ + public BitmapDisplay(String title, String heading) + { + super(title, heading); + } + + /** + * Simple constructor, likely used by the MARS Tools menu mechanism + */ + public BitmapDisplay() + { + super("Bitmap Display, " + version, heading); + } + + + /** + * Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that + * instantiates a Bitmap object then invokes its go() method. "stand-alone" means it is not invoked from the MARS + * Tools menu. "Pure" means there is no driver program to invoke the application. + */ + public static void main(String[] args) + { + new BitmapDisplay("Bitmap Display stand-alone, " + version, heading).go(); + } + + + /** + * Required MarsTool method to return Tool name. + * + * @return Tool name. MARS will display this in menu item. + */ + public String getName() + { + return "Bitmap Display"; + } + + + /** + * Override the inherited method, which registers us as an Observer over the static data segment (starting address + * 0x10010000) only. This version will register us as observer over the the memory range as selected by the base + * address combo box and capacity of the visualization display (number of visualization elements times the number of + * memory words each one represents). It does so by calling the inherited 2-parameter overload of this method. If + * you use the inherited GUI buttons, this method is invoked when you click "Connect" button on MarsTool or the + * "Assemble and Run" button on a Mars-based app. + */ + protected void addAsObserver() + { + int highAddress = baseAddress + theGrid.getRows() * theGrid.getColumns() * Memory.WORD_LENGTH_BYTES; + // Special case: baseAddress<0 means we're in kernel memory (0x80000000 and up) and most likely + // in memory map address space (0xffff0000 and up). In this case, we need to make sure the high address + // does not drop off the high end of 32 bit address space. Highest allowable word address is 0xfffffffc, + // which is interpreted in Java int as -4. + if (baseAddress < 0 && highAddress > -4) + { highAddress = -4; - } - addAsObserver(baseAddress, highAddress); - } - - - /** - * Method that constructs the main display area. It is organized vertically - * into two major components: the display configuration which an be modified - * using combo boxes, and the visualization display which is updated as the - * attached MIPS program executes. - * @return the GUI component containing these two areas - */ - protected JComponent buildMainDisplayArea() { - results = new JPanel(); - results.add(buildOrganizationArea()); - results.add(buildVisualizationArea()); - return results; - } - - - ////////////////////////////////////////////////////////////////////////////////////// - // Rest of the protected methods. These override do-nothing methods inherited from - // the abstract superclass. - ////////////////////////////////////////////////////////////////////////////////////// - - /** - * Update display when connected MIPS program accesses (data) memory. - * @param memory the attached memory - * @param accessNotice information provided by memory in MemoryAccessNotice object - */ - protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) { - if (accessNotice.getAccessType() == AccessNotice.WRITE) { - updateColorForAddress((MemoryAccessNotice)accessNotice); - } - } - - - /** - * Initialize all JComboBox choice structures not already initialized at declaration. - * Overrides inherited method that does nothing. - */ - protected void initializePreGUI() { - initializeDisplayBaseChoices(); - // NOTE: Can't call "createNewGrid()" here because it uses settings from - // several combo boxes that have not been created yet. But a default grid - // needs to be allocated for initial canvas display. - theGrid = new Grid(displayAreaHeightInPixels/unitPixelHeight, - displayAreaWidthInPixels/unitPixelWidth); - } - - - /** - * The only post-GUI initialization is to create the initial Grid object based on the default settings - * of the various combo boxes. Overrides inherited method that does nothing. - */ - - protected void initializePostGUI() { - theGrid = createNewGrid(); - updateBaseAddress(); - } - - - /** - * Method to reset counters and display when the Reset button selected. - * Overrides inherited method that does nothing. - */ - protected void reset() { - resetCounts(); - updateDisplay(); - } - - /** - * Updates display immediately after each update (AccessNotice) is processed, after - * display configuration changes as needed, and after each execution step when Mars - * is running in timed mode. Overrides inherited method that does nothing. - */ - protected void updateDisplay() { - canvas.repaint(); - } - - - /** - * Overrides default method, to provide a Help button for this tool/app. - */ - protected JComponent getHelpComponent() { - final String helpContent = - "Use this program to simulate a basic bitmap display where\n"+ - "each memory word in a specified address space corresponds to\n"+ - "one display pixel in row-major order starting at the upper left\n"+ - "corner of the display. This tool may be run either from the\n"+ - "MARS Tools menu or as a stand-alone application.\n"+ - "\n"+ - "You can easily learn to use this small program by playing with\n"+ - "it! Each rectangular unit on the display represents one memory\n"+ - "word in a contiguous address space starting with the specified\n"+ - "base address. The value stored in that word will be interpreted\n"+ - "as a 24-bit RGB color value with the red component in bits 16-23,\n"+ - "the green component in bits 8-15, and the blue component in bits 0-7.\n"+ - "Each time a memory word within the display address space is written\n"+ - "by the MIPS program, its position in the display will be rendered\n"+ - "in the color that its value represents.\n"+ - "\n"+ - "Version 1.0 is very basic and was constructed from the Memory\n"+ - "Reference Visualization tool's code. Feel free to improve it and\n"+ - "send me your code for consideration in the next MARS release.\n"+ - "\n"+ - "Contact Pete Sanderson at psanderson@otterbein.edu with\n"+ - "questions or comments.\n"; - JButton help = new JButton("Help"); - help.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - JOptionPane.showMessageDialog(theWindow, helpContent); - } - }); - return help; - } - - ////////////////////////////////////////////////////////////////////////////////////// - // Private methods defined to support the above. - ////////////////////////////////////////////////////////////////////////////////////// - - // UI components and layout for left half of GUI, where settings are specified. - private JComponent buildOrganizationArea() { - JPanel organization = new JPanel(new GridLayout(8,1)); - - visualizationUnitPixelWidthSelector = new JComboBox(visualizationUnitPixelWidthChoices); - visualizationUnitPixelWidthSelector.setEditable(false); - visualizationUnitPixelWidthSelector.setBackground(backgroundColor); - visualizationUnitPixelWidthSelector.setSelectedIndex(defaultVisualizationUnitPixelWidthIndex); - visualizationUnitPixelWidthSelector.setToolTipText("Width in pixels of rectangle representing memory word"); - visualizationUnitPixelWidthSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - unitPixelWidth = getIntComboBoxSelection(visualizationUnitPixelWidthSelector); - theGrid = createNewGrid(); - updateDisplay(); - } - }); - visualizationUnitPixelHeightSelector = new JComboBox(visualizationUnitPixelHeightChoices); - visualizationUnitPixelHeightSelector.setEditable(false); - visualizationUnitPixelHeightSelector.setBackground(backgroundColor); - visualizationUnitPixelHeightSelector.setSelectedIndex(defaultVisualizationUnitPixelHeightIndex); - visualizationUnitPixelHeightSelector.setToolTipText("Height in pixels of rectangle representing memory word"); - visualizationUnitPixelHeightSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - unitPixelHeight = getIntComboBoxSelection(visualizationUnitPixelHeightSelector); - theGrid = createNewGrid(); - updateDisplay(); - } - }); - visualizationPixelWidthSelector = new JComboBox(displayAreaPixelWidthChoices); - visualizationPixelWidthSelector.setEditable(false); - visualizationPixelWidthSelector.setBackground(backgroundColor); - visualizationPixelWidthSelector.setSelectedIndex(defaultDisplayWidthIndex); - visualizationPixelWidthSelector.setToolTipText("Total width in pixels of display area"); - visualizationPixelWidthSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - displayAreaWidthInPixels = getIntComboBoxSelection(visualizationPixelWidthSelector); - canvas.setPreferredSize(getDisplayAreaDimension()); - canvas.setSize(getDisplayAreaDimension()); - theGrid = createNewGrid(); - updateDisplay(); - } - }); - visualizationPixelHeightSelector = new JComboBox(displayAreaPixelHeightChoices); - visualizationPixelHeightSelector.setEditable(false); - visualizationPixelHeightSelector.setBackground(backgroundColor); - visualizationPixelHeightSelector.setSelectedIndex(defaultDisplayHeightIndex); - visualizationPixelHeightSelector.setToolTipText("Total height in pixels of display area"); - visualizationPixelHeightSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - displayAreaHeightInPixels = getIntComboBoxSelection(visualizationPixelHeightSelector); - canvas.setPreferredSize(getDisplayAreaDimension()); - canvas.setSize(getDisplayAreaDimension()); - theGrid = createNewGrid(); - updateDisplay(); - } - }); - displayBaseAddressSelector = new JComboBox(displayBaseAddressChoices); - displayBaseAddressSelector.setEditable(false); - displayBaseAddressSelector.setBackground(backgroundColor); - displayBaseAddressSelector.setSelectedIndex(defaultBaseAddressIndex); - displayBaseAddressSelector.setToolTipText("Base address for display area (upper left corner)"); - displayBaseAddressSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - // This may also affect what address range we should be registered as an Observer - // for. The default (inherited) address range is the MIPS static data segment - // starting at 0x10010000. To change this requires override of - // AbstractMarsToolAndApplication.addAsObserver(). The no-argument version of - // that method is called automatically when "Connect" button is clicked for MarsTool - // and when "Assemble and Run" button is clicked for Mars application. - updateBaseAddress(); - // If display base address is changed while connected to MIPS (this can only occur - // when being used as a MarsTool), we have to delete ourselves as an observer and re-register. - if (connectButton != null && connectButton.isConnected()) { + } + addAsObserver(baseAddress, highAddress); + } + + + /** + * Method that constructs the main display area. It is organized vertically into two major components: the display + * configuration which an be modified using combo boxes, and the visualization display which is updated as the + * attached MIPS program executes. + * + * @return the GUI component containing these two areas + */ + protected JComponent buildMainDisplayArea() + { + results = new JPanel(); + results.add(buildOrganizationArea()); + results.add(buildVisualizationArea()); + return results; + } + + + ////////////////////////////////////////////////////////////////////////////////////// + // Rest of the protected methods. These override do-nothing methods inherited from + // the abstract superclass. + ////////////////////////////////////////////////////////////////////////////////////// + + /** + * Update display when connected MIPS program accesses (data) memory. + * + * @param memory the attached memory + * @param accessNotice information provided by memory in MemoryAccessNotice object + */ + protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) + { + if (accessNotice.getAccessType() == AccessNotice.WRITE) + { + updateColorForAddress((MemoryAccessNotice) accessNotice); + } + } + + + /** + * Initialize all JComboBox choice structures not already initialized at declaration. Overrides inherited method + * that does nothing. + */ + protected void initializePreGUI() + { + initializeDisplayBaseChoices(); + // NOTE: Can't call "createNewGrid()" here because it uses settings from + // several combo boxes that have not been created yet. But a default grid + // needs to be allocated for initial canvas display. + theGrid = new Grid(displayAreaHeightInPixels / unitPixelHeight, + displayAreaWidthInPixels / unitPixelWidth); + } + + + /** + * The only post-GUI initialization is to create the initial Grid object based on the default settings of the + * various combo boxes. Overrides inherited method that does nothing. + */ + + protected void initializePostGUI() + { + theGrid = createNewGrid(); + updateBaseAddress(); + } + + + /** + * Method to reset counters and display when the Reset button selected. Overrides inherited method that does + * nothing. + */ + protected void reset() + { + resetCounts(); + updateDisplay(); + } + + /** + * Updates display immediately after each update (AccessNotice) is processed, after display configuration changes as + * needed, and after each execution step when Mars is running in timed mode. Overrides inherited method that does + * nothing. + */ + protected void updateDisplay() + { + canvas.repaint(); + } + + + /** + * Overrides default method, to provide a Help button for this tool/app. + */ + protected JComponent getHelpComponent() + { + final String helpContent = + "Use this program to simulate a basic bitmap display where\n" + + "each memory word in a specified address space corresponds to\n" + + "one display pixel in row-major order starting at the upper left\n" + + "corner of the display. This tool may be run either from the\n" + + "MARS Tools menu or as a stand-alone application.\n" + + "\n" + + "You can easily learn to use this small program by playing with\n" + + "it! Each rectangular unit on the display represents one memory\n" + + "word in a contiguous address space starting with the specified\n" + + "base address. The value stored in that word will be interpreted\n" + + "as a 24-bit RGB color value with the red component in bits 16-23,\n" + + "the green component in bits 8-15, and the blue component in bits 0-7.\n" + + "Each time a memory word within the display address space is written\n" + + "by the MIPS program, its position in the display will be rendered\n" + + "in the color that its value represents.\n" + + "\n" + + "Version 1.0 is very basic and was constructed from the Memory\n" + + "Reference Visualization tool's code. Feel free to improve it and\n" + + "send me your code for consideration in the next MARS release.\n" + + "\n" + + "Contact Pete Sanderson at psanderson@otterbein.edu with\n" + + "questions or comments.\n"; + JButton help = new JButton("Help"); + help.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + JOptionPane.showMessageDialog(theWindow, helpContent); + } + }); + return help; + } + + ////////////////////////////////////////////////////////////////////////////////////// + // Private methods defined to support the above. + ////////////////////////////////////////////////////////////////////////////////////// + + // UI components and layout for left half of GUI, where settings are specified. + private JComponent buildOrganizationArea() + { + JPanel organization = new JPanel(new GridLayout(8, 1)); + + visualizationUnitPixelWidthSelector = new JComboBox(visualizationUnitPixelWidthChoices); + visualizationUnitPixelWidthSelector.setEditable(false); + visualizationUnitPixelWidthSelector.setBackground(backgroundColor); + visualizationUnitPixelWidthSelector.setSelectedIndex(defaultVisualizationUnitPixelWidthIndex); + visualizationUnitPixelWidthSelector.setToolTipText("Width in pixels of rectangle representing memory word"); + visualizationUnitPixelWidthSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + unitPixelWidth = getIntComboBoxSelection(visualizationUnitPixelWidthSelector); + theGrid = createNewGrid(); + updateDisplay(); + } + }); + visualizationUnitPixelHeightSelector = new JComboBox(visualizationUnitPixelHeightChoices); + visualizationUnitPixelHeightSelector.setEditable(false); + visualizationUnitPixelHeightSelector.setBackground(backgroundColor); + visualizationUnitPixelHeightSelector.setSelectedIndex(defaultVisualizationUnitPixelHeightIndex); + visualizationUnitPixelHeightSelector.setToolTipText("Height in pixels of rectangle representing memory word"); + visualizationUnitPixelHeightSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + unitPixelHeight = getIntComboBoxSelection(visualizationUnitPixelHeightSelector); + theGrid = createNewGrid(); + updateDisplay(); + } + }); + visualizationPixelWidthSelector = new JComboBox(displayAreaPixelWidthChoices); + visualizationPixelWidthSelector.setEditable(false); + visualizationPixelWidthSelector.setBackground(backgroundColor); + visualizationPixelWidthSelector.setSelectedIndex(defaultDisplayWidthIndex); + visualizationPixelWidthSelector.setToolTipText("Total width in pixels of display area"); + visualizationPixelWidthSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + displayAreaWidthInPixels = getIntComboBoxSelection(visualizationPixelWidthSelector); + canvas.setPreferredSize(getDisplayAreaDimension()); + canvas.setSize(getDisplayAreaDimension()); + theGrid = createNewGrid(); + updateDisplay(); + } + }); + visualizationPixelHeightSelector = new JComboBox(displayAreaPixelHeightChoices); + visualizationPixelHeightSelector.setEditable(false); + visualizationPixelHeightSelector.setBackground(backgroundColor); + visualizationPixelHeightSelector.setSelectedIndex(defaultDisplayHeightIndex); + visualizationPixelHeightSelector.setToolTipText("Total height in pixels of display area"); + visualizationPixelHeightSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + displayAreaHeightInPixels = getIntComboBoxSelection(visualizationPixelHeightSelector); + canvas.setPreferredSize(getDisplayAreaDimension()); + canvas.setSize(getDisplayAreaDimension()); + theGrid = createNewGrid(); + updateDisplay(); + } + }); + displayBaseAddressSelector = new JComboBox(displayBaseAddressChoices); + displayBaseAddressSelector.setEditable(false); + displayBaseAddressSelector.setBackground(backgroundColor); + displayBaseAddressSelector.setSelectedIndex(defaultBaseAddressIndex); + displayBaseAddressSelector.setToolTipText("Base address for display area (upper left corner)"); + displayBaseAddressSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // This may also affect what address range we should be registered as an Observer + // for. The default (inherited) address range is the MIPS static data segment + // starting at 0x10010000. To change this requires override of + // AbstractMarsToolAndApplication.addAsObserver(). The no-argument version of + // that method is called automatically when "Connect" button is clicked for MarsTool + // and when "Assemble and Run" button is clicked for Mars application. + updateBaseAddress(); + // If display base address is changed while connected to MIPS (this can only occur + // when being used as a MarsTool), we have to delete ourselves as an observer and re-register. + if (connectButton != null && connectButton.isConnected()) + { deleteAsObserver(); addAsObserver(); - } - theGrid = createNewGrid(); - updateDisplay(); - } - }); - - // ALL COMPONENTS FOR "ORGANIZATION" SECTION - - JPanel unitWidthInPixelsRow = getPanelWithBorderLayout(); - unitWidthInPixelsRow.setBorder(emptyBorder); - unitWidthInPixelsRow.add(new JLabel("Unit Width in Pixels "),BorderLayout.WEST); - unitWidthInPixelsRow.add(visualizationUnitPixelWidthSelector, BorderLayout.EAST); - - JPanel unitHeightInPixelsRow = getPanelWithBorderLayout(); - unitHeightInPixelsRow.setBorder(emptyBorder); - unitHeightInPixelsRow.add(new JLabel("Unit Height in Pixels "),BorderLayout.WEST); - unitHeightInPixelsRow.add(visualizationUnitPixelHeightSelector,BorderLayout.EAST); - - JPanel widthInPixelsRow = getPanelWithBorderLayout(); - widthInPixelsRow.setBorder(emptyBorder); - widthInPixelsRow.add(new JLabel("Display Width in Pixels "),BorderLayout.WEST); - widthInPixelsRow.add(visualizationPixelWidthSelector, BorderLayout.EAST); - - JPanel heightInPixelsRow = getPanelWithBorderLayout(); - heightInPixelsRow.setBorder(emptyBorder); - heightInPixelsRow.add(new JLabel("Display Height in Pixels "),BorderLayout.WEST); - heightInPixelsRow.add(visualizationPixelHeightSelector,BorderLayout.EAST); - - JPanel baseAddressRow = getPanelWithBorderLayout(); - baseAddressRow.setBorder(emptyBorder); - baseAddressRow.add(new JLabel("Base address for display "),BorderLayout.WEST); - baseAddressRow.add(displayBaseAddressSelector,BorderLayout.EAST); - - - // Lay 'em out in the grid... - organization.add(unitWidthInPixelsRow); - organization.add(unitHeightInPixelsRow); - organization.add(widthInPixelsRow); - organization.add(heightInPixelsRow); - organization.add(baseAddressRow); - return organization; - } - - // UI components and layout for right half of GUI, the visualization display area. - private JComponent buildVisualizationArea() { - canvas = new GraphicsPanel(); - canvas.setPreferredSize(getDisplayAreaDimension()); - canvas.setToolTipText("Bitmap display area"); - return canvas; - } - - // For greatest flexibility, initialize the display base choices directly from - // the constants defined in the Memory class. This method called prior to - // building the GUI. Here are current values from Memory.java: - //dataSegmentBaseAddress=0x10000000, globalPointer=0x10008000 - //dataBaseAddress=0x10010000, heapBaseAddress=0x10040000, memoryMapBaseAddress=0xffff0000 - private void initializeDisplayBaseChoices() { - int[] displayBaseAddressArray = {Memory.dataSegmentBaseAddress, Memory.globalPointer, Memory.dataBaseAddress, - Memory.heapBaseAddress, Memory.memoryMapBaseAddress }; - // Must agree with above in number and order... - String[] descriptions = { " (global data)", " ($gp)", " (static data)", " (heap)", " (memory map)" }; - displayBaseAddresses = displayBaseAddressArray; - displayBaseAddressChoices = new String[displayBaseAddressArray.length]; - for (int i=0; i 10 characters long, slice off the first 10 and apply Integer.parseInt() to it to get custom base address. */ - } - - // Returns Dimension object with current width and height of display area as determined - // by current settings of respective combo boxes. - private Dimension getDisplayAreaDimension() { - return new Dimension(displayAreaWidthInPixels, displayAreaHeightInPixels); - } - - // reset all counters in the Grid. - private void resetCounts() { - theGrid.reset(); - } - - // Will return int equivalent of specified combo box's current selection. - // The selection must be a String that parses to an int. - private int getIntComboBoxSelection(JComboBox comboBox) { - try { - return Integer.parseInt((String)comboBox.getSelectedItem()); - } - catch (NumberFormatException nfe) { - // Can occur only if initialization list contains badly formatted numbers. This - // is a developer's error, not a user error, and better be caught before release. - return 1; - } - } - - // Use this for consistent results. - private JPanel getPanelWithBorderLayout() { - return new JPanel(new BorderLayout(2,2)); - } - - // Method to determine grid dimensions based on current control settings. - // Each grid element corresponds to one visualization unit. - private Grid createNewGrid() { - int rows = displayAreaHeightInPixels/unitPixelHeight; - int columns = displayAreaWidthInPixels/unitPixelWidth; - return new Grid(rows,columns); - } - - // Given memory address, update color for the corresponding grid element. - private void updateColorForAddress(MemoryAccessNotice notice) { - int address = notice.getAddress(); - int value = notice.getValue(); - int offset = (address - baseAddress)/Memory.WORD_LENGTH_BYTES; - try { + } + + // Returns Dimension object with current width and height of display area as determined + // by current settings of respective combo boxes. + private Dimension getDisplayAreaDimension() + { + return new Dimension(displayAreaWidthInPixels, displayAreaHeightInPixels); + } + + // reset all counters in the Grid. + private void resetCounts() + { + theGrid.reset(); + } + + // Will return int equivalent of specified combo box's current selection. + // The selection must be a String that parses to an int. + private int getIntComboBoxSelection(JComboBox comboBox) + { + try + { + return Integer.parseInt((String) comboBox.getSelectedItem()); + } + catch (NumberFormatException nfe) + { + // Can occur only if initialization list contains badly formatted numbers. This + // is a developer's error, not a user error, and better be caught before release. + return 1; + } + } + + // Use this for consistent results. + private JPanel getPanelWithBorderLayout() + { + return new JPanel(new BorderLayout(2, 2)); + } + + // Method to determine grid dimensions based on current control settings. + // Each grid element corresponds to one visualization unit. + private Grid createNewGrid() + { + int rows = displayAreaHeightInPixels / unitPixelHeight; + int columns = displayAreaWidthInPixels / unitPixelWidth; + return new Grid(rows, columns); + } + + // Given memory address, update color for the corresponding grid element. + private void updateColorForAddress(MemoryAccessNotice notice) + { + int address = notice.getAddress(); + int value = notice.getValue(); + int offset = (address - baseAddress) / Memory.WORD_LENGTH_BYTES; + try + { theGrid.setElement(offset / theGrid.getColumns(), offset % theGrid.getColumns(), value); - } - catch (IndexOutOfBoundsException e) { - // If address is out of range for display, do nothing. - } - } - - - ////////////////////////////////////////////////////////////////////////////////////// - // Specialized inner classes for modeling and animation. - ////////////////////////////////////////////////////////////////////////////////////// - - - ///////////////////////////////////////////////////////////////////////////// - // Class that represents the panel for visualizing and animating memory reference - // patterns. - private class GraphicsPanel extends JPanel { - - // override default paint method to assure display updated correctly every time - // the panel is repainted. - public void paint(Graphics g) { - paintGrid(g, theGrid); - } - - // Paint the color codes. - private void paintGrid(Graphics g, Grid grid) { + } + catch (IndexOutOfBoundsException e) + { + // If address is out of range for display, do nothing. + } + } + + + ////////////////////////////////////////////////////////////////////////////////////// + // Specialized inner classes for modeling and animation. + ////////////////////////////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////////////////// + // Class that represents the panel for visualizing and animating memory reference + // patterns. + private class GraphicsPanel extends JPanel + { + + // override default paint method to assure display updated correctly every time + // the panel is repainted. + public void paint(Graphics g) + { + paintGrid(g, theGrid); + } + + // Paint the color codes. + private void paintGrid(Graphics g, Grid grid) + { int upperLeftX = 0, upperLeftY = 0; - for (int i=0; i=0 && row<=rows && column>=0 && column<=columns) ? grid[row][column] : null; - } - - // Returns value in given grid element without doing any row/column index checking. - // Is faster than getElement but will throw array index out of bounds exception if - // parameter values are outside the bounds of the grid. - private Color getElementFast(int row, int column) { - return grid[row][column]; - } - - // Set the grid element. - private void setElement(int row, int column, int color) { + } + + // Returns value in given grid element; null if row or column is out of range. + private Color getElement(int row, int column) + { + return (row >= 0 && row <= rows && column >= 0 && column <= columns) ? grid[row][column] : null; + } + + // Returns value in given grid element without doing any row/column index checking. + // Is faster than getElement but will throw array index out of bounds exception if + // parameter values are outside the bounds of the grid. + private Color getElementFast(int row, int column) + { + return grid[row][column]; + } + + // Set the grid element. + private void setElement(int row, int column, int color) + { grid[row][column] = new Color(color); - } - - // Set the grid element. - private void setElement(int row, int column, Color color) { + } + + // Set the grid element. + private void setElement(int row, int column, Color color) + { grid[row][column] = color; - } - - // Just set all grid elements to black. - private void reset() { - for (int i=0; iVersion 1.2 fixes a bug in the hit/miss animator under full or N-way set associative. It was - * animating the block of initial access (first block of set). Now it animates the block - * of final access (where address found or stored). Also added log display to GUI (previously System.out).

- */ - public class CacheSimulator extends AbstractMarsToolAndApplication { - private static boolean debug = false; // controls display of debugging info - private static String version = "Version 1.2"; - private static String heading = "Simulate and illustrate data cache performance"; - // Major GUI components - private JComboBox cacheBlockSizeSelector, cacheBlockCountSelector, - cachePlacementSelector, cacheReplacementSelector, - cacheSetSizeSelector; - private JTextField memoryAccessCountDisplay, cacheHitCountDisplay, cacheMissCountDisplay, - replacementPolicyDisplay,cachableAddressesDisplay, - cacheSizeDisplay; - private JProgressBar cacheHitRateDisplay; - private Animation animations; - - private JPanel logPanel; - private JScrollPane logScroll; - private JTextArea logText; - private JCheckBox logShow; - - // Some GUI settings - private EmptyBorder emptyBorder = new EmptyBorder(4,4,4,4); - private Font countFonts = new Font("Times", Font.BOLD,12); - private Color backgroundColor = Color.WHITE; - - // Values for Combo Boxes - private int[] cacheBlockSizeChoicesInt, cacheBlockCountChoicesInt; - private String[] cacheBlockSizeChoices = {"1","2","4","8","16","32","64","128","256","512","1024","2048"}; - private String[] cacheBlockCountChoices = {"1","2","4","8","16","32","64","128","256","512","1024","2048"}; - private String[] placementPolicyChoices = {"Direct Mapping", "Fully Associative", "N-way Set Associative" }; - private final int DIRECT = 0, FULL = 1, SET = 2; // NOTE: these have to match placementPolicyChoices order! - private String[] replacementPolicyChoices = {"LRU","Random"}; - private final int LRU = 0, RANDOM = 1; // NOTE: these have to match replacementPolicyChoices order! - private String[] cacheSetSizeChoices; // will change dynamically based on the other selections - private int defaultCacheBlockSizeIndex = 2; - private int defaultCacheBlockCountIndex = 3; - private int defaultPlacementPolicyIndex = DIRECT; - private int defaultReplacementPolicyIndex = LRU; - private int defaultCacheSetSizeIndex = 0; - - // Cache-related data structures - private AbstractCache theCache; - private int memoryAccessCount, cacheHitCount, cacheMissCount; - private double cacheHitRate; - - // RNG used for random replacement policy. For testing, set seed for reproducible stream - private Random randu = new Random(0); - - /** - * Simple constructor, likely used to run a stand-alone cache simulator. - * @param title String containing title for title bar - * @param heading String containing text for heading shown in upper part of window. - */ - public CacheSimulator(String title, String heading) { - super(title,heading); - } - - /** - * Simple constructor, likely used by the MARS Tools menu mechanism - */ - public CacheSimulator() { - super ("Data Cache Simulation Tool, "+version, heading); - } - - - /** - * Main provided for pure stand-alone use. Recommended stand-alone use is to write a - * driver program that instantiates a CacheSimulator object then invokes its go() method. - * "stand-alone" means it is not invoked from the MARS Tools menu. "Pure" means there - * is no driver program to invoke the Cache Simulator. - */ - public static void main(String[] args) { - new CacheSimulator("Data Cache Simulator stand-alone, "+version,heading).go(); - } - - - /** - * Required MarsTool method to return Tool name. - * @return Tool name. MARS will display this in menu item. - */ - public String getName() { - return "Data Cache Simulator"; - } - - - /** - * Method that constructs the main cache simulator display area. It is organized vertically - * into three major components: the cache configuration which an be modified - * using combo boxes, the cache performance which is updated as the - * attached MIPS program executes, and the runtime log which is optionally used - * to display log of each cache access. - * @return the GUI component containing these three areas - */ - protected JComponent buildMainDisplayArea() { - // OVERALL STRUCTURE OF MAIN UI (CENTER) - Box results = Box.createVerticalBox(); - results.add(buildOrganizationArea()); - results.add(buildPerformanceArea()); - results.add(buildLogArea()); - return results; - } - - //////////////////////////////////////////////////////////////////////////// - private JComponent buildLogArea() { - logPanel = new JPanel(); - TitledBorder ltb =new TitledBorder("Runtime Log"); - ltb.setTitleJustification(TitledBorder.CENTER); - logPanel.setBorder(ltb); - logShow = new JCheckBox("Enabled",debug); - logShow.addItemListener( - new ItemListener() { - public void itemStateChanged(ItemEvent e) { - debug = e.getStateChange() == ItemEvent.SELECTED; - resetLogDisplay(); - logText.setEnabled(debug); - logText.setBackground( debug ? Color.WHITE : logPanel.getBackground() ); - } - }); - logPanel.add(logShow); - logText = new JTextArea(5,70); - logText.setEnabled(debug); - logText.setBackground( debug ? Color.WHITE : logPanel.getBackground() ); - logText.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); - logText.setToolTipText("Displays cache activity log if enabled"); - logScroll = new JScrollPane(logText,JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - logPanel.add(logScroll); - return logPanel; - } - - - //////////////////////////////////////////////////////////////////////////// - private JComponent buildOrganizationArea() { - JPanel organization = new JPanel(new GridLayout(3,2)); - TitledBorder otb =new TitledBorder("Cache Organization"); - otb.setTitleJustification(TitledBorder.CENTER); - organization.setBorder(otb); - cachePlacementSelector = new JComboBox(placementPolicyChoices); - cachePlacementSelector.setEditable(false); - cachePlacementSelector.setBackground(backgroundColor); - cachePlacementSelector.setSelectedIndex(defaultPlacementPolicyIndex); - cachePlacementSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - updateCacheSetSizeSelector(); - reset(); - } - }); - - cacheReplacementSelector = new JComboBox(replacementPolicyChoices); - cacheReplacementSelector.setEditable(false); - cacheReplacementSelector.setBackground(backgroundColor); - cacheReplacementSelector.setSelectedIndex(defaultReplacementPolicyIndex); - - cacheBlockSizeSelector = new JComboBox(cacheBlockSizeChoices); - cacheBlockSizeSelector.setEditable(false); - cacheBlockSizeSelector.setBackground(backgroundColor); - cacheBlockSizeSelector.setSelectedIndex(defaultCacheBlockSizeIndex); - cacheBlockSizeSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - updateCacheSizeDisplay(); - reset(); - } - }); - cacheBlockCountSelector = new JComboBox(cacheBlockCountChoices); - cacheBlockCountSelector.setEditable(false); - cacheBlockCountSelector.setBackground(backgroundColor); - cacheBlockCountSelector.setSelectedIndex(defaultCacheBlockCountIndex); - cacheBlockCountSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - updateCacheSetSizeSelector(); - theCache = createNewCache(); - resetCounts(); - updateDisplay(); - updateCacheSizeDisplay(); - animations.fillAnimationBoxWithCacheBlocks(); - } - }); - - cacheSetSizeSelector = new JComboBox(cacheSetSizeChoices); - cacheSetSizeSelector.setEditable(false); - cacheSetSizeSelector.setBackground(backgroundColor); - cacheSetSizeSelector.setSelectedIndex(defaultCacheSetSizeIndex); - cacheSetSizeSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - reset(); - } - }); - - // ALL COMPONENTS FOR "CACHE ORGANIZATION" SECTION - JPanel placementPolicyRow = getPanelWithBorderLayout(); - placementPolicyRow.setBorder(emptyBorder); - placementPolicyRow.add(new JLabel("Placement Policy "),BorderLayout.WEST); - placementPolicyRow.add(cachePlacementSelector,BorderLayout.EAST); - - JPanel replacementPolicyRow = getPanelWithBorderLayout(); - replacementPolicyRow.setBorder(emptyBorder); - replacementPolicyRow.add(new JLabel("Block Replacement Policy "),BorderLayout.WEST); + +/** + * A data cache simulator. It can be run either as a stand-alone Java application having access to the mars package, or + * through MARS as an item in its Tools menu. It makes maximum use of methods inherited from its abstract superclass + * AbstractMarsToolOrApp. Pete Sanderson, v 1.0: 16-18 October 2006, v 1.1: 7 November 2006. v 1.2: 23 December 2010. + *

Version 1.2 fixes a bug in the hit/miss animator under full or N-way set associative. It was + * animating the block of initial access (first block of set). Now it animates the block of final access (where address + * found or stored). Also added log display to GUI (previously System.out).

+ */ +public class CacheSimulator extends AbstractMarsToolAndApplication +{ + private static boolean debug = false; // controls display of debugging info + + private static final String version = "Version 1.2"; + + private static final String heading = "Simulate and illustrate data cache performance"; + + private final int DIRECT = 0, FULL = 1, SET = 2; // NOTE: these have to match placementPolicyChoices order! + + private final int LRU = 0, RANDOM = 1; // NOTE: these have to match replacementPolicyChoices order! + + // Major GUI components + private JComboBox cacheBlockSizeSelector, cacheBlockCountSelector, + cachePlacementSelector, cacheReplacementSelector, + cacheSetSizeSelector; + + private JTextField memoryAccessCountDisplay, cacheHitCountDisplay, cacheMissCountDisplay, + replacementPolicyDisplay, cachableAddressesDisplay, + cacheSizeDisplay; + + private JProgressBar cacheHitRateDisplay; + + private Animation animations; + + private JPanel logPanel; + + private JScrollPane logScroll; + + private JTextArea logText; + + private JCheckBox logShow; + + // Some GUI settings + private final EmptyBorder emptyBorder = new EmptyBorder(4, 4, 4, 4); + + private final Font countFonts = new Font("Times", Font.BOLD, 12); + + private final Color backgroundColor = Color.WHITE; + + // Values for Combo Boxes + private int[] cacheBlockSizeChoicesInt, cacheBlockCountChoicesInt; + + private final String[] cacheBlockSizeChoices = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048"}; + + private final String[] cacheBlockCountChoices = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048"}; + + private final String[] placementPolicyChoices = {"Direct Mapping", "Fully Associative", "N-way Set Associative"}; + + private final String[] replacementPolicyChoices = {"LRU", "Random"}; + + private String[] cacheSetSizeChoices; // will change dynamically based on the other selections + + private final int defaultCacheBlockSizeIndex = 2; + + private final int defaultCacheBlockCountIndex = 3; + + private final int defaultPlacementPolicyIndex = DIRECT; + + private final int defaultReplacementPolicyIndex = LRU; + + private final int defaultCacheSetSizeIndex = 0; + + // Cache-related data structures + private AbstractCache theCache; + + private int memoryAccessCount, cacheHitCount, cacheMissCount; + + private double cacheHitRate; + + // RNG used for random replacement policy. For testing, set seed for reproducible stream + private final Random randu = new Random(0); + + /** + * Simple constructor, likely used to run a stand-alone cache simulator. + * + * @param title String containing title for title bar + * @param heading String containing text for heading shown in upper part of window. + */ + public CacheSimulator(String title, String heading) + { + super(title, heading); + } + + /** + * Simple constructor, likely used by the MARS Tools menu mechanism + */ + public CacheSimulator() + { + super("Data Cache Simulation Tool, " + version, heading); + } + + + /** + * Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that + * instantiates a CacheSimulator object then invokes its go() method. "stand-alone" means it is not invoked from the + * MARS Tools menu. "Pure" means there is no driver program to invoke the Cache Simulator. + */ + public static void main(String[] args) + { + new CacheSimulator("Data Cache Simulator stand-alone, " + version, heading).go(); + } + + + /** + * Required MarsTool method to return Tool name. + * + * @return Tool name. MARS will display this in menu item. + */ + public String getName() + { + return "Data Cache Simulator"; + } + + + /** + * Method that constructs the main cache simulator display area. It is organized vertically into three major + * components: the cache configuration which an be modified using combo boxes, the cache performance which is + * updated as the attached MIPS program executes, and the runtime log which is optionally used to display log of + * each cache access. + * + * @return the GUI component containing these three areas + */ + protected JComponent buildMainDisplayArea() + { + // OVERALL STRUCTURE OF MAIN UI (CENTER) + Box results = Box.createVerticalBox(); + results.add(buildOrganizationArea()); + results.add(buildPerformanceArea()); + results.add(buildLogArea()); + return results; + } + + //////////////////////////////////////////////////////////////////////////// + private JComponent buildLogArea() + { + logPanel = new JPanel(); + TitledBorder ltb = new TitledBorder("Runtime Log"); + ltb.setTitleJustification(TitledBorder.CENTER); + logPanel.setBorder(ltb); + logShow = new JCheckBox("Enabled", debug); + logShow.addItemListener( + new ItemListener() + { + public void itemStateChanged(ItemEvent e) + { + debug = e.getStateChange() == ItemEvent.SELECTED; + resetLogDisplay(); + logText.setEnabled(debug); + logText.setBackground(debug ? Color.WHITE : logPanel.getBackground()); + } + }); + logPanel.add(logShow); + logText = new JTextArea(5, 70); + logText.setEnabled(debug); + logText.setBackground(debug ? Color.WHITE : logPanel.getBackground()); + logText.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12)); + logText.setToolTipText("Displays cache activity log if enabled"); + logScroll = new JScrollPane(logText, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + logPanel.add(logScroll); + return logPanel; + } + + + //////////////////////////////////////////////////////////////////////////// + private JComponent buildOrganizationArea() + { + JPanel organization = new JPanel(new GridLayout(3, 2)); + TitledBorder otb = new TitledBorder("Cache Organization"); + otb.setTitleJustification(TitledBorder.CENTER); + organization.setBorder(otb); + cachePlacementSelector = new JComboBox(placementPolicyChoices); + cachePlacementSelector.setEditable(false); + cachePlacementSelector.setBackground(backgroundColor); + cachePlacementSelector.setSelectedIndex(defaultPlacementPolicyIndex); + cachePlacementSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + updateCacheSetSizeSelector(); + reset(); + } + }); + + cacheReplacementSelector = new JComboBox(replacementPolicyChoices); + cacheReplacementSelector.setEditable(false); + cacheReplacementSelector.setBackground(backgroundColor); + cacheReplacementSelector.setSelectedIndex(defaultReplacementPolicyIndex); + + cacheBlockSizeSelector = new JComboBox(cacheBlockSizeChoices); + cacheBlockSizeSelector.setEditable(false); + cacheBlockSizeSelector.setBackground(backgroundColor); + cacheBlockSizeSelector.setSelectedIndex(defaultCacheBlockSizeIndex); + cacheBlockSizeSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + updateCacheSizeDisplay(); + reset(); + } + }); + cacheBlockCountSelector = new JComboBox(cacheBlockCountChoices); + cacheBlockCountSelector.setEditable(false); + cacheBlockCountSelector.setBackground(backgroundColor); + cacheBlockCountSelector.setSelectedIndex(defaultCacheBlockCountIndex); + cacheBlockCountSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + updateCacheSetSizeSelector(); + theCache = createNewCache(); + resetCounts(); + updateDisplay(); + updateCacheSizeDisplay(); + animations.fillAnimationBoxWithCacheBlocks(); + } + }); + + cacheSetSizeSelector = new JComboBox(cacheSetSizeChoices); + cacheSetSizeSelector.setEditable(false); + cacheSetSizeSelector.setBackground(backgroundColor); + cacheSetSizeSelector.setSelectedIndex(defaultCacheSetSizeIndex); + cacheSetSizeSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + reset(); + } + }); + + // ALL COMPONENTS FOR "CACHE ORGANIZATION" SECTION + JPanel placementPolicyRow = getPanelWithBorderLayout(); + placementPolicyRow.setBorder(emptyBorder); + placementPolicyRow.add(new JLabel("Placement Policy "), BorderLayout.WEST); + placementPolicyRow.add(cachePlacementSelector, BorderLayout.EAST); + + JPanel replacementPolicyRow = getPanelWithBorderLayout(); + replacementPolicyRow.setBorder(emptyBorder); + replacementPolicyRow.add(new JLabel("Block Replacement Policy "), BorderLayout.WEST); /* replacementPolicyDisplay = new JTextField("N/A",6); replacementPolicyDisplay.setEditable(false); replacementPolicyDisplay.setBackground(backgroundColor); replacementPolicyDisplay.setHorizontalAlignment(JTextField.RIGHT); */ - replacementPolicyRow.add(cacheReplacementSelector, BorderLayout.EAST); - - JPanel cacheSetSizeRow = getPanelWithBorderLayout(); - cacheSetSizeRow.setBorder(emptyBorder); - cacheSetSizeRow.add(new JLabel("Set size (blocks) "),BorderLayout.WEST); - cacheSetSizeRow.add(cacheSetSizeSelector,BorderLayout.EAST); - - // Cachable address range "selection" removed for now... + replacementPolicyRow.add(cacheReplacementSelector, BorderLayout.EAST); + + JPanel cacheSetSizeRow = getPanelWithBorderLayout(); + cacheSetSizeRow.setBorder(emptyBorder); + cacheSetSizeRow.add(new JLabel("Set size (blocks) "), BorderLayout.WEST); + cacheSetSizeRow.add(cacheSetSizeSelector, BorderLayout.EAST); + + // Cachable address range "selection" removed for now... /* JPanel cachableAddressesRow = getPanelWithBorderLayout(); cachableAddressesRow.setBorder(emptyBorder); @@ -266,639 +317,746 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. cachableAddressesDisplay.setHorizontalAlignment(JTextField.RIGHT); cachableAddressesRow.add(cachableAddressesDisplay, BorderLayout.EAST); */ - JPanel cacheNumberBlocksRow = getPanelWithBorderLayout(); - cacheNumberBlocksRow.setBorder(emptyBorder); - cacheNumberBlocksRow.add(new JLabel("Number of blocks "), BorderLayout.WEST); - cacheNumberBlocksRow.add(cacheBlockCountSelector, BorderLayout.EAST); - - JPanel cacheBlockSizeRow = getPanelWithBorderLayout(); - cacheBlockSizeRow.setBorder(emptyBorder); - cacheBlockSizeRow.add(new JLabel("Cache block size (words) "), BorderLayout.WEST); - cacheBlockSizeRow.add(cacheBlockSizeSelector, BorderLayout.EAST); - - JPanel cacheTotalSizeRow = getPanelWithBorderLayout(); - cacheTotalSizeRow.setBorder(emptyBorder); - cacheTotalSizeRow.add(new JLabel("Cache size (bytes) "), BorderLayout.WEST); - cacheSizeDisplay = new JTextField(8); - cacheSizeDisplay.setHorizontalAlignment(JTextField.RIGHT); - cacheSizeDisplay.setEditable(false); - cacheSizeDisplay.setBackground(backgroundColor); - cacheSizeDisplay.setFont(countFonts); - cacheTotalSizeRow.add(cacheSizeDisplay, BorderLayout.EAST); - updateCacheSizeDisplay(); - - // Lay 'em out in the grid... - organization.add(placementPolicyRow); - organization.add(cacheNumberBlocksRow); - organization.add(replacementPolicyRow); - organization.add(cacheBlockSizeRow); - //organization.add(cachableAddressesRow); - organization.add(cacheSetSizeRow); - organization.add(cacheTotalSizeRow); - return organization; - } - - - //////////////////////////////////////////////////////////////////////////// - private JComponent buildPerformanceArea() { - JPanel performance = new JPanel(new GridLayout(1,2)); - TitledBorder ptb =new TitledBorder("Cache Performance"); - ptb.setTitleJustification(TitledBorder.CENTER); - performance.setBorder(ptb); - JPanel memoryAccessCountRow = getPanelWithBorderLayout(); - memoryAccessCountRow.setBorder(emptyBorder); - memoryAccessCountRow.add(new JLabel("Memory Access Count "), BorderLayout.WEST); - memoryAccessCountDisplay = new JTextField(10); - memoryAccessCountDisplay.setHorizontalAlignment(JTextField.RIGHT); - memoryAccessCountDisplay.setEditable(false); - memoryAccessCountDisplay.setBackground(backgroundColor); - memoryAccessCountDisplay.setFont(countFonts); - memoryAccessCountRow.add(memoryAccessCountDisplay, BorderLayout.EAST); - - JPanel cacheHitCountRow = getPanelWithBorderLayout(); - cacheHitCountRow.setBorder(emptyBorder); - cacheHitCountRow.add(new JLabel("Cache Hit Count "), BorderLayout.WEST); - cacheHitCountDisplay = new JTextField(10); - cacheHitCountDisplay.setHorizontalAlignment(JTextField.RIGHT); - cacheHitCountDisplay.setEditable(false); - cacheHitCountDisplay.setBackground(backgroundColor); - cacheHitCountDisplay.setFont(countFonts); - cacheHitCountRow.add(cacheHitCountDisplay, BorderLayout.EAST); - - JPanel cacheMissCountRow = getPanelWithBorderLayout(); - cacheMissCountRow.setBorder(emptyBorder); - cacheMissCountRow.add(new JLabel("Cache Miss Count "), BorderLayout.WEST); - cacheMissCountDisplay = new JTextField(10); - cacheMissCountDisplay.setHorizontalAlignment(JTextField.RIGHT); - cacheMissCountDisplay.setEditable(false); - cacheMissCountDisplay.setBackground(backgroundColor); - cacheMissCountDisplay.setFont(countFonts); - cacheMissCountRow.add(cacheMissCountDisplay, BorderLayout.EAST); - - JPanel cacheHitRateRow = getPanelWithBorderLayout(); - cacheHitRateRow.setBorder(emptyBorder); - cacheHitRateRow.add(new JLabel("Cache Hit Rate "), BorderLayout.WEST); - cacheHitRateDisplay = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100); - cacheHitRateDisplay.setStringPainted(true); - cacheHitRateDisplay.setForeground(Color.BLUE); - cacheHitRateDisplay.setBackground(backgroundColor); - cacheHitRateDisplay.setFont(countFonts); - cacheHitRateRow.add(cacheHitRateDisplay, BorderLayout.EAST); - - resetCounts(); - updateDisplay(); - - // Vertically align these 4 measures in a grid, then add to left column of main grid. - JPanel performanceMeasures = new JPanel(new GridLayout(4,1)); - performanceMeasures.add(memoryAccessCountRow); - performanceMeasures.add(cacheHitCountRow); - performanceMeasures.add(cacheMissCountRow); - performanceMeasures.add(cacheHitRateRow); - performance.add(performanceMeasures); - - // LET'S TRY SOME ANIMATION ON THE RIGHT SIDE... - animations = new Animation(); - animations.fillAnimationBoxWithCacheBlocks(); - JPanel animationsPanel = new JPanel(new GridLayout(1,2)); - Box animationsLabel = Box.createVerticalBox(); - JPanel tableTitle1 = new JPanel(new FlowLayout(FlowLayout.LEFT)); - JPanel tableTitle2 = new JPanel(new FlowLayout(FlowLayout.LEFT)); - tableTitle1.add(new JLabel("Cache Block Table")); - tableTitle2.add(new JLabel("(block 0 at top)")); - animationsLabel.add(tableTitle1); - animationsLabel.add(tableTitle2); - Dimension colorKeyBoxSize = new Dimension(8,8); - - JPanel emptyKey = new JPanel(new FlowLayout(FlowLayout.LEFT)); - JPanel emptyBox = new JPanel(); - emptyBox.setSize(colorKeyBoxSize); - emptyBox.setBackground(animations.defaultColor); - emptyBox.setBorder(BorderFactory.createLineBorder(Color.BLACK)); - emptyKey.add(emptyBox); - emptyKey.add(new JLabel(" = empty")); - - JPanel missBox = new JPanel(); - JPanel missKey = new JPanel(new FlowLayout(FlowLayout.LEFT)); - missBox.setSize(colorKeyBoxSize); - missBox.setBackground(animations.missColor); - missBox.setBorder(BorderFactory.createLineBorder(Color.BLACK)); - missKey.add(missBox); - missKey.add(new JLabel(" = miss")); - - JPanel hitKey = new JPanel(new FlowLayout(FlowLayout.LEFT)); - JPanel hitBox = new JPanel(); - hitBox.setSize(colorKeyBoxSize); - hitBox.setBackground(animations.hitColor); - hitBox.setBorder(BorderFactory.createLineBorder(Color.BLACK)); - hitKey.add(hitBox); - hitKey.add(new JLabel(" = hit")); - - animationsLabel.add(emptyKey); - animationsLabel.add(hitKey); - animationsLabel.add(missKey); - animationsLabel.add(Box.createVerticalGlue()); - animationsPanel.add(animationsLabel); - animationsPanel.add(animations.getAnimationBox()); - - performance.add(animationsPanel); - return performance; - } - - - - ////////////////////////////////////////////////////////////////////////////////////// - // Rest of the protected methods. These override do-nothing methods inherited from - // the abstract superclass. - ////////////////////////////////////////////////////////////////////////////////////// - - /** - * Apply caching policies and update display when connected MIPS program accesses (data) memory. - * @param memory the attached memory - * @param accessNotice information provided by memory in MemoryAccessNotice object - */ - protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) { - MemoryAccessNotice notice = (MemoryAccessNotice) accessNotice; - memoryAccessCount++; - CacheAccessResult cacheAccessResult = theCache.isItAHitThenReadOnMiss(notice.getAddress()); - if (cacheAccessResult.isHit()) { + JPanel cacheNumberBlocksRow = getPanelWithBorderLayout(); + cacheNumberBlocksRow.setBorder(emptyBorder); + cacheNumberBlocksRow.add(new JLabel("Number of blocks "), BorderLayout.WEST); + cacheNumberBlocksRow.add(cacheBlockCountSelector, BorderLayout.EAST); + + JPanel cacheBlockSizeRow = getPanelWithBorderLayout(); + cacheBlockSizeRow.setBorder(emptyBorder); + cacheBlockSizeRow.add(new JLabel("Cache block size (words) "), BorderLayout.WEST); + cacheBlockSizeRow.add(cacheBlockSizeSelector, BorderLayout.EAST); + + JPanel cacheTotalSizeRow = getPanelWithBorderLayout(); + cacheTotalSizeRow.setBorder(emptyBorder); + cacheTotalSizeRow.add(new JLabel("Cache size (bytes) "), BorderLayout.WEST); + cacheSizeDisplay = new JTextField(8); + cacheSizeDisplay.setHorizontalAlignment(JTextField.RIGHT); + cacheSizeDisplay.setEditable(false); + cacheSizeDisplay.setBackground(backgroundColor); + cacheSizeDisplay.setFont(countFonts); + cacheTotalSizeRow.add(cacheSizeDisplay, BorderLayout.EAST); + updateCacheSizeDisplay(); + + // Lay 'em out in the grid... + organization.add(placementPolicyRow); + organization.add(cacheNumberBlocksRow); + organization.add(replacementPolicyRow); + organization.add(cacheBlockSizeRow); + //organization.add(cachableAddressesRow); + organization.add(cacheSetSizeRow); + organization.add(cacheTotalSizeRow); + return organization; + } + + + //////////////////////////////////////////////////////////////////////////// + private JComponent buildPerformanceArea() + { + JPanel performance = new JPanel(new GridLayout(1, 2)); + TitledBorder ptb = new TitledBorder("Cache Performance"); + ptb.setTitleJustification(TitledBorder.CENTER); + performance.setBorder(ptb); + JPanel memoryAccessCountRow = getPanelWithBorderLayout(); + memoryAccessCountRow.setBorder(emptyBorder); + memoryAccessCountRow.add(new JLabel("Memory Access Count "), BorderLayout.WEST); + memoryAccessCountDisplay = new JTextField(10); + memoryAccessCountDisplay.setHorizontalAlignment(JTextField.RIGHT); + memoryAccessCountDisplay.setEditable(false); + memoryAccessCountDisplay.setBackground(backgroundColor); + memoryAccessCountDisplay.setFont(countFonts); + memoryAccessCountRow.add(memoryAccessCountDisplay, BorderLayout.EAST); + + JPanel cacheHitCountRow = getPanelWithBorderLayout(); + cacheHitCountRow.setBorder(emptyBorder); + cacheHitCountRow.add(new JLabel("Cache Hit Count "), BorderLayout.WEST); + cacheHitCountDisplay = new JTextField(10); + cacheHitCountDisplay.setHorizontalAlignment(JTextField.RIGHT); + cacheHitCountDisplay.setEditable(false); + cacheHitCountDisplay.setBackground(backgroundColor); + cacheHitCountDisplay.setFont(countFonts); + cacheHitCountRow.add(cacheHitCountDisplay, BorderLayout.EAST); + + JPanel cacheMissCountRow = getPanelWithBorderLayout(); + cacheMissCountRow.setBorder(emptyBorder); + cacheMissCountRow.add(new JLabel("Cache Miss Count "), BorderLayout.WEST); + cacheMissCountDisplay = new JTextField(10); + cacheMissCountDisplay.setHorizontalAlignment(JTextField.RIGHT); + cacheMissCountDisplay.setEditable(false); + cacheMissCountDisplay.setBackground(backgroundColor); + cacheMissCountDisplay.setFont(countFonts); + cacheMissCountRow.add(cacheMissCountDisplay, BorderLayout.EAST); + + JPanel cacheHitRateRow = getPanelWithBorderLayout(); + cacheHitRateRow.setBorder(emptyBorder); + cacheHitRateRow.add(new JLabel("Cache Hit Rate "), BorderLayout.WEST); + cacheHitRateDisplay = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100); + cacheHitRateDisplay.setStringPainted(true); + cacheHitRateDisplay.setForeground(Color.BLUE); + cacheHitRateDisplay.setBackground(backgroundColor); + cacheHitRateDisplay.setFont(countFonts); + cacheHitRateRow.add(cacheHitRateDisplay, BorderLayout.EAST); + + resetCounts(); + updateDisplay(); + + // Vertically align these 4 measures in a grid, then add to left column of main grid. + JPanel performanceMeasures = new JPanel(new GridLayout(4, 1)); + performanceMeasures.add(memoryAccessCountRow); + performanceMeasures.add(cacheHitCountRow); + performanceMeasures.add(cacheMissCountRow); + performanceMeasures.add(cacheHitRateRow); + performance.add(performanceMeasures); + + // LET'S TRY SOME ANIMATION ON THE RIGHT SIDE... + animations = new Animation(); + animations.fillAnimationBoxWithCacheBlocks(); + JPanel animationsPanel = new JPanel(new GridLayout(1, 2)); + Box animationsLabel = Box.createVerticalBox(); + JPanel tableTitle1 = new JPanel(new FlowLayout(FlowLayout.LEFT)); + JPanel tableTitle2 = new JPanel(new FlowLayout(FlowLayout.LEFT)); + tableTitle1.add(new JLabel("Cache Block Table")); + tableTitle2.add(new JLabel("(block 0 at top)")); + animationsLabel.add(tableTitle1); + animationsLabel.add(tableTitle2); + Dimension colorKeyBoxSize = new Dimension(8, 8); + + JPanel emptyKey = new JPanel(new FlowLayout(FlowLayout.LEFT)); + JPanel emptyBox = new JPanel(); + emptyBox.setSize(colorKeyBoxSize); + emptyBox.setBackground(animations.defaultColor); + emptyBox.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + emptyKey.add(emptyBox); + emptyKey.add(new JLabel(" = empty")); + + JPanel missBox = new JPanel(); + JPanel missKey = new JPanel(new FlowLayout(FlowLayout.LEFT)); + missBox.setSize(colorKeyBoxSize); + missBox.setBackground(animations.missColor); + missBox.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + missKey.add(missBox); + missKey.add(new JLabel(" = miss")); + + JPanel hitKey = new JPanel(new FlowLayout(FlowLayout.LEFT)); + JPanel hitBox = new JPanel(); + hitBox.setSize(colorKeyBoxSize); + hitBox.setBackground(animations.hitColor); + hitBox.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + hitKey.add(hitBox); + hitKey.add(new JLabel(" = hit")); + + animationsLabel.add(emptyKey); + animationsLabel.add(hitKey); + animationsLabel.add(missKey); + animationsLabel.add(Box.createVerticalGlue()); + animationsPanel.add(animationsLabel); + animationsPanel.add(animations.getAnimationBox()); + + performance.add(animationsPanel); + return performance; + } + + + ////////////////////////////////////////////////////////////////////////////////////// + // Rest of the protected methods. These override do-nothing methods inherited from + // the abstract superclass. + ////////////////////////////////////////////////////////////////////////////////////// + + /** + * Apply caching policies and update display when connected MIPS program accesses (data) memory. + * + * @param memory the attached memory + * @param accessNotice information provided by memory in MemoryAccessNotice object + */ + protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) + { + MemoryAccessNotice notice = (MemoryAccessNotice) accessNotice; + memoryAccessCount++; + CacheAccessResult cacheAccessResult = theCache.isItAHitThenReadOnMiss(notice.getAddress()); + if (cacheAccessResult.isHit()) + { cacheHitCount++; animations.showHit(cacheAccessResult.getBlock()); - } - else { + } + else + { cacheMissCount++; animations.showMiss(cacheAccessResult.getBlock()); - } - cacheHitRate = cacheHitCount / (double)memoryAccessCount; - } - - - /** - * Initialize all JComboBox choice structures not already initialized at declaration. - * Also creates initial default cache object. Overrides inherited method that does nothing. - */ - protected void initializePreGUI() { - cacheBlockSizeChoicesInt = new int[cacheBlockSizeChoices.length]; - for (int i=0; itotalVerticalPixels)? 1 : totalVerticalPixels/numberOfBlocks; + int blockPixelHeight = (numberOfBlocks > totalVerticalPixels) ? 1 : totalVerticalPixels / numberOfBlocks; int blockPixelWidth = 40; Dimension blockDimension = new Dimension(blockPixelWidth, blockPixelHeight); blocks = new JTextField[numberOfBlocks]; - for (int i=0; i0){ - CounterValue--; - } - else{ - CounterValue=CounterValueMax; - if((Coprocessor0.getValue(Coprocessor0.STATUS) & 2)==0){ - mars.simulator.Simulator.externalInterruptingDevice = /*Exceptions.*/EXTERNAL_INTERRUPT_TIMER; - } - } - } - protected void reset(){ - sevenSegPanel.resetSevenSegment(); - hexaKeyPanel.resetHexaKeyboard(); - SecondCounter.resetOneSecondCounter(); + + public DigitalLabSim() + { + super(heading + ", " + version, heading); } - protected JComponent buildMainDisplayArea() { - panelTools=new JPanel(new GridLayout(1,2)); - sevenSegPanel=new SevenSegmentPanel(); - panelTools.add(sevenSegPanel); - hexaKeyPanel = new HexaKeyboard(); - panelTools.add(hexaKeyPanel); - SecondCounter= new OneSecondCounter(); - return panelTools; + + public static void main(String[] args) + { + new DigitalLabSim(heading + ", " + version, heading).go(); } - private synchronized void updateMMIOControlAndData(int dataAddr, int dataValue) { - if (!this.isBeingUsedAsAMarsTool || (this.isBeingUsedAsAMarsTool && connectButton.isConnected())) { - synchronized (Globals.memoryAndRegistersLock) { - try { - Globals.memory.setByte(dataAddr, dataValue); - } - catch (AddressErrorException aee) { - System.out.println("Tool author specified incorrect MMIO address!"+aee); - System.exit(0); - } - } - if (Globals.getGui() != null && Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().getCodeHighlighting() ) { - Globals.getGui().getMainPane().getExecutePane().getDataSegmentWindow().updateValues(); - } + + public String getName() + { + return "Digital Lab Sim"; + } + + protected void addAsObserver() + { + addAsObserver(IN_ADRESS_DISPLAY_1, IN_ADRESS_DISPLAY_1); + addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); + } + + public void update(Observable ressource, Object accessNotice) + { + MemoryAccessNotice notice = (MemoryAccessNotice) accessNotice; + int address = notice.getAddress(); + char value = (char) notice.getValue(); + if (address == IN_ADRESS_DISPLAY_1) + { + updateSevenSegment(1, value); } - } - protected JComponent getHelpComponent() { - final String helpContent = - " This tool is composed of 3 parts : two seven-segment displays, an hexadecimal keyboard and counter \n"+ - "Seven segment display\n"+ - " Byte value at address 0xFFFF0010 : command right seven segment display \n "+ - " Byte value at address 0xFFFF0011 : command left seven segment display \n "+ - " Each bit of these two bytes are connected to segments (bit 0 for a segment, 1 for b segment and 7 for point \n \n"+ - "Hexadecimal keyboard\n"+ - " Byte value at address 0xFFFF0012 : command row number of hexadecimal keyboard (bit 0 to 3) and enable keyboard interrupt (bit 7) \n" + - " Byte value at address 0xFFFF0014 : receive row and column of the key pressed, 0 if not key pressed \n"+ - " The mips program have to scan, one by one, each row (send 1,2,4,8...)"+ - " and then observe if a key is pressed (that mean byte value at adresse 0xFFFF0014 is different from zero). "+ - " This byte value is composed of row number (4 left bits) and column number (4 right bits)"+ - " Here you'll find the code for each key : 0x11,0x21,0x41,0x81,0x12,0x22,0x42,0x82,0x14,0x24,0x44,0x84,0x18,0x28,0x48,0x88. \n"+ - " For exemple key number 2 return 0x41, that mean the key is on column 3 and row 1. \n"+ - " If keyboard interruption is enable, an exception is started, with cause register bit number 11 set.\n \n"+ - "Counter\n"+ - " Byte value at address 0xFFFF0013 : If one bit of this byte is set, the counter interruption is enable.\n"+ - " If counter interruption is enable, every 30 instructions, an exception is started with cause register bit number 10.\n" + - " (contributed by Didier Teifreto, dteifreto@lifc.univ-fcomte.fr)" - ; + else if (address == IN_ADRESS_DISPLAY_2) + { + updateSevenSegment(0, value); + } + else if (address == IN_ADRESS_HEXA_KEYBOARD) + { + updateHexaKeyboard(value); + } + else if (address == IN_ADRESS_COUNTER) + { + updateOneSecondCounter(value); + } + if (CounterInterruptOnOff) + { + if (CounterValue > 0) + { + CounterValue--; + } + else + { + CounterValue = CounterValueMax; + if ((Coprocessor0.getValue(Coprocessor0.STATUS) & 2) == 0) + { + mars.simulator.Simulator.externalInterruptingDevice = /*Exceptions.*/EXTERNAL_INTERRUPT_TIMER; + } + } + } + } + + protected void reset() + { + sevenSegPanel.resetSevenSegment(); + hexaKeyPanel.resetHexaKeyboard(); + SecondCounter.resetOneSecondCounter(); + } + + protected JComponent buildMainDisplayArea() + { + panelTools = new JPanel(new GridLayout(1, 2)); + sevenSegPanel = new SevenSegmentPanel(); + panelTools.add(sevenSegPanel); + hexaKeyPanel = new HexaKeyboard(); + panelTools.add(hexaKeyPanel); + SecondCounter = new OneSecondCounter(); + return panelTools; + } + + private synchronized void updateMMIOControlAndData(int dataAddr, int dataValue) + { + if (!this.isBeingUsedAsAMarsTool || (this.isBeingUsedAsAMarsTool && connectButton.isConnected())) + { + synchronized (Globals.memoryAndRegistersLock) + { + try + { + Globals.memory.setByte(dataAddr, dataValue); + } + catch (AddressErrorException aee) + { + System.out.println("Tool author specified incorrect MMIO address!" + aee); + System.exit(0); + } + } + if (Globals.getGui() != null && Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().getCodeHighlighting()) + { + Globals.getGui().getMainPane().getExecutePane().getDataSegmentWindow().updateValues(); + } + } + } + + protected JComponent getHelpComponent() + { + final String helpContent = + " This tool is composed of 3 parts : two seven-segment displays, an hexadecimal keyboard and counter \n" + + "Seven segment display\n" + + " Byte value at address 0xFFFF0010 : command right seven segment display \n " + + " Byte value at address 0xFFFF0011 : command left seven segment display \n " + + " Each bit of these two bytes are connected to segments (bit 0 for a segment, 1 for b segment and 7 for point \n \n" + + "Hexadecimal keyboard\n" + + " Byte value at address 0xFFFF0012 : command row number of hexadecimal keyboard (bit 0 to 3) and enable keyboard interrupt (bit 7) \n" + + " Byte value at address 0xFFFF0014 : receive row and column of the key pressed, 0 if not key pressed \n" + + " The mips program have to scan, one by one, each row (send 1,2,4,8...)" + + " and then observe if a key is pressed (that mean byte value at adresse 0xFFFF0014 is different from zero). " + + " This byte value is composed of row number (4 left bits) and column number (4 right bits)" + + " Here you'll find the code for each key : 0x11,0x21,0x41,0x81,0x12,0x22,0x42,0x82,0x14,0x24,0x44,0x84,0x18,0x28,0x48,0x88. \n" + + " For exemple key number 2 return 0x41, that mean the key is on column 3 and row 1. \n" + + " If keyboard interruption is enable, an exception is started, with cause register bit number 11 set.\n \n" + + "Counter\n" + + " Byte value at address 0xFFFF0013 : If one bit of this byte is set, the counter interruption is enable.\n" + + " If counter interruption is enable, every 30 instructions, an exception is started with cause register bit number 10.\n" + + " (contributed by Didier Teifreto, dteifreto@lifc.univ-fcomte.fr)"; JButton help = new JButton("Help"); help.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - JTextArea ja = new JTextArea(helpContent); - ja.setRows(20); - ja.setColumns(60); - ja.setLineWrap(true); - ja.setWrapStyleWord(true); - JOptionPane.showMessageDialog(theWindow, new JScrollPane(ja), + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + JTextArea ja = new JTextArea(helpContent); + ja.setRows(20); + ja.setColumns(60); + ja.setLineWrap(true); + ja.setWrapStyleWord(true); + JOptionPane.showMessageDialog(theWindow, new JScrollPane(ja), "Simulating the Hexa Keyboard and Seven segment display", JOptionPane.INFORMATION_MESSAGE); - } - }); - return help; + } + }); + return help; }/* ....................Seven Segment display start here................................... */ - /* ...........................Seven segment display start here ..............................*/ - public void updateSevenSegment(int number,char value) { - sevenSegPanel.display[number].modifyDisplay(value); + + /* ...........................Seven segment display start here ..............................*/ + public void updateSevenSegment(int number, char value) + { + sevenSegPanel.display[number].modifyDisplay(value); } - public class SevenSegmentDisplay extends JComponent { - public char aff; - public SevenSegmentDisplay(char aff){ - this.aff=aff; - this.setPreferredSize(new Dimension(60,80)); - } - public void modifyDisplay(char val){ - aff=val; - this.repaint(); - } - public void SwitchSegment(Graphics g,char segment){ - switch(segment){ - case 'a': //a segment - int[]pxa1={12,9,12}; - int[]pxa2={36,39,36}; - int[]pya={5,8,11}; - g.fillPolygon(pxa1,pya, 3); - g.fillPolygon(pxa2,pya, 3); - g.fillRect(12,5,24,6); - break; - case 'b': //b segment - int[]pxb={37,40,43}; - int[]pyb1={12,9,12}; - int[]pyb2={36,39,36}; - g.fillPolygon(pxb,pyb1, 3); - g.fillPolygon(pxb,pyb2, 3); - g.fillRect(37,12,6,24); - break; - case 'c': // c segment - int[]pxc={37,40,43}; - int[]pyc1={44,41,44}; - int[]pyc2={68,71,68}; - g.fillPolygon(pxc,pyc1, 3); - g.fillPolygon(pxc,pyc2, 3); - g.fillRect(37,44,6,24); - break; - case 'd': // d segment - int[]pxd1={12,9,12}; - int[]pxd2={36,39,36}; - int[]pyd={69,72,75}; - g.fillPolygon(pxd1,pyd, 3); - g.fillPolygon(pxd2,pyd, 3); - g.fillRect(12,69,24,6); - break; - case 'e': // e segment - int[]pxe={5,8,11}; - int[]pye1={44,41,44}; - int[]pye2={68,71,68}; - g.fillPolygon(pxe,pye1, 3); - g.fillPolygon(pxe,pye2, 3); - g.fillRect(5,44,6,24); - break; - case 'f': // f segment - int[]pxf={5,8,11}; - int[]pyf1={12,9,12}; - int[]pyf2={36,39,36}; - g.fillPolygon(pxf,pyf1, 3); - g.fillPolygon(pxf,pyf2, 3); - g.fillRect(5,12,6,24); - break; - case 'g': // g segment - int[]pxg1={12,9,12}; - int[]pxg2={36,39,36}; - int[]pyg={37,40,43}; - g.fillPolygon(pxg1,pyg, 3); - g.fillPolygon(pxg2,pyg, 3); - g.fillRect(12,37,24,6); - break; - case 'h': // decimal point - g.fillOval(49,68,8,8); - break; - } - } - public void paint(Graphics g) { - char c='a'; - while(c<='h'){ - if ((aff & 0x1) == 1) - g.setColor(Color.RED); - else - g.setColor(Color.LIGHT_GRAY); - SwitchSegment(g,c); - aff = (char)(aff >>>1); - c++; - } - } + + /* ...........................Seven segment display end here ..............................*/ + /* ....................Hexa Keyboard start here................................... */ + public void updateHexaKeyboard(char row) + { + int key = KeyBoardValueButtonClick; + if ((key != -1) && ((1 << (key / 4)) == (row & 0xF))) + { + updateMMIOControlAndData(OUT_ADRESS_HEXA_KEYBOARD, + (char) (1 << (key / 4)) | (1 << (4 + (key % 4)))); + } + else + { + updateMMIOControlAndData(OUT_ADRESS_HEXA_KEYBOARD, 0); + } + KeyboardInterruptOnOff = (row & 0xF0) != 0; } - public class SevenSegmentPanel extends JPanel { - public SevenSegmentDisplay[] display; - public SevenSegmentPanel(){ - int i; - FlowLayout fl= new FlowLayout(); - this.setLayout(fl); - display= new SevenSegmentDisplay[2]; - for (i=0;i<2;i++){ - display[i]= new SevenSegmentDisplay((char)(0)); - this.add(display[i]); - } - } - public void modifyDisplay(int num,char val){ - display[num].modifyDisplay(val); - display[num].repaint(); - } - public void resetSevenSegment() { - int i; - for (i=0;i<2;i++) - modifyDisplay(i, (char)0); - } + + /* ....................Hexa Keyboard end here................................... */ + /* ....................Timer start here................................... */ + public void updateOneSecondCounter(char value) + { + if (value != 0) + { + CounterInterruptOnOff = true; + CounterValue = CounterValueMax; + } + else + { + CounterInterruptOnOff = false; + } } - /* ...........................Seven segment display end here ..............................*/ -/* ....................Hexa Keyboard start here................................... */ - public void updateHexaKeyboard(char row) { - int key = KeyBoardValueButtonClick; - if ((key !=-1)&& ((1 << (key / 4))==(row & 0xF))){ - updateMMIOControlAndData(OUT_ADRESS_HEXA_KEYBOARD, - (char)(1 << (key / 4)) | (1 << (4+(key%4)))); - } - else{ - updateMMIOControlAndData(OUT_ADRESS_HEXA_KEYBOARD, 0); - } - if ((row & 0xF0) != 0) - KeyboardInterruptOnOff = true; - else - KeyboardInterruptOnOff = false; + + public class SevenSegmentDisplay extends JComponent + { + public char aff; + + public SevenSegmentDisplay(char aff) + { + this.aff = aff; + this.setPreferredSize(new Dimension(60, 80)); + } + + public void modifyDisplay(char val) + { + aff = val; + this.repaint(); + } + + public void SwitchSegment(Graphics g, char segment) + { + switch (segment) + { + case 'a': //a segment + int[] pxa1 = {12, 9, 12}; + int[] pxa2 = {36, 39, 36}; + int[] pya = {5, 8, 11}; + g.fillPolygon(pxa1, pya, 3); + g.fillPolygon(pxa2, pya, 3); + g.fillRect(12, 5, 24, 6); + break; + case 'b': //b segment + int[] pxb = {37, 40, 43}; + int[] pyb1 = {12, 9, 12}; + int[] pyb2 = {36, 39, 36}; + g.fillPolygon(pxb, pyb1, 3); + g.fillPolygon(pxb, pyb2, 3); + g.fillRect(37, 12, 6, 24); + break; + case 'c': // c segment + int[] pxc = {37, 40, 43}; + int[] pyc1 = {44, 41, 44}; + int[] pyc2 = {68, 71, 68}; + g.fillPolygon(pxc, pyc1, 3); + g.fillPolygon(pxc, pyc2, 3); + g.fillRect(37, 44, 6, 24); + break; + case 'd': // d segment + int[] pxd1 = {12, 9, 12}; + int[] pxd2 = {36, 39, 36}; + int[] pyd = {69, 72, 75}; + g.fillPolygon(pxd1, pyd, 3); + g.fillPolygon(pxd2, pyd, 3); + g.fillRect(12, 69, 24, 6); + break; + case 'e': // e segment + int[] pxe = {5, 8, 11}; + int[] pye1 = {44, 41, 44}; + int[] pye2 = {68, 71, 68}; + g.fillPolygon(pxe, pye1, 3); + g.fillPolygon(pxe, pye2, 3); + g.fillRect(5, 44, 6, 24); + break; + case 'f': // f segment + int[] pxf = {5, 8, 11}; + int[] pyf1 = {12, 9, 12}; + int[] pyf2 = {36, 39, 36}; + g.fillPolygon(pxf, pyf1, 3); + g.fillPolygon(pxf, pyf2, 3); + g.fillRect(5, 12, 6, 24); + break; + case 'g': // g segment + int[] pxg1 = {12, 9, 12}; + int[] pxg2 = {36, 39, 36}; + int[] pyg = {37, 40, 43}; + g.fillPolygon(pxg1, pyg, 3); + g.fillPolygon(pxg2, pyg, 3); + g.fillRect(12, 37, 24, 6); + break; + case 'h': // decimal point + g.fillOval(49, 68, 8, 8); + break; + } + } + + public void paint(Graphics g) + { + char c = 'a'; + while (c <= 'h') + { + if ((aff & 0x1) == 1) + { + g.setColor(Color.RED); + } + else + { + g.setColor(Color.LIGHT_GRAY); + } + SwitchSegment(g, c); + aff = (char) (aff >>> 1); + c++; + } + } } - public class HexaKeyboard extends JPanel{ - public JButton[] button; - public HexaKeyboard(){ - int i; - GridLayout layout = new GridLayout(4,4); - this.setLayout(layout); - button = new JButton[16]; - for (i=0;i<16;i++){ - button[i]= new JButton(Integer.toHexString(i)); - button[i].setBackground(Color.WHITE); - button[i].setMargin(new Insets(10,10,10,10)); - button[i].addMouseListener(new EcouteurClick(i)); - this.add(button[i]); - } - } - public void resetHexaKeyboard(){ - int i; - KeyBoardValueButtonClick=-1; - for(i=0;i<16;i++){ - button[i].setBackground(Color.WHITE); - } - } - public class EcouteurClick implements MouseListener{ - private int buttonValue; - public EcouteurClick(int val){ - buttonValue=val; - } - public void mouseEntered(MouseEvent arg0) { } - public void mouseExited(MouseEvent arg0) {} - public void mousePressed(MouseEvent arg0) {} - public void mouseReleased(MouseEvent arg0) {} - public void mouseClicked(MouseEvent arg0) { - int i; - if(KeyBoardValueButtonClick!=-1){//Button already pressed -> now realease - KeyBoardValueButtonClick = -1; - updateMMIOControlAndData(OUT_ADRESS_HEXA_KEYBOARD,0); - for(i=0;i<16;i++) - button[i].setBackground(Color.WHITE); - } - else{ // new button pressed - KeyBoardValueButtonClick = buttonValue; - button[KeyBoardValueButtonClick].setBackground(Color.GREEN); - if( KeyboardInterruptOnOff && (Coprocessor0.getValue(Coprocessor0.STATUS) & 2)==0){ - mars.simulator.Simulator.externalInterruptingDevice = /*Exceptions.*/EXTERNAL_INTERRUPT_HEXA_KEYBOARD; - } - } - } - } + + public class SevenSegmentPanel extends JPanel + { + public SevenSegmentDisplay[] display; + + public SevenSegmentPanel() + { + int i; + FlowLayout fl = new FlowLayout(); + this.setLayout(fl); + display = new SevenSegmentDisplay[2]; + for (i = 0; i < 2; i++) + { + display[i] = new SevenSegmentDisplay((char) (0)); + this.add(display[i]); + } + } + + public void modifyDisplay(int num, char val) + { + display[num].modifyDisplay(val); + display[num].repaint(); + } + + public void resetSevenSegment() + { + int i; + for (i = 0; i < 2; i++) + { + modifyDisplay(i, (char) 0); + } + } } -/* ....................Hexa Keyboard end here................................... */ -/* ....................Timer start here................................... */ - public void updateOneSecondCounter(char value) { - if (value !=0){ - CounterInterruptOnOff=true; - CounterValue=CounterValueMax; - } - else{ - CounterInterruptOnOff=false; - } + + public class HexaKeyboard extends JPanel + { + public JButton[] button; + + public HexaKeyboard() + { + int i; + GridLayout layout = new GridLayout(4, 4); + this.setLayout(layout); + button = new JButton[16]; + for (i = 0; i < 16; i++) + { + button[i] = new JButton(Integer.toHexString(i)); + button[i].setBackground(Color.WHITE); + button[i].setMargin(new Insets(10, 10, 10, 10)); + button[i].addMouseListener(new EcouteurClick(i)); + this.add(button[i]); + } + } + + public void resetHexaKeyboard() + { + int i; + KeyBoardValueButtonClick = -1; + for (i = 0; i < 16; i++) + { + button[i].setBackground(Color.WHITE); + } + } + + public class EcouteurClick implements MouseListener + { + private final int buttonValue; + + public EcouteurClick(int val) + { + buttonValue = val; + } + + public void mouseEntered(MouseEvent arg0) + { + } + + public void mouseExited(MouseEvent arg0) + { + } + + public void mousePressed(MouseEvent arg0) + { + } + + public void mouseReleased(MouseEvent arg0) + { + } + + public void mouseClicked(MouseEvent arg0) + { + int i; + if (KeyBoardValueButtonClick != -1) + {//Button already pressed -> now realease + KeyBoardValueButtonClick = -1; + updateMMIOControlAndData(OUT_ADRESS_HEXA_KEYBOARD, 0); + for (i = 0; i < 16; i++) + { + button[i].setBackground(Color.WHITE); + } + } + else + { // new button pressed + KeyBoardValueButtonClick = buttonValue; + button[KeyBoardValueButtonClick].setBackground(Color.GREEN); + if (KeyboardInterruptOnOff && (Coprocessor0.getValue(Coprocessor0.STATUS) & 2) == 0) + { + mars.simulator.Simulator.externalInterruptingDevice = /*Exceptions.*/EXTERNAL_INTERRUPT_HEXA_KEYBOARD; + } + } + } + } } - public class OneSecondCounter{ - public OneSecondCounter(){ - CounterInterruptOnOff=false; - } - public void resetOneSecondCounter(){ - CounterInterruptOnOff=false; - CounterValue=CounterValueMax; - } + + public class OneSecondCounter + { + public OneSecondCounter() + { + CounterInterruptOnOff = false; + } + + public void resetOneSecondCounter() + { + CounterInterruptOnOff = false; + CounterValue = CounterValueMax; + } } } diff --git a/src/main/java/mars/tools/FloatRepresentation.java b/src/main/java/mars/tools/FloatRepresentation.java index 6283b96..ecdf184 100644 --- a/src/main/java/mars/tools/FloatRepresentation.java +++ b/src/main/java/mars/tools/FloatRepresentation.java @@ -1,17 +1,19 @@ - package mars.tools; - import mars.*; - import mars.util.*; - import mars.assembler.*; - import mars.mips.instructions.*; - import mars.mips.hardware.*; - import java.util.*; - import java.io.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.event.*; - import javax.swing.border.*; - import javax.swing.text.html.*; +package mars.tools; + +import mars.Globals; +import mars.mips.hardware.AccessNotice; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.Register; +import mars.util.Binary; + +import javax.swing.*; +import javax.swing.border.TitledBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.Observable; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -40,868 +42,1035 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Tool to help students learn about IEEE 754 representation of 32 bit floating point values. This representation is + * used by MIPS "float" directive and instructions and also the Java (and most other languages) "float" data type. As + * written, it can ALMOST be adapted to 64 bit by changing a few constants. + */ +public class FloatRepresentation extends AbstractMarsToolAndApplication +{ + private static final String title = "Floating Point Representation, "; + + private static final String defaultHex = "00000000"; + + private static final String defaultDecimal = "0.0"; + + private static final String defaultBinarySign = "0"; + + private static final String defaultBinaryExponent = "00000000"; + + private static final String defaultBinaryFraction = "00000000000000000000000"; + + private static final int maxLengthHex = 8; + + private static final int maxLengthBinarySign = 1; + + private static final int maxLengthBinaryExponent = 8; + + private static final int maxLengthBinaryFraction = 23; + + private static final int maxLengthBinaryTotal = maxLengthBinarySign + maxLengthBinaryExponent + maxLengthBinaryFraction; + + private static final int maxLengthDecimal = 20; + + private static final String denormalizedLabel = " significand (denormalized - no 'hidden bit')"; + + private static final String normalizedLabel = " significand ('hidden bit' underlined) "; + + private static final Font instructionsFont = new Font("Arial", Font.PLAIN, 14); + + private static final Font hexDisplayFont = new Font("Courier", Font.PLAIN, 32); + + private static final Font binaryDisplayFont = new Font("Courier", Font.PLAIN, 18); + + private static final Font decimalDisplayFont = new Font("Courier", Font.PLAIN, 18); + + private static final Color hexDisplayColor = Color.red; + + private static final Color binaryDisplayColor = Color.black; + + private static final Color decimalDisplayColor = Color.blue; + + private static final String expansionFontTag = ""; + + private static final String instructionFontTag = ""; + + private static final int exponentBias = 127; // 32 bit floating point exponent bias + + //Put here because inner class cannot have static members. + private static final String zeroes = + "0000000000000000000000000000000000000000000000000000000000000000"; //64 + + private static final String HTMLspaces = "        "; + + private static final String version = "Version 1.1"; + + private static final String heading = "32-bit IEEE 754 Floating Point Representation"; + + private Register attachedRegister = null; + + private Register[] fpRegisters; + + private final FloatRepresentation thisFloatTool; + + // Panels to hold binary displays and decorations (labels, arrows) + private JPanel binarySignDecoratedDisplay, + binaryExponentDecoratedDisplay, binaryFractionDecoratedDisplay; + + // Editable fields for the hex, binary and decimal representations. + private JTextField hexDisplay, decimalDisplay, + binarySignDisplay, binaryExponentDisplay, binaryFractionDisplay; + + // Non-editable fields to display formula translating binary to decimal. + private JLabel expansionDisplay; + + private final JLabel significandLabel = new JLabel(denormalizedLabel, JLabel.CENTER); + + private BinaryToDecimalFormulaGraphic binaryToDecimalFormulaGraphic; + + // Non-editable field to display instructions + private InstructionsPane instructions; + + private final String defaultInstructions = "Modify any value then press the Enter key to update all values."; + /** - * Tool to help students learn about IEEE 754 representation of 32 bit - * floating point values. This representation is used by MIPS "float" - * directive and instructions and also the Java (and most other languages) - * "float" data type. As written, it can ALMOST be adapted to 64 bit by - * changing a few constants. - */ - public class FloatRepresentation extends AbstractMarsToolAndApplication { - private static String version = "Version 1.1"; - private static String heading = "32-bit IEEE 754 Floating Point Representation"; - private static final String title = "Floating Point Representation, "; - - private static final String defaultHex = "00000000"; - private static final String defaultDecimal = "0.0"; - private static final String defaultBinarySign = "0"; - private static final String defaultBinaryExponent = "00000000"; - private static final String defaultBinaryFraction = "00000000000000000000000"; - private static final int maxLengthHex = 8; - private static final int maxLengthBinarySign = 1; - private static final int maxLengthBinaryExponent = 8; - private static final int maxLengthBinaryFraction = 23; - private static final int maxLengthBinaryTotal = maxLengthBinarySign+maxLengthBinaryExponent+maxLengthBinaryFraction; - private static final int maxLengthDecimal = 20; - private static final String denormalizedLabel = " significand (denormalized - no 'hidden bit')"; - private static final String normalizedLabel = " significand ('hidden bit' underlined) "; - private static final Font instructionsFont = new Font("Arial",Font.PLAIN,14); - private static final Font hexDisplayFont = new Font("Courier",Font.PLAIN,32); - private static final Font binaryDisplayFont = new Font("Courier",Font.PLAIN,18); - private static final Font decimalDisplayFont = new Font("Courier",Font.PLAIN,18); - private static final Color hexDisplayColor = Color.red; - private static final Color binaryDisplayColor = Color.black; - private static final Color decimalDisplayColor = Color.blue; - private static final String expansionFontTag = ""; - private static final String instructionFontTag = ""; - private static final int exponentBias = 127; // 32 bit floating point exponent bias - - private Register attachedRegister = null; - private Register[] fpRegisters; - private FloatRepresentation thisFloatTool; - // Panels to hold binary displays and decorations (labels, arrows) - private JPanel binarySignDecoratedDisplay, - binaryExponentDecoratedDisplay, binaryFractionDecoratedDisplay; - // Editable fields for the hex, binary and decimal representations. - private JTextField hexDisplay, decimalDisplay, - binarySignDisplay, binaryExponentDisplay, binaryFractionDisplay; - // Non-editable fields to display formula translating binary to decimal. - private JLabel expansionDisplay; - private JLabel significandLabel = new JLabel(denormalizedLabel, JLabel.CENTER); - private BinaryToDecimalFormulaGraphic binaryToDecimalFormulaGraphic; - // Non-editable field to display instructions - private InstructionsPane instructions; - private String defaultInstructions = "Modify any value then press the Enter key to update all values."; - - /** - * Simple constructor, likely used to run a stand-alone memory reference visualizer. - * @param title String containing title for title bar - * @param heading String containing text for heading shown in upper part of window. - */ - public FloatRepresentation(String title, String heading) { - super(title,heading); - thisFloatTool = this; - } - - /** - * Simple constructor, likely used by the MARS Tools menu mechanism - */ - public FloatRepresentation() { - this(title+version, heading); - } - - /** - * Main provided for pure stand-alone use. Recommended stand-alone use is to write a - * driver program that instantiates a FloatRepresentation object then invokes its go() method. - * "stand-alone" means it is not invoked from the MARS Tools menu. "Pure" means there - * is no driver program to invoke the application. - */ - public static void main(String[] args) { - new FloatRepresentation(title+version,heading).go(); - } - - /** - * Fetch tool name (for display in MARS Tools menu) - * @return String containing tool name - */ - public String getName() { - return "Floating Point Representation"; - } - - /** - * Override the inherited method, which registers us as an Observer over the static data segment - * (starting address 0x10010000) only. This version will register us as observer over the selected - * floating point register, if any. If no register is selected, it will not do anything. - * If you use the inherited GUI buttons, this method is invoked when you click "Connect" button - * on MarsTool or the "Assemble and Run" button on a Mars-based app. - */ - protected void addAsObserver() { - addAsObserver(attachedRegister); - } - - /** - * Delete this app/tool as an Observer of the attached register. This overrides - * the inherited version which deletes only as an Observer of memory. - * This method is called when the default "Disconnect" button on a MarsTool is selected or - * when the MIPS program execution triggered by the default "Assemble and run" on a stand-alone - * Mars app terminates (e.g. when the button is re-enabled). - */ - protected void deleteAsObserver() { - deleteAsObserver(attachedRegister); - } - - /** - * Method that constructs the main display area. This will be vertically sandwiched between - * the standard heading area at the top and the control area at the bottom. - * @return the GUI component containing the application/tool-specific part of the user interface - */ - protected JComponent buildMainDisplayArea() { - return buildDisplayArea(); - } - - /** - * Override inherited update() to update display when "attached" register is modified - * either by MIPS program or by user editing it on the MARS user interface. - * The latter is the reason for overriding the inherited update() method. - * The inherited method will filter out notices triggered by the MARS GUI or the user. - * @param register the attached register - * @param accessNotice information provided by register in RegisterAccessNotice object - */ - public void update(Observable register, Object accessNotice) { - if (((AccessNotice)accessNotice).getAccessType()==AccessNotice.WRITE) { + * Simple constructor, likely used to run a stand-alone memory reference visualizer. + * + * @param title String containing title for title bar + * @param heading String containing text for heading shown in upper part of window. + */ + public FloatRepresentation(String title, String heading) + { + super(title, heading); + thisFloatTool = this; + } + + /** + * Simple constructor, likely used by the MARS Tools menu mechanism + */ + public FloatRepresentation() + { + this(title + version, heading); + } + + /** + * Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that + * instantiates a FloatRepresentation object then invokes its go() method. "stand-alone" means it is not invoked + * from the MARS Tools menu. "Pure" means there is no driver program to invoke the application. + */ + public static void main(String[] args) + { + new FloatRepresentation(title + version, heading).go(); + } + + /** + * Fetch tool name (for display in MARS Tools menu) + * + * @return String containing tool name + */ + public String getName() + { + return "Floating Point Representation"; + } + + /** + * Override the inherited method, which registers us as an Observer over the static data segment (starting address + * 0x10010000) only. This version will register us as observer over the selected floating point register, if any. + * If no register is selected, it will not do anything. If you use the inherited GUI buttons, this method is invoked + * when you click "Connect" button on MarsTool or the "Assemble and Run" button on a Mars-based app. + */ + protected void addAsObserver() + { + addAsObserver(attachedRegister); + } + + /** + * Delete this app/tool as an Observer of the attached register. This overrides the inherited version which deletes + * only as an Observer of memory. This method is called when the default "Disconnect" button on a MarsTool is + * selected or when the MIPS program execution triggered by the default "Assemble and run" on a stand-alone Mars app + * terminates (e.g. when the button is re-enabled). + */ + protected void deleteAsObserver() + { + deleteAsObserver(attachedRegister); + } + + /** + * Method that constructs the main display area. This will be vertically sandwiched between the standard heading + * area at the top and the control area at the bottom. + * + * @return the GUI component containing the application/tool-specific part of the user interface + */ + protected JComponent buildMainDisplayArea() + { + return buildDisplayArea(); + } + + + ////////////////////////////////////////////////////////////////////////////////////// + // Private methods defined to support the above. + + /** + * Override inherited update() to update display when "attached" register is modified either by MIPS program or by + * user editing it on the MARS user interface. The latter is the reason for overriding the inherited update() + * method. The inherited method will filter out notices triggered by the MARS GUI or the user. + * + * @param register the attached register + * @param accessNotice information provided by register in RegisterAccessNotice object + */ + public void update(Observable register, Object accessNotice) + { + if (((AccessNotice) accessNotice).getAccessType() == AccessNotice.WRITE) + { updateDisplays(new FlavorsOfFloat().buildOneFromInt(attachedRegister.getValue())); - } - } - - /** - * Method to reset display values to 0 when the Reset button selected. - * If attached to a MIPS register at the time, the register will be reset as well. - * Overrides inherited method that does nothing. - */ - protected void reset() { - instructions.setText(defaultInstructions); - updateDisplaysAndRegister(new FlavorsOfFloat()); - } - - - ////////////////////////////////////////////////////////////////////////////////////// - // Private methods defined to support the above. - - protected JComponent buildDisplayArea() { - // Panel to hold all floating point dislay and editing components - Box mainPanel = Box.createVerticalBox(); - JPanel leftPanel = new JPanel(new GridLayout(5,1,0,0)); - JPanel rightPanel = new JPanel(new GridLayout(5,1,0,0)); - Box subMainPanel = Box.createHorizontalBox(); - subMainPanel.add(leftPanel); - subMainPanel.add(rightPanel); - mainPanel.add(subMainPanel); - - // Editable display for hexadecimal version of the float value - hexDisplay = new JTextField(defaultHex, maxLengthHex+1); - hexDisplay.setFont(hexDisplayFont); - hexDisplay.setForeground(hexDisplayColor); - hexDisplay.setHorizontalAlignment(JTextField.RIGHT); - hexDisplay.setToolTipText(""+maxLengthHex+"-digit hexadecimal (base 16) display"); - hexDisplay.setEditable(true); - hexDisplay.revalidate(); - hexDisplay.addKeyListener(new HexDisplayKeystrokeListener(8)); - - JPanel hexPanel = new JPanel(); - hexPanel.add(hexDisplay); - //################ Grid Row : Hexadecimal ################################## - leftPanel.add(hexPanel); - - HexToBinaryGraphicPanel hexToBinaryGraphic = new HexToBinaryGraphicPanel(); - //################ Grid Row : Hex-to-binary graphic ######################## - leftPanel.add(hexToBinaryGraphic); - - // Editable display for binary version of float value. - // It is split into 3 separately editable components (sign,exponent,fraction) - - binarySignDisplay = new JTextField(defaultBinarySign, maxLengthBinarySign+1); - binarySignDisplay.setFont(binaryDisplayFont); - binarySignDisplay.setForeground(binaryDisplayColor); - binarySignDisplay.setHorizontalAlignment(JTextField.RIGHT); - binarySignDisplay.setToolTipText("The sign bit"); - binarySignDisplay.setEditable(true); - binarySignDisplay.revalidate(); - - binaryExponentDisplay = new JTextField(defaultBinaryExponent, maxLengthBinaryExponent+1); - binaryExponentDisplay.setFont(binaryDisplayFont); - binaryExponentDisplay.setForeground(binaryDisplayColor); - binaryExponentDisplay.setHorizontalAlignment(JTextField.RIGHT); - binaryExponentDisplay.setToolTipText(""+maxLengthBinaryExponent+"-bit exponent"); - binaryExponentDisplay.setEditable(true); - binaryExponentDisplay.revalidate(); - - binaryFractionDisplay = new BinaryFractionDisplayTextField(defaultBinaryFraction, maxLengthBinaryFraction+1); - binaryFractionDisplay.setFont(binaryDisplayFont); - binaryFractionDisplay.setForeground(binaryDisplayColor); - binaryFractionDisplay.setHorizontalAlignment(JTextField.RIGHT); - binaryFractionDisplay.setToolTipText(""+maxLengthBinaryFraction+"-bit fraction"); - binaryFractionDisplay.setEditable(true); - binaryFractionDisplay.revalidate(); - - binarySignDisplay.addKeyListener(new BinaryDisplayKeystrokeListener(maxLengthBinarySign)); - binaryExponentDisplay.addKeyListener(new BinaryDisplayKeystrokeListener(maxLengthBinaryExponent)); - binaryFractionDisplay.addKeyListener(new BinaryDisplayKeystrokeListener(maxLengthBinaryFraction)); - JPanel binaryPanel = new JPanel(); - - binarySignDecoratedDisplay = new JPanel(new BorderLayout()); - binaryExponentDecoratedDisplay = new JPanel(new BorderLayout()); - binaryFractionDecoratedDisplay = new JPanel(new BorderLayout()); - binarySignDecoratedDisplay.add(binarySignDisplay,BorderLayout.CENTER); - binarySignDecoratedDisplay.add(new JLabel("sign",JLabel.CENTER),BorderLayout.SOUTH); - binaryExponentDecoratedDisplay.add(binaryExponentDisplay,BorderLayout.CENTER); - binaryExponentDecoratedDisplay.add(new JLabel("exponent",JLabel.CENTER),BorderLayout.SOUTH); - binaryFractionDecoratedDisplay.add(binaryFractionDisplay,BorderLayout.CENTER); - binaryFractionDecoratedDisplay.add(new JLabel("fraction",JLabel.CENTER),BorderLayout.SOUTH); - - binaryPanel.add(binarySignDecoratedDisplay); - binaryPanel.add(binaryExponentDecoratedDisplay); - binaryPanel.add(binaryFractionDecoratedDisplay); - - //################ Grid Row : Binary ################################## - leftPanel.add(binaryPanel); - - //################ Grid Row : Binary to decimal formula arrows ########## - binaryToDecimalFormulaGraphic = new BinaryToDecimalFormulaGraphic(); - binaryToDecimalFormulaGraphic.setBackground(leftPanel.getBackground()); - leftPanel.add(binaryToDecimalFormulaGraphic); - - // Non-Editable display for expansion of binary representation - - expansionDisplay = new JLabel(new FlavorsOfFloat().expansionString); - expansionDisplay.setFont(new Font("Monospaced",Font.PLAIN,12)); - expansionDisplay.setFocusable(false); // causes it to be skipped in "tab sequence". - expansionDisplay.setBackground(leftPanel.getBackground()); - JPanel expansionDisplayBox = new JPanel(new GridLayout(2,1)); - expansionDisplayBox.add(expansionDisplay); - expansionDisplayBox.add(significandLabel); // initialized at top - //################ Grid Row : Formula mapping binary to decimal ######## - leftPanel.add(expansionDisplayBox); - - // Editable display for decimal version of float value. - decimalDisplay = new JTextField(defaultDecimal, maxLengthDecimal+1); - decimalDisplay.setFont(decimalDisplayFont); - decimalDisplay.setForeground(decimalDisplayColor); - decimalDisplay.setHorizontalAlignment(JTextField.RIGHT); - decimalDisplay.setToolTipText("Decimal floating point value"); - decimalDisplay.setMargin(new Insets(0,0,0,0)); - decimalDisplay.setEditable(true); - decimalDisplay.revalidate(); - decimalDisplay.addKeyListener(new DecimalDisplayKeystokeListenter()); - Box decimalDisplayBox = Box.createVerticalBox(); - decimalDisplayBox.add(Box.createVerticalStrut(5)); - decimalDisplayBox.add(decimalDisplay); - decimalDisplayBox.add(Box.createVerticalStrut(15)); - - FlowLayout rightPanelLayout = new FlowLayout(FlowLayout.LEFT); - JPanel place1 = new JPanel(rightPanelLayout); - JPanel place2 = new JPanel(rightPanelLayout); - JPanel place3 = new JPanel(rightPanelLayout); - JPanel place4 = new JPanel(rightPanelLayout); - - JEditorPane hexExplain = new JEditorPane("text/html",expansionFontTag+"<  Hexadecimal representation"+""); - hexExplain.setEditable(false); - hexExplain.setFocusable(false); - hexExplain.setForeground(Color.black); - hexExplain.setBackground(place1.getBackground()); - JEditorPane hexToBinExplain = new JEditorPane("text/html",expansionFontTag+"<  Each hex digit represents 4 bits"+""); - hexToBinExplain.setEditable(false); - hexToBinExplain.setFocusable(false); - hexToBinExplain.setBackground(place2.getBackground()); - JEditorPane binExplain = new JEditorPane("text/html",expansionFontTag+"<  Binary representation"+""); - binExplain.setEditable(false); - binExplain.setFocusable(false); - binExplain.setBackground(place3.getBackground()); - JEditorPane binToDecExplain = new JEditorPane("text/html",expansionFontTag+"<  Binary-to-decimal conversion"+""); - binToDecExplain.setEditable(false); - binToDecExplain.setFocusable(false); - binToDecExplain.setBackground(place4.getBackground()); - place1.add(hexExplain); - place2.add(hexToBinExplain); - place3.add(binExplain); - place4.add(binToDecExplain); - //################ 4 Grid Rows : Explanations ######################### - rightPanel.add(place1); - rightPanel.add(place2); - rightPanel.add(place3); - rightPanel.add(place4); - //################ Grid Row : Decimal ################################## - rightPanel.add(decimalDisplayBox); - - //######## mainPanel is vertical box, instructions get a row ################# - JPanel instructionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - instructions = new InstructionsPane(instructionsPanel); - instructionsPanel.add(instructions); - instructionsPanel.setBorder(new TitledBorder("Instructions")); - mainPanel.add(instructionsPanel); - - // Means of selecting and deselecting an attached floating point register - - fpRegisters = Coprocessor1.getRegisters(); - String[] registerList = new String[fpRegisters.length+1]; - registerList[0] = "None"; - for (int i=0; i"); + hexExplain.setEditable(false); + hexExplain.setFocusable(false); + hexExplain.setForeground(Color.black); + hexExplain.setBackground(place1.getBackground()); + JEditorPane hexToBinExplain = new JEditorPane("text/html", expansionFontTag + "<  Each hex digit represents 4 bits" + ""); + hexToBinExplain.setEditable(false); + hexToBinExplain.setFocusable(false); + hexToBinExplain.setBackground(place2.getBackground()); + JEditorPane binExplain = new JEditorPane("text/html", expansionFontTag + "<  Binary representation" + ""); + binExplain.setEditable(false); + binExplain.setFocusable(false); + binExplain.setBackground(place3.getBackground()); + JEditorPane binToDecExplain = new JEditorPane("text/html", expansionFontTag + "<  Binary-to-decimal conversion" + ""); + binToDecExplain.setEditable(false); + binToDecExplain.setFocusable(false); + binToDecExplain.setBackground(place4.getBackground()); + place1.add(hexExplain); + place2.add(hexToBinExplain); + place3.add(binExplain); + place4.add(binToDecExplain); + //################ 4 Grid Rows : Explanations ######################### + rightPanel.add(place1); + rightPanel.add(place2); + rightPanel.add(place3); + rightPanel.add(place4); + //################ Grid Row : Decimal ################################## + rightPanel.add(decimalDisplayBox); + + //######## mainPanel is vertical box, instructions get a row ################# + JPanel instructionsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + instructions = new InstructionsPane(instructionsPanel); + instructionsPanel.add(instructions); + instructionsPanel.setBorder(new TitledBorder("Instructions")); + mainPanel.add(instructionsPanel); + + // Means of selecting and deselecting an attached floating point register + + fpRegisters = Coprocessor1.getRegisters(); + String[] registerList = new String[fpRegisters.length + 1]; + registerList[0] = "None"; + for (int i = 0; i < fpRegisters.length; i++) + { + registerList[i + 1] = fpRegisters[i].getName(); + } + JComboBox registerSelect = new JComboBox(registerList); + registerSelect.setSelectedIndex(0); // No register attached + registerSelect.setToolTipText("Attach to selected FP register"); + registerSelect.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + JComboBox cb = (JComboBox) e.getSource(); + int selectedIndex = cb.getSelectedIndex(); + if (isObserving()) + { deleteAsObserver(); - } - if (selectedIndex==0) { + } + if (selectedIndex == 0) + { attachedRegister = null; updateDisplays(new FlavorsOfFloat()); instructions.setText("The program is not attached to any MIPS floating point registers."); - } - else { - attachedRegister = fpRegisters[selectedIndex-1]; + } + else + { + attachedRegister = fpRegisters[selectedIndex - 1]; updateDisplays(new FlavorsOfFloat().buildOneFromInt(attachedRegister.getValue())); - if (isObserving()) { - addAsObserver(); + if (isObserving()) + { + addAsObserver(); } - instructions.setText("The program and register "+attachedRegister.getName()+" will respond to each other when MIPS program connected or running."); - } - } - }); - - JPanel registerPanel = new JPanel(new BorderLayout(5,5)); - JPanel registerAndLabel = new JPanel(); - registerAndLabel.add(new JLabel("MIPS floating point Register of interest: ")); - registerAndLabel.add(registerSelect); - registerPanel.add(registerAndLabel,BorderLayout.WEST); - registerPanel.add(new JLabel(" "),BorderLayout.NORTH); // just for padding - mainPanel.add(registerPanel); - return mainPanel; - } // end of buildDisplayArea() - - - // If display is attached to a register then update the register value. - private synchronized void updateAnyAttachedRegister(int intValue) { - if (attachedRegister != null) { - synchronized (Globals.memoryAndRegistersLock) { - attachedRegister.setValue(intValue); + instructions.setText("The program and register " + attachedRegister.getName() + " will respond to each other when MIPS program connected or running."); + } + } + }); + + JPanel registerPanel = new JPanel(new BorderLayout(5, 5)); + JPanel registerAndLabel = new JPanel(); + registerAndLabel.add(new JLabel("MIPS floating point Register of interest: ")); + registerAndLabel.add(registerSelect); + registerPanel.add(registerAndLabel, BorderLayout.WEST); + registerPanel.add(new JLabel(" "), BorderLayout.NORTH); // just for padding + mainPanel.add(registerPanel); + return mainPanel; + } // end of buildDisplayArea() + + // If display is attached to a register then update the register value. + private synchronized void updateAnyAttachedRegister(int intValue) + { + if (attachedRegister != null) + { + synchronized (Globals.memoryAndRegistersLock) + { + attachedRegister.setValue(intValue); } - // HERE'S A HACK!! Want to immediately display the updated register value in MARS - // but that code was not written for event-driven update (e.g. Observer) -- - // it was written to poll the registers for their values. So we force it to do so. - if (Globals.getGui() != null) { - Globals.getGui().getRegistersPane().getCoprocessor1Window().updateRegisters(); + // HERE'S A HACK!! Want to immediately display the updated register value in MARS + // but that code was not written for event-driven update (e.g. Observer) -- + // it was written to poll the registers for their values. So we force it to do so. + if (Globals.getGui() != null) + { + Globals.getGui().getRegistersPane().getCoprocessor1Window().updateRegisters(); } - } - } - - // Updates all components displaying various representations of the 32 bit - // floating point value. - private void updateDisplays(FlavorsOfFloat flavors) { - int hexIndex = (flavors.hexString.charAt(0)=='0' && (flavors.hexString.charAt(1)=='x'||flavors.hexString.charAt(1)=='X')) ? 2 : 0; - hexDisplay.setText(flavors.hexString.substring(hexIndex).toUpperCase()); // lop off leading "Ox" if present - binarySignDisplay.setText(flavors.binaryString.substring(0, maxLengthBinarySign)); - binaryExponentDisplay.setText(flavors.binaryString.substring(maxLengthBinarySign, maxLengthBinarySign+maxLengthBinaryExponent)); - binaryFractionDisplay.setText(flavors.binaryString.substring(maxLengthBinarySign+maxLengthBinaryExponent, maxLengthBinaryTotal)); - decimalDisplay.setText(flavors.decimalString); - binaryToDecimalFormulaGraphic.drawSubtractLabel(Binary.binaryStringToInt((flavors.binaryString.substring(maxLengthBinarySign, maxLengthBinarySign+maxLengthBinaryExponent)))); - expansionDisplay.setText(flavors.expansionString); - updateSignificandLabel(flavors); - } - - // Should be called only by those who know a register should be changed due to - // user action (reset button or Enter key on one of the input fields). Note - // this will not update the register unless we are an active Observer. - private void updateDisplaysAndRegister(FlavorsOfFloat flavors) { - updateDisplays(flavors); - if (isObserving()) { + } + } + + // Updates all components displaying various representations of the 32 bit + // floating point value. + private void updateDisplays(FlavorsOfFloat flavors) + { + int hexIndex = (flavors.hexString.charAt(0) == '0' && (flavors.hexString.charAt(1) == 'x' || flavors.hexString.charAt(1) == 'X')) ? 2 : 0; + hexDisplay.setText(flavors.hexString.substring(hexIndex).toUpperCase()); // lop off leading "Ox" if present + binarySignDisplay.setText(flavors.binaryString.substring(0, maxLengthBinarySign)); + binaryExponentDisplay.setText(flavors.binaryString.substring(maxLengthBinarySign, maxLengthBinarySign + maxLengthBinaryExponent)); + binaryFractionDisplay.setText(flavors.binaryString.substring(maxLengthBinarySign + maxLengthBinaryExponent, maxLengthBinaryTotal)); + decimalDisplay.setText(flavors.decimalString); + binaryToDecimalFormulaGraphic.drawSubtractLabel(Binary.binaryStringToInt((flavors.binaryString.substring(maxLengthBinarySign, maxLengthBinarySign + maxLengthBinaryExponent)))); + expansionDisplay.setText(flavors.expansionString); + updateSignificandLabel(flavors); + } + + /////////////////////////////////////////////////////////////////////////////// + /////// + /////// THE REST OF THE TOOL CONSISTS OF LITTLE PRIVATE CLASSES THAT MAKE + /////// LIFE EASIER FOR THE ABOVE CODE. + /////// + /////////////////////////////////////////////////////////////////////////////// + + // Should be called only by those who know a register should be changed due to + // user action (reset button or Enter key on one of the input fields). Note + // this will not update the register unless we are an active Observer. + private void updateDisplaysAndRegister(FlavorsOfFloat flavors) + { + updateDisplays(flavors); + if (isObserving()) + { updateAnyAttachedRegister(flavors.intValue); - } - } - - // Called by updateDisplays() to determine whether or not the significand label needs - // to be changed and if so to change it. The label explains presence/absence of - // normalizing "hidden bit". - private void updateSignificandLabel(FlavorsOfFloat flavors) { - // Will change significandLabel text only if it needs to be changed... - if (flavors.binaryString.substring(maxLengthBinarySign, maxLengthBinarySign+maxLengthBinaryExponent) - .equals(zeroes.substring(maxLengthBinarySign, maxLengthBinarySign+maxLengthBinaryExponent))) { - // Will change text only if it truly is changing.... - if (significandLabel.getText().indexOf("deno")<0) { - significandLabel.setText(denormalizedLabel); + } + } + + // Called by updateDisplays() to determine whether or not the significand label needs + // to be changed and if so to change it. The label explains presence/absence of + // normalizing "hidden bit". + private void updateSignificandLabel(FlavorsOfFloat flavors) + { + // Will change significandLabel text only if it needs to be changed... + if (flavors.binaryString.substring(maxLengthBinarySign, maxLengthBinarySign + maxLengthBinaryExponent) + .equals(zeroes.substring(maxLengthBinarySign, maxLengthBinarySign + maxLengthBinaryExponent))) + { + // Will change text only if it truly is changing.... + if (significandLabel.getText().indexOf("deno") < 0) + { + significandLabel.setText(denormalizedLabel); } - } - else { - if (significandLabel.getText().indexOf("unde")<0) { - significandLabel.setText(normalizedLabel); + } + else + { + if (significandLabel.getText().indexOf("unde") < 0) + { + significandLabel.setText(normalizedLabel); } - } - } - - /////////////////////////////////////////////////////////////////////////////// - /////// - /////// THE REST OF THE TOOL CONSISTS OF LITTLE PRIVATE CLASSES THAT MAKE - /////// LIFE EASIER FOR THE ABOVE CODE. - /////// - /////////////////////////////////////////////////////////////////////////////// - - //////////////////////////////////////////////////////////////////////////// - // - // Class of objects that encapsulats 5 different representations of a 32 bit - // floating point value: - // string with hexadecimal value. - // String with binary value. 32 characters long. - // String with decimal float value. variable length. - // int with 32 bit representation of float value ("int bits"). - // String for display only, showing formula for expanding bits to decimal. - // - private class FlavorsOfFloat { - String hexString; - String binaryString; - String decimalString; - String expansionString; - int intValue; - - // Default object - private FlavorsOfFloat() { + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Class of objects that encapsulats 5 different representations of a 32 bit + // floating point value: + // string with hexadecimal value. + // String with binary value. 32 characters long. + // String with decimal float value. variable length. + // int with 32 bit representation of float value ("int bits"). + // String for display only, showing formula for expanding bits to decimal. + // + private class FlavorsOfFloat + { + String hexString; + + String binaryString; + + String decimalString; + + String expansionString; + + int intValue; + + // Default object + private FlavorsOfFloat() + { hexString = defaultHex; - decimalString = defaultDecimal; - binaryString = defaultBinarySign+defaultBinaryExponent+defaultBinaryFraction; + decimalString = defaultDecimal; + binaryString = defaultBinarySign + defaultBinaryExponent + defaultBinaryFraction; expansionString = buildExpansionFromBinaryString(binaryString); intValue = Float.floatToIntBits(Float.parseFloat(decimalString)); - } - - // Assign all fields given a string representing 32 bit hex value. - public FlavorsOfFloat buildOneFromHexString(String hexString) { + } + + // Assign all fields given a string representing 32 bit hex value. + public FlavorsOfFloat buildOneFromHexString(String hexString) + { this.hexString = "0x" + addLeadingZeroes( - ((hexString.indexOf("0X")==0 || hexString.indexOf("0x")==0) - ? hexString.substring(2) : hexString), maxLengthHex); - this.binaryString = Binary.hexStringToBinaryString(this.hexString); - this.decimalString = new Float(Float.intBitsToFloat(Binary.binaryStringToInt(this.binaryString))).toString(); + ((hexString.indexOf("0X") == 0 || hexString.indexOf("0x") == 0) + ? hexString.substring(2) : hexString), maxLengthHex); + this.binaryString = Binary.hexStringToBinaryString(this.hexString); + this.decimalString = Float.toString(Float.intBitsToFloat(Binary.binaryStringToInt(this.binaryString))); this.expansionString = buildExpansionFromBinaryString(this.binaryString); this.intValue = Binary.binaryStringToInt(this.binaryString); return this; - } - - // Assign all fields given a string representing 32 bit binary value - private FlavorsOfFloat buildOneFromBinaryString() { + } + + // Assign all fields given a string representing 32 bit binary value + private FlavorsOfFloat buildOneFromBinaryString() + { this.binaryString = getFullBinaryStringFromDisplays(); this.hexString = Binary.binaryStringToHexString(binaryString); - this.decimalString = new Float(Float.intBitsToFloat(Binary.binaryStringToInt(this.binaryString))).toString(); + this.decimalString = Float.toString(Float.intBitsToFloat(Binary.binaryStringToInt(this.binaryString))); this.expansionString = buildExpansionFromBinaryString(this.binaryString); this.intValue = Binary.binaryStringToInt(this.binaryString); return this; - } - - // Assign all fields given string representing floating point decimal value. - private FlavorsOfFloat buildOneFromDecimalString(String decimalString) { + } + + // Assign all fields given string representing floating point decimal value. + private FlavorsOfFloat buildOneFromDecimalString(String decimalString) + { float floatValue; - try { - floatValue = Float.parseFloat(decimalString); - } - catch (NumberFormatException nfe) { - return null; - } - this.decimalString = new Float(floatValue).toString(); + try + { + floatValue = Float.parseFloat(decimalString); + } + catch (NumberFormatException nfe) + { + return null; + } + this.decimalString = Float.toString(floatValue); this.intValue = Float.floatToIntBits(floatValue);// use floatToRawIntBits? this.binaryString = Binary.intToBinaryString(this.intValue); this.hexString = Binary.binaryStringToHexString(this.binaryString); this.expansionString = buildExpansionFromBinaryString(this.binaryString); return this; - } - - // Assign all fields given int representing 32 bit representation of float value - private FlavorsOfFloat buildOneFromInt(int intValue) { + } + + // Assign all fields given int representing 32 bit representation of float value + private FlavorsOfFloat buildOneFromInt(int intValue) + { this.intValue = intValue; this.binaryString = Binary.intToBinaryString(intValue); this.hexString = Binary.binaryStringToHexString(this.binaryString); - this.decimalString = new Float(Float.intBitsToFloat(Binary.binaryStringToInt(this.binaryString))).toString(); + this.decimalString = Float.toString(Float.intBitsToFloat(Binary.binaryStringToInt(this.binaryString))); this.expansionString = buildExpansionFromBinaryString(this.binaryString); return this; - } - - // Build binary expansion formula for display -- will not be editable. - public String buildExpansionFromBinaryString(String binaryString) { + } + + // Build binary expansion formula for display -- will not be editable. + public String buildExpansionFromBinaryString(String binaryString) + { int biasedExponent = Binary.binaryStringToInt( - binaryString.substring(maxLengthBinarySign,maxLengthBinarySign+maxLengthBinaryExponent)); - String stringExponent = Integer.toString(biasedExponent - exponentBias); - // stringExponent length will range from 1 to 4 (e.g. "0" to "-128") characters. - // Right-pad with HTML spaces (" ") to total length 5 displayed characters. - return ""+expansionFontTag - + "-1"+binaryString.substring(0,maxLengthBinarySign)+"  *  2" - + stringExponent + HTMLspaces.substring(0, (5-stringExponent.length())*6) - + "  *  " - + ((biasedExponent==0) ? " ." : "1.") - + binaryString.substring(maxLengthBinarySign+maxLengthBinaryExponent, maxLengthBinaryTotal) - + " ="; - } - - // Handy utility to concatentate the binary field values into one 32 character string - // Left-pad each field with zeroes as needed to reach its full length. - private String getFullBinaryStringFromDisplays() { + binaryString.substring(maxLengthBinarySign, maxLengthBinarySign + maxLengthBinaryExponent)); + String stringExponent = Integer.toString(biasedExponent - exponentBias); + // stringExponent length will range from 1 to 4 (e.g. "0" to "-128") characters. + // Right-pad with HTML spaces (" ") to total length 5 displayed characters. + return "" + expansionFontTag + + "-1" + binaryString.charAt(0) + "  *  2" + + stringExponent + HTMLspaces.substring(0, (5 - stringExponent.length()) * 6) + + "  *  " + + ((biasedExponent == 0) ? " ." : "1.") + + binaryString.substring(maxLengthBinarySign + maxLengthBinaryExponent, maxLengthBinaryTotal) + + " ="; + } + + // Handy utility to concatentate the binary field values into one 32 character string + // Left-pad each field with zeroes as needed to reach its full length. + private String getFullBinaryStringFromDisplays() + { return addLeadingZeroes(binarySignDisplay.getText(), maxLengthBinarySign) + addLeadingZeroes(binaryExponentDisplay.getText(), maxLengthBinaryExponent) + addLeadingZeroes(binaryFractionDisplay.getText(), maxLengthBinaryFraction); - } - - // Handy utility. Pads with leading zeroes to specified length, maximum 64 of 'em. - private String addLeadingZeroes(String str, int length) { - return (str.length() < length) - ? zeroes.substring(0,Math.min(zeroes.length(),length-str.length()))+str - : str; - } - - } - //Put here because inner class cannot have static members. - private static final String zeroes = - "0000000000000000000000000000000000000000000000000000000000000000"; //64 - private static final String HTMLspaces = "        "; - - - // NOTE: It would be nice to use InputVerifier class to verify user input - // but I want keystroke-level monitoring to assure that no invalid - // keystrokes are echoed and that maximum string length is not exceeded. - - - - - ////////////////////////////////////////////////////////////////// - // - // Class to handle input keystrokes for hexadecimal field - // - private class HexDisplayKeystrokeListener extends KeyAdapter { - - private int digitLength; // maximum number of digits long - - public HexDisplayKeystrokeListener(int length) { + } + + // Handy utility. Pads with leading zeroes to specified length, maximum 64 of 'em. + private String addLeadingZeroes(String str, int length) + { + return (str.length() < length) + ? zeroes.substring(0, Math.min(zeroes.length(), length - str.length())) + str + : str; + } + + } + + + // NOTE: It would be nice to use InputVerifier class to verify user input + // but I want keystroke-level monitoring to assure that no invalid + // keystrokes are echoed and that maximum string length is not exceeded. + + ////////////////////////////////////////////////////////////////// + // + // Class to handle input keystrokes for hexadecimal field + // + private class HexDisplayKeystrokeListener extends KeyAdapter + { + + private final int digitLength; // maximum number of digits long + + public HexDisplayKeystrokeListener(int length) + { digitLength = length; - } - - - // Process user keystroke. If not valid for the context, this - // will consume the stroke and beep. - public void keyTyped(KeyEvent e) { + } + + + // Process user keystroke. If not valid for the context, this + // will consume the stroke and beep. + public void keyTyped(KeyEvent e) + { JTextField source = (JTextField) e.getComponent(); - if (e.getKeyChar()== KeyEvent.VK_BACK_SPACE || e.getKeyChar()== KeyEvent.VK_TAB ) - return; - if (!isHexDigit(e.getKeyChar()) || - source.getText().length()==digitLength && source.getSelectedText()==null) { - if (e.getKeyChar()!= KeyEvent.VK_ENTER && e.getKeyChar() != KeyEvent.VK_TAB) { - Toolkit.getDefaultToolkit().beep(); - if (source.getText().length()==digitLength && source.getSelectedText()==null) { - instructions.setText("Maximum length of this field is "+digitLength+"."); - } - else { - instructions.setText("Only digits and A-F (or a-f) are accepted in hexadecimal field."); - } - } - e.consume(); - } - } - - // Enter key is echoed on component after keyPressed but before keyTyped? - // Consuming the VK_ENTER event in keyTyped does not suppress it but this will. - public void keyPressed(KeyEvent e) { - if (e.getKeyChar()== KeyEvent.VK_ENTER || e.getKeyChar()== KeyEvent.VK_TAB) { - updateDisplaysAndRegister(new FlavorsOfFloat().buildOneFromHexString(((JTextField)e.getSource()).getText())); - instructions.setText(defaultInstructions); - e.consume(); + if (e.getKeyChar() == KeyEvent.VK_BACK_SPACE || e.getKeyChar() == KeyEvent.VK_TAB) + { + return; } - } - - // handy utility. - private boolean isHexDigit(char digit) { + if (!isHexDigit(e.getKeyChar()) || + source.getText().length() == digitLength && source.getSelectedText() == null) + { + if (e.getKeyChar() != KeyEvent.VK_ENTER && e.getKeyChar() != KeyEvent.VK_TAB) + { + Toolkit.getDefaultToolkit().beep(); + if (source.getText().length() == digitLength && source.getSelectedText() == null) + { + instructions.setText("Maximum length of this field is " + digitLength + "."); + } + else + { + instructions.setText("Only digits and A-F (or a-f) are accepted in hexadecimal field."); + } + } + e.consume(); + } + } + + // Enter key is echoed on component after keyPressed but before keyTyped? + // Consuming the VK_ENTER event in keyTyped does not suppress it but this will. + public void keyPressed(KeyEvent e) + { + if (e.getKeyChar() == KeyEvent.VK_ENTER || e.getKeyChar() == KeyEvent.VK_TAB) + { + updateDisplaysAndRegister(new FlavorsOfFloat().buildOneFromHexString(((JTextField) e.getSource()).getText())); + instructions.setText(defaultInstructions); + e.consume(); + } + } + + // handy utility. + private boolean isHexDigit(char digit) + { boolean result = false; - switch (digit) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': - case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': - result = true; + switch (digit) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + result = true; } - return result; - } - } - - ////////////////////////////////////////////////////////////////// - // - // Class to handle input keystrokes for binary field - // - private class BinaryDisplayKeystrokeListener extends KeyAdapter { - - private int bitLength; // maximum number of bits permitted - - public BinaryDisplayKeystrokeListener(int length) { + return result; + } + } + + ////////////////////////////////////////////////////////////////// + // + // Class to handle input keystrokes for binary field + // + private class BinaryDisplayKeystrokeListener extends KeyAdapter + { + + private final int bitLength; // maximum number of bits permitted + + public BinaryDisplayKeystrokeListener(int length) + { bitLength = length; - } - - // Process user keystroke. If not valid for the context, this - // will consume the stroke and beep. - public void keyTyped(KeyEvent e) { + } + + // Process user keystroke. If not valid for the context, this + // will consume the stroke and beep. + public void keyTyped(KeyEvent e) + { JTextField source = (JTextField) e.getComponent(); - if (e.getKeyChar()== KeyEvent.VK_BACK_SPACE) - return; + if (e.getKeyChar() == KeyEvent.VK_BACK_SPACE) + { + return; + } if (!isBinaryDigit(e.getKeyChar()) || - e.getKeyChar() == KeyEvent.VK_ENTER || - source.getText().length()==bitLength && source.getSelectedText()==null) { - if (e.getKeyChar()!= KeyEvent.VK_ENTER) { - Toolkit.getDefaultToolkit().beep(); - if (source.getText().length()==bitLength && source.getSelectedText()==null) { - instructions.setText("Maximum length of this field is "+bitLength+"."); - } - else { - instructions.setText("Only 0 and 1 are accepted in binary field."); - } - } - e.consume(); - } - } - - // Enter key is echoed on component after keyPressed but before keyTyped? - // Consuming the VK_ENTER event in keyTyped does not suppress it but this will. - public void keyPressed(KeyEvent e) { - if (e.getKeyChar()== KeyEvent.VK_ENTER) { - updateDisplaysAndRegister(new FlavorsOfFloat().buildOneFromBinaryString()); - instructions.setText(defaultInstructions); - e.consume(); + e.getKeyChar() == KeyEvent.VK_ENTER || + source.getText().length() == bitLength && source.getSelectedText() == null) + { + if (e.getKeyChar() != KeyEvent.VK_ENTER) + { + Toolkit.getDefaultToolkit().beep(); + if (source.getText().length() == bitLength && source.getSelectedText() == null) + { + instructions.setText("Maximum length of this field is " + bitLength + "."); + } + else + { + instructions.setText("Only 0 and 1 are accepted in binary field."); + } + } + e.consume(); } - } - - // handy utility - private boolean isBinaryDigit(char digit) { + } + + // Enter key is echoed on component after keyPressed but before keyTyped? + // Consuming the VK_ENTER event in keyTyped does not suppress it but this will. + public void keyPressed(KeyEvent e) + { + if (e.getKeyChar() == KeyEvent.VK_ENTER) + { + updateDisplaysAndRegister(new FlavorsOfFloat().buildOneFromBinaryString()); + instructions.setText(defaultInstructions); + e.consume(); + } + } + + // handy utility + private boolean isBinaryDigit(char digit) + { boolean result = false; - switch (digit) { - case '0': case '1': - result = true; + switch (digit) + { + case '0': + case '1': + result = true; } - return result; - } - - } - - - ////////////////////////////////////////////////////////////////// - // - // Class to handle input keystrokes for decimal field - // - private class DecimalDisplayKeystokeListenter extends KeyAdapter { - - // Process user keystroke. If not valid for the context, this - // will consume the stroke and beep. - public void keyTyped(KeyEvent e) { + return result; + } + + } + + + ////////////////////////////////////////////////////////////////// + // + // Class to handle input keystrokes for decimal field + // + private class DecimalDisplayKeystokeListenter extends KeyAdapter + { + + // Process user keystroke. If not valid for the context, this + // will consume the stroke and beep. + public void keyTyped(KeyEvent e) + { JTextField source = (JTextField) e.getComponent(); - if (e.getKeyChar()== KeyEvent.VK_BACK_SPACE) - return; - if (!isDecimalFloatDigit(e.getKeyChar())) { - if (e.getKeyChar()!= KeyEvent.VK_ENTER) { - instructions.setText("Only digits, period, signs and E (or e) are accepted in decimal field."); - Toolkit.getDefaultToolkit().beep(); - } - e.consume(); - } - } - - // Enter key is echoed on component after keyPressed but before keyTyped? - // Consuming the VK_ENTER event in keyTyped does not suppress it but this will. - public void keyPressed(KeyEvent e) { - if (e.getKeyChar()== KeyEvent.VK_ENTER) { - FlavorsOfFloat fof = new FlavorsOfFloat().buildOneFromDecimalString(((JTextField)e.getSource()).getText()); - if (fof==null) { - Toolkit.getDefaultToolkit().beep(); - instructions.setText("'"+((JTextField)e.getSource()).getText()+"' is not a valid floating point number."); - } - else { - updateDisplaysAndRegister(fof); - instructions.setText(defaultInstructions); - } - e.consume(); + if (e.getKeyChar() == KeyEvent.VK_BACK_SPACE) + { + return; } - } - - // handy utility - private boolean isDecimalFloatDigit(char digit) { + if (!isDecimalFloatDigit(e.getKeyChar())) + { + if (e.getKeyChar() != KeyEvent.VK_ENTER) + { + instructions.setText("Only digits, period, signs and E (or e) are accepted in decimal field."); + Toolkit.getDefaultToolkit().beep(); + } + e.consume(); + } + } + + // Enter key is echoed on component after keyPressed but before keyTyped? + // Consuming the VK_ENTER event in keyTyped does not suppress it but this will. + public void keyPressed(KeyEvent e) + { + if (e.getKeyChar() == KeyEvent.VK_ENTER) + { + FlavorsOfFloat fof = new FlavorsOfFloat().buildOneFromDecimalString(((JTextField) e.getSource()).getText()); + if (fof == null) + { + Toolkit.getDefaultToolkit().beep(); + instructions.setText("'" + ((JTextField) e.getSource()).getText() + "' is not a valid floating point number."); + } + else + { + updateDisplaysAndRegister(fof); + instructions.setText(defaultInstructions); + } + e.consume(); + } + } + + // handy utility + private boolean isDecimalFloatDigit(char digit) + { boolean result = false; - switch (digit) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '-': case '+': case '.': case 'e': case 'E': - result = true; + switch (digit) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + case '+': + case '.': + case 'e': + case 'E': + result = true; } - return result; - } - - } - - //////////////////////////////////////////////////////////////////////////// - // - // Use this to draw graphics visually relating the hexadecimal values - // displayed above) to the binary values (displayed below). - // - class HexToBinaryGraphicPanel extends JPanel { - + return result; + } + + } + + //////////////////////////////////////////////////////////////////////////// + // + // Use this to draw graphics visually relating the hexadecimal values + // displayed above) to the binary values (displayed below). + // + class HexToBinaryGraphicPanel extends JPanel + { + // This overrides inherited JPanel method. Override is necessary to // assure my drawn graphics get painted immediately after painting the // underlying JPanel (see first statement). - public void paintComponent(Graphics g) { + public void paintComponent(Graphics g) + { super.paintComponent(g); - g.setColor(Color.red); + g.setColor(Color.red); //FontMetrics fontMetrics = hexDisplay.getGraphics().getFontMetrics(); int upperY = 0; int lowerY = 60; - int hexColumnWidth = hexDisplay.getWidth()/hexDisplay.getColumns(); - // assume all 3 binary displays use same geometry, so column width same for all. - int binaryColumnWidth = binaryFractionDisplay.getWidth()/binaryFractionDisplay.getColumns(); + int hexColumnWidth = hexDisplay.getWidth() / hexDisplay.getColumns(); + // assume all 3 binary displays use same geometry, so column width same for all. + int binaryColumnWidth = binaryFractionDisplay.getWidth() / binaryFractionDisplay.getColumns(); Polygon p; // loop will handle the lower order 5 "nibbles" (hex digits) - for (int i=1; i<6; i++) { - p = new Polygon(); - p.addPoint(hexDisplay.getX()+hexColumnWidth*(hexDisplay.getColumns()-i)+hexColumnWidth/2, upperY); - p.addPoint(binaryFractionDecoratedDisplay.getX()+binaryColumnWidth*(binaryFractionDisplay.getColumns()-((i*5)-i)), lowerY); - p.addPoint(binaryFractionDecoratedDisplay.getX()+binaryColumnWidth*(binaryFractionDisplay.getColumns()-(((i*5)-i)-4)), lowerY); - g.fillPolygon(p); + for (int i = 1; i < 6; i++) + { + p = new Polygon(); + p.addPoint(hexDisplay.getX() + hexColumnWidth * (hexDisplay.getColumns() - i) + hexColumnWidth / 2, upperY); + p.addPoint(binaryFractionDecoratedDisplay.getX() + binaryColumnWidth * (binaryFractionDisplay.getColumns() - ((i * 5) - i)), lowerY); + p.addPoint(binaryFractionDecoratedDisplay.getX() + binaryColumnWidth * (binaryFractionDisplay.getColumns() - (((i * 5) - i) - 4)), lowerY); + g.fillPolygon(p); } // Nibble 5 straddles binary display of exponent and fraction. p = new Polygon(); - p.addPoint(hexDisplay.getX()+hexColumnWidth*(hexDisplay.getColumns()-6)+hexColumnWidth/2, upperY); - p.addPoint(binaryFractionDecoratedDisplay.getX()+binaryColumnWidth*(binaryFractionDisplay.getColumns()-20), lowerY); - p.addPoint(binaryExponentDecoratedDisplay.getX()+binaryColumnWidth*(binaryExponentDisplay.getColumns()-1), lowerY); + p.addPoint(hexDisplay.getX() + hexColumnWidth * (hexDisplay.getColumns() - 6) + hexColumnWidth / 2, upperY); + p.addPoint(binaryFractionDecoratedDisplay.getX() + binaryColumnWidth * (binaryFractionDisplay.getColumns() - 20), lowerY); + p.addPoint(binaryExponentDecoratedDisplay.getX() + binaryColumnWidth * (binaryExponentDisplay.getColumns() - 1), lowerY); g.fillPolygon(p); - // Nibble 6 maps to binary display of exponent. + // Nibble 6 maps to binary display of exponent. p = new Polygon(); - p.addPoint(hexDisplay.getX()+hexColumnWidth*(hexDisplay.getColumns()-7)+hexColumnWidth/2, upperY); - p.addPoint(binaryExponentDecoratedDisplay.getX()+binaryColumnWidth*(binaryExponentDisplay.getColumns()-1), lowerY); - p.addPoint(binaryExponentDecoratedDisplay.getX()+binaryColumnWidth*(binaryExponentDisplay.getColumns()-5), lowerY); + p.addPoint(hexDisplay.getX() + hexColumnWidth * (hexDisplay.getColumns() - 7) + hexColumnWidth / 2, upperY); + p.addPoint(binaryExponentDecoratedDisplay.getX() + binaryColumnWidth * (binaryExponentDisplay.getColumns() - 1), lowerY); + p.addPoint(binaryExponentDecoratedDisplay.getX() + binaryColumnWidth * (binaryExponentDisplay.getColumns() - 5), lowerY); g.fillPolygon(p); - // Nibble 7 straddles binary display of sign and exponent. + // Nibble 7 straddles binary display of sign and exponent. p = new Polygon(); - p.addPoint(hexDisplay.getX()+hexColumnWidth*(hexDisplay.getColumns()-8)+hexColumnWidth/2, upperY); - p.addPoint(binaryExponentDecoratedDisplay.getX()+binaryColumnWidth*(binaryExponentDisplay.getColumns()-5), lowerY); + p.addPoint(hexDisplay.getX() + hexColumnWidth * (hexDisplay.getColumns() - 8) + hexColumnWidth / 2, upperY); + p.addPoint(binaryExponentDecoratedDisplay.getX() + binaryColumnWidth * (binaryExponentDisplay.getColumns() - 5), lowerY); p.addPoint(binarySignDecoratedDisplay.getX(), lowerY); g.fillPolygon(p); - } - - } - - ////////////////////////////////////////////////////////////////////// - // - // Panel to hold arrows explaining transformation of binary represntation - // into formula for calculating decimal value. - // - class BinaryToDecimalFormulaGraphic extends JPanel { - final String subtractLabelTrailer = " - 127"; - final int arrowHeadOffset = 5; - final int lowerY = 0; - final int upperY = 50; - int centerX, exponentCenterX; - int subtractLabelWidth, subtractLabelHeight; - int centerY = (upperY-lowerY)/2; - int upperYArrowHead = upperY-arrowHeadOffset; - int currentExponent = Binary.binaryStringToInt(defaultBinaryExponent); - public void paintComponent(Graphics g) { - super.paintComponent(g); - // Arrow down from binary sign field - centerX = binarySignDecoratedDisplay.getX()+binarySignDecoratedDisplay.getWidth()/2; - g.drawLine(centerX,lowerY,centerX,upperY); - g.drawLine(centerX-arrowHeadOffset,upperYArrowHead,centerX,upperY); - g.drawLine(centerX+arrowHeadOffset,upperYArrowHead,centerX,upperY); - // Arrow down from binary exponent field - centerX = binaryExponentDecoratedDisplay.getX()+binaryExponentDecoratedDisplay.getWidth()/2; - g.drawLine(centerX,lowerY,centerX,upperY); - g.drawLine(centerX-arrowHeadOffset,upperYArrowHead,centerX,upperY); - g.drawLine(centerX+arrowHeadOffset,upperYArrowHead,centerX,upperY); - // Label on exponent arrow. The two assignments serve to initialize two - // instance variables that are used by drawSubtractLabel(). They are - // initialized here because they cannot be initialized sooner AND because - // the drawSubtractLabel() method will later be called by updateDisplays(), - // an outsider which has no other access to that information. Once set they - // do not change so it does no harm that they are "re-initialized" each time - // this method is called (which occurs only upon startup and when this portion - // of the GUI needs to be repainted). - exponentCenterX = centerX; + } + + } + + ////////////////////////////////////////////////////////////////////// + // + // Panel to hold arrows explaining transformation of binary represntation + // into formula for calculating decimal value. + // + class BinaryToDecimalFormulaGraphic extends JPanel + { + final String subtractLabelTrailer = " - 127"; + + final int arrowHeadOffset = 5; + + final int lowerY = 0; + + final int upperY = 50; + + int centerX, exponentCenterX; + + int subtractLabelWidth, subtractLabelHeight; + + int centerY = (upperY - lowerY) / 2; + + int upperYArrowHead = upperY - arrowHeadOffset; + + int currentExponent = Binary.binaryStringToInt(defaultBinaryExponent); + + public void paintComponent(Graphics g) + { + super.paintComponent(g); + // Arrow down from binary sign field + centerX = binarySignDecoratedDisplay.getX() + binarySignDecoratedDisplay.getWidth() / 2; + g.drawLine(centerX, lowerY, centerX, upperY); + g.drawLine(centerX - arrowHeadOffset, upperYArrowHead, centerX, upperY); + g.drawLine(centerX + arrowHeadOffset, upperYArrowHead, centerX, upperY); + // Arrow down from binary exponent field + centerX = binaryExponentDecoratedDisplay.getX() + binaryExponentDecoratedDisplay.getWidth() / 2; + g.drawLine(centerX, lowerY, centerX, upperY); + g.drawLine(centerX - arrowHeadOffset, upperYArrowHead, centerX, upperY); + g.drawLine(centerX + arrowHeadOffset, upperYArrowHead, centerX, upperY); + // Label on exponent arrow. The two assignments serve to initialize two + // instance variables that are used by drawSubtractLabel(). They are + // initialized here because they cannot be initialized sooner AND because + // the drawSubtractLabel() method will later be called by updateDisplays(), + // an outsider which has no other access to that information. Once set they + // do not change so it does no harm that they are "re-initialized" each time + // this method is called (which occurs only upon startup and when this portion + // of the GUI needs to be repainted). + exponentCenterX = centerX; subtractLabelHeight = g.getFontMetrics().getHeight(); drawSubtractLabel(g, buildSubtractLabel(currentExponent)); - // Arrow down from binary fraction field - centerX = binaryFractionDecoratedDisplay.getX()+binaryFractionDecoratedDisplay.getWidth()/2; - g.drawLine(centerX,lowerY,centerX,upperY); - g.drawLine(centerX-arrowHeadOffset,upperYArrowHead,centerX,upperY); - g.drawLine(centerX+arrowHeadOffset,upperYArrowHead,centerX,upperY); - } - - // To be used only by "outsiders" to update the display of the exponent and bias. - public void drawSubtractLabel(int exponent) { - if (exponent != currentExponent) { // no need to redraw if it hasn't changed... - currentExponent = exponent; - drawSubtractLabel(getGraphics(), buildSubtractLabel(exponent)); + // Arrow down from binary fraction field + centerX = binaryFractionDecoratedDisplay.getX() + binaryFractionDecoratedDisplay.getWidth() / 2; + g.drawLine(centerX, lowerY, centerX, upperY); + g.drawLine(centerX - arrowHeadOffset, upperYArrowHead, centerX, upperY); + g.drawLine(centerX + arrowHeadOffset, upperYArrowHead, centerX, upperY); + } + + // To be used only by "outsiders" to update the display of the exponent and bias. + public void drawSubtractLabel(int exponent) + { + if (exponent != currentExponent) + { // no need to redraw if it hasn't changed... + currentExponent = exponent; + drawSubtractLabel(getGraphics(), buildSubtractLabel(exponent)); } - } - + } + // Is called by both drawSubtractLabel() just above and by paintComponent(). - private void drawSubtractLabel(Graphics g, String label) { + private void drawSubtractLabel(Graphics g, String label) + { // Clear the existing subtract label. The "+2" overwrites the arrow at initial paint when label width is 0. - // Originally used "clearRect()" but changed to "fillRect()" with background color, because when running - // as a MarsTool it would clear with a different color. + // Originally used "clearRect()" but changed to "fillRect()" with background color, because when running + // as a MarsTool it would clear with a different color. Color saved = g.getColor(); g.setColor(binaryToDecimalFormulaGraphic.getBackground()); - g.fillRect(exponentCenterX-subtractLabelWidth/2,centerY-subtractLabelHeight/2,subtractLabelWidth+2,subtractLabelHeight); + g.fillRect(exponentCenterX - subtractLabelWidth / 2, centerY - subtractLabelHeight / 2, subtractLabelWidth + 2, subtractLabelHeight); g.setColor(saved); subtractLabelWidth = g.getFontMetrics().stringWidth(label); - g.drawString(label,exponentCenterX-subtractLabelWidth/2,centerY+subtractLabelHeight/2-3); // -3 makes it more visually appealing - } - - // format the label for a given integer exponent value... - private String buildSubtractLabel(int value) { - return Integer.toString(value) + subtractLabelTrailer; - } - - } - - - ///////////////////////////////////////////////////////////////////////// - // - // Handly little class defined only to allow client to use "setText()" without - // needing to know how/whether the text needs to be formatted. This one is - // used to display instructions. - - class InstructionsPane extends JLabel { - - InstructionsPane(Component parent) { + g.drawString(label, exponentCenterX - subtractLabelWidth / 2, centerY + subtractLabelHeight / 2 - 3); // -3 makes it more visually appealing + } + + // format the label for a given integer exponent value... + private String buildSubtractLabel(int value) + { + return value + subtractLabelTrailer; + } + + } + + + ///////////////////////////////////////////////////////////////////////// + // + // Handly little class defined only to allow client to use "setText()" without + // needing to know how/whether the text needs to be formatted. This one is + // used to display instructions. + + class InstructionsPane extends JLabel + { + + InstructionsPane(Component parent) + { super(defaultInstructions); this.setFont(instructionsFont); this.setBackground(parent.getBackground()); - } - - public void setText(String text) { + } + + public void setText(String text) + { super.setText(text); - } - } - - - //////////////////////////////////////////////////////////////////////////// - // - // Use this to draw custom background in the binary fraction display. - // - class BinaryFractionDisplayTextField extends JTextField { - - public BinaryFractionDisplayTextField(String value, int columns) { - super(value,columns); - } - + } + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Use this to draw custom background in the binary fraction display. + // + class BinaryFractionDisplayTextField extends JTextField + { + + public BinaryFractionDisplayTextField(String value, int columns) + { + super(value, columns); + } + // This overrides inherited JPanel method. Override is necessary to // assure my drawn graphics get painted immediately after painting the // underlying JPanel (see first statement). - public void paintComponent(Graphics g) { - super.paintComponent(g); - // The code below is commented out because I decided to abandon - // my effort to provide "striped" background that alternates colors - // for every 4 characters (bits) of the display. This would make - // the correspondence between bits and hex digits very clear. - // NOTE: this is the only reason for subclassing JTextField. + public void paintComponent(Graphics g) + { + super.paintComponent(g); + // The code below is commented out because I decided to abandon + // my effort to provide "striped" background that alternates colors + // for every 4 characters (bits) of the display. This would make + // the correspondence between bits and hex digits very clear. + // NOTE: this is the only reason for subclassing JTextField. /* int columnWidth = getWidth()/getColumns(); @@ -943,7 +1112,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. p.addPoint(binarySignDisplay.getX(), lowerY); g.fillPolygon(p); */ - } - } - - } \ No newline at end of file + } + } + +} diff --git a/src/main/java/mars/tools/FunctionUnitVisualization.java b/src/main/java/mars/tools/FunctionUnitVisualization.java index 377cf89..a5a6687 100644 --- a/src/main/java/mars/tools/FunctionUnitVisualization.java +++ b/src/main/java/mars/tools/FunctionUnitVisualization.java @@ -1,66 +1,79 @@ package mars.tools; -import java.awt.BorderLayout; -import java.awt.EventQueue; - -import javax.swing.JFrame; -import javax.swing.JPanel; +import javax.swing.*; import javax.swing.border.EmptyBorder; +import java.awt.*; -public class FunctionUnitVisualization extends JFrame { +public class FunctionUnitVisualization extends JFrame +{ - private JPanel contentPane; - private String instruction; - private int register = 1; - private int control = 2; - private int aluControl = 3; - private int alu = 4; - private int currentUnit; + private final JPanel contentPane; - /** - * Launch the application. - */ + private final String instruction; + + private final int register = 1; + + private final int control = 2; + + private final int aluControl = 3; + + private final int alu = 4; + + private int currentUnit; + + /** + * Launch the application. + */ - /** - * Create the frame. - */ - public FunctionUnitVisualization(String instruction, int functionalUnit) { - this.instruction = instruction; - //setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - setBounds(100, 100, 840, 575); - contentPane = new JPanel(); - contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); - contentPane.setLayout(new BorderLayout(0, 0)); - setContentPane(contentPane); - if(functionalUnit == register){ - currentUnit = register; - UnitAnimation reg = new UnitAnimation(instruction, register); - contentPane.add(reg); - reg.startAnimation(instruction); - } - else if(functionalUnit == control){ - currentUnit = control; - UnitAnimation reg = new UnitAnimation(instruction, control); - contentPane.add(reg); - reg.startAnimation(instruction); - } - - else if(functionalUnit == aluControl){ - currentUnit = aluControl; - UnitAnimation reg = new UnitAnimation(instruction, aluControl); - contentPane.add(reg); - reg.startAnimation(instruction); - } - - } - public void run() { - try { - FunctionUnitVisualization frame = new FunctionUnitVisualization(instruction, currentUnit); - frame.setVisible(true); - } catch (Exception e) { - e.printStackTrace(); - } - } + /** + * Create the frame. + */ + public FunctionUnitVisualization(String instruction, int functionalUnit) + { + this.instruction = instruction; + //setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + setBounds(100, 100, 840, 575); + contentPane = new JPanel(); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + contentPane.setLayout(new BorderLayout(0, 0)); + setContentPane(contentPane); + if (functionalUnit == register) + { + currentUnit = register; + UnitAnimation reg = new UnitAnimation(instruction, register); + contentPane.add(reg); + reg.startAnimation(instruction); + } + else if (functionalUnit == control) + { + currentUnit = control; + UnitAnimation reg = new UnitAnimation(instruction, control); + contentPane.add(reg); + reg.startAnimation(instruction); + } + + else if (functionalUnit == aluControl) + { + currentUnit = aluControl; + UnitAnimation reg = new UnitAnimation(instruction, aluControl); + contentPane.add(reg); + reg.startAnimation(instruction); + } + + } + + public void run() + { + try + { + FunctionUnitVisualization frame = new FunctionUnitVisualization(instruction, currentUnit); + frame.setVisible(true); + } + catch (Exception e) + { + e.printStackTrace(); + } + } } diff --git a/src/main/java/mars/tools/InstructionCounter.java b/src/main/java/mars/tools/InstructionCounter.java index e54f5a9..b1596eb 100644 --- a/src/main/java/mars/tools/InstructionCounter.java +++ b/src/main/java/mars/tools/InstructionCounter.java @@ -26,17 +26,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package mars.tools; -import java.awt.GridBagConstraints; -import java.awt.GridBagLayout; -import java.awt.Insets; -import java.util.Observable; - -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JTextField; - import mars.ProgramStatement; import mars.mips.hardware.AccessNotice; import mars.mips.hardware.AddressErrorException; @@ -45,226 +34,266 @@ import mars.mips.hardware.MemoryAccessNotice; import mars.mips.instructions.BasicInstruction; import mars.mips.instructions.BasicInstructionFormat; +import javax.swing.*; +import java.awt.*; +import java.util.Observable; + /** - * - * Instruction counter tool. Can be used to know how many instructions - * were executed to complete a given program. - * + * Instruction counter tool. Can be used to know how many instructions were executed to complete a given program. + *

* Code slightly based on MemoryReferenceVisualization. - * - * @author Felipe Lessa * + * @author Felipe Lessa */ //@SuppressWarnings("serial") -public class InstructionCounter extends AbstractMarsToolAndApplication { - private static String name = "Instruction Counter"; - private static String version = "Version 1.0 (Felipe Lessa)"; - private static String heading = "Counting the number of instructions executed"; - +public class InstructionCounter extends AbstractMarsToolAndApplication +{ + private static final String name = "Instruction Counter"; + + private static final String version = "Version 1.0 (Felipe Lessa)"; + + private static final String heading = "Counting the number of instructions executed"; + /** * Number of instructions executed until now. */ protected int counter = 0; - private JTextField counterField; - + /** * Number of instructions of type R. */ protected int counterR = 0; - private JTextField counterRField; - private JProgressBar progressbarR; - + /** * Number of instructions of type I. */ protected int counterI = 0; - private JTextField counterIField; - private JProgressBar progressbarI; - + /** * Number of instructions of type J. */ protected int counterJ = 0; - private JTextField counterJField; - private JProgressBar progressbarJ; - - + /** - * The last address we saw. We ignore it because the only way for a - * program to execute twice the same instruction is to enter an infinite - * loop, which is not insteresting in the POV of counting instructions. + * The last address we saw. We ignore it because the only way for a program to execute twice the same instruction is + * to enter an infinite loop, which is not insteresting in the POV of counting instructions. */ protected int lastAddress = -1; - - /** - * Simple constructor, likely used to run a stand-alone memory reference visualizer. - * @param title String containing title for title bar - * @param heading String containing text for heading shown in upper part of window. - */ - public InstructionCounter(String title, String heading) { - super(title,heading); + + private JTextField counterField; + + private JTextField counterRField; + + private JProgressBar progressbarR; + + private JTextField counterIField; + + private JProgressBar progressbarI; + + private JTextField counterJField; + + private JProgressBar progressbarJ; + + /** + * Simple constructor, likely used to run a stand-alone memory reference visualizer. + * + * @param title String containing title for title bar + * @param heading String containing text for heading shown in upper part of window. + */ + public InstructionCounter(String title, String heading) + { + super(title, heading); } - + /** * Simple construction, likely used by the MARS Tools menu mechanism. */ - public InstructionCounter() { - super(name + ", " + version, heading); + public InstructionCounter() + { + super(name + ", " + version, heading); } -// @Override - public String getName() { - return name; - } - -// @Override - protected JComponent buildMainDisplayArea() { - // Create everything - JPanel panel = new JPanel(new GridBagLayout()); + // @Override + public String getName() + { + return name; + } - counterField = new JTextField("0", 10); - counterField.setEditable(false); - - counterRField = new JTextField("0", 10); - counterRField.setEditable(false); - progressbarR = new JProgressBar(JProgressBar.HORIZONTAL); - progressbarR.setStringPainted(true); - - counterIField = new JTextField("0", 10); - counterIField.setEditable(false); - progressbarI = new JProgressBar(JProgressBar.HORIZONTAL); - progressbarI.setStringPainted(true); - - counterJField = new JTextField("0", 10); - counterJField.setEditable(false); - progressbarJ = new JProgressBar(JProgressBar.HORIZONTAL); - progressbarJ.setStringPainted(true); - - // Add them to the panel - - // Fields - GridBagConstraints c = new GridBagConstraints(); - c.anchor = GridBagConstraints.LINE_START; - c.gridheight = c.gridwidth = 1; - c.gridx = 3; - c.gridy = 1; - c.insets = new Insets(0, 0, 17, 0); - panel.add(counterField, c); + // @Override + protected JComponent buildMainDisplayArea() + { + // Create everything + JPanel panel = new JPanel(new GridBagLayout()); - c.insets = new Insets(0, 0, 0, 0); - c.gridy++; - panel.add(counterRField, c); - - c.gridy++; - panel.add(counterIField, c); - - c.gridy++; - panel.add(counterJField, c); - - // Labels - c.anchor = GridBagConstraints.LINE_END; - c.gridx = 1; - c.gridwidth = 2; - c.gridy = 1; - c.insets = new Insets(0, 0, 17, 0); - panel.add(new JLabel("Instructions so far: "), c); + counterField = new JTextField("0", 10); + counterField.setEditable(false); - c.insets = new Insets(0, 0, 0, 0); - c.gridx = 2; - c.gridwidth = 1; - c.gridy++; - panel.add(new JLabel("R-type: "), c); - - c.gridy++; - panel.add(new JLabel("I-type: "), c); - - c.gridy++; - panel.add(new JLabel("J-type: "), c); - - // Progress bars - c.insets = new Insets(3, 3, 3, 3); - c.gridx = 4; - c.gridy = 2; - panel.add(progressbarR, c); - - c.gridy++; - panel.add(progressbarI, c); - - c.gridy++; - panel.add(progressbarJ, c); - - return panel; - } - -// @Override - protected void addAsObserver() { - addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); - } + counterRField = new JTextField("0", 10); + counterRField.setEditable(false); + progressbarR = new JProgressBar(JProgressBar.HORIZONTAL); + progressbarR.setStringPainted(true); -// @Override - protected void processMIPSUpdate(Observable resource, AccessNotice notice) { - if (!notice.accessIsFromMIPS()) return; - if (notice.getAccessType() != AccessNotice.READ) return; - MemoryAccessNotice m = (MemoryAccessNotice) notice; - int a = m.getAddress(); - if (a == lastAddress) return; - lastAddress = a; - counter++; - try { - ProgramStatement stmt = Memory.getInstance().getStatement(a); - BasicInstruction instr = (BasicInstruction) stmt.getInstruction(); - BasicInstructionFormat format = instr.getInstructionFormat(); - if (format == BasicInstructionFormat.R_FORMAT) - counterR++; - else if (format == BasicInstructionFormat.I_FORMAT - || format == BasicInstructionFormat.I_BRANCH_FORMAT) - counterI++; - else if (format == BasicInstructionFormat.J_FORMAT) - counterJ++; - } catch (AddressErrorException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - updateDisplay(); - } - -// @Override - protected void initializePreGUI() { - counter = counterR = counterI = counterJ = 0; - lastAddress = -1; - } - -// @Override - protected void reset() { - counter = counterR = counterI = counterJ = 0; - lastAddress = -1; - updateDisplay(); - } - -// @Override - protected void updateDisplay() { - counterField.setText(String.valueOf(counter)); - - counterRField.setText(String.valueOf(counterR)); - progressbarR.setMaximum(counter); - progressbarR.setValue(counterR); - - counterIField.setText(String.valueOf(counterI)); - progressbarI.setMaximum(counter); - progressbarI.setValue(counterI); - - counterJField.setText(String.valueOf(counterJ)); - progressbarJ.setMaximum(counter); - progressbarJ.setValue(counterJ); - - if (counter == 0) { - progressbarR.setString("0%"); - progressbarI.setString("0%"); - progressbarJ.setString("0%"); - } else { - progressbarR.setString((counterR * 100)/counter + "%"); - progressbarI.setString((counterI * 100)/counter + "%"); - progressbarJ.setString((counterJ * 100)/counter + "%"); - } - } + counterIField = new JTextField("0", 10); + counterIField.setEditable(false); + progressbarI = new JProgressBar(JProgressBar.HORIZONTAL); + progressbarI.setStringPainted(true); + + counterJField = new JTextField("0", 10); + counterJField.setEditable(false); + progressbarJ = new JProgressBar(JProgressBar.HORIZONTAL); + progressbarJ.setStringPainted(true); + + // Add them to the panel + + // Fields + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.LINE_START; + c.gridheight = c.gridwidth = 1; + c.gridx = 3; + c.gridy = 1; + c.insets = new Insets(0, 0, 17, 0); + panel.add(counterField, c); + + c.insets = new Insets(0, 0, 0, 0); + c.gridy++; + panel.add(counterRField, c); + + c.gridy++; + panel.add(counterIField, c); + + c.gridy++; + panel.add(counterJField, c); + + // Labels + c.anchor = GridBagConstraints.LINE_END; + c.gridx = 1; + c.gridwidth = 2; + c.gridy = 1; + c.insets = new Insets(0, 0, 17, 0); + panel.add(new JLabel("Instructions so far: "), c); + + c.insets = new Insets(0, 0, 0, 0); + c.gridx = 2; + c.gridwidth = 1; + c.gridy++; + panel.add(new JLabel("R-type: "), c); + + c.gridy++; + panel.add(new JLabel("I-type: "), c); + + c.gridy++; + panel.add(new JLabel("J-type: "), c); + + // Progress bars + c.insets = new Insets(3, 3, 3, 3); + c.gridx = 4; + c.gridy = 2; + panel.add(progressbarR, c); + + c.gridy++; + panel.add(progressbarI, c); + + c.gridy++; + panel.add(progressbarJ, c); + + return panel; + } + + // @Override + protected void addAsObserver() + { + addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); + } + + // @Override + protected void processMIPSUpdate(Observable resource, AccessNotice notice) + { + if (!notice.accessIsFromMIPS()) + { + return; + } + if (notice.getAccessType() != AccessNotice.READ) + { + return; + } + MemoryAccessNotice m = (MemoryAccessNotice) notice; + int a = m.getAddress(); + if (a == lastAddress) + { + return; + } + lastAddress = a; + counter++; + try + { + ProgramStatement stmt = Memory.getInstance().getStatement(a); + BasicInstruction instr = (BasicInstruction) stmt.getInstruction(); + BasicInstructionFormat format = instr.getInstructionFormat(); + if (format == BasicInstructionFormat.R_FORMAT) + { + counterR++; + } + else if (format == BasicInstructionFormat.I_FORMAT + || format == BasicInstructionFormat.I_BRANCH_FORMAT) + { + counterI++; + } + else if (format == BasicInstructionFormat.J_FORMAT) + { + counterJ++; + } + } + catch (AddressErrorException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + updateDisplay(); + } + + // @Override + protected void initializePreGUI() + { + counter = counterR = counterI = counterJ = 0; + lastAddress = -1; + } + + // @Override + protected void reset() + { + counter = counterR = counterI = counterJ = 0; + lastAddress = -1; + updateDisplay(); + } + + // @Override + protected void updateDisplay() + { + counterField.setText(String.valueOf(counter)); + + counterRField.setText(String.valueOf(counterR)); + progressbarR.setMaximum(counter); + progressbarR.setValue(counterR); + + counterIField.setText(String.valueOf(counterI)); + progressbarI.setMaximum(counter); + progressbarI.setValue(counterI); + + counterJField.setText(String.valueOf(counterJ)); + progressbarJ.setMaximum(counter); + progressbarJ.setValue(counterJ); + + if (counter == 0) + { + progressbarR.setString("0%"); + progressbarI.setString("0%"); + progressbarJ.setString("0%"); + } + else + { + progressbarR.setString((counterR * 100) / counter + "%"); + progressbarI.setString((counterI * 100) / counter + "%"); + progressbarJ.setString((counterJ * 100) / counter + "%"); + } + } } diff --git a/src/main/java/mars/tools/InstructionStatistics.java b/src/main/java/mars/tools/InstructionStatistics.java index bc3021a..cff3073 100644 --- a/src/main/java/mars/tools/InstructionStatistics.java +++ b/src/main/java/mars/tools/InstructionStatistics.java @@ -25,327 +25,361 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - package mars.tools; +package mars.tools; - import java.awt.GridBagConstraints; - import java.awt.GridBagLayout; - import java.awt.Insets; - import java.util.Observable; +import mars.ProgramStatement; +import mars.mips.hardware.AccessNotice; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Memory; +import mars.mips.hardware.MemoryAccessNotice; - import javax.swing.JComponent; - import javax.swing.JLabel; - import javax.swing.JPanel; - import javax.swing.JProgressBar; - import javax.swing.JTextField; - - import mars.ProgramStatement; - import mars.mips.hardware.AccessNotice; - import mars.mips.hardware.AddressErrorException; - import mars.mips.hardware.Memory; - import mars.mips.hardware.MemoryAccessNotice; +import javax.swing.*; +import java.awt.*; +import java.util.Observable; /** - * - * A MARS tool for obtaining instruction statistics by instruction category. - *

- * The code of this tools is initially based on the Instruction counter tool by Felipe Lassa. - * - * @author Ingo Kofler + * A MARS tool for obtaining instruction statistics by instruction category. + *

+ * The code of this tools is initially based on the Instruction counter tool by Felipe Lassa. * + * @author Ingo Kofler */ - // @SuppressWarnings("serial") - public class InstructionStatistics extends AbstractMarsToolAndApplication { - - /** name of the tool */ - private static String NAME = "Instruction Statistics"; - - /** version and author information of the tool */ - private static String VERSION = "Version 1.0 (Ingo Kofler)"; - - /** heading of the tool */ - private static String HEADING = ""; - - - +// @SuppressWarnings("serial") +public class InstructionStatistics extends AbstractMarsToolAndApplication +{ + /** number of instruction categories used by this tool */ - private static final int MAX_CATEGORY = 5; - + private static final int MAX_CATEGORY = 5; + /** constant for ALU instructions category */ - private static final int CATEGORY_ALU = 0; - + private static final int CATEGORY_ALU = 0; + /** constant for jump instructions category */ - private static final int CATEGORY_JUMP = 1; - + private static final int CATEGORY_JUMP = 1; + /** constant for branch instructions category */ - private static final int CATEGORY_BRANCH = 2; - + private static final int CATEGORY_BRANCH = 2; + /** constant for memory instructions category */ - private static final int CATEGORY_MEM = 3; - + private static final int CATEGORY_MEM = 3; + /** constant for any other instruction category */ - private static final int CATEGORY_OTHER = 4; - - - - + private static final int CATEGORY_OTHER = 4; + + /** name of the tool */ + private static final String NAME = "Instruction Statistics"; + + /** version and author information of the tool */ + private static final String VERSION = "Version 1.0 (Ingo Kofler)"; + + /** heading of the tool */ + private static final String HEADING = ""; + + /** + * The last address we saw. We ignore it because the only way for a program to execute twice the same instruction is + * to enter an infinite loop, which is not insteresting in the POV of counting instructions. + */ + protected int lastAddress = -1; + /** text field for visualizing the total number of instructions processed */ - private JTextField m_tfTotalCounter; - - /** array of text field - one for each instruction category */ - private JTextField m_tfCounters[]; - + private JTextField m_tfTotalCounter; + + /** array of text field - one for each instruction category */ + private JTextField[] m_tfCounters; + /** array of progress pars - one for each instruction category */ - private JProgressBar m_pbCounters[]; - - + private JProgressBar[] m_pbCounters; + /** counter for the total number of instructions processed */ - private int m_totalCounter = 0; - + private int m_totalCounter = 0; + /** array of counter variables - one for each instruction category */ - private int m_counters[] = new int[MAX_CATEGORY]; - - /** names of the instruction categories as array */ - private String m_categoryLabels[] = { "ALU", "Jump", "Branch", "Memory", "Other" }; - - + private final int[] m_counters = new int[MAX_CATEGORY]; + + // From Felipe Lessa's instruction counter. Prevent double-counting of instructions // which happens because 2 read events are generated. + + /** names of the instruction categories as array */ + private final String[] m_categoryLabels = {"ALU", "Jump", "Branch", "Memory", "Other"}; + /** - * The last address we saw. We ignore it because the only way for a - * program to execute twice the same instruction is to enter an infinite - * loop, which is not insteresting in the POV of counting instructions. + * Simple constructor, likely used to run a stand-alone enhanced instruction counter. + * + * @param title String containing title for title bar + * @param heading String containing text for heading shown in upper part of window. */ - protected int lastAddress = -1; - - /** - * Simple constructor, likely used to run a stand-alone enhanced instruction counter. - * @param title String containing title for title bar - * @param heading String containing text for heading shown in upper part of window. - */ - public InstructionStatistics(String title, String heading) { - super(title, heading); - } - - + public InstructionStatistics(String title, String heading) + { + super(title, heading); + } + + /** * Simple construction, likely used by the MARS Tools menu mechanism. */ - public InstructionStatistics() { - super(InstructionStatistics.NAME + ", " + InstructionStatistics.VERSION, InstructionStatistics.HEADING); - } - - + public InstructionStatistics() + { + super(InstructionStatistics.NAME + ", " + InstructionStatistics.VERSION, InstructionStatistics.HEADING); + } + + /** * returns the name of the tool - * + * * @return the tools's name */ - public String getName() { - return NAME; - } - - - /** - * creates the display area for the tool as required by the API - * - * @return a panel that holds the GUI of the tool - */ - protected JComponent buildMainDisplayArea() { - - // Create GUI elements for the tool - JPanel panel = new JPanel(new GridBagLayout()); - - m_tfTotalCounter = new JTextField("0", 10); - m_tfTotalCounter.setEditable(false); - - m_tfCounters = new JTextField[MAX_CATEGORY]; - m_pbCounters = new JProgressBar[MAX_CATEGORY]; - - // for each category a text field and a progress bar is created - for (int i=0; i < InstructionStatistics.MAX_CATEGORY; i++) { + public String getName() + { + return NAME; + } + + + /** + * creates the display area for the tool as required by the API + * + * @return a panel that holds the GUI of the tool + */ + protected JComponent buildMainDisplayArea() + { + + // Create GUI elements for the tool + JPanel panel = new JPanel(new GridBagLayout()); + + m_tfTotalCounter = new JTextField("0", 10); + m_tfTotalCounter.setEditable(false); + + m_tfCounters = new JTextField[MAX_CATEGORY]; + m_pbCounters = new JProgressBar[MAX_CATEGORY]; + + // for each category a text field and a progress bar is created + for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) + { m_tfCounters[i] = new JTextField("0", 10); m_tfCounters[i].setEditable(false); m_pbCounters[i] = new JProgressBar(JProgressBar.HORIZONTAL); m_pbCounters[i].setStringPainted(true); - } - - GridBagConstraints c = new GridBagConstraints(); - c.anchor = GridBagConstraints.LINE_START; - c.gridheight = c.gridwidth = 1; - - // create the label and text field for the total instruction counter - c.gridx = 2; - c.gridy = 1; - c.insets = new Insets(0, 0, 17, 0); - panel.add(new JLabel("Total: "), c); - c.gridx = 3; - panel.add(m_tfTotalCounter, c); - - c.insets = new Insets(3, 3, 3, 3); - - // create label, text field and progress bar for each category - for (int i=0; i < InstructionStatistics.MAX_CATEGORY; i++) { + } + + GridBagConstraints c = new GridBagConstraints(); + c.anchor = GridBagConstraints.LINE_START; + c.gridheight = c.gridwidth = 1; + + // create the label and text field for the total instruction counter + c.gridx = 2; + c.gridy = 1; + c.insets = new Insets(0, 0, 17, 0); + panel.add(new JLabel("Total: "), c); + c.gridx = 3; + panel.add(m_tfTotalCounter, c); + + c.insets = new Insets(3, 3, 3, 3); + + // create label, text field and progress bar for each category + for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) + { c.gridy++; - c.gridx=2; + c.gridx = 2; panel.add(new JLabel(m_categoryLabels[i] + ": "), c); - c.gridx=3; + c.gridx = 3; panel.add(m_tfCounters[i], c); - c.gridx=4; - panel.add(m_pbCounters[i], c); - } - - return panel; - } - - - /** - * registers the tool as observer for the text segment of the MIPS program - * - */ - protected void addAsObserver() { - addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); - } - - - /** - * decodes the instruction and determines the category of the instruction. - * - * The instruction is decoded by extracting the operation and function code of the 32-bit instruction. - * Only the most relevant instructions are decoded and categorized. - * - * @param stmt the instruction to decode - * @return the category of the instruction - * @see InstructionStatistics#CATEGORY_ALU - * @see InstructionStatistics#CATEGORY_JUMP - * @see InstructionStatistics#CATEGORY_BRANCH - * @see InstructionStatistics#CATEGORY_MEM - * @see InstructionStatistics#CATEGORY_OTHER - */ - protected int getInstructionCategory(ProgramStatement stmt) { - - int opCode = stmt.getBinaryStatement() >>> (32-6); - int funct = stmt.getBinaryStatement() & 0x1F; - - if (opCode == 0x00) { - if (funct == 0x00 ) - return InstructionStatistics.CATEGORY_ALU; // sll - if (0x02 <= funct && funct <= 0x07) - return InstructionStatistics.CATEGORY_ALU; // srl, sra, sllv, srlv, srav - if (funct == 0x08 || funct == 0x09) - return InstructionStatistics.CATEGORY_JUMP; // jr, jalr - if (0x10 <= funct && funct <= 0x2F) - return InstructionStatistics.CATEGORY_ALU; // mfhi, mthi, mflo, mtlo, mult, multu, div, divu, add, addu, sub, subu, and, or, xor, nor, slt, sltu - return InstructionStatistics.CATEGORY_OTHER; - } - if (opCode == 0x01) { - if (0x00 <= funct && funct <= 0x07) - return InstructionStatistics.CATEGORY_BRANCH; // bltz, bgez, bltzl, bgezl - if (0x10 <= funct && funct <= 0x13) - return InstructionStatistics.CATEGORY_BRANCH; // bltzal, bgezal, bltzall, bgczall + c.gridx = 4; + panel.add(m_pbCounters[i], c); + } + + return panel; + } + + + /** + * registers the tool as observer for the text segment of the MIPS program + */ + protected void addAsObserver() + { + addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); + } + + + /** + * decodes the instruction and determines the category of the instruction. + *

+ * The instruction is decoded by extracting the operation and function code of the 32-bit instruction. Only the most + * relevant instructions are decoded and categorized. + * + * @param stmt the instruction to decode + * @return the category of the instruction + * @see InstructionStatistics#CATEGORY_ALU + * @see InstructionStatistics#CATEGORY_JUMP + * @see InstructionStatistics#CATEGORY_BRANCH + * @see InstructionStatistics#CATEGORY_MEM + * @see InstructionStatistics#CATEGORY_OTHER + */ + protected int getInstructionCategory(ProgramStatement stmt) + { + + int opCode = stmt.getBinaryStatement() >>> (32 - 6); + int funct = stmt.getBinaryStatement() & 0x1F; + + if (opCode == 0x00) + { + if (funct == 0x00) + { + return InstructionStatistics.CATEGORY_ALU; // sll + } + if (0x02 <= funct && funct <= 0x07) + { + return InstructionStatistics.CATEGORY_ALU; // srl, sra, sllv, srlv, srav + } + if (funct == 0x08 || funct == 0x09) + { + return InstructionStatistics.CATEGORY_JUMP; // jr, jalr + } + if (0x10 <= funct && funct <= 0x2F) + { + return InstructionStatistics.CATEGORY_ALU; // mfhi, mthi, mflo, mtlo, mult, multu, div, divu, add, addu, sub, subu, and, or, xor, nor, slt, sltu + } return InstructionStatistics.CATEGORY_OTHER; - } - if (opCode == 0x02 || opCode == 0x03) + } + if (opCode == 0x01) + { + if (0x00 <= funct && funct <= 0x07) + { + return InstructionStatistics.CATEGORY_BRANCH; // bltz, bgez, bltzl, bgezl + } + if (0x10 <= funct && funct <= 0x13) + { + return InstructionStatistics.CATEGORY_BRANCH; // bltzal, bgezal, bltzall, bgczall + } + return InstructionStatistics.CATEGORY_OTHER; + } + if (opCode == 0x02 || opCode == 0x03) + { return InstructionStatistics.CATEGORY_JUMP; // j, jal - if (0x04 <= opCode && opCode <= 0x07) + } + if (0x04 <= opCode && opCode <= 0x07) + { return InstructionStatistics.CATEGORY_BRANCH; // beq, bne, blez, bgtz - if (0x08 <= opCode && opCode <= 0x0F) + } + if (0x08 <= opCode && opCode <= 0x0F) + { return InstructionStatistics.CATEGORY_ALU; // addi, addiu, slti, sltiu, andi, ori, xori, lui - if (0x14 <= opCode && opCode <= 0x17) + } + if (0x14 <= opCode && opCode <= 0x17) + { return InstructionStatistics.CATEGORY_BRANCH; // beql, bnel, blezl, bgtzl - if (0x20 <= opCode && opCode <= 0x26) + } + if (0x20 <= opCode && opCode <= 0x26) + { return InstructionStatistics.CATEGORY_MEM; // lb, lh, lwl, lw, lbu, lhu, lwr - if (0x28 <= opCode && opCode <= 0x2E) + } + if (0x28 <= opCode && opCode <= 0x2E) + { return InstructionStatistics.CATEGORY_MEM; // sb, sh, swl, sw, swr - - return InstructionStatistics.CATEGORY_OTHER; - } - - - /** - * method that is called each time the MIPS simulator accesses the text segment. - * Before an instruction is executed by the simulator, the instruction is fetched from the program memory. - * This memory access is observed and the corresponding instruction is decoded and categorized by the tool. - * According to the category the counter values are increased and the display gets updated. - * - * @param resource the observed resource - * @param notice signals the type of access (memory, register etc.) - */ - protected void processMIPSUpdate(Observable resource, AccessNotice notice) { - - if (!notice.accessIsFromMIPS()) - return; - - // check for a read access in the text segment - if (notice.getAccessType() == AccessNotice.READ && notice instanceof MemoryAccessNotice) { - - // now it is safe to make a cast of the notice - MemoryAccessNotice memAccNotice = (MemoryAccessNotice) notice; - - // The next three statments are from Felipe Lessa's instruction counter. Prevents double-counting. + } + + return InstructionStatistics.CATEGORY_OTHER; + } + + + /** + * method that is called each time the MIPS simulator accesses the text segment. Before an instruction is executed + * by the simulator, the instruction is fetched from the program memory. This memory access is observed and the + * corresponding instruction is decoded and categorized by the tool. According to the category the counter values + * are increased and the display gets updated. + * + * @param resource the observed resource + * @param notice signals the type of access (memory, register etc.) + */ + protected void processMIPSUpdate(Observable resource, AccessNotice notice) + { + + if (!notice.accessIsFromMIPS()) + { + return; + } + + // check for a read access in the text segment + if (notice.getAccessType() == AccessNotice.READ && notice instanceof MemoryAccessNotice) + { + + // now it is safe to make a cast of the notice + MemoryAccessNotice memAccNotice = (MemoryAccessNotice) notice; + + // The next three statments are from Felipe Lessa's instruction counter. Prevents double-counting. int a = memAccNotice.getAddress(); - if (a == lastAddress) - return; + if (a == lastAddress) + { + return; + } lastAddress = a; - - try { - - // access the statement in the text segment without notifying other tools etc. - ProgramStatement stmt = Memory.getInstance().getStatementNoNotify(memAccNotice.getAddress()); - - // necessary to handle possible null pointers at the end of the program - // (e.g., if the simulator tries to execute the next instruction after the last instruction in the text segment) - if (stmt != null) { - int category = getInstructionCategory(stmt); - - m_totalCounter ++; - m_counters[category] ++; - updateDisplay(); - } - } - catch (AddressErrorException e) { - // silently ignore these exceptions - } - } - } - - - /** - * performs initialization tasks of the counters before the GUI is created. - * - */ - protected void initializePreGUI() { - m_totalCounter = 0; - lastAddress = -1; // from Felipe Lessa's instruction counter tool - for (int i=0; i < InstructionStatistics.MAX_CATEGORY; i++) - m_counters[i] = 0; - } - - - /** - * resets the counter values of the tool and updates the display. - * - */ - protected void reset() { - m_totalCounter = 0; - lastAddress = -1; // from Felipe Lessa's instruction counter tool - for (int i=0; i < InstructionStatistics.MAX_CATEGORY; i++) - m_counters[i] = 0; - updateDisplay(); - } - - - /** - * updates the text fields and progress bars according to the current counter values. - * - */ - protected void updateDisplay() { - m_tfTotalCounter.setText(String.valueOf(m_totalCounter)); - - for (int i=0; i < InstructionStatistics.MAX_CATEGORY; i++) { + + try + { + + // access the statement in the text segment without notifying other tools etc. + ProgramStatement stmt = Memory.getInstance().getStatementNoNotify(memAccNotice.getAddress()); + + // necessary to handle possible null pointers at the end of the program + // (e.g., if the simulator tries to execute the next instruction after the last instruction in the text segment) + if (stmt != null) + { + int category = getInstructionCategory(stmt); + + m_totalCounter++; + m_counters[category]++; + updateDisplay(); + } + } + catch (AddressErrorException e) + { + // silently ignore these exceptions + } + } + } + + + /** + * performs initialization tasks of the counters before the GUI is created. + */ + protected void initializePreGUI() + { + m_totalCounter = 0; + lastAddress = -1; // from Felipe Lessa's instruction counter tool + for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) + { + m_counters[i] = 0; + } + } + + + /** + * resets the counter values of the tool and updates the display. + */ + protected void reset() + { + m_totalCounter = 0; + lastAddress = -1; // from Felipe Lessa's instruction counter tool + for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) + { + m_counters[i] = 0; + } + updateDisplay(); + } + + + /** + * updates the text fields and progress bars according to the current counter values. + */ + protected void updateDisplay() + { + m_tfTotalCounter.setText(String.valueOf(m_totalCounter)); + + for (int i = 0; i < InstructionStatistics.MAX_CATEGORY; i++) + { m_tfCounters[i].setText(String.valueOf(m_counters[i])); m_pbCounters[i].setMaximum(m_totalCounter); m_pbCounters[i].setValue(m_counters[i]); - } - } - } + } + } +} diff --git a/src/main/java/mars/tools/IntroToTools.java b/src/main/java/mars/tools/IntroToTools.java index 440e893..444d1fc 100644 --- a/src/main/java/mars/tools/IntroToTools.java +++ b/src/main/java/mars/tools/IntroToTools.java @@ -1,8 +1,7 @@ - package mars.tools; - import javax.swing.*; - import java.awt.*; - import java.awt.event.*; - import mars.*; +package mars.tools; + +import javax.swing.*; +import java.awt.*; /* @@ -32,127 +31,133 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * The "hello world" of MarsTools! - */ - public class IntroToTools extends AbstractMarsToolAndApplication { - - private static String heading = "Introduction to MARS Tools and Applications"; - private static String version = " Version 1.0"; - - /** - * Simple constructor, likely used to run a stand-alone memory reference visualizer. - * @param title String containing title for title bar - * @param heading String containing text for heading shown in upper part of window. - */ - public IntroToTools(String title, String heading) { - super(title,heading); - } - - /** - * Simple constructor, likely used by the MARS Tools menu mechanism - */ - public IntroToTools() { - super (heading+", "+version, heading); - } - - - /** - * Main provided for pure stand-alone use. Recommended stand-alone use is to write a - * driver program that instantiates a MemoryReferenceVisualization object then invokes its go() method. - * "stand-alone" means it is not invoked from the MARS Tools menu. "Pure" means there - * is no driver program to invoke the application. - */ - public static void main(String[] args) { - new IntroToTools(heading+", "+version,heading).go(); - } - - - /** - * Required method to return Tool name. - * @return Tool name. MARS will display this in menu item. - */ - public String getName() { - return "Introduction to Tools"; - } - - /** - * Implementation of the inherited abstract method to build the main - * display area of the GUI. It will be placed in the CENTER area of a - * BorderLayout. The title is in the NORTH area, and the controls are - * in the SOUTH area. - */ - protected JComponent buildMainDisplayArea() { - JTextArea message = new JTextArea(); - message.setEditable(false); - message.setLineWrap(true); - message.setWrapStyleWord(true); - message.setFont(new Font("Ariel",Font.PLAIN,12)); - message.setText( - "Hello! This Tool does not do anything but you may use its "+ - "source code as a starting point to build your own MARS Tool "+ - "or Application."+ - "\n\n"+ - "A MARS Tool is a program listed in the MARS Tools menu. It is launched "+ - "when you select its menu item and typically interacts with executing MIPS "+ - "programs to do something exciting and informative or at least interesting."+ - "\n\n"+ - "A MARS Application is a stand-alone program for similarly interacting with "+ - "executing MIPS programs. It uses MARS' MIPS assembler and "+ - "runtime simulator in the background to control MIPS execution."+ - "\n\n"+ - "The basic requirements for building a MARS Tool are:"+ - "\n"+ - " 1. It must be a class that implements the MarsTool interface. "+ - "This has only two methods: 'String getName()' to return the "+ - "name to be displayed in its Tools menu item, and "+ - "'void action()' which is invoked when that menu item "+ - "is selected by the MARS user."+ - "\n"+ - " 2. It must be stored in the mars.tools package (in folder "+ - "mars/tools)"+ - "\n"+ - " 3. It must be successfully compiled in that package. This "+ - "normally means the MARS distribution needs to be extracted from the "+ - "JAR file before you can develop your Tool."+ - "\n\n"+ - "If these requirements are met, MARS will recognize and load "+ - "your Tool into its Tools menu the next time it runs."+ - "\n\n"+ - "There are no fixed requirements for building a MARS Application, a "+ - "stand-alone program that utilizes the MARS API."+ - "\n\n"+ - "You can build a program that may be run as either a MARS Tool or an Application. "+ - "The easiest way is to extend an abstract class provided in the MARS distribution: "+ - "mars.tools.AbstractMarsToolAndApplication. "+ - "\n"+ - " 1. It defines a suite of methods and provides default definitions for "+ - "all but two: getName() and buildMainDisplayArea()."+ - "\n"+ - " 2. String getName() was introduced above."+ - "\n"+ - " 3. JComponent buildMainDisplayArea() returns the JComponent to be placed in the "+ - "BorderLayout.CENTER region of the tool/app's user interface. The NORTH and "+ - "SOUTH are defined to contain a heading and a set of button controls, respectively. "+ - "\n"+ - " 4. It defines a default 'void go()' method to launch the application."+ - "\n"+ - " 5. Conventional usage is to define your application as a subclass then launch it "+ - "by invoking its go() method."+ - "\n\n"+ - "The frame/dialog you are reading right now is an example of an "+ - "AbstractMarsToolAndApplication subclass. If you run it as an application, you "+ - "will notice the set of controls at the bottom of the window differ from those "+ - "you get when running it from MARS' Tools menu. It includes additional controls "+ - "to load and control the execution of pre-existing MIPS programs."+ - "\n\n"+ - "See the mars.tools.AbstractMarsToolAndApplication API or the source code of "+ - "existing tool/apps for further information."+ - "\n" - ); - message.setCaretPosition(0); // Assure first line is visible and at top of scroll pane. - return new JScrollPane(message); - } - - } \ No newline at end of file + +/** + * The "hello world" of MarsTools! + */ +public class IntroToTools extends AbstractMarsToolAndApplication +{ + + private static final String heading = "Introduction to MARS Tools and Applications"; + + private static final String version = " Version 1.0"; + + /** + * Simple constructor, likely used to run a stand-alone memory reference visualizer. + * + * @param title String containing title for title bar + * @param heading String containing text for heading shown in upper part of window. + */ + public IntroToTools(String title, String heading) + { + super(title, heading); + } + + /** + * Simple constructor, likely used by the MARS Tools menu mechanism + */ + public IntroToTools() + { + super(heading + ", " + version, heading); + } + + + /** + * Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that + * instantiates a MemoryReferenceVisualization object then invokes its go() method. "stand-alone" means it is not + * invoked from the MARS Tools menu. "Pure" means there is no driver program to invoke the application. + */ + public static void main(String[] args) + { + new IntroToTools(heading + ", " + version, heading).go(); + } + + + /** + * Required method to return Tool name. + * + * @return Tool name. MARS will display this in menu item. + */ + public String getName() + { + return "Introduction to Tools"; + } + + /** + * Implementation of the inherited abstract method to build the main display area of the GUI. It will be placed in + * the CENTER area of a BorderLayout. The title is in the NORTH area, and the controls are in the SOUTH area. + */ + protected JComponent buildMainDisplayArea() + { + JTextArea message = new JTextArea(); + message.setEditable(false); + message.setLineWrap(true); + message.setWrapStyleWord(true); + message.setFont(new Font("Ariel", Font.PLAIN, 12)); + message.setText( + "Hello! This Tool does not do anything but you may use its " + + "source code as a starting point to build your own MARS Tool " + + "or Application." + + "\n\n" + + "A MARS Tool is a program listed in the MARS Tools menu. It is launched " + + "when you select its menu item and typically interacts with executing MIPS " + + "programs to do something exciting and informative or at least interesting." + + "\n\n" + + "A MARS Application is a stand-alone program for similarly interacting with " + + "executing MIPS programs. It uses MARS' MIPS assembler and " + + "runtime simulator in the background to control MIPS execution." + + "\n\n" + + "The basic requirements for building a MARS Tool are:" + + "\n" + + " 1. It must be a class that implements the MarsTool interface. " + + "This has only two methods: 'String getName()' to return the " + + "name to be displayed in its Tools menu item, and " + + "'void action()' which is invoked when that menu item " + + "is selected by the MARS user." + + "\n" + + " 2. It must be stored in the mars.tools package (in folder " + + "mars/tools)" + + "\n" + + " 3. It must be successfully compiled in that package. This " + + "normally means the MARS distribution needs to be extracted from the " + + "JAR file before you can develop your Tool." + + "\n\n" + + "If these requirements are met, MARS will recognize and load " + + "your Tool into its Tools menu the next time it runs." + + "\n\n" + + "There are no fixed requirements for building a MARS Application, a " + + "stand-alone program that utilizes the MARS API." + + "\n\n" + + "You can build a program that may be run as either a MARS Tool or an Application. " + + "The easiest way is to extend an abstract class provided in the MARS distribution: " + + "mars.tools.AbstractMarsToolAndApplication. " + + "\n" + + " 1. It defines a suite of methods and provides default definitions for " + + "all but two: getName() and buildMainDisplayArea()." + + "\n" + + " 2. String getName() was introduced above." + + "\n" + + " 3. JComponent buildMainDisplayArea() returns the JComponent to be placed in the " + + "BorderLayout.CENTER region of the tool/app's user interface. The NORTH and " + + "SOUTH are defined to contain a heading and a set of button controls, respectively. " + + "\n" + + " 4. It defines a default 'void go()' method to launch the application." + + "\n" + + " 5. Conventional usage is to define your application as a subclass then launch it " + + "by invoking its go() method." + + "\n\n" + + "The frame/dialog you are reading right now is an example of an " + + "AbstractMarsToolAndApplication subclass. If you run it as an application, you " + + "will notice the set of controls at the bottom of the window differ from those " + + "you get when running it from MARS' Tools menu. It includes additional controls " + + "to load and control the execution of pre-existing MIPS programs." + + "\n\n" + + "See the mars.tools.AbstractMarsToolAndApplication API or the source code of " + + "existing tool/apps for further information." + + "\n" + ); + message.setCaretPosition(0); // Assure first line is visible and at top of scroll pane. + return new JScrollPane(message); + } + +} diff --git a/src/main/java/mars/tools/KeyboardAndDisplaySimulator.java b/src/main/java/mars/tools/KeyboardAndDisplaySimulator.java index 9ecec36..cb6c68b 100644 --- a/src/main/java/mars/tools/KeyboardAndDisplaySimulator.java +++ b/src/main/java/mars/tools/KeyboardAndDisplaySimulator.java @@ -1,17 +1,23 @@ - package mars.tools; - import mars.util.Binary; - import mars.venus.*; - import javax.swing.*; - import javax.swing.border.*; - import javax.swing.event.*; - import java.awt.*; - import java.awt.event.*; - import java.util.*; - import mars.Globals; - import mars.venus.RunSpeedPanel; - import mars.mips.hardware.*; - import mars.simulator.Exceptions; - import javax.swing.text.DefaultCaret; +package mars.tools; + +import mars.Globals; +import mars.mips.hardware.*; +import mars.simulator.Exceptions; +import mars.util.Binary; +import mars.venus.AbstractFontSettingDialog; + +import javax.swing.*; +import javax.swing.border.TitledBorder; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.text.DefaultCaret; +import java.awt.*; +import java.awt.event.*; +import java.util.Arrays; +import java.util.Observable; +import java.util.Random; /* @@ -42,172 +48,266 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - /** - * Keyboard and Display Simulator. It can be run either as a stand-alone Java application having - * access to the mars package, or through MARS as an item in its Tools menu. It makes - * maximum use of methods inherited from its abstract superclass AbstractMarsToolAndApplication. - * Pete Sanderson
- * Version 1.0, 24 July 2008.
- * Version 1.1, 24 November 2008 corrects two omissions: (1) the tool failed to register as an observer - * of kernel text memory when counting instruction executions for transmitter ready bit - * reset delay, and (2) the tool failed to test the Status register's Exception Level bit before - * raising the exception that results in the interrupt (if the Exception Level bit is 1, that - * means an interrupt is being processed, so disable further interrupts). - * - * Version 1.2, August 2009, soft-codes the MMIO register locations for new memory configuration - * feature of MARS 3.7. Previously memory segment addresses were fixed and final. Now they - * can be modified dynamically so the tool has to get its values dynamically as well. - * - * Version 1.3, August 2011, corrects bug to enable Display window to scroll when needed. - * - * Version 1.4, August 2014, adds two features: (1) ASCII control character 12 (form feed) when - * transmitted will clear the Display window. (2) ASCII control character 7 (bell) when - * transmitted with properly coded (X,Y) values will reposition the cursor to the specified - * position of a virtual text-based terminal. X represents column, Y represents row. - */ - - public class KeyboardAndDisplaySimulator extends AbstractMarsToolAndApplication { - - private static String version = "Version 1.4"; - private static String heading = "Keyboard and Display MMIO Simulator"; - private static String displayPanelTitle, keyboardPanelTitle; - private static char VT_FILL = ' '; // fill character for virtual terminal (random access mode) - - public static Dimension preferredTextAreaDimension = new Dimension(400,200); - private static Insets textAreaInsets = new Insets(4,4,4,4); - - // Time delay to process Transmitter Data is simulated by counting instruction executions. - // After this many executions, the Transmitter Controller Ready bit set to 1. - private final TransmitterDelayTechnique[] delayTechniques = { - new FixedLengthDelay(), - new UniformlyDistributedDelay(), - new NormallyDistributedDelay() - }; - public static int RECEIVER_CONTROL; // keyboard Ready in low-order bit - public static int RECEIVER_DATA; // keyboard character in low-order byte - public static int TRANSMITTER_CONTROL; // display Ready in low-order bit - public static int TRANSMITTER_DATA; // display character in low-order byte - // These are used to track instruction counts to simulate driver delay of Transmitter Data - private boolean countingInstructions; - private int instructionCount; - private int transmitDelayInstructionCountLimit; - private int currentDelayInstructionLimit; - - // Should the transmitted character be displayed before the transmitter delay period? - // If not, hold onto it and print at the end of delay period. - private int intWithCharacterToDisplay; - private boolean displayAfterDelay = true; - - // Whether or not display position is sequential (JTextArea append) - // or random access (row, column). Supports new random access feature. DPS 17-July-2014 - private boolean displayRandomAccessMode = false; - private int rows, columns; - private DisplayResizeAdapter updateDisplayBorder; - private KeyboardAndDisplaySimulator simulator; - - // Major GUI components - private JPanel keyboardAndDisplay; - private JScrollPane displayScrollPane; - private JTextArea display; - private JPanel displayPanel, displayOptions; - private JComboBox delayTechniqueChooser; - private DelayLengthPanel delayLengthPanel; - private JSlider delayLengthSlider; - private JCheckBox displayAfterDelayCheckBox; - private JPanel keyboardPanel; - private JScrollPane keyAccepterScrollPane; - private JTextArea keyEventAccepter; - private JButton fontButton; - private Font defaultFont = new Font(Font.MONOSPACED, Font.PLAIN, 12); - /** - * Simple constructor, likely used to run a stand-alone keyboard/display simulator. - * @param title String containing title for title bar - * @param heading String containing text for heading shown in upper part of window. - */ - public KeyboardAndDisplaySimulator(String title, String heading) { - super(title,heading); - simulator = this; - } - - /** - * Simple constructor, likely used by the MARS Tools menu mechanism - */ - public KeyboardAndDisplaySimulator() { - super(heading + ", " + version, heading); - simulator = this; - } - - - /** - * Main provided for pure stand-alone use. Recommended stand-alone use is to write a - * driver program that instantiates a KeyboardAndDisplaySimulator object then invokes its go() method. - * "stand-alone" means it is not invoked from the MARS Tools menu. "Pure" means there - * is no driver program to invoke the application. - */ - public static void main(String[] args) { - new KeyboardAndDisplaySimulator(heading+" stand-alone, "+version,heading).go(); - } - - - /** - * Required MarsTool method to return Tool name. - * @return Tool name. MARS will display this in menu item. - */ - public String getName() { - return heading; - } - - // Set the MMIO addresses. Prior to MARS 3.7 these were final because - // MIPS address space was final as well. Now we will get MMIO base address - // each time to reflect possible change in memory configuration. DPS 6-Aug-09 - protected void initializePreGUI() { - RECEIVER_CONTROL = Memory.memoryMapBaseAddress; //0xffff0000; // keyboard Ready in low-order bit - RECEIVER_DATA = Memory.memoryMapBaseAddress + 4; //0xffff0004; // keyboard character in low-order byte - TRANSMITTER_CONTROL = Memory.memoryMapBaseAddress + 8; //0xffff0008; // display Ready in low-order bit - TRANSMITTER_DATA = Memory.memoryMapBaseAddress + 12; //0xffff000c; // display character in low-order byte - displayPanelTitle = "DISPLAY: Store to Transmitter Data "+Binary.intToHexString(TRANSMITTER_DATA); - keyboardPanelTitle = "KEYBOARD: Characters typed here are stored to Receiver Data "+Binary.intToHexString(RECEIVER_DATA); - - } - - - /** - * Override the inherited method, which registers us as an Observer over the static data segment - * (starting address 0x10010000) only. - * - * When user enters keystroke, set RECEIVER_CONTROL and RECEIVER_DATA using the action listener. - * When user loads word (lw) from RECEIVER_DATA (we are notified of the read), then clear RECEIVER_CONTROL. - * When user stores word (sw) to TRANSMITTER_DATA (we are notified of the write), then clear TRANSMITTER_CONTROL, read TRANSMITTER_DATA, - * echo the character to display, wait for delay period, then set TRANSMITTER_CONTROL. - * - * If you use the inherited GUI buttons, this method is invoked when you click "Connect" button on MarsTool or the - * "Assemble and Run" button on a Mars-based app. - */ - protected void addAsObserver() { - // Set transmitter Control ready bit to 1, means we're ready to accept display character. - updateMMIOControl(TRANSMITTER_CONTROL, readyBitSet(TRANSMITTER_CONTROL)); - // We want to be an observer only of MIPS reads from RECEIVER_DATA and writes to TRANSMITTER_DATA. - // Use the Globals.memory.addObserver() methods instead of inherited method to achieve this. - addAsObserver(RECEIVER_DATA,RECEIVER_DATA); - addAsObserver(TRANSMITTER_DATA, TRANSMITTER_DATA); - // We want to be notified of each instruction execution, because instruction count is the - // basis for delay in re-setting (literally) the TRANSMITTER_CONTROL register. SPIM does - // this too. This simulates the time required for the display unit to process the - // TRANSMITTER_DATA. - addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); - addAsObserver(Memory.kernelTextBaseAddress, Memory.kernelTextLimitAddress); - } - - - /** - * Method that constructs the main display area. It is organized vertically - * into two major components: the display and the keyboard. The display itself - * is a JTextArea and it echoes characters placed into the low order byte of - * the Transmitter Data location, 0xffff000c. They keyboard is also a JTextArea - * places each typed character into the Receive Data location 0xffff0004. - * @return the GUI component containing these two areas - */ - protected JComponent buildMainDisplayArea() { +/** + * Keyboard and Display Simulator. It can be run either as a stand-alone Java application having access to the mars + * package, or through MARS as an item in its Tools menu. It makes maximum use of methods inherited from its abstract + * superclass AbstractMarsToolAndApplication. Pete Sanderson
Version 1.0, 24 July 2008.
Version 1.1, 24 November + * 2008 corrects two omissions: (1) the tool failed to register as an observer of kernel text memory when counting + * instruction executions for transmitter ready bit reset delay, and (2) the tool failed to test the Status register's + * Exception Level bit before raising the exception that results in the interrupt (if the Exception Level bit is 1, that + * means an interrupt is being processed, so disable further interrupts). + *

+ * Version 1.2, August 2009, soft-codes the MMIO register locations for new memory configuration feature of MARS 3.7. + * Previously memory segment addresses were fixed and final. Now they can be modified dynamically so the tool has to + * get its values dynamically as well. + *

+ * Version 1.3, August 2011, corrects bug to enable Display window to scroll when needed. + *

+ * Version 1.4, August 2014, adds two features: (1) ASCII control character 12 (form feed) when transmitted will clear + * the Display window. (2) ASCII control character 7 (bell) when transmitted with properly coded (X,Y) values will + * reposition the cursor to the specified position of a virtual text-based terminal. X represents column, Y represents + * row. + */ + +public class KeyboardAndDisplaySimulator extends AbstractMarsToolAndApplication +{ + + private static final char CLEAR_SCREEN = 12; // ASCII Form Feed + + private static final char SET_CURSOR_X_Y = 7; // ASCII Bell (ding ding!) + + public static Dimension preferredTextAreaDimension = new Dimension(400, 200); + + public static int RECEIVER_CONTROL; // keyboard Ready in low-order bit + + public static int RECEIVER_DATA; // keyboard character in low-order byte + + public static int TRANSMITTER_CONTROL; // display Ready in low-order bit + + public static int TRANSMITTER_DATA; // display character in low-order byte + + private static final String version = "Version 1.4"; + + private static final String heading = "Keyboard and Display MMIO Simulator"; + + private static String displayPanelTitle, keyboardPanelTitle; + + private static final char VT_FILL = ' '; // fill character for virtual terminal (random access mode) + + private static final Insets textAreaInsets = new Insets(4, 4, 4, 4); + + // Time delay to process Transmitter Data is simulated by counting instruction executions. + // After this many executions, the Transmitter Controller Ready bit set to 1. + private final TransmitterDelayTechnique[] delayTechniques = { + new FixedLengthDelay(), + new UniformlyDistributedDelay(), + new NormallyDistributedDelay() + }; + + // These are used to track instruction counts to simulate driver delay of Transmitter Data + private boolean countingInstructions; + + private int instructionCount; + + private int transmitDelayInstructionCountLimit; + + private int currentDelayInstructionLimit; + + // Should the transmitted character be displayed before the transmitter delay period? + // If not, hold onto it and print at the end of delay period. + private int intWithCharacterToDisplay; + + private boolean displayAfterDelay = true; + + // Whether or not display position is sequential (JTextArea append) + // or random access (row, column). Supports new random access feature. DPS 17-July-2014 + private boolean displayRandomAccessMode = false; + + private int rows, columns; + + private DisplayResizeAdapter updateDisplayBorder; + + private final KeyboardAndDisplaySimulator simulator; + + // Major GUI components + private JPanel keyboardAndDisplay; + + private JScrollPane displayScrollPane; + + private JTextArea display; + + private JPanel displayPanel, displayOptions; + + private JComboBox delayTechniqueChooser; + + private DelayLengthPanel delayLengthPanel; + + private JSlider delayLengthSlider; + + private JCheckBox displayAfterDelayCheckBox; + + private JPanel keyboardPanel; + + private JScrollPane keyAccepterScrollPane; + + private JTextArea keyEventAccepter; + + private JButton fontButton; + + private final Font defaultFont = new Font(Font.MONOSPACED, Font.PLAIN, 12); + + + /** + * Simple constructor, likely used to run a stand-alone keyboard/display simulator. + * + * @param title String containing title for title bar + * @param heading String containing text for heading shown in upper part of window. + */ + public KeyboardAndDisplaySimulator(String title, String heading) + { + super(title, heading); + simulator = this; + } + + + /** + * Simple constructor, likely used by the MARS Tools menu mechanism + */ + public KeyboardAndDisplaySimulator() + { + super(heading + ", " + version, heading); + simulator = this; + } + + /** + * Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that + * instantiates a KeyboardAndDisplaySimulator object then invokes its go() method. "stand-alone" means it is not + * invoked from the MARS Tools menu. "Pure" means there is no driver program to invoke the application. + */ + public static void main(String[] args) + { + new KeyboardAndDisplaySimulator(heading + " stand-alone, " + version, heading).go(); + } + + ///////////////////////////////////////////////////////////////////// + // Return value of the given MMIO control register after ready (low order) bit set (to 1). + // Have to preserve the value of Interrupt Enable bit (bit 1) + private static boolean isReadyBitSet(int mmioControlRegister) + { + try + { + return (Globals.memory.get(mmioControlRegister, Memory.WORD_LENGTH_BYTES) & 1) == 1; + } + catch (AddressErrorException aee) + { + System.out.println("Tool author specified incorrect MMIO address!" + aee); + System.exit(0); + } + return false; // to satisfy the compiler -- this will never happen. + } + + ///////////////////////////////////////////////////////////////////// + // Return value of the given MMIO control register after ready (low order) bit set (to 1). + // Have to preserve the value of Interrupt Enable bit (bit 1) + private static int readyBitSet(int mmioControlRegister) + { + try + { + return Globals.memory.get(mmioControlRegister, Memory.WORD_LENGTH_BYTES) | 1; + } + catch (AddressErrorException aee) + { + System.out.println("Tool author specified incorrect MMIO address!" + aee); + System.exit(0); + } + return 1; // to satisfy the compiler -- this will never happen. + } + + + ////////////////////////////////////////////////////////////////////////////////////// + // Rest of the protected methods. These all override do-nothing methods inherited from + // the abstract superclass. + ////////////////////////////////////////////////////////////////////////////////////// + + ///////////////////////////////////////////////////////////////////// + // Return value of the given MMIO control register after ready (low order) bit cleared (to 0). + // Have to preserve the value of Interrupt Enable bit (bit 1). Bits 2 and higher don't matter. + private static int readyBitCleared(int mmioControlRegister) + { + try + { + return Globals.memory.get(mmioControlRegister, Memory.WORD_LENGTH_BYTES) & 2; + } + catch (AddressErrorException aee) + { + System.out.println("Tool author specified incorrect MMIO address!" + aee); + System.exit(0); + } + return 0; // to satisfy the compiler -- this will never happen. + } + + /** + * Required MarsTool method to return Tool name. + * + * @return Tool name. MARS will display this in menu item. + */ + public String getName() + { + return heading; + } + + // Set the MMIO addresses. Prior to MARS 3.7 these were final because + // MIPS address space was final as well. Now we will get MMIO base address + // each time to reflect possible change in memory configuration. DPS 6-Aug-09 + protected void initializePreGUI() + { + RECEIVER_CONTROL = Memory.memoryMapBaseAddress; //0xffff0000; // keyboard Ready in low-order bit + RECEIVER_DATA = Memory.memoryMapBaseAddress + 4; //0xffff0004; // keyboard character in low-order byte + TRANSMITTER_CONTROL = Memory.memoryMapBaseAddress + 8; //0xffff0008; // display Ready in low-order bit + TRANSMITTER_DATA = Memory.memoryMapBaseAddress + 12; //0xffff000c; // display character in low-order byte + displayPanelTitle = "DISPLAY: Store to Transmitter Data " + Binary.intToHexString(TRANSMITTER_DATA); + keyboardPanelTitle = "KEYBOARD: Characters typed here are stored to Receiver Data " + Binary.intToHexString(RECEIVER_DATA); + + } + + /** + * Override the inherited method, which registers us as an Observer over the static data segment (starting address + * 0x10010000) only. + *

+ * When user enters keystroke, set RECEIVER_CONTROL and RECEIVER_DATA using the action listener. When user loads + * word (lw) from RECEIVER_DATA (we are notified of the read), then clear RECEIVER_CONTROL. When user stores word + * (sw) to TRANSMITTER_DATA (we are notified of the write), then clear TRANSMITTER_CONTROL, read TRANSMITTER_DATA, + * echo the character to display, wait for delay period, then set TRANSMITTER_CONTROL. + *

+ * If you use the inherited GUI buttons, this method is invoked when you click "Connect" button on MarsTool or the + * "Assemble and Run" button on a Mars-based app. + */ + protected void addAsObserver() + { + // Set transmitter Control ready bit to 1, means we're ready to accept display character. + updateMMIOControl(TRANSMITTER_CONTROL, readyBitSet(TRANSMITTER_CONTROL)); + // We want to be an observer only of MIPS reads from RECEIVER_DATA and writes to TRANSMITTER_DATA. + // Use the Globals.memory.addObserver() methods instead of inherited method to achieve this. + addAsObserver(RECEIVER_DATA, RECEIVER_DATA); + addAsObserver(TRANSMITTER_DATA, TRANSMITTER_DATA); + // We want to be notified of each instruction execution, because instruction count is the + // basis for delay in re-setting (literally) the TRANSMITTER_CONTROL register. SPIM does + // this too. This simulates the time required for the display unit to process the + // TRANSMITTER_DATA. + addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); + addAsObserver(Memory.kernelTextBaseAddress, Memory.kernelTextLimitAddress); + } + + /** + * Method that constructs the main display area. It is organized vertically into two major components: the display + * and the keyboard. The display itself is a JTextArea and it echoes characters placed into the low order byte of + * the Transmitter Data location, 0xffff000c. They keyboard is also a JTextArea places each typed character into + * the Receive Data location 0xffff0004. + * + * @return the GUI component containing these two areas + */ + protected JComponent buildMainDisplayArea() + { // Changed arrangement of the display and keyboard panels from GridLayout(2,1) // to BorderLayout to hold a JSplitPane containing both panels. This permits user // to apportion the relative sizes of the display and keyboard panels within @@ -215,173 +315,201 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // display positioning feature. Previously, both the display and the keyboard // text areas were equal in size and there was no way for the user to change that. // DPS 17-July-2014 - keyboardAndDisplay = new JPanel(new BorderLayout()); - JSplitPane both = new JSplitPane(JSplitPane.VERTICAL_SPLIT, buildDisplay(),buildKeyboard()); - both.setResizeWeight(0.5); - keyboardAndDisplay.add(both); - return keyboardAndDisplay; - } - - - ////////////////////////////////////////////////////////////////////////////////////// - // Rest of the protected methods. These all override do-nothing methods inherited from - // the abstract superclass. - ////////////////////////////////////////////////////////////////////////////////////// - - /** - * Update display when connected MIPS program accesses (data) memory. - * @param memory the attached memory - * @param accessNotice information provided by memory in MemoryAccessNotice object - */ - protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) { - MemoryAccessNotice notice = (MemoryAccessNotice) accessNotice; - // If MIPS program has just read (loaded) the receiver (keyboard) data register, - // then clear the Ready bit to indicate there is no longer a keystroke available. - // If Ready bit was initially clear, they'll get the old keystroke -- serves 'em right - // for not checking! - if (notice.getAddress()==RECEIVER_DATA && notice.getAccessType()==AccessNotice.READ) { + keyboardAndDisplay = new JPanel(new BorderLayout()); + JSplitPane both = new JSplitPane(JSplitPane.VERTICAL_SPLIT, buildDisplay(), buildKeyboard()); + both.setResizeWeight(0.5); + keyboardAndDisplay.add(both); + return keyboardAndDisplay; + } + + /** + * Update display when connected MIPS program accesses (data) memory. + * + * @param memory the attached memory + * @param accessNotice information provided by memory in MemoryAccessNotice object + */ + protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) + { + MemoryAccessNotice notice = (MemoryAccessNotice) accessNotice; + // If MIPS program has just read (loaded) the receiver (keyboard) data register, + // then clear the Ready bit to indicate there is no longer a keystroke available. + // If Ready bit was initially clear, they'll get the old keystroke -- serves 'em right + // for not checking! + if (notice.getAddress() == RECEIVER_DATA && notice.getAccessType() == AccessNotice.READ) + { updateMMIOControl(RECEIVER_CONTROL, readyBitCleared(RECEIVER_CONTROL)); - } - // MIPS program has just written (stored) the transmitter (display) data register. If transmitter - // Ready bit is clear, device is not ready yet so ignore this event -- serves 'em right for not checking! - // If transmitter Ready bit is set, then clear it to indicate the display device is processing the character. - // Also start an intruction counter that will simulate the delay of the slower - // display device processing the character. - if (isReadyBitSet(TRANSMITTER_CONTROL) && notice.getAddress()==TRANSMITTER_DATA && notice.getAccessType()==AccessNotice.WRITE) { + } + // MIPS program has just written (stored) the transmitter (display) data register. If transmitter + // Ready bit is clear, device is not ready yet so ignore this event -- serves 'em right for not checking! + // If transmitter Ready bit is set, then clear it to indicate the display device is processing the character. + // Also start an intruction counter that will simulate the delay of the slower + // display device processing the character. + if (isReadyBitSet(TRANSMITTER_CONTROL) && notice.getAddress() == TRANSMITTER_DATA && notice.getAccessType() == AccessNotice.WRITE) + { updateMMIOControl(TRANSMITTER_CONTROL, readyBitCleared(TRANSMITTER_CONTROL)); intWithCharacterToDisplay = notice.getValue(); - if (!displayAfterDelay) displayCharacter(intWithCharacterToDisplay); + if (!displayAfterDelay) + { + displayCharacter(intWithCharacterToDisplay); + } this.countingInstructions = true; this.instructionCount = 0; this.transmitDelayInstructionCountLimit = generateDelay(); - } - // We have been notified of a MIPS instruction execution. - // If we are in transmit delay period, increment instruction count and if limit - // has been reached, set the transmitter Ready flag to indicate the MIPS program - // can write another character to the transmitter data register. If the Interrupt-Enabled - // bit had been set by the MIPS program, generate an interrupt! - if ( this.countingInstructions && - notice.getAccessType()==AccessNotice.READ && - (Memory.inTextSegment(notice.getAddress()) || Memory.inKernelTextSegment(notice.getAddress()))) { + } + // We have been notified of a MIPS instruction execution. + // If we are in transmit delay period, increment instruction count and if limit + // has been reached, set the transmitter Ready flag to indicate the MIPS program + // can write another character to the transmitter data register. If the Interrupt-Enabled + // bit had been set by the MIPS program, generate an interrupt! + if (this.countingInstructions && + notice.getAccessType() == AccessNotice.READ && + (Memory.inTextSegment(notice.getAddress()) || Memory.inKernelTextSegment(notice.getAddress()))) + { this.instructionCount++; - if (this.instructionCount >= this.transmitDelayInstructionCountLimit) { - if (displayAfterDelay) displayCharacter(intWithCharacterToDisplay); - this.countingInstructions = false; - int updatedTransmitterControl = readyBitSet(TRANSMITTER_CONTROL); - updateMMIOControl(TRANSMITTER_CONTROL, updatedTransmitterControl); - if (updatedTransmitterControl != 1 - && (Coprocessor0.getValue(Coprocessor0.STATUS) & 2)==0 // Added by Carl Hauser Nov 2008 - && (Coprocessor0.getValue(Coprocessor0.STATUS) & 1)==1) { - // interrupt-enabled bit is set in both Tranmitter Control and in - // Coprocessor0 Status register, and Interrupt Level Bit is 0, so trigger external interrupt. - mars.simulator.Simulator.externalInterruptingDevice = Exceptions.EXTERNAL_INTERRUPT_DISPLAY; - } + if (this.instructionCount >= this.transmitDelayInstructionCountLimit) + { + if (displayAfterDelay) + { + displayCharacter(intWithCharacterToDisplay); + } + this.countingInstructions = false; + int updatedTransmitterControl = readyBitSet(TRANSMITTER_CONTROL); + updateMMIOControl(TRANSMITTER_CONTROL, updatedTransmitterControl); + if (updatedTransmitterControl != 1 + && (Coprocessor0.getValue(Coprocessor0.STATUS) & 2) == 0 // Added by Carl Hauser Nov 2008 + && (Coprocessor0.getValue(Coprocessor0.STATUS) & 1) == 1) + { + // interrupt-enabled bit is set in both Tranmitter Control and in + // Coprocessor0 Status register, and Interrupt Level Bit is 0, so trigger external interrupt. + mars.simulator.Simulator.externalInterruptingDevice = Exceptions.EXTERNAL_INTERRUPT_DISPLAY; + } } - } - } - - private static final char CLEAR_SCREEN = 12; // ASCII Form Feed - private static final char SET_CURSOR_X_Y = 7; // ASCII Bell (ding ding!) - - // Method to display the character stored in the low-order byte of - // the parameter. We also recognize two non-printing characters: - // Decimal 12 (Ascii Form Feed) to clear the display - // Decimal 7 (Ascii Bell) to place the cursor at a specified (X,Y) position. - // of a virtual text terminal. The position is specified in the high - // order 24 bits of the transmitter word (X in 20-31, Y in 8-19). - // Thus the parameter is the entire word, not just the low-order byte. - // Once the latter is performed, the display mode changes to random - // access, which has repercussions for the implementation of character display. - private void displayCharacter(int intWithCharacterToDisplay) { - char characterToDisplay = (char) (intWithCharacterToDisplay & 0x000000FF); - if (characterToDisplay == CLEAR_SCREEN) { + } + } + + // Method to display the character stored in the low-order byte of + // the parameter. We also recognize two non-printing characters: + // Decimal 12 (Ascii Form Feed) to clear the display + // Decimal 7 (Ascii Bell) to place the cursor at a specified (X,Y) position. + // of a virtual text terminal. The position is specified in the high + // order 24 bits of the transmitter word (X in 20-31, Y in 8-19). + // Thus the parameter is the entire word, not just the low-order byte. + // Once the latter is performed, the display mode changes to random + // access, which has repercussions for the implementation of character display. + private void displayCharacter(int intWithCharacterToDisplay) + { + char characterToDisplay = (char) (intWithCharacterToDisplay & 0x000000FF); + if (characterToDisplay == CLEAR_SCREEN) + { initializeDisplay(displayRandomAccessMode); - } - else if (characterToDisplay == SET_CURSOR_X_Y) { - // First call will activate random access mode. - // We're using JTextArea, where caret has to be within text. - // So initialize text to all spaces to fill the JTextArea to its - // current capacity. Then set caret. Subsequent character - // displays will replace, not append, in the text. - if (!displayRandomAccessMode) { - displayRandomAccessMode = true; - initializeDisplay(displayRandomAccessMode); + } + else if (characterToDisplay == SET_CURSOR_X_Y) + { + // First call will activate random access mode. + // We're using JTextArea, where caret has to be within text. + // So initialize text to all spaces to fill the JTextArea to its + // current capacity. Then set caret. Subsequent character + // displays will replace, not append, in the text. + if (!displayRandomAccessMode) + { + displayRandomAccessMode = true; + initializeDisplay(displayRandomAccessMode); } - // For SET_CURSOR_X_Y, we need data from the rest of the word. - // High order 3 bytes are split in half to store (X,Y) value. - // High 12 bits contain X value, next 12 bits contain Y value. + // For SET_CURSOR_X_Y, we need data from the rest of the word. + // High order 3 bytes are split in half to store (X,Y) value. + // High 12 bits contain X value, next 12 bits contain Y value. int x = (intWithCharacterToDisplay & 0xFFF00000) >>> 20; - int y = (intWithCharacterToDisplay & 0x000FFF00) >>> 8; - // If X or Y values are outside current range, set to range limit. - if (x<0) x=0; - if (x>=columns) x=columns-1; - if (y<0) y=0; - if (y>=rows) y=rows-1; - // display is a JTextArea whose character positioning in the text is linear. - // Converting (row,column) to linear position requires knowing how many columns - // are in each row. I add one because each row except the last ends with '\n' that - // does not count as a column but occupies a position in the text string. - // The values of rows and columns is set in initializeDisplay(). - display.setCaretPosition( y*(columns+1) + x); - } - else { - if (displayRandomAccessMode) { - try { - int caretPosition = display.getCaretPosition(); - // if caret is positioned at the end of a line (at the '\n'), skip over the '\n' - if ( (caretPosition+1) % (columns+1) == 0) { - caretPosition++; - display.setCaretPosition(caretPosition); - } - display.replaceRange(""+characterToDisplay, caretPosition, caretPosition+1); - } - catch (IllegalArgumentException e) { - // tried to write off the end of the defined grid. - display.setCaretPosition(display.getCaretPosition()-1); - display.replaceRange(""+characterToDisplay,display.getCaretPosition(),display.getCaretPosition()+1); - } - } - else { - display.append(""+characterToDisplay); + int y = (intWithCharacterToDisplay & 0x000FFF00) >>> 8; + // If X or Y values are outside current range, set to range limit. + if (x < 0) + { + x = 0; } - } - } - - /** - * Initialization code to be executed after the GUI is configured. Overrides inherited default. - */ - - protected void initializePostGUI() { - initializeTransmitDelaySimulator(); - keyEventAccepter.requestFocusInWindow(); - } - - - /** - * Method to reset counters and display when the Reset button selected. - * Overrides inherited method that does nothing. - */ - protected void reset() { - displayRandomAccessMode = false; - initializeTransmitDelaySimulator(); - initializeDisplay(displayRandomAccessMode); - keyEventAccepter.setText(""); - ((TitledBorder)displayPanel.getBorder()).setTitle(displayPanelTitle); - displayPanel.repaint(); - keyEventAccepter.requestFocusInWindow(); - updateMMIOControl(TRANSMITTER_CONTROL, readyBitSet(TRANSMITTER_CONTROL)); - } - - - // The display JTextArea (top half) is initialized either to the empty - // string, or to a string filled with lines of spaces. It will do the - // latter only if the MIPS program has sent the BELL character (Ascii 7) to - // the transmitter. This sets the caret (cursor) to a specific (x,y) position - // on a text-based virtual display. The lines of spaces is necessary because - // the caret can only be placed at a position within the current text string. - private void initializeDisplay(boolean randomAccess) { - String initialText = ""; - if (randomAccess) { + if (x >= columns) + { + x = columns - 1; + } + if (y < 0) + { + y = 0; + } + if (y >= rows) + { + y = rows - 1; + } + // display is a JTextArea whose character positioning in the text is linear. + // Converting (row,column) to linear position requires knowing how many columns + // are in each row. I add one because each row except the last ends with '\n' that + // does not count as a column but occupies a position in the text string. + // The values of rows and columns is set in initializeDisplay(). + display.setCaretPosition(y * (columns + 1) + x); + } + else + { + if (displayRandomAccessMode) + { + try + { + int caretPosition = display.getCaretPosition(); + // if caret is positioned at the end of a line (at the '\n'), skip over the '\n' + if ((caretPosition + 1) % (columns + 1) == 0) + { + caretPosition++; + display.setCaretPosition(caretPosition); + } + display.replaceRange("" + characterToDisplay, caretPosition, caretPosition + 1); + } + catch (IllegalArgumentException e) + { + // tried to write off the end of the defined grid. + display.setCaretPosition(display.getCaretPosition() - 1); + display.replaceRange("" + characterToDisplay, display.getCaretPosition(), display.getCaretPosition() + 1); + } + } + else + { + display.append("" + characterToDisplay); + } + } + } + + /** + * Initialization code to be executed after the GUI is configured. Overrides inherited default. + */ + + protected void initializePostGUI() + { + initializeTransmitDelaySimulator(); + keyEventAccepter.requestFocusInWindow(); + } + + /** + * Method to reset counters and display when the Reset button selected. Overrides inherited method that does + * nothing. + */ + protected void reset() + { + displayRandomAccessMode = false; + initializeTransmitDelaySimulator(); + initializeDisplay(displayRandomAccessMode); + keyEventAccepter.setText(""); + ((TitledBorder) displayPanel.getBorder()).setTitle(displayPanelTitle); + displayPanel.repaint(); + keyEventAccepter.requestFocusInWindow(); + updateMMIOControl(TRANSMITTER_CONTROL, readyBitSet(TRANSMITTER_CONTROL)); + } + + // The display JTextArea (top half) is initialized either to the empty + // string, or to a string filled with lines of spaces. It will do the + // latter only if the MIPS program has sent the BELL character (Ascii 7) to + // the transmitter. This sets the caret (cursor) to a specific (x,y) position + // on a text-based virtual display. The lines of spaces is necessary because + // the caret can only be placed at a position within the current text string. + private void initializeDisplay(boolean randomAccess) + { + String initialText = ""; + if (randomAccess) + { Dimension textDimensions = getDisplayPanelTextDimensions(); columns = (int) textDimensions.getWidth(); rows = (int) textDimensions.getHeight(); @@ -390,458 +518,463 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Arrays.fill(charArray, VT_FILL); String row = new String(charArray); StringBuffer str = new StringBuffer(row); - for (int i=1; i North (up), 90 --> East (right), etc. - private boolean MarsBotLeaveTrack = false; // true --> leave track when moving, false --> do not ... - private double MarsBotXPosition = 0; // X pixel position of MarsBot - private double MarsBotYPosition = 0; // Y pixel position of MarsBot - private boolean MarsBotMoving = false; // true --> MarsBot is moving, false --> MarsBot not moving - +public class MarsBot implements Observer, MarsTool +{ + private static final int GRAPHIC_WIDTH = 512; + + private static final int GRAPHIC_HEIGHT = 512; + + private static final int ADDR_HEADING = 0xffff8010; + + private static final int ADDR_LEAVETRACK = 0xffff8020; + + private static final int ADDR_WHEREAREWEX = 0xffff8030; + + private static final int ADDR_WHEREAREWEY = 0xffff8040; + + private static final int ADDR_MOVE = 0xffff8050; + // The begin and end points of a "track" segment are kept in neighboring pairs // of elements of the array. arrayOfTrack[i] is the start pt, arrayOfTrack[i+1] is // the end point of a path that should leave a track. - private final int trackPts = 256; // TBD Hardcoded. Array contains start-end points for segments in track. - private Point[] arrayOfTrack = new Point[trackPts]; - private int trackIndex = 0; - + private final int trackPts = 256; // TBD Hardcoded. Array contains start-end points for segments in track. + + private MarsBotDisplay graphicArea; + + private int MarsBotHeading = 0; // 0 --> North (up), 90 --> East (right), etc. + + private boolean MarsBotLeaveTrack = false; // true --> leave track when moving, false --> do not ... + + private double MarsBotXPosition = 0; // X pixel position of MarsBot + + private double MarsBotYPosition = 0; // Y pixel position of MarsBot + + private boolean MarsBotMoving = false; // true --> MarsBot is moving, false --> MarsBot not moving + + private final Point[] arrayOfTrack = new Point[trackPts]; + + private int trackIndex = 0; + + public String getName() + { + return "Mars Bot"; + } + + /* + * This will set up the Bot's GUI. Invoked when Bot menu item selected. + */ + public void action() + { + BotRunnable br1 = new BotRunnable(); + Thread t1 = new Thread(br1); + t1.start(); + // New: DPS 27 Feb 2006. Register observer for memory subrange. + try + { + Globals.memory.addObserver(this, 0xffff8000, 0xffff8060); + } + catch (AddressErrorException aee) + { + System.out.println(aee); + } + } + + /* ------------------------------------------------------------------------- */ + + /* + * This method observes MIPS program directives to modify Bot activity (that is, + * MIPS program write to MMIO) and updates instance variables to reflect that + * directive. + */ + public void update(Observable o, Object arg) + { + MemoryAccessNotice notice; + int address; + if (arg instanceof MemoryAccessNotice) + { + notice = (MemoryAccessNotice) arg; + address = notice.getAddress(); + if (address < 0 && notice.getAccessType() == AccessNotice.WRITE) + { + String message = ""; + if (address == ADDR_HEADING) + { + message = "MarsBot.update: got move heading value: "; + MarsBotHeading = notice.getValue(); + //System.out.println(message + notice.getValue() ); + } + else if (address == ADDR_LEAVETRACK) + { + message = "MarsBot.update: got leave track directive value "; + + // If we HAD NOT been leaving a track, but we should NOW leave + // a track, put start point into array. + if (!MarsBotLeaveTrack && notice.getValue() == 1) + { + MarsBotLeaveTrack = true; + arrayOfTrack[trackIndex] = new Point((int) MarsBotXPosition, (int) MarsBotYPosition); + trackIndex++; // the index of the end point + } + // If we HAD NOT been leaving a track, and get another directive + // to NOT leave a track, do nothing (nothing to do). + else if (!MarsBotLeaveTrack && notice.getValue() == 0) + { + // NO ACTION + } + // If we HAD been leaving a track, and get another directive + // to LEAVE a track, do nothing (nothing to do). + else if (MarsBotLeaveTrack && notice.getValue() == 1) + { + // NO ACTION + } + // If we HAD been leaving a track, and get another directive + // to NOT leave a track, put end point into array. + else if (MarsBotLeaveTrack && notice.getValue() == 0) + { + MarsBotLeaveTrack = false; + arrayOfTrack[trackIndex] = new Point((int) MarsBotXPosition, (int) MarsBotYPosition); + trackIndex++; // the index of the next start point + } + + //System.out.println("MarsBotDisplay.paintComponent: putting point in track array at " + trackIndex); + + //System.out.println(message + notice.getValue() ); + } + else if (address == ADDR_MOVE) + { + message = "MarsBot.update: got move control value: "; + MarsBotMoving = notice.getValue() != 0; + //System.out.println(message + notice.getValue() ); + } + else if (address == ADDR_WHEREAREWEX || + address == ADDR_WHEREAREWEY) + { + // Ignore these memory writes, because the writes originated within + // this tool. This tool is being notified of the writes in the usual + // manner, but the writes are already known to this tool. + // NO ACTION + } + else + { + //message = "MarsBot.update: HEY!!! unknown address of " + Integer.toString(address) + ", value: "; + //System.out.println(message + notice.getValue() ); + } + + } + } + + } + // private inner class - private class BotRunnable implements Runnable - { - JPanel panel; - public BotRunnable() // constructor - { + private class BotRunnable implements Runnable + { + JPanel panel; + + public BotRunnable() // constructor + { final JFrame frame = new JFrame("Bot"); panel = new JPanel(new BorderLayout()); graphicArea = new MarsBotDisplay(GRAPHIC_WIDTH, GRAPHIC_HEIGHT); JPanel buttonPanel = new JPanel(); JButton clearButton = new JButton("Clear"); clearButton.addActionListener( - new ActionListener() - { - public void actionPerformed(ActionEvent e) - { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { graphicArea.clear(); MarsBotLeaveTrack = false; // true --> leave track when moving, false --> do not ... MarsBotXPosition = 0; // X pixel position of MarsBot MarsBotYPosition = 0; // Y pixel position of MarsBot MarsBotMoving = false; // true --> MarsBot is moving, false --> MarsBot not moving - + trackIndex = 0; - - } - - }); + + } + + }); buttonPanel.add(clearButton); JButton closeButton = new JButton("Close"); closeButton.addActionListener( - new ActionListener() - { - public void actionPerformed(ActionEvent e) - { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { frame.setVisible(false); - - } - - }); + + } + + }); buttonPanel.add(closeButton); panel.add(graphicArea, BorderLayout.CENTER); panel.add(buttonPanel, BorderLayout.SOUTH); @@ -82,19 +213,19 @@ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // changed 12/12/09 DPS (was EXIT) frame.setSize(GRAPHIC_WIDTH + 200, GRAPHIC_HEIGHT + 100); // TBD SIZE frame.setVisible(true); // show(); - - } // end BotRunnable() constructor - - public void run() - { - + + } // end BotRunnable() constructor + + public void run() + { + double tempAngle; // infinite loop: move the bot according to the current directives // (which may be to NOT move) do { - if (MarsBotMoving) - { + if (MarsBotMoving) + { //System.out.println("BotRunnable.run: bot IS moving."); // TBD This is an arbitrary distance for bot movement. This could just // as easily be a random distance to simulate terrain, etc. @@ -102,99 +233,102 @@ // The "mathematical angle" is zero at east, 90 at north, etc. // The "heading" is 0 at north, 90 at east, etc. // Conversion: MathAngle = [(360 - heading) + 90] mod 360 - tempAngle = ((360 - MarsBotHeading) + 90) % 360; - MarsBotXPosition += Math.cos(Math.toRadians(tempAngle)); // Math.cos parameter unit is radians - MarsBotYPosition += -Math.sin(Math.toRadians(tempAngle)); // Negate value because Y coord grows down - + tempAngle = ((360 - MarsBotHeading) + 90) % 360; + MarsBotXPosition += Math.cos(Math.toRadians(tempAngle)); // Math.cos parameter unit is radians + MarsBotYPosition += -Math.sin(Math.toRadians(tempAngle)); // Negate value because Y coord grows down + // Write this new information to MARS memory area - try - { - Globals.memory.setWord(ADDR_WHEREAREWEX, (int) MarsBotXPosition); - Globals.memory.setWord(ADDR_WHEREAREWEY, (int) MarsBotYPosition); - - } - catch ( AddressErrorException e) - { + try + { + Globals.memory.setWord(ADDR_WHEREAREWEX, (int) MarsBotXPosition); + Globals.memory.setWord(ADDR_WHEREAREWEY, (int) MarsBotYPosition); + + } + catch (AddressErrorException e) + { // TBD TBD TBD No action - } - + } + //System.out.println(" ------- Heading is " + MarsBotHeading + ", angle is " + tempAngle); //System.out.println(" ------- New X,Y is (" + MarsBotXPosition + "," + MarsBotYPosition + ")" ); - + // Whether or not we're leaving a track, write the current point to the // current position in the array. // -- If we are not leaving a track now, we will need the current point to // start a future track, and that goes into the array. // -- If we are leaving a track now, the current point may end the track, // and that goes into the array. - arrayOfTrack[trackIndex] = new Point((int)MarsBotXPosition, (int)MarsBotYPosition); - - } - else - { + arrayOfTrack[trackIndex] = new Point((int) MarsBotXPosition, (int) MarsBotYPosition); + + } + else + { // Action for if the MarsBot isn't moving // System.out.println("BotRunnable.run: bot is not moving."); - } - + } + // TBD Pause whether the bot is or is not moving. This gives the MIPS program // opportunity to consider results of movement, or to make the bot move. // ??? What is relationship of robot speed to MARS's // execution time for a single instruction? Does the robot speed have to // be slow enough to allow a MARS busy loop to detect the bot position // at a specific pixel? - try - { + try + { //System.out.println(" Hello from the bot runner"); - Thread.sleep(40); - } - catch (InterruptedException exception) - {// no action - } - - panel.repaint(); // show new bot position - } while (true); - - } // end run method of BotRunnable class - - } // end BotRunnable class - + Thread.sleep(40); + } + catch (InterruptedException exception) + {// no action + } + + panel.repaint(); // show new bot position + } + while (true); + + } // end run method of BotRunnable class + + } // end BotRunnable class + /* ------------------------------------------------------------------------- */ - private class MarsBotDisplay extends JPanel - { - private int width; - private int height; - private boolean clearTheDisplay = true; - - - public MarsBotDisplay(int tw, int th) - { + private class MarsBotDisplay extends JPanel + { + private final int width; + + private final int height; + + private boolean clearTheDisplay = true; + + + public MarsBotDisplay(int tw, int th) + { width = tw; height = th; - - } - - public void redraw() - { + + } + + public void redraw() + { repaint(); - } - - public void clear() - { + } + + public void clear() + { // clear the graphic display clearTheDisplay = true; //System.out.println("MarsBotDisplay.clear: called to clear the display"); repaint(); - } - - public void paintComponent(Graphics g) - { + } + + public void paintComponent(Graphics g) + { long tempN; // System.out.println("MarsBotDisplay.paintComponent: I'm painting! n is " + n); - - + + // Recover Graphics2D Graphics2D g2 = (Graphics2D) g; - + /* if (clearTheDisplay) { @@ -203,30 +337,30 @@ clearTheDisplay = false; } */ - + // Draw the track left behind, for each segment of the path g2.setColor(Color.blue); for (int i = 1; i <= trackIndex; i += 2) // Index grows by two (begin-end pair) { - //System.out.print("."); - try - { - g2.drawLine((int)arrayOfTrack[i-1].getX(), (int)arrayOfTrack[i-1].getY(), - (int)arrayOfTrack[i].getX(), (int)arrayOfTrack[i].getY() ); - } - catch (ArrayIndexOutOfBoundsException e) - { - // No action TBD sloppy - } - catch (NullPointerException e) - { - // No action TBD sloppy - } + //System.out.print("."); + try + { + g2.drawLine((int) arrayOfTrack[i - 1].getX(), (int) arrayOfTrack[i - 1].getY(), + (int) arrayOfTrack[i].getX(), (int) arrayOfTrack[i].getY()); + } + catch (ArrayIndexOutOfBoundsException e) + { + // No action TBD sloppy + } + catch (NullPointerException e) + { + // No action TBD sloppy + } } - + g2.setColor(Color.black); g2.fillRect((int) MarsBotXPosition, (int) MarsBotYPosition, 20, 20); // Draw bot at its current position - + /* g2.setColor(Color.blue); g2.setFont(new Font(g2.getFont().getName(), g2.getFont().getStyle(), 20) ); // same font and style in larger size @@ -236,121 +370,11 @@ 60); g2.drawString(" " + n, width/2, height/2); */ - - - } - - } // end private inner class MarsBotDisplay - - /* ------------------------------------------------------------------------- */ - - - public String getName() - { - return "Mars Bot"; - } - - /* - * This will set up the Bot's GUI. Invoked when Bot menu item selected. - */ - public void action() - { - BotRunnable br1 = new BotRunnable(); - Thread t1 = new Thread(br1); - t1.start(); - // New: DPS 27 Feb 2006. Register observer for memory subrange. - try { - Globals.memory.addObserver(this,0xffff8000,0xffff8060); - } - catch (AddressErrorException aee) { - System.out.println(aee); - } - } - - /* - * This method observes MIPS program directives to modify Bot activity (that is, - * MIPS program write to MMIO) and updates instance variables to reflect that - * directive. - */ - public void update(Observable o, Object arg) - { - MemoryAccessNotice notice; - int address; - if (arg instanceof MemoryAccessNotice) - { - notice = (MemoryAccessNotice) arg; - address = notice.getAddress(); - if (address < 0 && notice.getAccessType() == AccessNotice.WRITE) - { - String message = ""; - if (address == ADDR_HEADING) - { - message = "MarsBot.update: got move heading value: "; - MarsBotHeading = notice.getValue(); - //System.out.println(message + notice.getValue() ); - } - else if (address == ADDR_LEAVETRACK) - { - message = "MarsBot.update: got leave track directive value "; - - // If we HAD NOT been leaving a track, but we should NOW leave - // a track, put start point into array. - if (MarsBotLeaveTrack == false && notice.getValue() == 1) - { - MarsBotLeaveTrack = true; - arrayOfTrack[trackIndex] = new Point((int) MarsBotXPosition, (int) MarsBotYPosition); - trackIndex++; // the index of the end point - } - // If we HAD NOT been leaving a track, and get another directive - // to NOT leave a track, do nothing (nothing to do). - else if (MarsBotLeaveTrack == false && notice.getValue() == 0) - { - // NO ACTION - } - // If we HAD been leaving a track, and get another directive - // to LEAVE a track, do nothing (nothing to do). - else if (MarsBotLeaveTrack == true && notice.getValue() == 1) - { - // NO ACTION - } - // If we HAD been leaving a track, and get another directive - // to NOT leave a track, put end point into array. - else if (MarsBotLeaveTrack == true && notice.getValue() == 0) - { - MarsBotLeaveTrack = false; - arrayOfTrack[trackIndex] = new Point((int) MarsBotXPosition, (int) MarsBotYPosition); - trackIndex++; // the index of the next start point - } - - //System.out.println("MarsBotDisplay.paintComponent: putting point in track array at " + trackIndex); - - //System.out.println(message + notice.getValue() ); - } - else if (address == ADDR_MOVE) - { - message = "MarsBot.update: got move control value: "; - if (notice.getValue() == 0) MarsBotMoving = false; - else MarsBotMoving = true; - //System.out.println(message + notice.getValue() ); - } - else if (address == ADDR_WHEREAREWEX || - address == ADDR_WHEREAREWEY) - { - // Ignore these memory writes, because the writes originated within - // this tool. This tool is being notified of the writes in the usual - // manner, but the writes are already known to this tool. - // NO ACTION - } - else - { - //message = "MarsBot.update: HEY!!! unknown address of " + Integer.toString(address) + ", value: "; - //System.out.println(message + notice.getValue() ); - } - - } - } - - } - - } + + + } + + } // end private inner class MarsBotDisplay + +} diff --git a/src/main/java/mars/tools/MarsTool.java b/src/main/java/mars/tools/MarsTool.java index 46a758d..3c4ab86 100644 --- a/src/main/java/mars/tools/MarsTool.java +++ b/src/main/java/mars/tools/MarsTool.java @@ -29,37 +29,30 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/** - * Interface for any tool that interacts with an executing MIPS program. - * A qualifying tool must be a class in the Tools package that - * implements the MarsTool interface, must be compiled into a .class file, - * and its .class file must be in the same Tools folder as MarsTool.class. - * Mars will detect a qualifying tool upon startup, create an instance - * using its no-argument constructor and add it to its Tools menu. - * When its menu item is selected, the action() method will be invoked. +/** + * Interface for any tool that interacts with an executing MIPS program. A qualifying tool must be a class in the Tools + * package that implements the MarsTool interface, must be compiled into a .class file, and its .class file must be in + * the same Tools folder as MarsTool.class. Mars will detect a qualifying tool upon startup, create an instance using + * its no-argument constructor and add it to its Tools menu. When its menu item is selected, the action() method will be + * invoked. * *

A tool may receive communication from MIPS system resources - * (registers or memory) by registering as an Observer with - * Mars.Memory and/or Mars.Register objects. - * - * It may also - * communicate directly with those resources through their - * published methods PROVIDED any such communication is - * wrapped inside a block synchronized on the - * Mars.Globals.memoryAndRegistersLock object. + * (registers or memory) by registering as an Observer with Mars.Memory and/or Mars.Register objects. + *

+ * It may also communicate directly with those resources through their published methods PROVIDED any such communication + * is wrapped inside a block synchronized on the Mars.Globals.memoryAndRegistersLock object. */ - -public interface MarsTool { - /** - * Return a name you have chosen for this tool. It will appear as the - * menu item. - */ - public abstract String getName(); - - /** - * Performs tool functions. It will be invoked when the tool is selected - * from the Tools menu. - */ - - public abstract void action(); -} \ No newline at end of file + +public interface MarsTool +{ + /** + * Return a name you have chosen for this tool. It will appear as the menu item. + */ + String getName(); + + /** + * Performs tool functions. It will be invoked when the tool is selected from the Tools menu. + */ + + void action(); +} diff --git a/src/main/java/mars/tools/MemoryReferenceVisualization.java b/src/main/java/mars/tools/MemoryReferenceVisualization.java index dbba176..b97740c 100644 --- a/src/main/java/mars/tools/MemoryReferenceVisualization.java +++ b/src/main/java/mars/tools/MemoryReferenceVisualization.java @@ -1,12 +1,18 @@ - package mars.tools; - import javax.swing.*; - import javax.swing.border.*; - import javax.swing.event.*; - import java.awt.*; - import java.awt.event.*; - import java.util.*; - import mars.tools.*; - import mars.mips.hardware.*; +package mars.tools; + +import mars.mips.hardware.AccessNotice; +import mars.mips.hardware.Memory; +import mars.mips.hardware.MemoryAccessNotice; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Arrays; +import java.util.Observable; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -35,451 +41,513 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Memory reference visualization. It can be run either as a stand-alone Java application having - * access to the mars package, or through MARS as an item in its Tools menu. It makes - * maximum use of methods inherited from its abstract superclass AbstractMarsToolAndApplication. - * Pete Sanderson, verison 1.0, 14 November 2006. - */ - public class MemoryReferenceVisualization extends AbstractMarsToolAndApplication { - - private static String version = "Version 1.0"; - private static String heading = "Visualizing memory reference patterns"; - - // Major GUI components - private JComboBox wordsPerUnitSelector, visualizationUnitPixelWidthSelector, visualizationUnitPixelHeightSelector, - visualizationPixelWidthSelector, visualizationPixelHeightSelector, displayBaseAddressSelector; - private JCheckBox drawHashMarksSelector; - private Graphics drawingArea; - private JPanel canvas; - private JPanel results; - - // Some GUI settings - private EmptyBorder emptyBorder = new EmptyBorder(4,4,4,4); - private Font countFonts = new Font("Times", Font.BOLD,12); - private Color backgroundColor = Color.WHITE; - - // Values for Combo Boxes - - private final String[] wordsPerUnitChoices = {"1","2","4","8","16","32","64","128","256","512","1024","2048"}; - private final int defaultWordsPerUnitIndex = 0; - private final String[] visualizationUnitPixelWidthChoices = {"1","2","4","8","16","32"}; - private final int defaultVisualizationUnitPixelWidthIndex = 4; - private final String[] visualizationUnitPixelHeightChoices = {"1","2","4","8","16","32"}; - private final int defaultVisualizationUnitPixelHeightIndex = 4; - private final String[] displayAreaPixelWidthChoices = {"64","128","256","512","1024"}; - private final int defaultDisplayWidthIndex = 2; - private final String[] displayAreaPixelHeightChoices = {"64","128","256","512","1024"}; - private final int defaultDisplayHeightIndex = 2; - private final boolean defaultDrawHashMarks = true; - - // Values for display canvas. Note their initialization uses the identifiers just above. - - private int unitPixelWidth = Integer.parseInt(visualizationUnitPixelWidthChoices[defaultVisualizationUnitPixelWidthIndex]); - private int unitPixelHeight = Integer.parseInt(visualizationUnitPixelHeightChoices[defaultVisualizationUnitPixelHeightIndex]); - private int wordsPerUnit = Integer.parseInt(wordsPerUnitChoices[defaultWordsPerUnitIndex]); - private int visualizationAreaWidthInPixels = Integer.parseInt(displayAreaPixelWidthChoices[defaultDisplayWidthIndex]); - private int visualizationAreaHeightInPixels = Integer.parseInt(displayAreaPixelHeightChoices[defaultDisplayHeightIndex]); - - //`Values for mapping of reference counts to colors for display. - - // This array of (count,color) pairs must be kept sorted! count is low end of subrange. - // This array will grow if user adds colors at additional counter points (see below). - private CounterColor[] defaultCounterColors = - { new CounterColor(0, Color.black), - new CounterColor(1, Color.blue), - new CounterColor(2, Color.green), - new CounterColor(3, Color.yellow), - new CounterColor(5, Color.orange), - new CounterColor(10, Color.red) - }; - /* Values for reference count color slider. These are all possible counter values for which - * colors can be assigned. As you can see just above, not all these values are assigned - * a default color. - */ - private int[] countTable = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 0-10 - 20, 30, 40, 50, 100, 200, 300, 400, 500, 1000, // 11-20 - 2000, 3000, 4000, 5000, 10000, 50000, 100000, 500000, 1000000 // 21-29 - }; - private final int COUNT_INDEX_INIT = 10; // array element #10, arbitrary starting point - - // The next four are initialized dynamically in initializeDisplayBaseChoices() - private String[] displayBaseAddressChoices; - private int[] displayBaseAddresses; - private int defaultBaseAddressIndex; - private int baseAddress; - - private Grid theGrid; - private CounterColorScale counterColorScale; - - /** - * Simple constructor, likely used to run a stand-alone memory reference visualizer. - * @param title String containing title for title bar - * @param heading String containing text for heading shown in upper part of window. - */ - public MemoryReferenceVisualization(String title, String heading) { - super(title,heading); - } - - /** - * Simple constructor, likely used by the MARS Tools menu mechanism - */ - public MemoryReferenceVisualization() { - super ("Memory Reference Visualization, "+version, heading); - } - - - /** - * Main provided for pure stand-alone use. Recommended stand-alone use is to write a - * driver program that instantiates a MemoryReferenceVisualization object then invokes its go() method. - * "stand-alone" means it is not invoked from the MARS Tools menu. "Pure" means there - * is no driver program to invoke the application. - */ - public static void main(String[] args) { - new MemoryReferenceVisualization("Memory Reference Visualization stand-alone, "+version,heading).go(); - } - - - /** - * Required MarsTool method to return Tool name. - * @return Tool name. MARS will display this in menu item. - */ - public String getName() { - return "Memory Reference Visualization"; - } - - - /** - * Override the inherited method, which registers us as an Observer over the static data segment - * (starting address 0x10010000) only. This version will register us as observer over the - * the memory range as selected by the base address combo box and capacity of the visualization display - * (number of visualization elements times the number of memory words each one represents). - * It does so by calling the inherited 2-parameter overload of this method. - * If you use the inherited GUI buttons, this - * method is invoked when you click "Connect" button on MarsTool or the - * "Assemble and Run" button on a Mars-based app. - */ - protected void addAsObserver() { - int highAddress = baseAddress+theGrid.getRows()*theGrid.getColumns()*Memory.WORD_LENGTH_BYTES*wordsPerUnit; - // Special case: baseAddress<0 means we're in kernel memory (0x80000000 and up) and most likely - // in memory map address space (0xffff0000 and up). In this case, we need to make sure the high address - // does not drop off the high end of 32 bit address space. Highest allowable word address is 0xfffffffc, - // which is interpreted in Java int as -4. - if (baseAddress < 0 && highAddress > -4) { + +/** + * Memory reference visualization. It can be run either as a stand-alone Java application having access to the mars + * package, or through MARS as an item in its Tools menu. It makes maximum use of methods inherited from its abstract + * superclass AbstractMarsToolAndApplication. Pete Sanderson, verison 1.0, 14 November 2006. + */ +public class MemoryReferenceVisualization extends AbstractMarsToolAndApplication +{ + + private static final String version = "Version 1.0"; + + private static final String heading = "Visualizing memory reference patterns"; + + private final String[] wordsPerUnitChoices = {"1", "2", "4", "8", "16", "32", "64", "128", "256", "512", "1024", "2048"}; + + private final int defaultWordsPerUnitIndex = 0; + + private final String[] visualizationUnitPixelWidthChoices = {"1", "2", "4", "8", "16", "32"}; + + private final int defaultVisualizationUnitPixelWidthIndex = 4; + + private final String[] visualizationUnitPixelHeightChoices = {"1", "2", "4", "8", "16", "32"}; + + private final int defaultVisualizationUnitPixelHeightIndex = 4; + + private final String[] displayAreaPixelWidthChoices = {"64", "128", "256", "512", "1024"}; + + private final int defaultDisplayWidthIndex = 2; + + // Values for Combo Boxes + + private final String[] displayAreaPixelHeightChoices = {"64", "128", "256", "512", "1024"}; + + private final int defaultDisplayHeightIndex = 2; + + private final boolean defaultDrawHashMarks = true; + + private final int COUNT_INDEX_INIT = 10; // array element #10, arbitrary starting point + + // Major GUI components + private JComboBox wordsPerUnitSelector, visualizationUnitPixelWidthSelector, visualizationUnitPixelHeightSelector, + visualizationPixelWidthSelector, visualizationPixelHeightSelector, displayBaseAddressSelector; + + private JCheckBox drawHashMarksSelector; + + private Graphics drawingArea; + + private JPanel canvas; + + private JPanel results; + + // Some GUI settings + private final EmptyBorder emptyBorder = new EmptyBorder(4, 4, 4, 4); + + private final Font countFonts = new Font("Times", Font.BOLD, 12); + + // Values for display canvas. Note their initialization uses the identifiers just above. + + private final Color backgroundColor = Color.WHITE; + + private int unitPixelWidth = Integer.parseInt(visualizationUnitPixelWidthChoices[defaultVisualizationUnitPixelWidthIndex]); + + private int unitPixelHeight = Integer.parseInt(visualizationUnitPixelHeightChoices[defaultVisualizationUnitPixelHeightIndex]); + + private int wordsPerUnit = Integer.parseInt(wordsPerUnitChoices[defaultWordsPerUnitIndex]); + + private int visualizationAreaWidthInPixels = Integer.parseInt(displayAreaPixelWidthChoices[defaultDisplayWidthIndex]); + + //`Values for mapping of reference counts to colors for display. + + private int visualizationAreaHeightInPixels = Integer.parseInt(displayAreaPixelHeightChoices[defaultDisplayHeightIndex]); + + // This array of (count,color) pairs must be kept sorted! count is low end of subrange. + // This array will grow if user adds colors at additional counter points (see below). + private final CounterColor[] defaultCounterColors = + {new CounterColor(0, Color.black), + new CounterColor(1, Color.blue), + new CounterColor(2, Color.green), + new CounterColor(3, Color.yellow), + new CounterColor(5, Color.orange), + new CounterColor(10, Color.red) + }; + + /* Values for reference count color slider. These are all possible counter values for which + * colors can be assigned. As you can see just above, not all these values are assigned + * a default color. + */ + private final int[] countTable = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // 0-10 + 20, 30, 40, 50, 100, 200, 300, 400, 500, 1000, // 11-20 + 2000, 3000, 4000, 5000, 10000, 50000, 100000, 500000, 1000000 // 21-29 + }; + + // The next four are initialized dynamically in initializeDisplayBaseChoices() + private String[] displayBaseAddressChoices; + + private int[] displayBaseAddresses; + + private int defaultBaseAddressIndex; + + private int baseAddress; + + private Grid theGrid; + + private CounterColorScale counterColorScale; + + /** + * Simple constructor, likely used to run a stand-alone memory reference visualizer. + * + * @param title String containing title for title bar + * @param heading String containing text for heading shown in upper part of window. + */ + public MemoryReferenceVisualization(String title, String heading) + { + super(title, heading); + } + + /** + * Simple constructor, likely used by the MARS Tools menu mechanism + */ + public MemoryReferenceVisualization() + { + super("Memory Reference Visualization, " + version, heading); + } + + + /** + * Main provided for pure stand-alone use. Recommended stand-alone use is to write a driver program that + * instantiates a MemoryReferenceVisualization object then invokes its go() method. "stand-alone" means it is not + * invoked from the MARS Tools menu. "Pure" means there is no driver program to invoke the application. + */ + public static void main(String[] args) + { + new MemoryReferenceVisualization("Memory Reference Visualization stand-alone, " + version, heading).go(); + } + + + /** + * Required MarsTool method to return Tool name. + * + * @return Tool name. MARS will display this in menu item. + */ + public String getName() + { + return "Memory Reference Visualization"; + } + + + /** + * Override the inherited method, which registers us as an Observer over the static data segment (starting address + * 0x10010000) only. This version will register us as observer over the the memory range as selected by the base + * address combo box and capacity of the visualization display (number of visualization elements times the number of + * memory words each one represents). It does so by calling the inherited 2-parameter overload of this method. If + * you use the inherited GUI buttons, this method is invoked when you click "Connect" button on MarsTool or the + * "Assemble and Run" button on a Mars-based app. + */ + protected void addAsObserver() + { + int highAddress = baseAddress + theGrid.getRows() * theGrid.getColumns() * Memory.WORD_LENGTH_BYTES * wordsPerUnit; + // Special case: baseAddress<0 means we're in kernel memory (0x80000000 and up) and most likely + // in memory map address space (0xffff0000 and up). In this case, we need to make sure the high address + // does not drop off the high end of 32 bit address space. Highest allowable word address is 0xfffffffc, + // which is interpreted in Java int as -4. + if (baseAddress < 0 && highAddress > -4) + { highAddress = -4; - } - addAsObserver(baseAddress, highAddress); - } - - - /** - * Method that constructs the main display area. It is organized vertically - * into two major components: the display configuration which an be modified - * using combo boxes, and the visualization display which is updated as the - * attached MIPS program executes. - * @return the GUI component containing these two areas - */ - protected JComponent buildMainDisplayArea() { - results = new JPanel(); - results.add(buildOrganizationArea()); - results.add(buildVisualizationArea()); - return results; - } - - - ////////////////////////////////////////////////////////////////////////////////////// - // Rest of the protected methods. These override do-nothing methods inherited from - // the abstract superclass. - ////////////////////////////////////////////////////////////////////////////////////// - - /** - * Update display when connected MIPS program accesses (data) memory. - * @param memory the attached memory - * @param accessNotice information provided by memory in MemoryAccessNotice object - */ - protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) { - incrementReferenceCountForAddress(((MemoryAccessNotice)accessNotice).getAddress()); - updateDisplay(); - } - - - /** - * Initialize all JComboBox choice structures not already initialized at declaration. - * Overrides inherited method that does nothing. - */ - protected void initializePreGUI() { - initializeDisplayBaseChoices(); - counterColorScale = new CounterColorScale(defaultCounterColors); - // NOTE: Can't call "createNewGrid()" here because it uses settings from - // several combo boxes that have not been created yet. But a default grid - // needs to be allocated for initial canvas display. - theGrid = new Grid(visualizationAreaHeightInPixels/unitPixelHeight, - visualizationAreaWidthInPixels/unitPixelWidth); - } - - - /** - * The only post-GUI initialization is to create the initial Grid object based on the default settings - * of the various combo boxes. Overrides inherited method that does nothing. - */ - - protected void initializePostGUI() { - wordsPerUnit = getIntComboBoxSelection(wordsPerUnitSelector); - theGrid = createNewGrid(); - updateBaseAddress(); - } - - - /** - * Method to reset counters and display when the Reset button selected. - * Overrides inherited method that does nothing. - */ - protected void reset() { - resetCounts(); - updateDisplay(); - } - - /** - * Updates display immediately after each update (AccessNotice) is processed, after - * display configuration changes as needed, and after each execution step when Mars - * is running in timed mode. Overrides inherited method that does nothing. - */ - protected void updateDisplay() { - canvas.repaint(); - } - - - /** - * Overrides default method, to provide a Help button for this tool/app. - */ - protected JComponent getHelpComponent() { - final String helpContent = - "Use this program to visualize dynamic memory reference\n"+ - "patterns in MIPS assembly programs. It may be run either\n"+ - "from MARS' Tools menu or as a stand-alone application. For\n"+ - "the latter, simply write a small driver to instantiate a\n"+ - "MemoryReferenceVisualization object and invoke its go() method.\n"+ - "\n"+ - "You can easily learn to use this small program by playing with\n"+ - "it! For the best animation, set the MIPS program to run in\n"+ - "timed mode using the Run Speed slider. Each rectangular unit\n"+ - "on the display represents one or more memory words (default 1)\n"+ - "and each time a memory word is accessed by the MIPS program,\n"+ - "its reference count is incremented then rendered in the color\n"+ - "assigned to the count value. You can change the count-color\n"+ - "assignments using the count slider and color patch. Select a\n"+ - "counter value then click on the color patch to change the color.\n"+ - "This color will apply beginning at the selected count and\n"+ - "extending up to the next slider-provided count.\n"+ - "\n"+ - "Contact Pete Sanderson at psanderson@otterbein.edu with\n"+ - "questions or comments.\n"; - JButton help = new JButton("Help"); - help.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - JOptionPane.showMessageDialog(theWindow, helpContent); - } - }); - return help; - } - - ////////////////////////////////////////////////////////////////////////////////////// - // Private methods defined to support the above. - ////////////////////////////////////////////////////////////////////////////////////// - - // UI components and layout for left half of GUI, where settings are specified. - private JComponent buildOrganizationArea() { - JPanel organization = new JPanel(new GridLayout(9,1)); - - drawHashMarksSelector = new JCheckBox(); - drawHashMarksSelector.setSelected(defaultDrawHashMarks); - drawHashMarksSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - updateDisplay(); - } - }); - wordsPerUnitSelector = new JComboBox(wordsPerUnitChoices); - wordsPerUnitSelector.setEditable(false); - wordsPerUnitSelector.setBackground(backgroundColor); - wordsPerUnitSelector.setSelectedIndex(defaultWordsPerUnitIndex); - wordsPerUnitSelector.setToolTipText("Number of memory words represented by one visualization element (rectangle)"); - wordsPerUnitSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - wordsPerUnit = getIntComboBoxSelection(wordsPerUnitSelector); - reset(); - } - }); - visualizationUnitPixelWidthSelector = new JComboBox(visualizationUnitPixelWidthChoices); - visualizationUnitPixelWidthSelector.setEditable(false); - visualizationUnitPixelWidthSelector.setBackground(backgroundColor); - visualizationUnitPixelWidthSelector.setSelectedIndex(defaultVisualizationUnitPixelWidthIndex); - visualizationUnitPixelWidthSelector.setToolTipText("Width in pixels of rectangle representing memory access"); - visualizationUnitPixelWidthSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - unitPixelWidth = getIntComboBoxSelection(visualizationUnitPixelWidthSelector); - theGrid = createNewGrid(); - updateDisplay(); - } - }); - visualizationUnitPixelHeightSelector = new JComboBox(visualizationUnitPixelHeightChoices); - visualizationUnitPixelHeightSelector.setEditable(false); - visualizationUnitPixelHeightSelector.setBackground(backgroundColor); - visualizationUnitPixelHeightSelector.setSelectedIndex(defaultVisualizationUnitPixelHeightIndex); - visualizationUnitPixelHeightSelector.setToolTipText("Height in pixels of rectangle representing memory access"); - visualizationUnitPixelHeightSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - unitPixelHeight = getIntComboBoxSelection(visualizationUnitPixelHeightSelector); - theGrid = createNewGrid(); - updateDisplay(); - } - }); - visualizationPixelWidthSelector = new JComboBox(displayAreaPixelWidthChoices); - visualizationPixelWidthSelector.setEditable(false); - visualizationPixelWidthSelector.setBackground(backgroundColor); - visualizationPixelWidthSelector.setSelectedIndex(defaultDisplayWidthIndex); - visualizationPixelWidthSelector.setToolTipText("Total width in pixels of visualization area"); - visualizationPixelWidthSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - visualizationAreaWidthInPixels = getIntComboBoxSelection(visualizationPixelWidthSelector); - canvas.setPreferredSize(getDisplayAreaDimension()); - canvas.setSize(getDisplayAreaDimension()); - theGrid = createNewGrid(); - canvas.repaint(); - updateDisplay(); - } - }); - visualizationPixelHeightSelector = new JComboBox(displayAreaPixelHeightChoices); - visualizationPixelHeightSelector.setEditable(false); - visualizationPixelHeightSelector.setBackground(backgroundColor); - visualizationPixelHeightSelector.setSelectedIndex(defaultDisplayHeightIndex); - visualizationPixelHeightSelector.setToolTipText("Total height in pixels of visualization area"); - visualizationPixelHeightSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - visualizationAreaHeightInPixels = getIntComboBoxSelection(visualizationPixelHeightSelector); - canvas.setPreferredSize(getDisplayAreaDimension()); - canvas.setSize(getDisplayAreaDimension()); - theGrid = createNewGrid(); - canvas.repaint(); - updateDisplay(); - } - }); - displayBaseAddressSelector = new JComboBox(displayBaseAddressChoices); - displayBaseAddressSelector.setEditable(false); - displayBaseAddressSelector.setBackground(backgroundColor); - displayBaseAddressSelector.setSelectedIndex(defaultBaseAddressIndex); - displayBaseAddressSelector.setToolTipText("Base address for visualization area (upper left corner)"); - displayBaseAddressSelector.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - // This may also affect what address range we should be registered as an Observer - // for. The default (inherited) address range is the MIPS static data segment - // starting at 0x10010000. To change this requires override of - // AbstractMarsToolAndApplication.addAsObserver(). The no-argument version of - // that method is called automatically when "Connect" button is clicked for MarsTool - // and when "Assemble and Run" button is clicked for Mars application. - updateBaseAddress(); - // If display base address is changed while connected to MIPS (this can only occur - // when being used as a MarsTool), we have to delete ourselves as an observer and re-register. - if (connectButton != null && connectButton.isConnected()) { + } + addAsObserver(baseAddress, highAddress); + } + + + /** + * Method that constructs the main display area. It is organized vertically into two major components: the display + * configuration which an be modified using combo boxes, and the visualization display which is updated as the + * attached MIPS program executes. + * + * @return the GUI component containing these two areas + */ + protected JComponent buildMainDisplayArea() + { + results = new JPanel(); + results.add(buildOrganizationArea()); + results.add(buildVisualizationArea()); + return results; + } + + + ////////////////////////////////////////////////////////////////////////////////////// + // Rest of the protected methods. These override do-nothing methods inherited from + // the abstract superclass. + ////////////////////////////////////////////////////////////////////////////////////// + + /** + * Update display when connected MIPS program accesses (data) memory. + * + * @param memory the attached memory + * @param accessNotice information provided by memory in MemoryAccessNotice object + */ + protected void processMIPSUpdate(Observable memory, AccessNotice accessNotice) + { + incrementReferenceCountForAddress(((MemoryAccessNotice) accessNotice).getAddress()); + updateDisplay(); + } + + + /** + * Initialize all JComboBox choice structures not already initialized at declaration. Overrides inherited method + * that does nothing. + */ + protected void initializePreGUI() + { + initializeDisplayBaseChoices(); + counterColorScale = new CounterColorScale(defaultCounterColors); + // NOTE: Can't call "createNewGrid()" here because it uses settings from + // several combo boxes that have not been created yet. But a default grid + // needs to be allocated for initial canvas display. + theGrid = new Grid(visualizationAreaHeightInPixels / unitPixelHeight, + visualizationAreaWidthInPixels / unitPixelWidth); + } + + + /** + * The only post-GUI initialization is to create the initial Grid object based on the default settings of the + * various combo boxes. Overrides inherited method that does nothing. + */ + + protected void initializePostGUI() + { + wordsPerUnit = getIntComboBoxSelection(wordsPerUnitSelector); + theGrid = createNewGrid(); + updateBaseAddress(); + } + + + /** + * Method to reset counters and display when the Reset button selected. Overrides inherited method that does + * nothing. + */ + protected void reset() + { + resetCounts(); + updateDisplay(); + } + + /** + * Updates display immediately after each update (AccessNotice) is processed, after display configuration changes as + * needed, and after each execution step when Mars is running in timed mode. Overrides inherited method that does + * nothing. + */ + protected void updateDisplay() + { + canvas.repaint(); + } + + + /** + * Overrides default method, to provide a Help button for this tool/app. + */ + protected JComponent getHelpComponent() + { + final String helpContent = + "Use this program to visualize dynamic memory reference\n" + + "patterns in MIPS assembly programs. It may be run either\n" + + "from MARS' Tools menu or as a stand-alone application. For\n" + + "the latter, simply write a small driver to instantiate a\n" + + "MemoryReferenceVisualization object and invoke its go() method.\n" + + "\n" + + "You can easily learn to use this small program by playing with\n" + + "it! For the best animation, set the MIPS program to run in\n" + + "timed mode using the Run Speed slider. Each rectangular unit\n" + + "on the display represents one or more memory words (default 1)\n" + + "and each time a memory word is accessed by the MIPS program,\n" + + "its reference count is incremented then rendered in the color\n" + + "assigned to the count value. You can change the count-color\n" + + "assignments using the count slider and color patch. Select a\n" + + "counter value then click on the color patch to change the color.\n" + + "This color will apply beginning at the selected count and\n" + + "extending up to the next slider-provided count.\n" + + "\n" + + "Contact Pete Sanderson at psanderson@otterbein.edu with\n" + + "questions or comments.\n"; + JButton help = new JButton("Help"); + help.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + JOptionPane.showMessageDialog(theWindow, helpContent); + } + }); + return help; + } + + ////////////////////////////////////////////////////////////////////////////////////// + // Private methods defined to support the above. + ////////////////////////////////////////////////////////////////////////////////////// + + // UI components and layout for left half of GUI, where settings are specified. + private JComponent buildOrganizationArea() + { + JPanel organization = new JPanel(new GridLayout(9, 1)); + + drawHashMarksSelector = new JCheckBox(); + drawHashMarksSelector.setSelected(defaultDrawHashMarks); + drawHashMarksSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + updateDisplay(); + } + }); + wordsPerUnitSelector = new JComboBox(wordsPerUnitChoices); + wordsPerUnitSelector.setEditable(false); + wordsPerUnitSelector.setBackground(backgroundColor); + wordsPerUnitSelector.setSelectedIndex(defaultWordsPerUnitIndex); + wordsPerUnitSelector.setToolTipText("Number of memory words represented by one visualization element (rectangle)"); + wordsPerUnitSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + wordsPerUnit = getIntComboBoxSelection(wordsPerUnitSelector); + reset(); + } + }); + visualizationUnitPixelWidthSelector = new JComboBox(visualizationUnitPixelWidthChoices); + visualizationUnitPixelWidthSelector.setEditable(false); + visualizationUnitPixelWidthSelector.setBackground(backgroundColor); + visualizationUnitPixelWidthSelector.setSelectedIndex(defaultVisualizationUnitPixelWidthIndex); + visualizationUnitPixelWidthSelector.setToolTipText("Width in pixels of rectangle representing memory access"); + visualizationUnitPixelWidthSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + unitPixelWidth = getIntComboBoxSelection(visualizationUnitPixelWidthSelector); + theGrid = createNewGrid(); + updateDisplay(); + } + }); + visualizationUnitPixelHeightSelector = new JComboBox(visualizationUnitPixelHeightChoices); + visualizationUnitPixelHeightSelector.setEditable(false); + visualizationUnitPixelHeightSelector.setBackground(backgroundColor); + visualizationUnitPixelHeightSelector.setSelectedIndex(defaultVisualizationUnitPixelHeightIndex); + visualizationUnitPixelHeightSelector.setToolTipText("Height in pixels of rectangle representing memory access"); + visualizationUnitPixelHeightSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + unitPixelHeight = getIntComboBoxSelection(visualizationUnitPixelHeightSelector); + theGrid = createNewGrid(); + updateDisplay(); + } + }); + visualizationPixelWidthSelector = new JComboBox(displayAreaPixelWidthChoices); + visualizationPixelWidthSelector.setEditable(false); + visualizationPixelWidthSelector.setBackground(backgroundColor); + visualizationPixelWidthSelector.setSelectedIndex(defaultDisplayWidthIndex); + visualizationPixelWidthSelector.setToolTipText("Total width in pixels of visualization area"); + visualizationPixelWidthSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + visualizationAreaWidthInPixels = getIntComboBoxSelection(visualizationPixelWidthSelector); + canvas.setPreferredSize(getDisplayAreaDimension()); + canvas.setSize(getDisplayAreaDimension()); + theGrid = createNewGrid(); + canvas.repaint(); + updateDisplay(); + } + }); + visualizationPixelHeightSelector = new JComboBox(displayAreaPixelHeightChoices); + visualizationPixelHeightSelector.setEditable(false); + visualizationPixelHeightSelector.setBackground(backgroundColor); + visualizationPixelHeightSelector.setSelectedIndex(defaultDisplayHeightIndex); + visualizationPixelHeightSelector.setToolTipText("Total height in pixels of visualization area"); + visualizationPixelHeightSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + visualizationAreaHeightInPixels = getIntComboBoxSelection(visualizationPixelHeightSelector); + canvas.setPreferredSize(getDisplayAreaDimension()); + canvas.setSize(getDisplayAreaDimension()); + theGrid = createNewGrid(); + canvas.repaint(); + updateDisplay(); + } + }); + displayBaseAddressSelector = new JComboBox(displayBaseAddressChoices); + displayBaseAddressSelector.setEditable(false); + displayBaseAddressSelector.setBackground(backgroundColor); + displayBaseAddressSelector.setSelectedIndex(defaultBaseAddressIndex); + displayBaseAddressSelector.setToolTipText("Base address for visualization area (upper left corner)"); + displayBaseAddressSelector.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // This may also affect what address range we should be registered as an Observer + // for. The default (inherited) address range is the MIPS static data segment + // starting at 0x10010000. To change this requires override of + // AbstractMarsToolAndApplication.addAsObserver(). The no-argument version of + // that method is called automatically when "Connect" button is clicked for MarsTool + // and when "Assemble and Run" button is clicked for Mars application. + updateBaseAddress(); + // If display base address is changed while connected to MIPS (this can only occur + // when being used as a MarsTool), we have to delete ourselves as an observer and re-register. + if (connectButton != null && connectButton.isConnected()) + { deleteAsObserver(); addAsObserver(); - } - theGrid = createNewGrid(); - updateDisplay(); - } - }); - - // ALL COMPONENTS FOR "ORGANIZATION" SECTION - - JPanel hashMarksRow = getPanelWithBorderLayout(); - hashMarksRow.setBorder(emptyBorder); - hashMarksRow.add(new JLabel("Show unit boundaries (grid marks)"), BorderLayout.WEST); - hashMarksRow.add(drawHashMarksSelector, BorderLayout.EAST); - - JPanel wordsPerUnitRow = getPanelWithBorderLayout(); - wordsPerUnitRow.setBorder(emptyBorder); - wordsPerUnitRow.add(new JLabel("Memory Words per Unit "),BorderLayout.WEST); - wordsPerUnitRow.add(wordsPerUnitSelector,BorderLayout.EAST); - - JPanel unitWidthInPixelsRow = getPanelWithBorderLayout(); - unitWidthInPixelsRow.setBorder(emptyBorder); - unitWidthInPixelsRow.add(new JLabel("Unit Width in Pixels "),BorderLayout.WEST); - unitWidthInPixelsRow.add(visualizationUnitPixelWidthSelector, BorderLayout.EAST); - - JPanel unitHeightInPixelsRow = getPanelWithBorderLayout(); - unitHeightInPixelsRow.setBorder(emptyBorder); - unitHeightInPixelsRow.add(new JLabel("Unit Height in Pixels "),BorderLayout.WEST); - unitHeightInPixelsRow.add(visualizationUnitPixelHeightSelector,BorderLayout.EAST); - - JPanel widthInPixelsRow = getPanelWithBorderLayout(); - widthInPixelsRow.setBorder(emptyBorder); - widthInPixelsRow.add(new JLabel("Display Width in Pixels "),BorderLayout.WEST); - widthInPixelsRow.add(visualizationPixelWidthSelector, BorderLayout.EAST); - - JPanel heightInPixelsRow = getPanelWithBorderLayout(); - heightInPixelsRow.setBorder(emptyBorder); - heightInPixelsRow.add(new JLabel("Display Height in Pixels "),BorderLayout.WEST); - heightInPixelsRow.add(visualizationPixelHeightSelector,BorderLayout.EAST); - - JPanel baseAddressRow = getPanelWithBorderLayout(); - baseAddressRow.setBorder(emptyBorder); - baseAddressRow.add(new JLabel("Base address for display "),BorderLayout.WEST); - baseAddressRow.add(displayBaseAddressSelector,BorderLayout.EAST); - - ColorChooserControls colorChooserControls = new ColorChooserControls(); - - // Lay 'em out in the grid... - organization.add(hashMarksRow); - organization.add(wordsPerUnitRow); - organization.add(unitWidthInPixelsRow); - organization.add(unitHeightInPixelsRow); - organization.add(widthInPixelsRow); - organization.add(heightInPixelsRow); - organization.add(baseAddressRow); - organization.add(colorChooserControls.colorChooserRow); - organization.add(colorChooserControls.countDisplayRow); - return organization; - } - - // UI components and layout for right half of GUI, the visualization display area. - private JComponent buildVisualizationArea() { - canvas = new GraphicsPanel(); - canvas.setPreferredSize(getDisplayAreaDimension()); - canvas.setToolTipText("Memory reference count visualization area"); - return canvas; - } - - // For greatest flexibility, initialize the display base choices directly from - // the constants defined in the Memory class. This method called prior to - // building the GUI. Here are current values from Memory.java: - //textBaseAddress=0x00400000, dataSegmentBaseAddress=0x10000000, globalPointer=0x10008000 - //dataBaseAddress=0x10010000, heapBaseAddress=0x10040000, memoryMapBaseAddress=0xffff0000 - private void initializeDisplayBaseChoices() { - int[] displayBaseAddressArray = {Memory.textBaseAddress, Memory.dataSegmentBaseAddress, Memory.globalPointer, Memory.dataBaseAddress, - Memory.heapBaseAddress, Memory.memoryMapBaseAddress }; - // Must agree with above in number and order... - String[] descriptions = { " (text)", " (global data)", " ($gp)", " (static data)", " (heap)", " (memory map)" }; - displayBaseAddresses = displayBaseAddressArray; - displayBaseAddressChoices = new String[displayBaseAddressArray.length]; - for (int i=0; i 10 characters long, slice off the first 10 and apply Integer.parseInt() to it to get custom base address. */ - } - - // Returns Dimension object with current width and height of display area as determined - // by current settings of respective combo boxes. - private Dimension getDisplayAreaDimension() { - return new Dimension(visualizationAreaWidthInPixels, visualizationAreaHeightInPixels); - } - - // reset all counters in the Grid. - private void resetCounts() { - theGrid.reset(); - } - - // Will return int equivalent of specified combo box's current selection. - // The selection must be a String that parses to an int. - private int getIntComboBoxSelection(JComboBox comboBox) { - try { - return Integer.parseInt((String)comboBox.getSelectedItem()); - } - catch (NumberFormatException nfe) { - // Can occur only if initialization list contains badly formatted numbers. This - // is a developer's error, not a user error, and better be caught before release. - return 1; - } - } - - // Use this for consistent results. - private JPanel getPanelWithBorderLayout() { - return new JPanel(new BorderLayout(2,2)); - } - - // Method to determine grid dimensions based on durrent control settings. - // Each grid element corresponds to one visualization unit. - private Grid createNewGrid() { - int rows = visualizationAreaHeightInPixels/unitPixelHeight; - int columns = visualizationAreaWidthInPixels/unitPixelWidth; - return new Grid(rows,columns); - } - - // Given memory address, increment the counter for the corresponding grid element. - // Need to consider words per unit (number of memory words that each visual element represents). - // If address maps to invalid grid element (e.g. is outside the current bounds based on all - // display settings) then nothing happens. - private void incrementReferenceCountForAddress(int address) { - int offset = (address - baseAddress)/Memory.WORD_LENGTH_BYTES/wordsPerUnit; - // If you care to do anything with it, the following will return -1 if the address - // maps outside the dimensions of the grid (e.g. below the base address or beyond end). - theGrid.incrementElement(offset / theGrid.getColumns(), offset % theGrid.getColumns()); - } - - - ////////////////////////////////////////////////////////////////////////////////////// - // Specialized inner classes for modeling and animation. - ////////////////////////////////////////////////////////////////////////////////////// - - - ///////////////////////////////////////////////////////////////////////////// - // Class that represents the panel for visualizing and animating memory reference - // patterns. - private class GraphicsPanel extends JPanel { - // override default paint method to assure visualized reference pattern is produced every time - // the panel is repainted. - public void paint(Graphics g) { + } + + // Returns Dimension object with current width and height of display area as determined + // by current settings of respective combo boxes. + private Dimension getDisplayAreaDimension() + { + return new Dimension(visualizationAreaWidthInPixels, visualizationAreaHeightInPixels); + } + + // reset all counters in the Grid. + private void resetCounts() + { + theGrid.reset(); + } + + // Will return int equivalent of specified combo box's current selection. + // The selection must be a String that parses to an int. + private int getIntComboBoxSelection(JComboBox comboBox) + { + try + { + return Integer.parseInt((String) comboBox.getSelectedItem()); + } + catch (NumberFormatException nfe) + { + // Can occur only if initialization list contains badly formatted numbers. This + // is a developer's error, not a user error, and better be caught before release. + return 1; + } + } + + // Use this for consistent results. + private JPanel getPanelWithBorderLayout() + { + return new JPanel(new BorderLayout(2, 2)); + } + + // Method to determine grid dimensions based on durrent control settings. + // Each grid element corresponds to one visualization unit. + private Grid createNewGrid() + { + int rows = visualizationAreaHeightInPixels / unitPixelHeight; + int columns = visualizationAreaWidthInPixels / unitPixelWidth; + return new Grid(rows, columns); + } + + // Given memory address, increment the counter for the corresponding grid element. + // Need to consider words per unit (number of memory words that each visual element represents). + // If address maps to invalid grid element (e.g. is outside the current bounds based on all + // display settings) then nothing happens. + private void incrementReferenceCountForAddress(int address) + { + int offset = (address - baseAddress) / Memory.WORD_LENGTH_BYTES / wordsPerUnit; + // If you care to do anything with it, the following will return -1 if the address + // maps outside the dimensions of the grid (e.g. below the base address or beyond end). + theGrid.incrementElement(offset / theGrid.getColumns(), offset % theGrid.getColumns()); + } + + + ////////////////////////////////////////////////////////////////////////////////////// + // Specialized inner classes for modeling and animation. + ////////////////////////////////////////////////////////////////////////////////////// + + + ///////////////////////////////////////////////////////////////////////////// + // Class that represents the panel for visualizing and animating memory reference + // patterns. + private class GraphicsPanel extends JPanel + { + // override default paint method to assure visualized reference pattern is produced every time + // the panel is repainted. + public void paint(Graphics g) + { paintGrid(g, theGrid); - if (drawHashMarksSelector.isSelected()) { - paintHashMarks(g, theGrid); + if (drawHashMarksSelector.isSelected()) + { + paintHashMarks(g, theGrid); } - } - - // Paint (ash marks on the grid. Their color is chosef to be in - // "contrast" to the current color for reference count of zero. - private void paintHashMarks(Graphics g, Grid grid) { + } + + // Paint (ash marks on the grid. Their color is chosef to be in + // "contrast" to the current color for reference count of zero. + private void paintHashMarks(Graphics g, Grid grid) + { g.setColor(getContrastingColor(counterColorScale.getColor(0))); - int leftX=0; - int rightX=visualizationAreaWidthInPixels; - int upperY=0; - int lowerY=visualizationAreaHeightInPixels; + int leftX = 0; + int rightX = visualizationAreaWidthInPixels; + int upperY = 0; + int lowerY = visualizationAreaHeightInPixels; // draw vertical hash marks - for (int j=0; j= 10) { - spaces = " "; - } - else if (value >=100) { - spaces = ""; + if (value >= 10) + { + spaces = " "; } - return "Counter value "+spaces+value; - } - - // Listener that both revises label as user slides and updates current index when sliding stops. - private class ColorChooserListener implements ChangeListener { - public void stateChanged(ChangeEvent e) { - JSlider source = (JSlider)e.getSource(); - if (!source.getValueIsAdjusting()) { - counterIndex = (int)source.getValue(); - } - else { - int count = countTable[(int)source.getValue()]; - sliderLabel.setText(setLabel(count)); - currentColorButton.setBackground(counterColorScale.getColor(count)); - } - } - } - } - - - //////////////////////////////////////////////////////////////////////////////// - // Object that represents mapping from counter value to color it is displayed as. - // - private class CounterColorScale { - CounterColor[] counterColors; - - CounterColorScale(CounterColor[] colors) { + else if (value >= 100) + { + spaces = ""; + } + return "Counter value " + spaces + value; + } + + // Listener that both revises label as user slides and updates current index when sliding stops. + private class ColorChooserListener implements ChangeListener + { + public void stateChanged(ChangeEvent e) + { + JSlider source = (JSlider) e.getSource(); + if (!source.getValueIsAdjusting()) + { + counterIndex = source.getValue(); + } + else + { + int count = countTable[source.getValue()]; + sliderLabel.setText(setLabel(count)); + currentColorButton.setBackground(counterColorScale.getColor(count)); + } + } + } + } + + + //////////////////////////////////////////////////////////////////////////////// + // Object that represents mapping from counter value to color it is displayed as. + // + private class CounterColorScale + { + CounterColor[] counterColors; + + CounterColorScale(CounterColor[] colors) + { counterColors = colors; - } - - // return color associated with specified counter value - private Color getColor(int count) { + } + + // return color associated with specified counter value + private Color getColor(int count) + { Color result = counterColors[0].associatedColor; - int index=0; - while (index < counterColors.length && count >= counterColors[index].colorRangeStart) { - result = counterColors[index].associatedColor; - index++; + int index = 0; + while (index < counterColors.length && count >= counterColors[index].colorRangeStart) + { + result = counterColors[index].associatedColor; + index++; } return result; - } - - // For a given counter value, return the counter value at the high end of the range of - // counter values having the same color. - private int getHighEndOfRange(int count) { + } + + // For a given counter value, return the counter value at the high end of the range of + // counter values having the same color. + private int getHighEndOfRange(int count) + { int highEnd = Integer.MAX_VALUE; - if (count < counterColors[counterColors.length-1].colorRangeStart) { - int index=0; - while (index < counterColors.length-1 && count >= counterColors[index].colorRangeStart) { - highEnd = counterColors[index+1].colorRangeStart - 1; - index++; - } + if (count < counterColors[counterColors.length - 1].colorRangeStart) + { + int index = 0; + while (index < counterColors.length - 1 && count >= counterColors[index].colorRangeStart) + { + highEnd = counterColors[index + 1].colorRangeStart - 1; + index++; + } } return highEnd; - } - - // The given entry should either be inserted into the the scale or replace an existing - // element. The latter occurs if the new CounterColor has same starting counter value - // as an existing one. - private void insertOrReplace(CounterColor newColor) { + } + + // The given entry should either be inserted into the the scale or replace an existing + // element. The latter occurs if the new CounterColor has same starting counter value + // as an existing one. + private void insertOrReplace(CounterColor newColor) + { int index = Arrays.binarySearch(counterColors, newColor); - if (index >= 0) { // found, so replace - counterColors[index] = newColor; - } - else { // not found, so insert - int insertIndex = -index-1; - CounterColor[] newSortedArray = new CounterColor[counterColors.length+1]; - System.arraycopy(counterColors, 0, newSortedArray, 0, insertIndex); - System.arraycopy(counterColors, insertIndex, newSortedArray, insertIndex+1, counterColors.length-insertIndex); - newSortedArray[insertIndex] = newColor; - counterColors = newSortedArray; + if (index >= 0) + { // found, so replace + counterColors[index] = newColor; } - } - } - - - /////////////////////////////////////////////////////////////////////////////////////// - // Each object represents beginning of a counter value range (non-negative integer) and - // color for rendering the range. High end of the range is defined as low end of the - // next range minus 1. For last range, high end is Integer.MAX_VALUE. - private class CounterColor implements Comparable { - private int colorRangeStart; - private Color associatedColor; - public CounterColor(int start, Color color) { + else + { // not found, so insert + int insertIndex = -index - 1; + CounterColor[] newSortedArray = new CounterColor[counterColors.length + 1]; + System.arraycopy(counterColors, 0, newSortedArray, 0, insertIndex); + System.arraycopy(counterColors, insertIndex, newSortedArray, insertIndex + 1, counterColors.length - insertIndex); + newSortedArray[insertIndex] = newColor; + counterColors = newSortedArray; + } + } + } + + + /////////////////////////////////////////////////////////////////////////////////////// + // Each object represents beginning of a counter value range (non-negative integer) and + // color for rendering the range. High end of the range is defined as low end of the + // next range minus 1. For last range, high end is Integer.MAX_VALUE. + private class CounterColor implements Comparable + { + private final int colorRangeStart; + + private final Color associatedColor; + + public CounterColor(int start, Color color) + { this.colorRangeStart = start; this.associatedColor = color; - } - - // Necessary for sorting in ascending order of range low end. - public int compareTo(Object other) { - if (other instanceof CounterColor) { - return this.colorRangeStart - ((CounterColor)other).colorRangeStart; - } - else { - throw new ClassCastException(); + } + + // Necessary for sorting in ascending order of range low end. + public int compareTo(Object other) + { + if (other instanceof CounterColor) + { + return this.colorRangeStart - ((CounterColor) other).colorRangeStart; } - } - } - - - //////////////////////////////////////////////////////////////////////// - // Represents grid of memory access counts - private class Grid { - - int[][] grid; - int rows, columns; - - private Grid(int rows, int columns) { + else + { + throw new ClassCastException(); + } + } + } + + + //////////////////////////////////////////////////////////////////////// + // Represents grid of memory access counts + private class Grid + { + + int[][] grid; + + int rows, columns; + + private Grid(int rows, int columns) + { grid = new int[rows][columns]; this.rows = rows; this.columns = columns; - // automatically initialized to 0, so I won't bother to.... - } - - private int getRows() { + // automatically initialized to 0, so I won't bother to.... + } + + private int getRows() + { return rows; - } - - private int getColumns() { + } + + private int getColumns() + { return columns; - } - - // Returns value in given grid element; -1 if row or column is out of range. - private int getElement(int row, int column) { - return (row>=0 && row<=rows && column>=0 && column<=columns) ? grid[row][column] : -1; - } - - // Returns value in given grid element without doing any row/column index checking. - // Is faster than getElement but will throw array index out of bounds exception if - // parameter values are outside the bounds of the grid. - private int getElementFast(int row, int column) { - return grid[row][column]; - } - - // Increment the given grid element and return incremented value. - // Returns -1 if row or column is out of range. - private int incrementElement(int row, int column) { - return (row>=0 && row<=rows && column>=0 && column<=columns) ? ++grid[row][column] : -1; - } - - // Just set all grid elements to 0. - private void reset() { - for (int i=0; i= 0 && row <= rows && column >= 0 && column <= columns) ? grid[row][column] : -1; + } + + // Returns value in given grid element without doing any row/column index checking. + // Is faster than getElement but will throw array index out of bounds exception if + // parameter values are outside the bounds of the grid. + private int getElementFast(int row, int column) + { + return grid[row][column]; + } + + // Increment the given grid element and return incremented value. + // Returns -1 if row or column is out of range. + private int incrementElement(int row, int column) + { + return (row >= 0 && row <= rows && column >= 0 && column <= columns) ? ++grid[row][column] : -1; + } + + // Just set all grid elements to 0. + private void reset() + { + for (int i = 0; i < rows; i++) + { + for (int j = 0; j < columns; j++) + { + grid[i][j] = 0; + } } - } - } - - } \ No newline at end of file + } + } + +} diff --git a/src/main/java/mars/tools/MipsXray.java b/src/main/java/mars/tools/MipsXray.java index 01e2f52..a78f4d3 100644 --- a/src/main/java/mars/tools/MipsXray.java +++ b/src/main/java/mars/tools/MipsXray.java @@ -32,1428 +32,1721 @@ import java.util.HashMap; import java.util.Observable; import java.util.Vector; -public class MipsXray extends AbstractMarsToolAndApplication{ - private static final long serialVersionUID = -1L; - private static String heading = "MIPS X-Ray - Animation of MIPS Datapath"; - private static String version = " Version 2.0"; - - protected Graphics g; - protected int lastAddress = -1; //address of instruction in memory - protected JLabel label; - private Container painel = this.getContentPane(); - private DatapathAnimation datapathAnimation; //class panel that runs datapath animation. +public class MipsXray extends AbstractMarsToolAndApplication +{ + private static final long serialVersionUID = -1L; + + private static final String heading = "MIPS X-Ray - Animation of MIPS Datapath"; + + private static final String version = " Version 2.0"; + + protected Graphics g; + + protected int lastAddress = -1; //address of instruction in memory + + protected JLabel label; + + private final Container painel = this.getContentPane(); + + private DatapathAnimation datapathAnimation; //class panel that runs datapath animation. private GraphicsConfiguration gc; + private BufferedImage datapath; - private String instructionBinary; - + + private String instructionBinary; + //Components to add menu bar in the plugin window. - private JButton Assemble, Step, runBackStep; + private JButton Assemble, Step, runBackStep; + private Action runAssembleAction, runStepAction, runBackstepAction; - + private VenusUI mainUI; + private JToolBar toolbar; + private Timer time; - - public MipsXray(String title, String heading) { - super(title,heading); - } - - /** - * Simple constructor, likely used by the MipsXray menu mechanism - */ - public MipsXray() { - super (heading+", "+version, heading); - } - - - /** - * Required method to return Tool name. - * @return Tool name. MARS will display this in menu item. - */ - public String getName() { - return "MIPS X-Ray"; - } - /** - * Overrides default method, to provide a Help button for this tool/app. - */ - protected JComponent getHelpComponent() { - final String helpContent = - "This plugin is used to visualizate the behavior of mips processor using the default datapath. \n" + - "It reads the source code instruction and generates an animation representing the inputs and \n" + - "outputs of functional blocks and the interconnection between them. The basic signals \n" + - "represented are, control signals, opcode bits and data of functional blocks.\n" + - "\n" + - "Besides the datapath representation, information for each instruction is displayed below\n" + - "the datapath. That display includes opcode value, with the correspondent colors used to\n" + - "represent the signals in datapath, mnemonic of the instruction processed at the moment, registers\n" + - "used in the instruction and a label that indicates the color code used to represent control signals\n" + - "\n" + - "To see the datapath of register bank and control units click inside the functional unit.\n\n" + - "Version 2.0\n" + - "Developed by Márcio Roberto, Guilherme Sales, Fabrício Vivas, Flávio Cardeal and Fábio Lúcio\n" + - "Contact Marcio Roberto at marcio.rdaraujo@gmail.com with questions or comments.\n"; - JButton help = new JButton("Help"); - help.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - JOptionPane.showMessageDialog(theWindow, helpContent); - } - }); - return help; - } - /** - * Implementation of the inherited abstract method to build the main - * display area of the GUI. It will be placed in the CENTER area of a - * BorderLayout. The title is in the NORTH area, and the controls are - * in the SOUTH area. - */ - protected JComponent buildAnimationSequence(){ - JPanel image = new JPanel(new GridBagLayout()); - return image; - } - - // Insert image in the panel and configure the parameters to run animation. - protected JComponent buildMainDisplayArea() { - mainUI = Globals.getGui(); - this.createActionObjects(); - toolbar= this.setUpToolBar(); - -// JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT)); - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); - try { - BufferedImage im = ImageIO.read( - getClass().getResource(Globals.imagesPath+"datapath.png") ); - - int transparency = im.getColorModel().getTransparency(); - datapath = gc.createCompatibleImage( im.getWidth(), im.getHeight(), - transparency ); - - Graphics2D g2d = datapath.createGraphics(); // graphics context - g2d.drawImage(im,0,0,null); - g2d.dispose(); - - } - catch(IOException e) { - System.out.println("Load Image error for " + - getClass().getResource(Globals.imagesPath+"datapath.png") + ":\n" + e); - e.printStackTrace(); - } - System.setProperty("sun.java2d.translaccel", "true"); - ImageIcon icon = new ImageIcon(getClass().getResource(Globals.imagesPath+"datapath.png")); - Image im = icon.getImage(); - icon = new ImageIcon(im); - - JLabel label = new JLabel(icon); - painel.add(label, BorderLayout.WEST); - painel.add(toolbar, BorderLayout.NORTH); - this.setResizable(false); - return (JComponent) painel; + public MipsXray(String title, String heading) + { + super(title, heading); } - - protected JComponent buildMainDisplayArea(String figure) { - mainUI = Globals.getGui(); - this.createActionObjects(); - toolbar= this.setUpToolBar(); - -// JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT)); - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); - try { - BufferedImage im = ImageIO.read( - getClass().getResource(Globals.imagesPath+figure) ); - - int transparency = im.getColorModel().getTransparency(); - datapath = gc.createCompatibleImage( im.getWidth(), im.getHeight(), - transparency ); - - Graphics2D g2d = datapath.createGraphics(); // graphics context - g2d.drawImage(im,0,0,null); - g2d.dispose(); - - } - catch(IOException e) { - System.out.println("Load Image error for " + - getClass().getResource(Globals.imagesPath+figure) + ":\n" + e); - e.printStackTrace(); - } - System.setProperty("sun.java2d.translaccel", "true"); - ImageIcon icon = new ImageIcon(getClass().getResource(Globals.imagesPath+figure)); - Image im = icon.getImage(); - icon = new ImageIcon(im); - - JLabel label = new JLabel(icon); - painel.add(label, BorderLayout.WEST); - painel.add(toolbar, BorderLayout.NORTH); - this.setResizable(false); - return (JComponent) painel; - } - - protected void addAsObserver() { - addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); - } - - //Function that gets the current instruction in memory and start animation with the selected instruction. - protected void processMIPSUpdate(Observable resource, AccessNotice notice) { - if (!notice.accessIsFromMIPS()) return; - if (notice.getAccessType() != AccessNotice.READ) return; - MemoryAccessNotice man = (MemoryAccessNotice) notice; - int currentAdress = man.getAddress(); - - if (currentAdress == lastAddress) return; - lastAddress = currentAdress; - ProgramStatement stmt; - - try { - BasicInstruction instr = null; - stmt = Memory.getInstance().getStatement(currentAdress); - if(stmt == null){ - return; - } - - instr = (BasicInstruction) stmt.getInstruction(); - instructionBinary = stmt.getMachineStatement(); - BasicInstructionFormat format = instr.getInstructionFormat(); - - painel.removeAll(); - datapathAnimation = new DatapathAnimation(instructionBinary); - this.createActionObjects(); - toolbar= this.setUpToolBar(); - painel.add(toolbar, BorderLayout.NORTH); - painel.add(datapathAnimation, BorderLayout.WEST); - datapathAnimation.startAnimation(instructionBinary ); - - } catch (AddressErrorException e) { - e.printStackTrace(); - } - - - } - - public void updateDisplay(){ - this.repaint(); - } - - //set the tool bar that controls the step in a time instruction running. - private JToolBar setUpToolBar() { - JToolBar toolBar = new JToolBar(); - Assemble = new JButton(runAssembleAction); - Assemble.setText(""); - runBackStep = new JButton(runBackstepAction); - runBackStep.setText(""); - - Step = new JButton(runStepAction); - Step.setText(""); - toolBar.add(Assemble); - toolBar.add(Step); - - return toolBar; - } - - //set action in the menu bar. - private void createActionObjects() { - Toolkit tk = Toolkit.getDefaultToolkit(); - Class cs = this.getClass(); - try{ - runAssembleAction = new RunAssembleAction("Assemble", - new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath+"Assemble22.png"))), - "Assemble the current file and clear breakpoints", new Integer(KeyEvent.VK_A), - KeyStroke.getKeyStroke( KeyEvent.VK_F3, 0), - mainUI); - - runStepAction = new RunStepAction("Step", - new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath+"StepForward22.png"))), - "Run one step at a time", new Integer(KeyEvent.VK_T), - KeyStroke.getKeyStroke( KeyEvent.VK_F7, 0), - mainUI); - runBackstepAction = new RunBackstepAction("Backstep", - new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath+"StepBack22.png"))), - "Undo the last step", new Integer(KeyEvent.VK_B), - KeyStroke.getKeyStroke( KeyEvent.VK_F8, 0), - mainUI); - } - catch(Exception e){ - System.out.println("Internal Error: images folder not found, or other null pointer exception while creating Action objects"); - e.printStackTrace(); - System.exit(0); - } - } + /** + * Simple constructor, likely used by the MipsXray menu mechanism + */ + public MipsXray() + { + super(heading + ", " + version, heading); + } -class Vertex { - private int numIndex; - private int init; - private int end; - private int current; - private String name; - public static final int movingUpside = 1; - public static final int movingDownside = 2; - public static final int movingLeft = 3; - public static final int movingRight = 4; - public int direction; - public int oppositeAxis; - private boolean isMovingXaxis; - private Color color; - private boolean first_interaction; - private boolean active; - private boolean isText; - private ArrayList targetVertex; - - public Vertex(int index, int init, int end, String name, int oppositeAxis, boolean isMovingXaxis, - String listOfColors, String listTargetVertex, boolean isText){ - this.numIndex = index; - this.init = init; - this.current = this.init; - this.end = end; - this.name = name; - this.oppositeAxis = oppositeAxis; - this.isMovingXaxis = isMovingXaxis; - this.first_interaction = true; - this.active = false; - this.isText = isText; - this.color = new Color(0,153,0); - if(isMovingXaxis == true){ - if( init < end) - direction = movingLeft; - else - direction = movingRight; - - } - else{ - if( init < end) - direction = movingUpside; - else - direction = movingDownside; - } - String[] list = listTargetVertex.split("#"); - targetVertex = new ArrayList(); - for(int i = 0; i < list.length; i++){ - targetVertex.add(Integer.parseInt(list[i])); - // System.out.println("Adding " + i + " " + Integer.parseInt(list[i])+ " in target"); - } - String[] listColor = listOfColors.split("#"); - this.color = new Color(Integer.parseInt(listColor[0]) , Integer.parseInt(listColor[1]), Integer.parseInt(listColor[2]) ); - } - - public int getDirection(){ - return direction; - } - - public boolean isText(){ - return this.isText; - } + /** + * Required method to return Tool name. + * + * @return Tool name. MARS will display this in menu item. + */ + public String getName() + { + return "MIPS X-Ray"; + } + + /** + * Overrides default method, to provide a Help button for this tool/app. + */ + protected JComponent getHelpComponent() + { + final String helpContent = + "This plugin is used to visualizate the behavior of mips processor using the default datapath. \n" + + "It reads the source code instruction and generates an animation representing the inputs and \n" + + "outputs of functional blocks and the interconnection between them. The basic signals \n" + + "represented are, control signals, opcode bits and data of functional blocks.\n" + + "\n" + + "Besides the datapath representation, information for each instruction is displayed below\n" + + "the datapath. That display includes opcode value, with the correspondent colors used to\n" + + "represent the signals in datapath, mnemonic of the instruction processed at the moment, registers\n" + + "used in the instruction and a label that indicates the color code used to represent control signals\n" + + "\n" + + "To see the datapath of register bank and control units click inside the functional unit.\n\n" + + "Version 2.0\n" + + "Developed by Márcio Roberto, Guilherme Sales, Fabrício Vivas, Flávio Cardeal and Fábio Lúcio\n" + + "Contact Marcio Roberto at marcio.rdaraujo@gmail.com with questions or comments.\n"; + JButton help = new JButton("Help"); + help.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + JOptionPane.showMessageDialog(theWindow, helpContent); + } + }); + return help; + } + + /** + * Implementation of the inherited abstract method to build the main display area of the GUI. It will be placed in + * the CENTER area of a BorderLayout. The title is in the NORTH area, and the controls are in the SOUTH area. + */ + protected JComponent buildAnimationSequence() + { + JPanel image = new JPanel(new GridBagLayout()); + return image; + } + + // Insert image in the panel and configure the parameters to run animation. + protected JComponent buildMainDisplayArea() + { + mainUI = Globals.getGui(); + this.createActionObjects(); + toolbar = this.setUpToolBar(); + + // JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT)); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); + try + { + BufferedImage im = ImageIO.read( + getClass().getResource(Globals.imagesPath + "datapath.png")); + + int transparency = im.getColorModel().getTransparency(); + datapath = gc.createCompatibleImage(im.getWidth(), im.getHeight(), + transparency); + + Graphics2D g2d = datapath.createGraphics(); // graphics context + g2d.drawImage(im, 0, 0, null); + g2d.dispose(); + + } + catch (IOException e) + { + System.out.println("Load Image error for " + + getClass().getResource(Globals.imagesPath + "datapath.png") + ":\n" + e); + e.printStackTrace(); + } + System.setProperty("sun.java2d.translaccel", "true"); + ImageIcon icon = new ImageIcon(getClass().getResource(Globals.imagesPath + "datapath.png")); + Image im = icon.getImage(); + icon = new ImageIcon(im); + + JLabel label = new JLabel(icon); + painel.add(label, BorderLayout.WEST); + painel.add(toolbar, BorderLayout.NORTH); + this.setResizable(false); + return (JComponent) painel; + } + + protected JComponent buildMainDisplayArea(String figure) + { + mainUI = Globals.getGui(); + this.createActionObjects(); + toolbar = this.setUpToolBar(); + + // JPanel jp = new JPanel(new FlowLayout(FlowLayout.LEFT)); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); + try + { + BufferedImage im = ImageIO.read( + getClass().getResource(Globals.imagesPath + figure)); + + int transparency = im.getColorModel().getTransparency(); + datapath = gc.createCompatibleImage(im.getWidth(), im.getHeight(), + transparency); + + Graphics2D g2d = datapath.createGraphics(); // graphics context + g2d.drawImage(im, 0, 0, null); + g2d.dispose(); + + } + catch (IOException e) + { + System.out.println("Load Image error for " + + getClass().getResource(Globals.imagesPath + figure) + ":\n" + e); + e.printStackTrace(); + } + System.setProperty("sun.java2d.translaccel", "true"); + ImageIcon icon = new ImageIcon(getClass().getResource(Globals.imagesPath + figure)); + Image im = icon.getImage(); + icon = new ImageIcon(im); + + JLabel label = new JLabel(icon); + painel.add(label, BorderLayout.WEST); + painel.add(toolbar, BorderLayout.NORTH); + this.setResizable(false); + return (JComponent) painel; + } + + protected void addAsObserver() + { + addAsObserver(Memory.textBaseAddress, Memory.textLimitAddress); + } + + //Function that gets the current instruction in memory and start animation with the selected instruction. + protected void processMIPSUpdate(Observable resource, AccessNotice notice) + { + + if (!notice.accessIsFromMIPS()) + { + return; + } + if (notice.getAccessType() != AccessNotice.READ) + { + return; + } + MemoryAccessNotice man = (MemoryAccessNotice) notice; + int currentAdress = man.getAddress(); + + if (currentAdress == lastAddress) + { + return; + } + lastAddress = currentAdress; + ProgramStatement stmt; + + try + { + BasicInstruction instr = null; + stmt = Memory.getInstance().getStatement(currentAdress); + if (stmt == null) + { + return; + } + + instr = (BasicInstruction) stmt.getInstruction(); + instructionBinary = stmt.getMachineStatement(); + BasicInstructionFormat format = instr.getInstructionFormat(); + + painel.removeAll(); + datapathAnimation = new DatapathAnimation(instructionBinary); + this.createActionObjects(); + toolbar = this.setUpToolBar(); + painel.add(toolbar, BorderLayout.NORTH); + painel.add(datapathAnimation, BorderLayout.WEST); + datapathAnimation.startAnimation(instructionBinary); + + } + catch (AddressErrorException e) + { + e.printStackTrace(); + } - public ArrayList getTargetVertex() { - return targetVertex; - } + } - public int getNumIndex() { - return numIndex; - } - public void setNumIndex(int numIndex) { - this.numIndex = numIndex; - } - public int getInit() { - return init; - } - public void setInit(int init) { - this.init = init; - } - public int getEnd() { - return end; - } - public void setEnd(int end) { - this.end = end; - } - public int getCurrent() { - return current; - } - public void setCurrent(int current) { - this.current = current; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public int getOppositeAxis() { - return oppositeAxis; - } - public void setOppositeAxis(int oppositeAxis) { - this.oppositeAxis = oppositeAxis; - } - public boolean isMovingXaxis() { - return isMovingXaxis; - } - public void setMovingXaxis(boolean isMovingXaxis) { - this.isMovingXaxis = isMovingXaxis; - } - public Color getColor() { - return color; - } - public void setColor(Color color) { - this.color = color; - } - public boolean isFirst_interaction() { - return first_interaction; - } - public void setFirst_interaction(boolean first_interaction) { - this.first_interaction = first_interaction; - } - public boolean isActive() { - return active; - } - public void setActive(boolean active) { - this.active = active; - } -} - - -//Internal class that set the parameters value, control the basic behavior of the animation , and execute the animation of the -//selected instruction in memory. -class DatapathAnimation extends JPanel - implements ActionListener, MouseListener { - /** - * - */ - private static final long serialVersionUID = -2681757800180958534L; - - //config variables - private int PERIOD = 5; // velocity of frames in ms - private static final int PWIDTH = 1000; // size of this panel - private static final int PHEIGHT = 574; - private GraphicsConfiguration gc; - private GraphicsDevice gd; // for reporting accl. memory usage - private int accelMemory; - private DecimalFormat df; - - private int counter; //verify then remove. - private boolean justStarted; //flag to start movement - - - private int indexX; //counter of screen position - private int indexY; - private boolean xIsMoving, yIsMoving; //flag for mouse movement. - - - -// private Vertex[][] inputGraph; - private Vector> outputGraph; - private ArrayList vertexList; - private ArrayList vertexTraversed; - //Screen Label variables - - private HashMap opcodeEquivalenceTable; - private HashMap functionEquivalenceTable; - private HashMap registerEquivalenceTable; - - private String instructionCode; - - private int countRegLabel; - private int countALULabel; - private int countPCLabel; - - //Colors variables - private Color green1 = new Color(0,153,0); - private Color green2 = new Color( 0,77,0); - private Color yellow2 = new Color(185,182,42); - private Color orange1 = new Color(255,102,0); - private Color orange = new Color(119,34,34); - private Color blue2 = new Color(0,153,255); - - private int register = 1; - private int control = 2; - private int aluControl = 3; - private int alu = 4; - private int currentUnit; - private Graphics2D g2d; - - - private BufferedImage datapath; - - public void mousePressed(MouseEvent e) { - PointerInfo a = MouseInfo.getPointerInfo(); - // System.out.println("olha, capturado x=" + a.getLocation().getX() + " y = " + a.getLocation().getY()); - } - - public DatapathAnimation(String instructionBinary) - { - df = new DecimalFormat("0.0"); // 1 dp - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - gd = ge.getDefaultScreenDevice(); - gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); - - accelMemory = gd.getAvailableAcceleratedMemory(); // in bytes - setBackground(Color.white); - setPreferredSize( new Dimension(PWIDTH, PHEIGHT) ); - - // load and initialise the images - initImages(); - - vertexList = new ArrayList(); - counter = 0; - justStarted = true; - instructionCode = instructionBinary; - - //declaration of labels definition. - opcodeEquivalenceTable = new HashMap(); - functionEquivalenceTable = new HashMap(); - registerEquivalenceTable = new HashMap(); - - countRegLabel = 400; - countALULabel = 380; - countPCLabel = 380; - loadHashMapValues(); - addMouseListener(this); - - - - } // end of ImagesTests() - - //set the binnary opcode value of the basic instructions of MIPS instruction set - public void loadHashMapValues(){ - importXmlStringData("/MipsXRayOpcode.xml",opcodeEquivalenceTable, "equivalence", "bits", "mnemonic"); - importXmlStringData("/MipsXRayOpcode.xml", functionEquivalenceTable, "function_equivalence", "bits", "mnemonic"); - importXmlStringData("/MipsXRayOpcode.xml", registerEquivalenceTable, "register_equivalence", "bits", "mnemonic"); - importXmlDatapathMap("/MipsXRayOpcode.xml", "datapath_map"); - } - - //import the list of opcodes of mips set of instructions - public void importXmlStringData(String xmlName, HashMap table, String elementTree, String tagId, String tagData){ - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(false); - DocumentBuilder docBuilder; - try { - //System.out.println(); - docBuilder = dbf.newDocumentBuilder(); - Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); - Element root = doc.getDocumentElement(); - Element equivalenceItem; - NodeList bitsList, mnemonic; - NodeList equivalenceList = root.getElementsByTagName(elementTree); - for(int i = 0; i < equivalenceList.getLength(); i++){ - equivalenceItem = (Element)equivalenceList.item(i); - bitsList = equivalenceItem.getElementsByTagName(tagId); - mnemonic = equivalenceItem.getElementsByTagName(tagData); - for(int j= 0; j < bitsList.getLength(); j++){ - table.put(bitsList.item(j).getTextContent(),mnemonic.item(j).getTextContent()); - } - } - } - catch (Exception e) { - e.printStackTrace(); - } - } - - //import the parameters of the animation on datapath - public void importXmlDatapathMap(String xmlName, String elementTree){ - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(false); - DocumentBuilder docBuilder; - try { - docBuilder = dbf.newDocumentBuilder(); - Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); - Element root = doc.getDocumentElement(); - Element datapath_mapItem; - NodeList index_vertex, name, init, end,color, other_axis, isMovingXaxis, targetVertex, sourceVertex, isText ; - NodeList datapath_mapList = root.getElementsByTagName(elementTree); - for(int i = 0; i < datapath_mapList.getLength(); i++){ //extract the vertex of the xml input and encapsulate into the vertex object - datapath_mapItem = (Element)datapath_mapList.item(i); - index_vertex = datapath_mapItem.getElementsByTagName("num_vertex"); - name = datapath_mapItem.getElementsByTagName("name"); - init = datapath_mapItem.getElementsByTagName("init"); - end = datapath_mapItem.getElementsByTagName("end"); - //definition of colors line - if(instructionCode.substring(0,6).equals("000000")){//R-type instructions - color = datapath_mapItem.getElementsByTagName("color_Rtype"); - //System.out.println("rtype"); - } - else if(instructionCode.substring(0,6).matches("00001[0-1]")){ //J-type instructions - color = datapath_mapItem.getElementsByTagName("color_Jtype"); - //System.out.println("jtype"); - } - else if(instructionCode.substring(0,6).matches("100[0-1][0-1][0-1]")){ //LOAD type instructions - color = datapath_mapItem.getElementsByTagName("color_LOADtype"); - //System.out.println("load type"); - } - else if(instructionCode.substring(0,6).matches("101[0-1][0-1][0-1]")){ //LOAD type instructions - color = datapath_mapItem.getElementsByTagName("color_STOREtype"); - //System.out.println("store type"); - } - else if(instructionCode.substring(0,6).matches("0001[0-1][0-1]")){ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("color_BRANCHtype"); - //System.out.println("branch type"); - } - else{ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("color_Itype"); - //System.out.println("immediate type"); - } - - other_axis = datapath_mapItem.getElementsByTagName("other_axis"); - isMovingXaxis = datapath_mapItem.getElementsByTagName("isMovingXaxis"); - targetVertex = datapath_mapItem.getElementsByTagName("target_vertex"); - isText = datapath_mapItem.getElementsByTagName("is_text"); - - for(int j= 0; j < index_vertex.getLength(); j++){ - Vertex vert = new Vertex(Integer.parseInt(index_vertex.item(j).getTextContent()), Integer.parseInt(init.item(j).getTextContent()), - Integer.parseInt(end.item(j).getTextContent()), name.item(j).getTextContent(), Integer.parseInt(other_axis.item(j).getTextContent()), - Boolean.parseBoolean(isMovingXaxis.item(j).getTextContent()), color.item(j).getTextContent(), targetVertex.item(j).getTextContent(), Boolean.parseBoolean(isText.item(j).getTextContent())); - vertexList.add(vert); - } - } - //loading matrix of control of vertex. - outputGraph = new Vector>(); - vertexTraversed = new ArrayList(); - int size = vertexList.size(); - Vertex vertex; - ArrayList targetList; - for(int i = 0; i < vertexList.size(); i++){ - vertex = vertexList.get(i); - targetList = vertex.getTargetVertex(); - Vector vertexOfTargets = new Vector(); - for(int k = 0; k < targetList.size(); k++){ - vertexOfTargets.add(vertexList.get(targetList.get(k))); - } - outputGraph.add(vertexOfTargets); - } - for(int i=0; i< outputGraph.size(); i++){ - Vector vert = outputGraph.get(i); - } - - vertexList.get(0).setActive(true); - vertexTraversed.add(vertexList.get(0)); - } - catch (Exception e) { - e.printStackTrace(); - } - - } - - //Set up the information showed in the screen of the current instruction. - public void setUpInstructionInfo( Graphics2D g2d){ - - FontRenderContext frc = g2d.getFontRenderContext(); - Font font = new Font("Digital-7", Font.PLAIN, 15); - Font fontTitle = new Font("Verdana", Font.PLAIN, 10); - - TextLayout textVariable; - if(instructionCode.substring(0,6).equals("000000")){ //R-type instructions description on screen definition. - textVariable = new TextLayout("REGISTER TYPE INSTRUCTION", new Font("Arial", Font.BOLD, 25), frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 280, 30); - //opcode label - textVariable = new TextLayout("opcode", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 530); - - //initialize of opcode - textVariable = new TextLayout(instructionCode.substring(0,6), font, frc); - g2d.setColor(Color.magenta); - textVariable.draw(g2d, 25, 550); - - //rs label - textVariable = new TextLayout("rs", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 90, 530); - - //initialize of rs - textVariable = new TextLayout(instructionCode.substring(6,11), font, frc); - g2d.setColor(Color.green); - textVariable.draw(g2d, 90, 550); - - //rt label - textVariable = new TextLayout("rt", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 150, 530); - - //initialize of rt - textVariable = new TextLayout(instructionCode.substring(11,16), font, frc); - g2d.setColor(Color.blue); - textVariable.draw(g2d, 150, 550); - - // rd label - textVariable = new TextLayout("rd", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 210, 530); - - //initialize of rd - textVariable = new TextLayout(instructionCode.substring(16,21), font, frc); - g2d.setColor(Color.cyan); - textVariable.draw(g2d, 210, 550); - - //shamt label - textVariable = new TextLayout("shamt", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 270, 530); - - //initialize of shamt - textVariable = new TextLayout(instructionCode.substring(21,26), font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 270, 550); - - //function label - textVariable = new TextLayout("function", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 330, 530); - - //initialize of function - textVariable = new TextLayout(instructionCode.substring(26,32), font, frc); - g2d.setColor(orange1); - textVariable.draw(g2d, 330, 550); - - - //instruction mnemonic - textVariable = new TextLayout("Instruction", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 480); - - //instruction name - textVariable = new TextLayout(functionEquivalenceTable.get(instructionCode.substring(26,32)), font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 25, 500); - - //register in RS - textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6,11)), font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 65, 500); - - //register in RT - textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(16,21)), font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 105, 500); - - //register in RD - textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(11,16)), font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 145, 500); - } - - else if(instructionCode.substring(0,6).matches("00001[0-1]")){ //jump intructions - textVariable = new TextLayout("JUMP TYPE INSTRUCTION", new Font("Verdana", Font.BOLD, 25), frc); //description of instruction code type for jump. - g2d.setColor(Color.black); - textVariable.draw(g2d, 280, 30); - - // label opcode - textVariable = new TextLayout("opcode", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 530); - - //initialize of opcode - textVariable = new TextLayout(instructionCode.substring(0,6), font, frc); - g2d.setColor(Color.magenta); - textVariable.draw(g2d, 25, 550); - - //label address - textVariable = new TextLayout("address", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 95, 530); - - textVariable = new TextLayout("Instruction", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 480); - - //initialize of adress - textVariable = new TextLayout(instructionCode.substring(6,32), font, frc); - g2d.setColor(Color.orange); - textVariable.draw(g2d, 95, 550); - - //instruction mnemonic - textVariable= new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0,6)), font, frc); - g2d.setColor(Color.cyan); - textVariable.draw(g2d, 65, 500); - - //instruction immediate - textVariable = new TextLayout("LABEL", font, frc); - g2d.setColor(Color.cyan); - textVariable.draw(g2d, 105, 500); - } - - else if(instructionCode.substring(0,6).matches("100[0-1][0-1][0-1]")){//load instruction - textVariable = new TextLayout("LOAD TYPE INSTRUCTION", new Font("Verdana", Font.BOLD, 25), frc); //description of instruction code type for load. - g2d.setColor(Color.black); - textVariable.draw(g2d, 280, 30); - //opcode label - textVariable = new TextLayout("opcode", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 530); - - //initialize of opcode - textVariable = new TextLayout(instructionCode.substring(0,6), font, frc); - g2d.setColor(Color.magenta); - textVariable.draw(g2d, 25, 550); - - //rs label - textVariable = new TextLayout("rs", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 90, 530); - - //initialize of rs - textVariable = new TextLayout(instructionCode.substring(6,11), font, frc); - g2d.setColor(Color.green); - textVariable.draw(g2d, 90, 550); - - //rt label - textVariable = new TextLayout("rt", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 145, 530); - - //initialize of rt - textVariable = new TextLayout(instructionCode.substring(11,16), font, frc); - g2d.setColor(Color.blue); - textVariable.draw(g2d, 145, 550); - - // rd label - textVariable = new TextLayout("Immediate", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 200, 530); - - //initialize of rd - textVariable = new TextLayout(instructionCode.substring(16,32), font, frc); - g2d.setColor(orange1); - textVariable.draw(g2d, 200, 550); - - //instruction mnemonic - textVariable = new TextLayout("Instruction", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 480); - - textVariable = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0,6)), font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 25, 500); - - textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6,11)), font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 65, 500); - - textVariable = new TextLayout("M[ "+ registerEquivalenceTable.get(instructionCode.substring(16,21)) + " + " + parseBinToInt(instructionCode.substring(6,32))+ " ]", font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 105, 500); - - //implement co-processors instruction - } - - else if(instructionCode.substring(0,6).matches("101[0-1][0-1][0-1]")){//store instruction - textVariable = new TextLayout("STORE TYPE INSTRUCTION", new Font("Verdana", Font.BOLD, 25), frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 280, 30); - //opcode label - textVariable = new TextLayout("opcode", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 530); - - //initialize of opcode - textVariable = new TextLayout(instructionCode.substring(0,6), font, frc); - g2d.setColor(Color.magenta); - textVariable.draw(g2d, 25, 550); - - //rs label - textVariable = new TextLayout("rs", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 90, 530); - - //initialize of rs - textVariable = new TextLayout(instructionCode.substring(6,11), font, frc); - g2d.setColor(Color.green); - textVariable.draw(g2d, 90, 550); - - //rt label - textVariable = new TextLayout("rt", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 145, 530); - - //initialize of rt - textVariable = new TextLayout(instructionCode.substring(11,16), font, frc); - g2d.setColor(Color.blue); - textVariable.draw(g2d, 145, 550); - - // rd label - textVariable = new TextLayout("Immediate", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 200, 530); - - //initialize of rd - textVariable = new TextLayout(instructionCode.substring(16,32), font, frc); - g2d.setColor(orange1); - textVariable.draw(g2d, 200, 550); - - //instruction mnemonic - textVariable= new TextLayout("Instruction", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 480); - - textVariable = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0,6)), font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 25, 500); - - textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6,11)), font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 65, 500); - - textVariable = new TextLayout("M[ "+ registerEquivalenceTable.get(instructionCode.substring(16,21)) + " + " + parseBinToInt(instructionCode.substring(6,32))+ " ]", font, frc); - g2d.setColor(Color.BLACK); - textVariable.draw(g2d, 105, 500); - - } - - else if(instructionCode.substring(0,6).matches("0100[0-1][0-1]")){ - //implement co-processors instruction - } - - else if(instructionCode.substring(0,6).matches("0001[0-1][0-1]")){ //branch instruction - textVariable = new TextLayout("BRANCH TYPE INSTRUCTION",new Font("Verdana", Font.BOLD, 25), frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 250, 30); - - //label opcode - textVariable = new TextLayout("opcode", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 440); - - textVariable = new TextLayout("opcode", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 530); - - //initialize of opcode - textVariable = new TextLayout(instructionCode.substring(0,6), font, frc); - g2d.setColor(Color.magenta); - textVariable.draw(g2d, 25, 550); - - //rs label - textVariable = new TextLayout("rs", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 90, 530); - - //initialize of rs - textVariable = new TextLayout(instructionCode.substring(6,11), font, frc); - g2d.setColor(Color.green); - textVariable.draw(g2d, 90, 550); - - //rt label - textVariable = new TextLayout("rt", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 145, 530); - - //initialize of rt - textVariable = new TextLayout(instructionCode.substring(11,16), font, frc); - g2d.setColor(Color.blue); - textVariable.draw(g2d, 145, 550); - - // rd label - textVariable = new TextLayout("Immediate", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 200, 530); - - - //initialize of immediate - textVariable= new TextLayout(instructionCode.substring(16,32), font, frc); - g2d.setColor(Color.cyan); - textVariable.draw(g2d, 200, 550); - - //instruction mnemonic - textVariable = new TextLayout("Instruction", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 480); - - textVariable = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0,6)), font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 25, 500); - - textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6,11)), font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 105, 500); - - textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(11,16)), font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 65, 500); - - textVariable = new TextLayout(parseBinToInt(instructionCode.substring(16,32)), font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 155, 500); - } - else{ //imediate instructions - textVariable = new TextLayout("IMMEDIATE TYPE INSTRUCTION",new Font("Verdana", Font.BOLD, 25), frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 250, 30); - - //label opcode - textVariable = new TextLayout("opcode", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 530); - - //initialize of opcode - textVariable = new TextLayout(instructionCode.substring(0,6), font, frc); - g2d.setColor(Color.magenta); - textVariable.draw(g2d, 25, 550); - - //rs label - textVariable = new TextLayout("rs", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 90, 530); - - //initialize of rs - textVariable = new TextLayout(instructionCode.substring(6,11), font, frc); - g2d.setColor(Color.green); - textVariable.draw(g2d, 90, 550); - - //rt label - textVariable = new TextLayout("rt", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 145, 530); - - //initialize of rt - textVariable = new TextLayout(instructionCode.substring(11,16), font, frc); - g2d.setColor(Color.blue); - textVariable.draw(g2d, 145, 550); - - // rd label - textVariable = new TextLayout("Immediate", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 200, 530); - - //initialize of immediate - textVariable= new TextLayout(instructionCode.substring(16,32), font, frc); - g2d.setColor(Color.cyan); - textVariable.draw(g2d, 200, 550); - - //instruction mnemonic - textVariable = new TextLayout("Instruction", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 480); - textVariable = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0,6)), font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 25, 500); - - textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6,11)), font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 105, 500); - - textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(11,16)), font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 65, 500); - - textVariable = new TextLayout(parseBinToInt(instructionCode.substring(16,32)), font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 155, 500); - } - - //Type of control signal labels - textVariable = new TextLayout("Control Signals", fontTitle, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 440); - - textVariable = new TextLayout("Active", font, frc); - g2d.setColor(Color.red); - textVariable.draw(g2d, 25, 455); - - textVariable = new TextLayout("Inactive", font, frc); - g2d.setColor(Color.gray); - textVariable.draw(g2d, 75, 455); - - textVariable = new TextLayout("To see details of control units and register bank click inside the functional block", font, frc); - g2d.setColor(Color.black); - textVariable.draw(g2d, 400, 550); - } - //end of instruction subtitle... - - - //set the initial state of the variables that controls the animation, and start the timer that triggers the animation. - public void startAnimation(String codeInstruction){ - instructionCode = codeInstruction; - time = new Timer(PERIOD, this); // start timer - time.start(); - // this.repaint(); - } - - //initialize the image of datapath. - private void initImages(){ - try { - BufferedImage im = ImageIO.read( - getClass().getResource(Globals.imagesPath+"datapath.png") ); - - int transparency = im.getColorModel().getTransparency(); - datapath = gc.createCompatibleImage( - im.getWidth(), im.getHeight(), - transparency ); - g2d = datapath.createGraphics(); - g2d.drawImage(im,0,0,null); - g2d.dispose(); - } - catch(IOException e) { - System.out.println("Load Image error for " + - getClass().getResource(Globals.imagesPath+"datapath.png") + ":\n" + e); - } - } - - - public void actionPerformed(ActionEvent e) - // triggered by the timer: update, repaint - { - if (justStarted) - justStarted = false; - if(xIsMoving) - indexX++; - if(yIsMoving) - indexY--; - repaint(); - } - - - public void paintComponent(Graphics g) - { - super.paintComponent(g); - g2d = (Graphics2D)g; - // use antialiasing - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - // smoother (and slower) image transformations (e.g. for resizing) - g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2d = (Graphics2D)g; - drawImage(g2d, datapath, 0,0,null); - executeAnimation(g); - counter = (counter + 1)% 100; - g2d.dispose(); - - } - - private void drawImage(Graphics2D g2d, BufferedImage im, int x, int y,Color c){ - if (im == null) { - g2d.setColor(c); - g2d.fillOval(x, y, 20, 20); - g2d.setColor(Color.black); - g2d.drawString(" ", x, y); - } - else - g2d.drawImage(im, x, y, this); - } - - //draw lines. - //method to draw the lines that run from left to right. - public void printTrackLtoR(Vertex v){ - int size; - int[] track; - size = v.getEnd() - v.getInit(); - track = new int[size]; - for(int i = 0; i < size; i++) - track[i] = v.getInit()+i; - if(v.isActive() == true){ - v.setFirst_interaction(false); - for(int i = 0; i < size; i++){ - if(track[i] <= v.getCurrent()){ - g2d.setColor(v.getColor()); - g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); - } - } - if (v.getCurrent() == track[size-1]) - v.setActive(false); - v.setCurrent(v.getCurrent()+1); - } - else if(v.isFirst_interaction() == false){ - for(int i = 0; i < size ; i++){ - g2d.setColor(v.getColor()); - g2d.fillRect(track[i],v.getOppositeAxis(), 3, 3); - } - } - - } - - //method to draw the lines that run from right to left. - //public boolean printTrackRtoL(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, - // boolean active, boolean firstInteraction){ - public void printTrackRtoL(Vertex v){ - int size; - int[] track; - size = v.getInit() - v.getEnd(); - track = new int[size]; - - for(int i = 0; i < size; i++) - track[i] = v.getInit()-i; - - if(v.isActive() == true){ - v.setFirst_interaction(false); - for(int i = 0; i < size; i++){ - if(track[i] >= v.getCurrent()){ - g2d.setColor(v.getColor()); - g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); - } - } - if (v.getCurrent() == track[size-1]) - v.setActive(false); - - v.setCurrent(v.getCurrent()-1); - } - else if(v.isFirst_interaction() == false){ - for(int i = 0; i < size ; i++){ - g2d.setColor(v.getColor()); - g2d.fillRect(track[i],v.getOppositeAxis(), 3, 3); - } - } - } - - //method to draw the lines that run from down to top. - // public boolean printTrackDtoU(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, - // boolean active, boolean firstInteraction){ - public void printTrackDtoU(Vertex v){ - int size; - int[] track; - - if(v.getInit() > v.getEnd()){ - size = v.getInit() - v.getEnd(); - track = new int[size]; - for(int i = 0; i < size; i++) - track[i] = v.getInit()-i; - } - else{ - size = v.getEnd() - v.getInit(); - track = new int[size]; - for(int i = 0; i < size; i++) - track[i] = v.getInit()+i; - } - - if(v.isActive() == true){ - v.setFirst_interaction(false); - for(int i = 0; i < size; i++){ - if(track[i] >= v.getCurrent()){ - g2d.setColor(v.getColor()); - g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); - } - } - if (v.getCurrent() == track[size-1]) - v.setActive(false); - v.setCurrent(v.getCurrent()-1); - - } - else if(v.isFirst_interaction() == false){ - for(int i = 0; i < size; i++){ - g2d.setColor(v.getColor()); - g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); - } - } - } - //method to draw the lines that run from top to down. - // public boolean printTrackUtoD(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, - // boolean active, boolean firstInteraction){ - public void printTrackUtoD(Vertex v){ - - int size; - int[] track; - size = v.getEnd() - v.getInit(); - track = new int[size]; - - for(int i = 0; i < size; i++) - track[i] = v.getInit()+i; - - if(v.isActive() == true){ - v.setFirst_interaction(false); - for(int i = 0; i < size; i++){ - if(track[i] <= v.getCurrent()){ - g2d.setColor(v.getColor()); - g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); - } - - } - if (v.getCurrent() == track[size-1]) - v.setActive(false); - v.setCurrent(v.getCurrent()+1); - } - else if(v.isFirst_interaction() == false){ - for(int i = 0; i < size; i++){ - g2d.setColor(v.getColor()); - g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); - } - } - } - - public void printTextDtoU(Vertex v){ - int size; - int[] track; - FontRenderContext frc = g2d.getFontRenderContext(); - - TextLayout actionInFunctionalBlock = new TextLayout(v.getName(), new Font("Verdana", Font.BOLD, 13), frc); - g2d.setColor(Color.RED); - - if(instructionCode.substring(0,6).matches("101[0-1][0-1][0-1]") - &&!instructionCode.substring(0,6).matches("0001[0-1][0-1]") - &&!instructionCode.substring(0,6).matches("00001[0-1]")){//load instruction - actionInFunctionalBlock = new TextLayout(" ", new Font("Verdana", Font.BOLD, 13), frc); - } - if(v.getName().equals("ALUVALUE")){ - if( instructionCode.substring(0,6).equals("000000"))//R-type instruction - actionInFunctionalBlock = new TextLayout(functionEquivalenceTable.get(instructionCode.substring(26,32)), new Font("Verdana", Font.BOLD, 13), frc); - else //other instructions - actionInFunctionalBlock = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0,6)), new Font("Verdana", Font.BOLD, 13), frc); - } - - if(instructionCode.substring(0,6).matches("0001[0-1][0-1]")&& v.getName().equals("CP+4")) //branch code - actionInFunctionalBlock = new TextLayout("PC+OFFSET", new Font("Verdana", Font.BOLD, 13), frc); - - if(v.getName().equals("WRITING")){ - if(!instructionCode.substring(0,6).matches("100[0-1][0-1][0-1]")) - actionInFunctionalBlock = new TextLayout(" ", new Font("Verdana", Font.BOLD, 13), frc); - } - if(v.isActive() == true){ - v.setFirst_interaction(false); - actionInFunctionalBlock.draw(g2d, v.getOppositeAxis(), v.getCurrent()); - if (v.getCurrent() == v.getEnd()) - v.setActive(false); - v.setCurrent(v.getCurrent()-1); - } - - - } - - //convert binnary value to integer. - public String parseBinToInt(String code){ - int value = 0; - - for(int i =code.length()-1; i >= 0; i--){ - if("1".equals(code.substring(i,i+1))){ - value = value + (int)Math.pow(2,code.length()-i-1); - } - } - - return Integer.toString(value); - } - - //set and execute the information about the current position of each line of information in the animation, - //verifies the previous status of the animation and increment the position of each line that interconnect the unit function. - private void executeAnimation(Graphics g){ - g2d = (Graphics2D)g; - setUpInstructionInfo(g2d); - Vertex vert; - for(int i = 0; i < vertexTraversed.size(); i++){ - vert = vertexTraversed.get(i); - if(vert.isMovingXaxis == true){ - if(vert.getDirection() == vert.movingLeft){ - printTrackLtoR(vert); - if(vert.isActive() == false){ - int j = vert.getTargetVertex().size(); - Vertex tempVertex; - for(int k = 0; k < j; k++){ - tempVertex = outputGraph.get(vert.getNumIndex()).get(k); - Boolean hasThisVertex = false; - for(int m = 0 ; m < vertexTraversed.size(); m++){ - if(tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) - hasThisVertex = true; - } - if(hasThisVertex == false){ - outputGraph.get(vert.getNumIndex()).get(k).setActive(true); - vertexTraversed.add( outputGraph.get(vert.getNumIndex()).get(k)); - } - } - } - } - else{ - printTrackRtoL(vert); - if(vert.isActive() == false){ - int j = vert.getTargetVertex().size(); - Vertex tempVertex; - for(int k = 0; k < j; k++){ - tempVertex = outputGraph.get(vert.getNumIndex()).get(k); - Boolean hasThisVertex = false; - for(int m = 0 ; m < vertexTraversed.size(); m++){ - if(tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) - hasThisVertex = true; - } - if(hasThisVertex == false){ - outputGraph.get(vert.getNumIndex()).get(k).setActive(true); - vertexTraversed.add( outputGraph.get(vert.getNumIndex()).get(k)); - } - } - } - } - } //end of condition of X axis - else{ - if(vert.getDirection() == vert.movingDownside){ - if(vert.isText == true) - printTextDtoU(vert); - else - printTrackDtoU(vert); - - if(vert.isActive() == false){ - int j = vert.getTargetVertex().size(); - Vertex tempVertex; - for(int k = 0; k < j; k++){ - tempVertex = outputGraph.get(vert.getNumIndex()).get(k); - Boolean hasThisVertex = false; - for(int m = 0 ; m < vertexTraversed.size(); m++){ - if(tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) - hasThisVertex = true; - } - if(hasThisVertex == false){ - outputGraph.get(vert.getNumIndex()).get(k).setActive(true); - vertexTraversed.add( outputGraph.get(vert.getNumIndex()).get(k)); - } - } - } - - } - else{ - - printTrackUtoD(vert); - if(vert.isActive() == false){ - int j = vert.getTargetVertex().size(); - Vertex tempVertex; - for(int k = 0; k < j; k++){ - tempVertex = outputGraph.get(vert.getNumIndex()).get(k); - Boolean hasThisVertex = false; - for(int m = 0 ; m < vertexTraversed.size(); m++){ - if(tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) - hasThisVertex = true; - } - if(hasThisVertex == false){ - outputGraph.get(vert.getNumIndex()).get(k).setActive(true); - vertexTraversed.add( outputGraph.get(vert.getNumIndex()).get(k)); - } - } - } - } - } - } - } - - @Override - public void mouseClicked(MouseEvent e) { - - PointerInfo a = MouseInfo.getPointerInfo(); - //limpar a imagem do painel e iniciar o detalhe da unidade funcional. - - - if(e.getPoint().getX() > 425 && e.getPoint().getX() < 520 && e.getPoint().getY() > 300 && e.getPoint().getY() < 425){ - buildMainDisplayArea("register.png"); - FunctionUnitVisualization fu = new FunctionUnitVisualization(instructionBinary, register); - fu.run(); - } - - if(e.getPoint().getX() > 355 && e.getPoint().getX() < 415 && e.getPoint().getY() > 180 && e.getPoint().getY() < 280){ - buildMainDisplayArea("control.png"); - FunctionUnitVisualization fu = new FunctionUnitVisualization(instructionBinary, control); - fu.run(); - } - - if(e.getPoint().getX() > 560 && e.getPoint().getX() < 620 && e.getPoint().getY() > 450 && e.getPoint().getY() < 520){ - buildMainDisplayArea("ALUcontrol.png"); - FunctionUnitVisualization fu = new FunctionUnitVisualization(instructionBinary, aluControl); - fu.run(); - } - - } - - @Override - public void mouseEntered(MouseEvent e) { - } - - @Override - public void mouseExited(MouseEvent e) { - } - - @Override - public void mouseReleased(MouseEvent e) { - } - } + public void updateDisplay() + { + this.repaint(); + } + + //set the tool bar that controls the step in a time instruction running. + private JToolBar setUpToolBar() + { + JToolBar toolBar = new JToolBar(); + Assemble = new JButton(runAssembleAction); + Assemble.setText(""); + runBackStep = new JButton(runBackstepAction); + runBackStep.setText(""); + + Step = new JButton(runStepAction); + Step.setText(""); + toolBar.add(Assemble); + toolBar.add(Step); + + return toolBar; + } + + //set action in the menu bar. + private void createActionObjects() + { + Toolkit tk = Toolkit.getDefaultToolkit(); + Class cs = this.getClass(); + try + { + runAssembleAction = new RunAssembleAction("Assemble", + new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "Assemble22.png"))), + "Assemble the current file and clear breakpoints", Integer.valueOf(KeyEvent.VK_A), + KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0), + mainUI); + + runStepAction = new RunStepAction("Step", + new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "StepForward22.png"))), + "Run one step at a time", Integer.valueOf(KeyEvent.VK_T), + KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0), + mainUI); + runBackstepAction = new RunBackstepAction("Backstep", + new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "StepBack22.png"))), + "Undo the last step", Integer.valueOf(KeyEvent.VK_B), + KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0), + mainUI); + } + catch (Exception e) + { + System.out.println("Internal Error: images folder not found, or other null pointer exception while creating Action objects"); + e.printStackTrace(); + System.exit(0); + } + } + + + class Vertex + { + public static final int movingUpside = 1; + + public static final int movingDownside = 2; + + public static final int movingLeft = 3; + + public static final int movingRight = 4; + + public int direction; + + public int oppositeAxis; + + private int numIndex; + + private int init; + + private int end; + + private int current; + + private String name; + + private boolean isMovingXaxis; + + private Color color; + + private boolean first_interaction; + + private boolean active; + + private final boolean isText; + + private final ArrayList targetVertex; + + public Vertex(int index, int init, int end, String name, int oppositeAxis, boolean isMovingXaxis, + String listOfColors, String listTargetVertex, boolean isText) + { + this.numIndex = index; + this.init = init; + this.current = this.init; + this.end = end; + this.name = name; + this.oppositeAxis = oppositeAxis; + this.isMovingXaxis = isMovingXaxis; + this.first_interaction = true; + this.active = false; + this.isText = isText; + this.color = new Color(0, 153, 0); + if (isMovingXaxis) + { + if (init < end) + { + direction = movingLeft; + } + else + { + direction = movingRight; + } + + } + else + { + if (init < end) + { + direction = movingUpside; + } + else + { + direction = movingDownside; + } + } + String[] list = listTargetVertex.split("#"); + targetVertex = new ArrayList(); + for (int i = 0; i < list.length; i++) + { + targetVertex.add(Integer.parseInt(list[i])); + // System.out.println("Adding " + i + " " + Integer.parseInt(list[i])+ " in target"); + } + String[] listColor = listOfColors.split("#"); + this.color = new Color(Integer.parseInt(listColor[0]), Integer.parseInt(listColor[1]), Integer.parseInt(listColor[2])); + } + + public int getDirection() + { + return direction; + } + + public boolean isText() + { + return this.isText; + } + + + public ArrayList getTargetVertex() + { + return targetVertex; + } + + public int getNumIndex() + { + return numIndex; + } + + public void setNumIndex(int numIndex) + { + this.numIndex = numIndex; + } + + public int getInit() + { + return init; + } + + public void setInit(int init) + { + this.init = init; + } + + public int getEnd() + { + return end; + } + + public void setEnd(int end) + { + this.end = end; + } + + public int getCurrent() + { + return current; + } + + public void setCurrent(int current) + { + this.current = current; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public int getOppositeAxis() + { + return oppositeAxis; + } + + public void setOppositeAxis(int oppositeAxis) + { + this.oppositeAxis = oppositeAxis; + } + + public boolean isMovingXaxis() + { + return isMovingXaxis; + } + + public void setMovingXaxis(boolean isMovingXaxis) + { + this.isMovingXaxis = isMovingXaxis; + } + + public Color getColor() + { + return color; + } + + public void setColor(Color color) + { + this.color = color; + } + + public boolean isFirst_interaction() + { + return first_interaction; + } + + public void setFirst_interaction(boolean first_interaction) + { + this.first_interaction = first_interaction; + } + + public boolean isActive() + { + return active; + } + + public void setActive(boolean active) + { + this.active = active; + } + } + + + //Internal class that set the parameters value, control the basic behavior of the animation , and execute the animation of the + //selected instruction in memory. + class DatapathAnimation extends JPanel + implements ActionListener, MouseListener + { + /** + * + */ + private static final long serialVersionUID = -2681757800180958534L; + + private static final int PWIDTH = 1000; // size of this panel + + private static final int PHEIGHT = 574; + + //config variables + private final int PERIOD = 5; // velocity of frames in ms + + private final GraphicsConfiguration gc; + + private final GraphicsDevice gd; // for reporting accl. memory usage + + private final int accelMemory; + + private final DecimalFormat df; + + private int counter; //verify then remove. + + private boolean justStarted; //flag to start movement + + + private int indexX; //counter of screen position + + private int indexY; + + private boolean xIsMoving, yIsMoving; //flag for mouse movement. + + + // private Vertex[][] inputGraph; + private Vector> outputGraph; + + private final ArrayList vertexList; + + private ArrayList vertexTraversed; + //Screen Label variables + + private final HashMap opcodeEquivalenceTable; + + private final HashMap functionEquivalenceTable; + + private final HashMap registerEquivalenceTable; + + private String instructionCode; + + private final int countRegLabel; + + private final int countALULabel; + + private final int countPCLabel; + + //Colors variables + private final Color green1 = new Color(0, 153, 0); + + private final Color green2 = new Color(0, 77, 0); + + private final Color yellow2 = new Color(185, 182, 42); + + private final Color orange1 = new Color(255, 102, 0); + + private final Color orange = new Color(119, 34, 34); + + private final Color blue2 = new Color(0, 153, 255); + + private final int register = 1; + + private final int control = 2; + + private final int aluControl = 3; + + private final int alu = 4; + + private int currentUnit; + + private Graphics2D g2d; + + + private BufferedImage datapath; + + public DatapathAnimation(String instructionBinary) + { + df = new DecimalFormat("0.0"); // 1 dp + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + gd = ge.getDefaultScreenDevice(); + gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); + + accelMemory = gd.getAvailableAcceleratedMemory(); // in bytes + setBackground(Color.white); + setPreferredSize(new Dimension(PWIDTH, PHEIGHT)); + + // load and initialise the images + initImages(); + + vertexList = new ArrayList(); + counter = 0; + justStarted = true; + instructionCode = instructionBinary; + + //declaration of labels definition. + opcodeEquivalenceTable = new HashMap(); + functionEquivalenceTable = new HashMap(); + registerEquivalenceTable = new HashMap(); + + countRegLabel = 400; + countALULabel = 380; + countPCLabel = 380; + loadHashMapValues(); + addMouseListener(this); + + + } // end of ImagesTests() + + public void mousePressed(MouseEvent e) + { + PointerInfo a = MouseInfo.getPointerInfo(); + // System.out.println("olha, capturado x=" + a.getLocation().getX() + " y = " + a.getLocation().getY()); + } + + //set the binnary opcode value of the basic instructions of MIPS instruction set + public void loadHashMapValues() + { + importXmlStringData("/MipsXRayOpcode.xml", opcodeEquivalenceTable, "equivalence", "bits", "mnemonic"); + importXmlStringData("/MipsXRayOpcode.xml", functionEquivalenceTable, "function_equivalence", "bits", "mnemonic"); + importXmlStringData("/MipsXRayOpcode.xml", registerEquivalenceTable, "register_equivalence", "bits", "mnemonic"); + importXmlDatapathMap("/MipsXRayOpcode.xml", "datapath_map"); + } + + //import the list of opcodes of mips set of instructions + public void importXmlStringData(String xmlName, HashMap table, String elementTree, String tagId, String tagData) + { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(false); + DocumentBuilder docBuilder; + try + { + //System.out.println(); + docBuilder = dbf.newDocumentBuilder(); + Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); + Element root = doc.getDocumentElement(); + Element equivalenceItem; + NodeList bitsList, mnemonic; + NodeList equivalenceList = root.getElementsByTagName(elementTree); + for (int i = 0; i < equivalenceList.getLength(); i++) + { + equivalenceItem = (Element) equivalenceList.item(i); + bitsList = equivalenceItem.getElementsByTagName(tagId); + mnemonic = equivalenceItem.getElementsByTagName(tagData); + for (int j = 0; j < bitsList.getLength(); j++) + { + table.put(bitsList.item(j).getTextContent(), mnemonic.item(j).getTextContent()); + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } + + //import the parameters of the animation on datapath + public void importXmlDatapathMap(String xmlName, String elementTree) + { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(false); + DocumentBuilder docBuilder; + try + { + docBuilder = dbf.newDocumentBuilder(); + Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); + Element root = doc.getDocumentElement(); + Element datapath_mapItem; + NodeList index_vertex, name, init, end, color, other_axis, isMovingXaxis, targetVertex, sourceVertex, isText; + NodeList datapath_mapList = root.getElementsByTagName(elementTree); + for (int i = 0; i < datapath_mapList.getLength(); i++) + { //extract the vertex of the xml input and encapsulate into the vertex object + datapath_mapItem = (Element) datapath_mapList.item(i); + index_vertex = datapath_mapItem.getElementsByTagName("num_vertex"); + name = datapath_mapItem.getElementsByTagName("name"); + init = datapath_mapItem.getElementsByTagName("init"); + end = datapath_mapItem.getElementsByTagName("end"); + //definition of colors line + if (instructionCode.startsWith("000000")) + {//R-type instructions + color = datapath_mapItem.getElementsByTagName("color_Rtype"); + //System.out.println("rtype"); + } + else if (instructionCode.substring(0, 6).matches("00001[0-1]")) + { //J-type instructions + color = datapath_mapItem.getElementsByTagName("color_Jtype"); + //System.out.println("jtype"); + } + else if (instructionCode.substring(0, 6).matches("100[0-1][0-1][0-1]")) + { //LOAD type instructions + color = datapath_mapItem.getElementsByTagName("color_LOADtype"); + //System.out.println("load type"); + } + else if (instructionCode.substring(0, 6).matches("101[0-1][0-1][0-1]")) + { //LOAD type instructions + color = datapath_mapItem.getElementsByTagName("color_STOREtype"); + //System.out.println("store type"); + } + else if (instructionCode.substring(0, 6).matches("0001[0-1][0-1]")) + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("color_BRANCHtype"); + //System.out.println("branch type"); + } + else + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("color_Itype"); + //System.out.println("immediate type"); + } + + other_axis = datapath_mapItem.getElementsByTagName("other_axis"); + isMovingXaxis = datapath_mapItem.getElementsByTagName("isMovingXaxis"); + targetVertex = datapath_mapItem.getElementsByTagName("target_vertex"); + isText = datapath_mapItem.getElementsByTagName("is_text"); + + for (int j = 0; j < index_vertex.getLength(); j++) + { + Vertex vert = new Vertex(Integer.parseInt(index_vertex.item(j).getTextContent()), Integer.parseInt(init.item(j).getTextContent()), + Integer.parseInt(end.item(j).getTextContent()), name.item(j).getTextContent(), Integer.parseInt(other_axis.item(j).getTextContent()), + Boolean.parseBoolean(isMovingXaxis.item(j).getTextContent()), color.item(j).getTextContent(), targetVertex.item(j).getTextContent(), Boolean.parseBoolean(isText.item(j).getTextContent())); + vertexList.add(vert); + } + } + //loading matrix of control of vertex. + outputGraph = new Vector>(); + vertexTraversed = new ArrayList(); + int size = vertexList.size(); + Vertex vertex; + ArrayList targetList; + for (int i = 0; i < vertexList.size(); i++) + { + vertex = vertexList.get(i); + targetList = vertex.getTargetVertex(); + Vector vertexOfTargets = new Vector(); + for (int k = 0; k < targetList.size(); k++) + { + vertexOfTargets.add(vertexList.get(targetList.get(k))); + } + outputGraph.add(vertexOfTargets); + } + for (int i = 0; i < outputGraph.size(); i++) + { + Vector vert = outputGraph.get(i); + } + + vertexList.get(0).setActive(true); + vertexTraversed.add(vertexList.get(0)); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + //Set up the information showed in the screen of the current instruction. + public void setUpInstructionInfo(Graphics2D g2d) + { + + FontRenderContext frc = g2d.getFontRenderContext(); + Font font = new Font("Digital-7", Font.PLAIN, 15); + Font fontTitle = new Font("Verdana", Font.PLAIN, 10); + + TextLayout textVariable; + if (instructionCode.startsWith("000000")) + { //R-type instructions description on screen definition. + textVariable = new TextLayout("REGISTER TYPE INSTRUCTION", new Font("Arial", Font.BOLD, 25), frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 280, 30); + //opcode label + textVariable = new TextLayout("opcode", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 530); + + //initialize of opcode + textVariable = new TextLayout(instructionCode.substring(0, 6), font, frc); + g2d.setColor(Color.magenta); + textVariable.draw(g2d, 25, 550); + + //rs label + textVariable = new TextLayout("rs", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 90, 530); + + //initialize of rs + textVariable = new TextLayout(instructionCode.substring(6, 11), font, frc); + g2d.setColor(Color.green); + textVariable.draw(g2d, 90, 550); + + //rt label + textVariable = new TextLayout("rt", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 150, 530); + + //initialize of rt + textVariable = new TextLayout(instructionCode.substring(11, 16), font, frc); + g2d.setColor(Color.blue); + textVariable.draw(g2d, 150, 550); + + // rd label + textVariable = new TextLayout("rd", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 210, 530); + + //initialize of rd + textVariable = new TextLayout(instructionCode.substring(16, 21), font, frc); + g2d.setColor(Color.cyan); + textVariable.draw(g2d, 210, 550); + + //shamt label + textVariable = new TextLayout("shamt", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 270, 530); + + //initialize of shamt + textVariable = new TextLayout(instructionCode.substring(21, 26), font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 270, 550); + + //function label + textVariable = new TextLayout("function", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 330, 530); + + //initialize of function + textVariable = new TextLayout(instructionCode.substring(26, 32), font, frc); + g2d.setColor(orange1); + textVariable.draw(g2d, 330, 550); + + + //instruction mnemonic + textVariable = new TextLayout("Instruction", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 480); + + //instruction name + textVariable = new TextLayout(functionEquivalenceTable.get(instructionCode.substring(26, 32)), font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 25, 500); + + //register in RS + textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6, 11)), font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 65, 500); + + //register in RT + textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(16, 21)), font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 105, 500); + + //register in RD + textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(11, 16)), font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 145, 500); + } + + else if (instructionCode.substring(0, 6).matches("00001[0-1]")) + { //jump intructions + textVariable = new TextLayout("JUMP TYPE INSTRUCTION", new Font("Verdana", Font.BOLD, 25), frc); //description of instruction code type for jump. + g2d.setColor(Color.black); + textVariable.draw(g2d, 280, 30); + + // label opcode + textVariable = new TextLayout("opcode", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 530); + + //initialize of opcode + textVariable = new TextLayout(instructionCode.substring(0, 6), font, frc); + g2d.setColor(Color.magenta); + textVariable.draw(g2d, 25, 550); + + //label address + textVariable = new TextLayout("address", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 95, 530); + + textVariable = new TextLayout("Instruction", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 480); + + //initialize of adress + textVariable = new TextLayout(instructionCode.substring(6, 32), font, frc); + g2d.setColor(Color.orange); + textVariable.draw(g2d, 95, 550); + + //instruction mnemonic + textVariable = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0, 6)), font, frc); + g2d.setColor(Color.cyan); + textVariable.draw(g2d, 65, 500); + + //instruction immediate + textVariable = new TextLayout("LABEL", font, frc); + g2d.setColor(Color.cyan); + textVariable.draw(g2d, 105, 500); + } + + else if (instructionCode.substring(0, 6).matches("100[0-1][0-1][0-1]")) + {//load instruction + textVariable = new TextLayout("LOAD TYPE INSTRUCTION", new Font("Verdana", Font.BOLD, 25), frc); //description of instruction code type for load. + g2d.setColor(Color.black); + textVariable.draw(g2d, 280, 30); + //opcode label + textVariable = new TextLayout("opcode", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 530); + + //initialize of opcode + textVariable = new TextLayout(instructionCode.substring(0, 6), font, frc); + g2d.setColor(Color.magenta); + textVariable.draw(g2d, 25, 550); + + //rs label + textVariable = new TextLayout("rs", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 90, 530); + + //initialize of rs + textVariable = new TextLayout(instructionCode.substring(6, 11), font, frc); + g2d.setColor(Color.green); + textVariable.draw(g2d, 90, 550); + + //rt label + textVariable = new TextLayout("rt", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 145, 530); + + //initialize of rt + textVariable = new TextLayout(instructionCode.substring(11, 16), font, frc); + g2d.setColor(Color.blue); + textVariable.draw(g2d, 145, 550); + + // rd label + textVariable = new TextLayout("Immediate", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 200, 530); + + //initialize of rd + textVariable = new TextLayout(instructionCode.substring(16, 32), font, frc); + g2d.setColor(orange1); + textVariable.draw(g2d, 200, 550); + + //instruction mnemonic + textVariable = new TextLayout("Instruction", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 480); + + textVariable = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0, 6)), font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 25, 500); + + textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6, 11)), font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 65, 500); + + textVariable = new TextLayout("M[ " + registerEquivalenceTable.get(instructionCode.substring(16, 21)) + " + " + parseBinToInt(instructionCode.substring(6, 32)) + " ]", font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 105, 500); + + //implement co-processors instruction + } + + else if (instructionCode.substring(0, 6).matches("101[0-1][0-1][0-1]")) + {//store instruction + textVariable = new TextLayout("STORE TYPE INSTRUCTION", new Font("Verdana", Font.BOLD, 25), frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 280, 30); + //opcode label + textVariable = new TextLayout("opcode", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 530); + + //initialize of opcode + textVariable = new TextLayout(instructionCode.substring(0, 6), font, frc); + g2d.setColor(Color.magenta); + textVariable.draw(g2d, 25, 550); + + //rs label + textVariable = new TextLayout("rs", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 90, 530); + + //initialize of rs + textVariable = new TextLayout(instructionCode.substring(6, 11), font, frc); + g2d.setColor(Color.green); + textVariable.draw(g2d, 90, 550); + + //rt label + textVariable = new TextLayout("rt", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 145, 530); + + //initialize of rt + textVariable = new TextLayout(instructionCode.substring(11, 16), font, frc); + g2d.setColor(Color.blue); + textVariable.draw(g2d, 145, 550); + + // rd label + textVariable = new TextLayout("Immediate", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 200, 530); + + //initialize of rd + textVariable = new TextLayout(instructionCode.substring(16, 32), font, frc); + g2d.setColor(orange1); + textVariable.draw(g2d, 200, 550); + + //instruction mnemonic + textVariable = new TextLayout("Instruction", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 480); + + textVariable = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0, 6)), font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 25, 500); + + textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6, 11)), font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 65, 500); + + textVariable = new TextLayout("M[ " + registerEquivalenceTable.get(instructionCode.substring(16, 21)) + " + " + parseBinToInt(instructionCode.substring(6, 32)) + " ]", font, frc); + g2d.setColor(Color.BLACK); + textVariable.draw(g2d, 105, 500); + + } + + else if (instructionCode.substring(0, 6).matches("0100[0-1][0-1]")) + { + //implement co-processors instruction + } + + else if (instructionCode.substring(0, 6).matches("0001[0-1][0-1]")) + { //branch instruction + textVariable = new TextLayout("BRANCH TYPE INSTRUCTION", new Font("Verdana", Font.BOLD, 25), frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 250, 30); + + //label opcode + textVariable = new TextLayout("opcode", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 440); + + textVariable = new TextLayout("opcode", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 530); + + //initialize of opcode + textVariable = new TextLayout(instructionCode.substring(0, 6), font, frc); + g2d.setColor(Color.magenta); + textVariable.draw(g2d, 25, 550); + + //rs label + textVariable = new TextLayout("rs", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 90, 530); + + //initialize of rs + textVariable = new TextLayout(instructionCode.substring(6, 11), font, frc); + g2d.setColor(Color.green); + textVariable.draw(g2d, 90, 550); + + //rt label + textVariable = new TextLayout("rt", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 145, 530); + + //initialize of rt + textVariable = new TextLayout(instructionCode.substring(11, 16), font, frc); + g2d.setColor(Color.blue); + textVariable.draw(g2d, 145, 550); + + // rd label + textVariable = new TextLayout("Immediate", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 200, 530); + + + //initialize of immediate + textVariable = new TextLayout(instructionCode.substring(16, 32), font, frc); + g2d.setColor(Color.cyan); + textVariable.draw(g2d, 200, 550); + + //instruction mnemonic + textVariable = new TextLayout("Instruction", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 480); + + textVariable = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0, 6)), font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 25, 500); + + textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6, 11)), font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 105, 500); + + textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(11, 16)), font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 65, 500); + + textVariable = new TextLayout(parseBinToInt(instructionCode.substring(16, 32)), font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 155, 500); + } + else + { //imediate instructions + textVariable = new TextLayout("IMMEDIATE TYPE INSTRUCTION", new Font("Verdana", Font.BOLD, 25), frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 250, 30); + + //label opcode + textVariable = new TextLayout("opcode", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 530); + + //initialize of opcode + textVariable = new TextLayout(instructionCode.substring(0, 6), font, frc); + g2d.setColor(Color.magenta); + textVariable.draw(g2d, 25, 550); + + //rs label + textVariable = new TextLayout("rs", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 90, 530); + + //initialize of rs + textVariable = new TextLayout(instructionCode.substring(6, 11), font, frc); + g2d.setColor(Color.green); + textVariable.draw(g2d, 90, 550); + + //rt label + textVariable = new TextLayout("rt", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 145, 530); + + //initialize of rt + textVariable = new TextLayout(instructionCode.substring(11, 16), font, frc); + g2d.setColor(Color.blue); + textVariable.draw(g2d, 145, 550); + + // rd label + textVariable = new TextLayout("Immediate", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 200, 530); + + //initialize of immediate + textVariable = new TextLayout(instructionCode.substring(16, 32), font, frc); + g2d.setColor(Color.cyan); + textVariable.draw(g2d, 200, 550); + + //instruction mnemonic + textVariable = new TextLayout("Instruction", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 480); + textVariable = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0, 6)), font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 25, 500); + + textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(6, 11)), font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 105, 500); + + textVariable = new TextLayout(registerEquivalenceTable.get(instructionCode.substring(11, 16)), font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 65, 500); + + textVariable = new TextLayout(parseBinToInt(instructionCode.substring(16, 32)), font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 155, 500); + } + + //Type of control signal labels + textVariable = new TextLayout("Control Signals", fontTitle, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 440); + + textVariable = new TextLayout("Active", font, frc); + g2d.setColor(Color.red); + textVariable.draw(g2d, 25, 455); + + textVariable = new TextLayout("Inactive", font, frc); + g2d.setColor(Color.gray); + textVariable.draw(g2d, 75, 455); + + textVariable = new TextLayout("To see details of control units and register bank click inside the functional block", font, frc); + g2d.setColor(Color.black); + textVariable.draw(g2d, 400, 550); + } + //end of instruction subtitle... + + + //set the initial state of the variables that controls the animation, and start the timer that triggers the animation. + public void startAnimation(String codeInstruction) + { + instructionCode = codeInstruction; + time = new Timer(PERIOD, this); // start timer + time.start(); + // this.repaint(); + } + + //initialize the image of datapath. + private void initImages() + { + try + { + BufferedImage im = ImageIO.read( + getClass().getResource(Globals.imagesPath + "datapath.png")); + + int transparency = im.getColorModel().getTransparency(); + datapath = gc.createCompatibleImage( + im.getWidth(), im.getHeight(), + transparency); + g2d = datapath.createGraphics(); + g2d.drawImage(im, 0, 0, null); + g2d.dispose(); + } + catch (IOException e) + { + System.out.println("Load Image error for " + + getClass().getResource(Globals.imagesPath + "datapath.png") + ":\n" + e); + } + } + + + public void actionPerformed(ActionEvent e) + // triggered by the timer: update, repaint + { + if (justStarted) + { + justStarted = false; + } + if (xIsMoving) + { + indexX++; + } + if (yIsMoving) + { + indexY--; + } + repaint(); + } + + + public void paintComponent(Graphics g) + { + super.paintComponent(g); + g2d = (Graphics2D) g; + // use antialiasing + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + // smoother (and slower) image transformations (e.g. for resizing) + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2d = (Graphics2D) g; + drawImage(g2d, datapath, 0, 0, null); + executeAnimation(g); + counter = (counter + 1) % 100; + g2d.dispose(); + + } + + private void drawImage(Graphics2D g2d, BufferedImage im, int x, int y, Color c) + { + if (im == null) + { + g2d.setColor(c); + g2d.fillOval(x, y, 20, 20); + g2d.setColor(Color.black); + g2d.drawString(" ", x, y); + } + else + { + g2d.drawImage(im, x, y, this); + } + } + + //draw lines. + //method to draw the lines that run from left to right. + public void printTrackLtoR(Vertex v) + { + int size; + int[] track; + size = v.getEnd() - v.getInit(); + track = new int[size]; + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() + i; + } + if (v.isActive()) + { + v.setFirst_interaction(false); + for (int i = 0; i < size; i++) + { + if (track[i] <= v.getCurrent()) + { + g2d.setColor(v.getColor()); + g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); + } + } + if (v.getCurrent() == track[size - 1]) + { + v.setActive(false); + } + v.setCurrent(v.getCurrent() + 1); + } + else if (!v.isFirst_interaction()) + { + for (int i = 0; i < size; i++) + { + g2d.setColor(v.getColor()); + g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); + } + } + + } + + //method to draw the lines that run from right to left. + //public boolean printTrackRtoL(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, + // boolean active, boolean firstInteraction){ + public void printTrackRtoL(Vertex v) + { + int size; + int[] track; + size = v.getInit() - v.getEnd(); + track = new int[size]; + + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() - i; + } + + if (v.isActive()) + { + v.setFirst_interaction(false); + for (int i = 0; i < size; i++) + { + if (track[i] >= v.getCurrent()) + { + g2d.setColor(v.getColor()); + g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); + } + } + if (v.getCurrent() == track[size - 1]) + { + v.setActive(false); + } + + v.setCurrent(v.getCurrent() - 1); + } + else if (!v.isFirst_interaction()) + { + for (int i = 0; i < size; i++) + { + g2d.setColor(v.getColor()); + g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); + } + } + } + + //method to draw the lines that run from down to top. + // public boolean printTrackDtoU(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, + // boolean active, boolean firstInteraction){ + public void printTrackDtoU(Vertex v) + { + int size; + int[] track; + + if (v.getInit() > v.getEnd()) + { + size = v.getInit() - v.getEnd(); + track = new int[size]; + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() - i; + } + } + else + { + size = v.getEnd() - v.getInit(); + track = new int[size]; + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() + i; + } + } + + if (v.isActive()) + { + v.setFirst_interaction(false); + for (int i = 0; i < size; i++) + { + if (track[i] >= v.getCurrent()) + { + g2d.setColor(v.getColor()); + g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); + } + } + if (v.getCurrent() == track[size - 1]) + { + v.setActive(false); + } + v.setCurrent(v.getCurrent() - 1); + + } + else if (!v.isFirst_interaction()) + { + for (int i = 0; i < size; i++) + { + g2d.setColor(v.getColor()); + g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); + } + } + } + + //method to draw the lines that run from top to down. + // public boolean printTrackUtoD(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, + // boolean active, boolean firstInteraction){ + public void printTrackUtoD(Vertex v) + { + + int size; + int[] track; + size = v.getEnd() - v.getInit(); + track = new int[size]; + + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() + i; + } + + if (v.isActive()) + { + v.setFirst_interaction(false); + for (int i = 0; i < size; i++) + { + if (track[i] <= v.getCurrent()) + { + g2d.setColor(v.getColor()); + g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); + } + + } + if (v.getCurrent() == track[size - 1]) + { + v.setActive(false); + } + v.setCurrent(v.getCurrent() + 1); + } + else if (!v.isFirst_interaction()) + { + for (int i = 0; i < size; i++) + { + g2d.setColor(v.getColor()); + g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); + } + } + } + + public void printTextDtoU(Vertex v) + { + int size; + int[] track; + FontRenderContext frc = g2d.getFontRenderContext(); + + TextLayout actionInFunctionalBlock = new TextLayout(v.getName(), new Font("Verdana", Font.BOLD, 13), frc); + g2d.setColor(Color.RED); + + if (instructionCode.substring(0, 6).matches("101[0-1][0-1][0-1]") + && !instructionCode.substring(0, 6).matches("0001[0-1][0-1]") + && !instructionCode.substring(0, 6).matches("00001[0-1]")) + {//load instruction + actionInFunctionalBlock = new TextLayout(" ", new Font("Verdana", Font.BOLD, 13), frc); + } + if (v.getName().equals("ALUVALUE")) + { + if (instructionCode.startsWith("000000"))//R-type instruction + { + actionInFunctionalBlock = new TextLayout(functionEquivalenceTable.get(instructionCode.substring(26, 32)), new Font("Verdana", Font.BOLD, 13), frc); + } + else //other instructions + { + actionInFunctionalBlock = new TextLayout(opcodeEquivalenceTable.get(instructionCode.substring(0, 6)), new Font("Verdana", Font.BOLD, 13), frc); + } + } + + if (instructionCode.substring(0, 6).matches("0001[0-1][0-1]") && v.getName().equals("CP+4")) //branch code + { + actionInFunctionalBlock = new TextLayout("PC+OFFSET", new Font("Verdana", Font.BOLD, 13), frc); + } + + if (v.getName().equals("WRITING")) + { + if (!instructionCode.substring(0, 6).matches("100[0-1][0-1][0-1]")) + { + actionInFunctionalBlock = new TextLayout(" ", new Font("Verdana", Font.BOLD, 13), frc); + } + } + if (v.isActive()) + { + v.setFirst_interaction(false); + actionInFunctionalBlock.draw(g2d, v.getOppositeAxis(), v.getCurrent()); + if (v.getCurrent() == v.getEnd()) + { + v.setActive(false); + } + v.setCurrent(v.getCurrent() - 1); + } + + + } + + //convert binnary value to integer. + public String parseBinToInt(String code) + { + int value = 0; + + for (int i = code.length() - 1; i >= 0; i--) + { + if ("1".equals(code.substring(i, i + 1))) + { + value = value + (int) Math.pow(2, code.length() - i - 1); + } + } + + return Integer.toString(value); + } + + //set and execute the information about the current position of each line of information in the animation, + //verifies the previous status of the animation and increment the position of each line that interconnect the unit function. + private void executeAnimation(Graphics g) + { + g2d = (Graphics2D) g; + setUpInstructionInfo(g2d); + Vertex vert; + for (int i = 0; i < vertexTraversed.size(); i++) + { + vert = vertexTraversed.get(i); + if (vert.isMovingXaxis) + { + if (vert.getDirection() == Vertex.movingLeft) + { + printTrackLtoR(vert); + if (!vert.isActive()) + { + int j = vert.getTargetVertex().size(); + Vertex tempVertex; + for (int k = 0; k < j; k++) + { + tempVertex = outputGraph.get(vert.getNumIndex()).get(k); + Boolean hasThisVertex = false; + for (int m = 0; m < vertexTraversed.size(); m++) + { + if (tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) + { + hasThisVertex = true; + break; + } + } + if (!hasThisVertex) + { + outputGraph.get(vert.getNumIndex()).get(k).setActive(true); + vertexTraversed.add(outputGraph.get(vert.getNumIndex()).get(k)); + } + } + } + } + else + { + printTrackRtoL(vert); + if (!vert.isActive()) + { + int j = vert.getTargetVertex().size(); + Vertex tempVertex; + for (int k = 0; k < j; k++) + { + tempVertex = outputGraph.get(vert.getNumIndex()).get(k); + Boolean hasThisVertex = false; + for (int m = 0; m < vertexTraversed.size(); m++) + { + if (tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) + { + hasThisVertex = true; + break; + } + } + if (!hasThisVertex) + { + outputGraph.get(vert.getNumIndex()).get(k).setActive(true); + vertexTraversed.add(outputGraph.get(vert.getNumIndex()).get(k)); + } + } + } + } + } //end of condition of X axis + else + { + if (vert.getDirection() == Vertex.movingDownside) + { + if (vert.isText) + { + printTextDtoU(vert); + } + else + { + printTrackDtoU(vert); + } + + if (!vert.isActive()) + { + int j = vert.getTargetVertex().size(); + Vertex tempVertex; + for (int k = 0; k < j; k++) + { + tempVertex = outputGraph.get(vert.getNumIndex()).get(k); + Boolean hasThisVertex = false; + for (int m = 0; m < vertexTraversed.size(); m++) + { + if (tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) + { + hasThisVertex = true; + break; + } + } + if (!hasThisVertex) + { + outputGraph.get(vert.getNumIndex()).get(k).setActive(true); + vertexTraversed.add(outputGraph.get(vert.getNumIndex()).get(k)); + } + } + } + + } + else + { + + printTrackUtoD(vert); + if (!vert.isActive()) + { + int j = vert.getTargetVertex().size(); + Vertex tempVertex; + for (int k = 0; k < j; k++) + { + tempVertex = outputGraph.get(vert.getNumIndex()).get(k); + Boolean hasThisVertex = false; + for (int m = 0; m < vertexTraversed.size(); m++) + { + if (tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) + { + hasThisVertex = true; + break; + } + } + if (!hasThisVertex) + { + outputGraph.get(vert.getNumIndex()).get(k).setActive(true); + vertexTraversed.add(outputGraph.get(vert.getNumIndex()).get(k)); + } + } + } + } + } + } + } + + @Override + public void mouseClicked(MouseEvent e) + { + + PointerInfo a = MouseInfo.getPointerInfo(); + //limpar a imagem do painel e iniciar o detalhe da unidade funcional. + + + if (e.getPoint().getX() > 425 && e.getPoint().getX() < 520 && e.getPoint().getY() > 300 && e.getPoint().getY() < 425) + { + buildMainDisplayArea("register.png"); + FunctionUnitVisualization fu = new FunctionUnitVisualization(instructionBinary, register); + fu.run(); + } + + if (e.getPoint().getX() > 355 && e.getPoint().getX() < 415 && e.getPoint().getY() > 180 && e.getPoint().getY() < 280) + { + buildMainDisplayArea("control.png"); + FunctionUnitVisualization fu = new FunctionUnitVisualization(instructionBinary, control); + fu.run(); + } + + if (e.getPoint().getX() > 560 && e.getPoint().getX() < 620 && e.getPoint().getY() > 450 && e.getPoint().getY() < 520) + { + buildMainDisplayArea("ALUcontrol.png"); + FunctionUnitVisualization fu = new FunctionUnitVisualization(instructionBinary, aluControl); + fu.run(); + } + + } + + @Override + public void mouseEntered(MouseEvent e) + { + } + + @Override + public void mouseExited(MouseEvent e) + { + } + + @Override + public void mouseReleased(MouseEvent e) + { + } + } } diff --git a/src/main/java/mars/tools/ScavengerHunt.java b/src/main/java/mars/tools/ScavengerHunt.java index ec68022..8c187ef 100644 --- a/src/main/java/mars/tools/ScavengerHunt.java +++ b/src/main/java/mars/tools/ScavengerHunt.java @@ -1,40 +1,55 @@ - package mars.tools; - import mars.*; - import mars.mips.hardware.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.util.*; - import javax.swing.JOptionPane; - import mars.util.*; +package mars.tools; + +import mars.Globals; +import mars.mips.hardware.AccessNotice; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.MemoryAccessNotice; +import mars.util.Binary; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.Observable; +import java.util.Observer; +import java.util.Random; /** - * Demo of Mars tool capability. Ken Vollmar, 27 Oct 2006 KenVollmar@missouristate.edu - * This tool displays movements by a series of players in a game of ScavengerHunt. - * Players will read and write MIPS memory-mapped locations to move and regain energy. - * See accompanying documentation for memory-mapped addresses, rules of the game, etc. + * Demo of Mars tool capability. Ken Vollmar, 27 Oct 2006 KenVollmar@missouristate.edu This tool displays movements + * by a series of players in a game of ScavengerHunt. Players will read and write MIPS memory-mapped locations to move + * and regain energy. See accompanying documentation for memory-mapped addresses, rules of the game, etc. */ - public class ScavengerHunt implements Observer, MarsTool - { - private static final int GRAPHIC_WIDTH = 712; - private static final int GRAPHIC_HEIGHT = 652; - - private static final int NUM_PLAYERS = 22; // Number of players in the game, including "baseline" player - private static final int MAX_X_MOVEMENT = 2; // Max. movement in X direction - private static final int MAX_Y_MOVEMENT = 2; // Max. movement in X direction - private static final double MAX_MOVE_DISTANCE = 2.5; // Max. distance (Euclidean measure) - private static final int ENERGY_AWARD = 20; // Energy awarded for each task completion - private static final int ENERGY_PER_MOVE = 1; // Energy used in making each move (regardless of distance) - private static final int SIZE_OF_TASK = 20; // Number of elements in the task - - private static final int NUM_LOCATIONS = 7; // Number of locations to which ScavengerHunt players travel. +public class ScavengerHunt implements Observer, MarsTool +{ + private static final int GRAPHIC_WIDTH = 712; + + private static final int GRAPHIC_HEIGHT = 652; + + private static final int NUM_PLAYERS = 22; // Number of players in the game, including "baseline" player + + private static final int MAX_X_MOVEMENT = 2; // Max. movement in X direction + + private static final int MAX_Y_MOVEMENT = 2; // Max. movement in X direction + + private static final double MAX_MOVE_DISTANCE = 2.5; // Max. distance (Euclidean measure) + + private static final int ENERGY_AWARD = 20; // Energy awarded for each task completion + + private static final int ENERGY_PER_MOVE = 1; // Energy used in making each move (regardless of distance) + + private static final int SIZE_OF_TASK = 20; // Number of elements in the task + + private static final int NUM_LOCATIONS = 7; // Number of locations to which ScavengerHunt players travel. // The first (n-1) locations are "random" locations, the last is START_AND_END_LOCATION - - private static final int START_AND_END_LOCATION = 255; // Start and end location of the ScavengerHunt - private static final int ADMINISTRATOR_ID = 999; // Special ID for administrator - - + + private static final int START_AND_END_LOCATION = 255; // Start and end location of the ScavengerHunt + + private static final int ADMINISTRATOR_ID = 999; // Special ID for administrator + + // MIPS addresses of administrative memory space. // The administrator MIPS program writes a value to the Authentication field prior to writing a new // value to the PlayerID field. The value of the Authentication field is not itself verified until @@ -44,423 +59,111 @@ // field. If the value of the Authentication field is correct, the value of the PlayerID field is used // for memory bounds checking. A new value of the Authentication field is expected at each change of the // PlayerID field (one-time pad system). - private static final int ADDR_AUTHENTICATION = 0xffffe000; // MIPS byte address of Authentication field - private static final int ADDR_PLAYER_ID = 0xffffe004; // MIPS byte address of PlayerID field - private static final int ADDR_GAME_ON = 0xffffe008; // MIPS byte address of signal that administration has initialized data - private static final int ADDR_NUM_TURNS = 0xffffe00c; // MIPS byte address of number of turns remaining in the game - - - + private static final int ADDR_AUTHENTICATION = 0xffffe000; // MIPS byte address of Authentication field + + private static final int ADDR_PLAYER_ID = 0xffffe004; // MIPS byte address of PlayerID field + + private static final int ADDR_GAME_ON = 0xffffe008; // MIPS byte address of signal that administration has initialized data + + private static final int ADDR_NUM_TURNS = 0xffffe00c; // MIPS byte address of number of turns remaining in the game + + // MIPS addresses of various data in each player's memory space. // Each player's assigned memory is the MEM_PER_PLAYER bytes which begin at // location ADDR_BASE + (ID * MEM_PER_PLAYER) - private static final int ADDR_BASE = 0xffff8000; // MIPS byte address of memory space for first player - private static final int MEM_PER_PLAYER = 0x400; // MIPS bytes of memory space given to each player - private static final int OFFSET_WHERE_AM_I_X = 0x0; // MIPS byte offset to this field - private static final int OFFSET_WHERE_AM_I_Y = 0x4; // MIPS byte offset to this field - private static final int OFFSET_MOVE_TO_X = 0x8; // MIPS byte offset to this field - private static final int OFFSET_MOVE_TO_Y = 0xc; // MIPS byte offset to this field - private static final int OFFSET_MOVE_READY = 0x10; // MIPS byte offset to this field - private static final int OFFSET_ENERGY = 0x14; // MIPS byte offset to this field - private static final int OFFSET_NUMBER_LOCATIONS = 0x18; // MIPS byte offset to this field - private static final int OFFSET_PLAYER_COLOR = 0x1c; // MIPS byte offset to this field - private static final int OFFSET_SIZE_OF_TASK = 0x20; // MIPS byte offset to this field - private static final int OFFSET_LOC_ARRAY = 0x24; // MIPS byte offset to this field - private static final int OFFSET_TASK_COMPLETE = 0x124; // MIPS byte offset to this field - private static final int OFFSET_TASK_ARRAY = 0x128; // MIPS byte offset to this field + private static final int ADDR_BASE = 0xffff8000; // MIPS byte address of memory space for first player + + private static final int MEM_PER_PLAYER = 0x400; // MIPS bytes of memory space given to each player + + private static final int OFFSET_WHERE_AM_I_X = 0x0; // MIPS byte offset to this field + + private static final int OFFSET_WHERE_AM_I_Y = 0x4; // MIPS byte offset to this field + + private static final int OFFSET_MOVE_TO_X = 0x8; // MIPS byte offset to this field + + private static final int OFFSET_MOVE_TO_Y = 0xc; // MIPS byte offset to this field + + private static final int OFFSET_MOVE_READY = 0x10; // MIPS byte offset to this field + + private static final int OFFSET_ENERGY = 0x14; // MIPS byte offset to this field + + private static final int OFFSET_NUMBER_LOCATIONS = 0x18; // MIPS byte offset to this field + + private static final int OFFSET_PLAYER_COLOR = 0x1c; // MIPS byte offset to this field + + private static final int OFFSET_SIZE_OF_TASK = 0x20; // MIPS byte offset to this field + + private static final int OFFSET_LOC_ARRAY = 0x24; // MIPS byte offset to this field + + private static final int OFFSET_TASK_COMPLETE = 0x124; // MIPS byte offset to this field + + private static final int OFFSET_TASK_ARRAY = 0x128; // MIPS byte offset to this field // Other MIPS memory locations are available to the player's use. - - private ScavengerHuntDisplay graphicArea; - private int authenticationValue = 0; - private boolean GameOn = false; // MIPS programs readiness - private static int SetWordCounter = 0; - private static int accessCounter = 0; - private static int playerID = ADMINISTRATOR_ID; // Range 0...(NUM_PLAYERS-1), plus ADMINISTRATOR_ID - private boolean KENVDEBUG = false; - - - // Used to define (X,Y) coordinate of a location to which ScavengerHunt players - // will travel. - private class Location - { - public int X; - public int Y; - } - - // private inner class to provide the data on each player needed for display - private class PlayerData - { - int whereAmIX = START_AND_END_LOCATION; // Read only. Memory Address: Base - int whereAmIY = START_AND_END_LOCATION; // Read only. Memory Address: Base + 0x4 - //int moveToX; // Memory Address: Base + 0x8 - //int moveToY; // Memory Address: Base + 0xc - //int goalX; // Read only. Memory Address: Base + 0x10 - //int goalY; // Read only. Memory Address: Base + 0x14 - int energy = 20; // Read only. Memory Address: Base + 0x18 - int color = 0; // Memory Address: Base + 0x1c - long finishTime; - //int locID; // ID of the location to which ScavengerHunt players are headed. Not used by player. - boolean hasVisitedLoc[] = new boolean[NUM_LOCATIONS]; // boolean: player has visited each location - boolean finis = false; - - // Class PlayerData has no constructor - - public void setWhereAmI(int gX, int gY) { whereAmIX = gX; whereAmIY = gY; } - //public void setGoal(int gX, int gY) { goalX = gX; goalY = gY; } - public void setEnergy(int e) { energy = e; } - public void setColor(int c) { color = c; } - public int getWhereAmIX() { - return whereAmIX; } - public int getWhereAmIY() { - return whereAmIY; } - public int getColor() { - return color; } - public boolean hasVisited(int i) { - return hasVisitedLoc[i]; } - public void setVisited(int i) { hasVisitedLoc[i] = true; } - public void setFinished() { finis = true; } - public boolean isFinished() { - return finis; } - public long getFinishTime() { - return finishTime; } - public long getFinishMin() { - return (finishTime / 60000); } // Minutes portion of finishTime - public long getFinishSec() { - return (finishTime % 60000) / 1000; // Seconds portion of finishTime - } // Seconds portion of finishTime - public long getFinishMillisec() { - return (finishTime % 1000); } // Millisec portion of finishTime - public void setFinishTime(long t) { finishTime = t; } - //public int getGoalX() { return goalX; } - //public int getGoalY() { return goalY; } - //public int getMoveToX() { return moveToX; } - //public int getMoveToY() { return moveToY; } - public int getEnergy() { - return energy; } - //public int getLocationID() { return locID; } - } // end class PlayerData - - private static PlayerData[] pd = new PlayerData[NUM_PLAYERS]; - private static Location[] loc = new Location[NUM_LOCATIONS]; - private Random randomStream; - private long startTime; - - - // private inner class - private class ScavengerHuntRunnable implements Runnable - { - JPanel panel; - public ScavengerHuntRunnable() // constructor - { - // final JFrame frame = new JFrame("ScavengerHunt"); - // Recommended by Pete Sanderson, 2 Nov. 2006, so that the Tool window and - // MARS window can be on the screen at the same time. - final JDialog frame = new JDialog(Globals.getGui(),"ScavengerHunt"); - - // System.out.println("ScavengerHuntRunnable.constructor: starting...."); - - panel = new JPanel(new BorderLayout()); - graphicArea = new ScavengerHuntDisplay(GRAPHIC_WIDTH, GRAPHIC_HEIGHT); - JPanel buttonPanel = new JPanel(); - JButton resetButton = new JButton("Reset"); - resetButton.addActionListener( - new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - graphicArea.clear(); - - // TBD ------- TBD - // Reset actions here - initializeScavengerData(); - //JOptionPane.showMessageDialog(null, "Reset needs to be implemented!" ); - - } - - }); - buttonPanel.add(resetButton); - - - panel.add(graphicArea, BorderLayout.CENTER); - panel.add(buttonPanel, BorderLayout.SOUTH); - - - // Snippet by Pete Sanderson, 2 Nov. 2006, to be a window-closing sequence - frame.addWindowListener( - new WindowAdapter() { - public void windowClosing(WindowEvent e) { - frame.setVisible(false); - frame.dispose(); - } - }); - - frame.getContentPane().add(panel); - frame.setLocationRelativeTo(null); - frame.pack(); - frame.setVisible(true); - frame.setTitle(" This is the ScavengerHunt"); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // TBD --- This should close only the Tool, not the entire MARS - frame.setPreferredSize(new Dimension(GRAPHIC_WIDTH, GRAPHIC_HEIGHT)); // TBD SIZE - frame.setVisible(true); // show(); - - } // end ScavengerHuntRunnable() constructor - - public void run() - { - - double tempAngle; - - // infinite loop: play the Scavenger Hunt game - do - { - - // Pause to slow down the redisplay of the game. This is separate from - // the execution speed of the MIPS program, so the display may lag behind - // the state of the MIPS program. - try - { - // System.out.println(" Hello from the ScavengerHuntRunnable runner, sleeping here ..."); - // System.out.print("."); - Thread.sleep(100); // millisec - } - catch (InterruptedException exception) - {// no action - } - - panel.repaint(); // show new ScavengerHunt position - } while (true); - - } // end run method of ScavengerHuntRunnable class - - } // end ScavengerHuntRunnable class - - /* ------------------------------------------------------------------------- */ - /** ScavengerHuntDisplay does not have access to the same MIPS Memory class object used by - * the ScavengerHunt class object. Need read-only access to a similar data structure maintained - * within ScavengerHunt. - * - */ - private class ScavengerHuntDisplay extends JPanel - { - private int width; - private int height; - private boolean clearTheDisplay = true; - - - public ScavengerHuntDisplay(int tw, int th) - { - // System.out.println("ScavengerHuntDisplay.constructor: starting...."); - width = tw; - height = th; - - } - - public void redraw() - { - repaint(); - } - - public void clear() - { - // clear the graphic display - clearTheDisplay = true; - //System.out.println("ScavengerHuntDisplay.clear: called to clear the display"); - repaint(); - } - - /** paintComponent does not have access to the same MIPS Memory class object used by - * the ScavengerHunt class object. Need read-only access to a similar data structure maintained - * within ScavengerHunt. - * - */ - public void paintComponent(Graphics g) - { - long tempN; - int xCoord; - int yCoord; - - // System.out.println("ScavengerHuntDisplay.paintComponent: I'm painting! n is " + n); - - - // Recover Graphics2D - Graphics2D g2 = (Graphics2D) g; - - if (! GameOn) // Make sure game is ready before continuing - { - g2.setColor(Color.lightGray); - g2.fillRect(0, 0, width - 1, height - 1); // Clear all previous drawn information - g2.setColor(Color.black); - g2.drawString(" ScavengerHunt not yet initialized by MIPS administrator program.", - 100, 200); - return; - } - - // Clear all previous drawn information - g2.setColor(Color.lightGray); - g2.fillRect(0, 0, width - 1, height - 1); - - - // Draw the locations to which the players will be moving - // All players have the same location data. - for (int i = 0; i < NUM_LOCATIONS; i++) - { - xCoord = loc[i].X; // toolReadPlayerData(0, OFFSET_LOC_ARRAY + (i*8) + 0); - yCoord = loc[i].Y; // toolReadPlayerData(0, OFFSET_LOC_ARRAY + (i*8) + 4); - g2.setColor(Color.blue); - g2.fillRect( xCoord, yCoord, 20, 20); // coord is upper left corner of oval - g2.setColor(Color.white); - g2.drawString(" " + i, xCoord + 4, yCoord + 15); // coord is lower left corner of string text box - - /* - System.out.println("ScavengerHuntDisplay.paintComponent: drew loc " + i + " at (" + - xCoord + - ", " + - yCoord + - ")" ); - */ - } - - //System.out.println("ScavengerHuntDisplay.paintComponent: special exit!"); - //System.exit(0); - - - // Draw scoreboard - g2.setColor(Color.black); - g2.drawString("Player", width - 160, 30); - g2.drawString("Locations", width - 110, 30); - g2.drawString("Energy", width - 50, 30); - g2.drawLine( width - 160, 35, width - 10, 35); // line under column headings - g2.drawLine( width - 120, 35, width - 120, 35 + (NUM_PLAYERS * 15)); // vertical line for location-visited marks - g2.drawLine( width - 50, 35, width - 50, 35 + (NUM_PLAYERS * 15)); // vertical line for location-visited marks - for (int i = 0; i < NUM_PLAYERS; i++) - { - // Draw player's symbol - g2.setColor( new Color(pd[i].getColor()) ); - // g2.setColor(Color.red); // TBD hardcoded - - - xCoord = pd[i].getWhereAmIX(); - yCoord = pd[i].getWhereAmIY(); - - // ystem.out.println("paintComponent loop " + i + ": Loc coord is (" + xCoord + ", " + yCoord); - - // Draw player symbol and label it with player ID number. - // Hardcoded size of location graphic and label. - g2.drawOval( xCoord, yCoord, 20, 20); // coord is upper left corner of oval - g2.drawString(" " + i, xCoord + 4, yCoord + 15); // coord is lower left corner of string text box - - // Draw player's info on scoreboard - g2.setColor(Color.black); - g2.drawString(" " + i, width - 150, 50 + (i*15)); // Player's ID on scoreboard - g2.drawString(" " + pd[i].getEnergy(), width - 40, 50 + (i*15)); // Player's energy on scoreboard - - // Display player's progress or finishing time, whichever is applicable - if (pd[i].isFinished()) - { - // Display finishing time - // This doesn't display leading zeroes to align time components (e.g. 27 millisec ought to print as 027) - g2.drawString (pd[i].getFinishMin() + ":" + // Minutes - pd[i].getFinishSec() + ":" + // Seconds - pd[i].getFinishMillisec() , // Milliseconds - width - 115, 50 + (i*15)); - // System.out.println("Time is " + pd[i].getFinishTime()); - //g2.drawString (" " + pd[i].getFinishTime(), - // width - 255, 50 + (i*15)); - } - else // player either has not finished or is just now finishing - { - int visCount = 0; - for (int j = 0; j < NUM_LOCATIONS; j++) - { - if (pd[i].hasVisited(j)) // count number of locations that player has visited - { - visCount++; - } - } - if (visCount == NUM_LOCATIONS) // player has visited every location -- finished! - { - pd[i].setFinished(); - pd[i].setFinishTime( System.currentTimeMillis() - startTime ); - - } - else // player has not yet visited every location - { - // Display locations that the player has actually visited - for (int j = 0; j < NUM_LOCATIONS; j++) - { - if (pd[i].hasVisited(j)) // Player has visited this location - { - g2.fillRect ((width -120)+(j*10), 42 + (i*15), 10, 8); - } - } - } - } // end player had not previously finished - - } // end display score/results for each player - - // System.out.println("paintComponent: Player " + 0 + " is at (" + pd[0].getWhereAmIX() + ", " + pd[0].getWhereAmIY() + ")" ); - - - - - /* - g2.setColor(Color.blue); - g2.setFont(new Font(g2.getFont().getName(), g2.getFont().getStyle(), 20) ); // same font and style in larger size - g2.drawOval( width/2 - 30, // TBD Hardcoded oval size - height/2 - 30, - 60, - 60); - g2.drawString(" " + n, width/2, height/2); - */ - - - } - - } // end private inner class ScavengerHuntDisplay - /* ------------------------------------------------------------------------- */ - - - + + private static int SetWordCounter = 0; + + private static final int accessCounter = 0; + + private static int playerID = ADMINISTRATOR_ID; // Range 0...(NUM_PLAYERS-1), plus ADMINISTRATOR_ID + + private static final PlayerData[] pd = new PlayerData[NUM_PLAYERS]; + + private static final Location[] loc = new Location[NUM_LOCATIONS]; + + private ScavengerHuntDisplay graphicArea; + + private int authenticationValue = 0; + + private boolean GameOn = false; // MIPS programs readiness + + private final boolean KENVDEBUG = false; + + private Random randomStream; + + private long startTime; + // A constructor for ScavengerHunt would be called immediately on MARS startup, perhaps // before all MARS classes have been created. Do not include any action in a constructor // but rather postpone any action until the action() class, // at which time all MARS classes will be ready for use. - public ScavengerHunt() - { + public ScavengerHunt() + { // System.out.println("ScavengerHunt.constructor: starting...."); - } - - public String getName() - { - return "ScavengerHunt"; - } - + } + + public String getName() + { + return "ScavengerHunt"; + } + /* * This will set up the ScavengerHunt's GUI. Invoked when ScavengerHunt menu item selected. */ - public void action() - { - - - - ScavengerHuntRunnable shr = new ScavengerHuntRunnable(); - Thread t1 = new Thread(shr); - t1.start(); - - + public void action() + { + + + ScavengerHuntRunnable shr = new ScavengerHuntRunnable(); + Thread t1 = new Thread(shr); + t1.start(); + + // Register as observer for a particular MIPS data range. Other ranges // are not used by this Tool. - try { + try + { Globals.memory.addObserver(this, 0xffff8000, 0xfffffff0); // must be on word boundaries - } - catch (AddressErrorException e) - { - System.out.println("\n\nScavengerHunt.action: Globals.memory.addObserver caused AddressErrorException.\n\n"); - System.exit(0); - } - - } // end ScavengerHunt.action() - + } + catch (AddressErrorException e) + { + System.out.println("\n\nScavengerHunt.action: Globals.memory.addObserver caused AddressErrorException.\n\n"); + System.exit(0); + } + + } // end ScavengerHunt.action() + + /* ------------------------------------------------------------------------- */ + /* * This method observes MIPS memory for directives to modify ScavengerHunt activity (that is, * MIPS program write to MMIO) and updates instance variables to reflect that directive. @@ -468,36 +171,40 @@ * must not write to those memory locations in order to prevent an infinite cycle of events. * This method observes certain locations and then may (and does) write to OTHER locations. */ - public void update(Observable o, Object arg) - { - MemoryAccessNotice notice; - int address; - int data; - boolean isWrite; - boolean isRead; - int energyLevel; - + public void update(Observable o, Object arg) + { + MemoryAccessNotice notice; + int address; + int data; + boolean isWrite; + boolean isRead; + int energyLevel; + // Here we are only interested in MemoryAccessNotice. For anything else, just return. - if ( ! (arg instanceof MemoryAccessNotice)) + if (!(arg instanceof MemoryAccessNotice)) + { return; - + } + // Get pertinent information about this MemoryAccessNotice. - notice = (MemoryAccessNotice) arg; - address = notice.getAddress(); - data = notice.getValue(); - isWrite = (notice.getAccessType() == AccessNotice.WRITE); - isRead = ! isWrite; - - + notice = (MemoryAccessNotice) arg; + address = notice.getAddress(); + data = notice.getValue(); + isWrite = (notice.getAccessType() == AccessNotice.WRITE); + isRead = !isWrite; + + // If we are only interested in MIPS memory WRITES, then just return on READS. // That's a matter of policy: perhaps players should be prohibited from // reading each other's memory spaces. - if ( ! isWrite) + if (!isWrite) + { return; - - //System.out.println("ScavengerHunt.update: observed write access by player " + playerID + " on Mem[ " + - // Binary.intToHexString(address) + " ]"); - + } + + //System.out.println("ScavengerHunt.update: observed write access by player " + playerID + " on Mem[ " + + // Binary.intToHexString(address) + " ]"); + // TBD TBD DEBUGGING SPECIAL /* accessCounter++; @@ -507,431 +214,428 @@ System.exit(0); } */ - // TBD TBD DEBUGGING SPECIAL - - - - + // TBD TBD DEBUGGING SPECIAL + + // Take the appropriate action, depending on data written and priority of user. - if (isWrite && playerID == ADMINISTRATOR_ID && address == ADDR_GAME_ON) - { - - // ADMINISTRATOR_ID can write to any location, because it's trusted software - //System.out.println( "ScavengerHunt.update(): Administrator wrote to Mem[ " + - // Binary.intToHexString(address) + " ] == " + Binary.intToHexString(data) ); - - // No need to authenticate since administrator runs first, then - // this location has no effect thereafter. + if (isWrite && playerID == ADMINISTRATOR_ID && address == ADDR_GAME_ON) + { + + // ADMINISTRATOR_ID can write to any location, because it's trusted software + //System.out.println( "ScavengerHunt.update(): Administrator wrote to Mem[ " + + // Binary.intToHexString(address) + " ] == " + Binary.intToHexString(data) ); + + // No need to authenticate since administrator runs first, then + // this location has no effect thereafter. GameOn = true; - // System.out.println( "ScavengerHunt.update(): Administrator wrote GAME_ON!" ); - + // System.out.println( "ScavengerHunt.update(): Administrator wrote GAME_ON!" ); + initializeScavengerData(); - } - else if (isWrite && address == ADDR_AUTHENTICATION) - { - // Anyone is allowed to write to the authentication location -- but if that value is not - // correct (authentic) then action can be taken. - // NO ACTION HERE - } - else if (isWrite && address == ADDR_NUM_TURNS) - { - // Anyone is allowed to write to the "number of turns" location - // NO ACTION HERE - } - else if (isWrite && address == ADDR_PLAYER_ID) // if the data written will change the PlayerID, authenticate the write - { - // 2006 Oct 31 dummy validation scheme, suitable for distribution - // to students for development: Initial authentication value is zero. - // Each successive authentication value is one greater - // than the preceding value, modulo 0xffffffff. + } + else if (isWrite && address == ADDR_AUTHENTICATION) + { + // Anyone is allowed to write to the authentication location -- but if that value is not + // correct (authentic) then action can be taken. + // NO ACTION HERE + } + else if (isWrite && address == ADDR_NUM_TURNS) + { + // Anyone is allowed to write to the "number of turns" location + // NO ACTION HERE + } + else if (isWrite && address == ADDR_PLAYER_ID) // if the data written will change the PlayerID, authenticate the write + { + // 2006 Oct 31 dummy validation scheme, suitable for distribution + // to students for development: Initial authentication value is zero. + // Each successive authentication value is one greater + // than the preceding value, modulo 0xffffffff. authenticationValue += 1; // "server's" updated version of the authenticationValue if (toolGetWord(ADDR_AUTHENTICATION) == authenticationValue) // Compare to "client's" version of the authenticationValue { - playerID = toolGetWord(ADDR_PLAYER_ID); // Use the new player ID - //System.out.println( "ScavengerHunt.update(): New playerID of " + playerID); + playerID = toolGetWord(ADDR_PLAYER_ID); // Use the new player ID + //System.out.println( "ScavengerHunt.update(): New playerID of " + playerID); } else { - System.out.println( "ScavengerHunt.update(): Invalid write of player ID! \nPlayer " + - playerID + " tried to write. Expected: " + - Binary.intToHexString(authenticationValue) + - ", got: " + Binary.intToHexString(toolGetWord(ADDR_AUTHENTICATION)) + "\n" ); + System.out.println("ScavengerHunt.update(): Invalid write of player ID! \nPlayer " + + playerID + " tried to write. Expected: " + + Binary.intToHexString(authenticationValue) + + ", got: " + Binary.intToHexString(toolGetWord(ADDR_AUTHENTICATION)) + "\n"); } - } - - else if (isWrite && - address == (ADDR_BASE + (playerID * MEM_PER_PLAYER) + OFFSET_MOVE_READY) && - data != 0) // Player wrote data to his/her assigned MoveReady location - { + } + + else if (isWrite && + address == (ADDR_BASE + (playerID * MEM_PER_PLAYER) + OFFSET_MOVE_READY) && + data != 0) // Player wrote data to his/her assigned MoveReady location + { /* System.out.println(" ******** ScavengerHunt.update: Player " + playerID + " requests move to (" + toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) + ", " + toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) + ")" ); */ - + energyLevel = toolReadPlayerData(playerID, OFFSET_ENERGY); // find if player has energy if (energyLevel <= 0) // No energy. Player not allowed to move { - //JOptionPane.showMessageDialog(null, "Player " + playerID + " can't move -- no energy.\n" + - // "(This msg. in ScavengerHunt.update()" ); - // System.out.println("Player " + playerID + " can't move -- no energy."); - return; + //JOptionPane.showMessageDialog(null, "Player " + playerID + " can't move -- no energy.\n" + + // "(This msg. in ScavengerHunt.update()" ); + // System.out.println("Player " + playerID + " can't move -- no energy."); + return; } - + if (toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) < 0 || - toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) > GRAPHIC_WIDTH || - toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) < 0 || - toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) > GRAPHIC_HEIGHT - ) // Out of bounds. Player not allowed to move + toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) > GRAPHIC_WIDTH || + toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) < 0 || + toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) > GRAPHIC_HEIGHT + ) // Out of bounds. Player not allowed to move { - //JOptionPane.showMessageDialog(null, "Player " + playerID + " can't move -- out of bounds.\n" + - // "(This msg. in ScavengerHunt.update()" ); - System.out.println("Player " + playerID + " can't move -- out of bounds."); - return; + //JOptionPane.showMessageDialog(null, "Player " + playerID + " can't move -- out of bounds.\n" + + // "(This msg. in ScavengerHunt.update()" ); + System.out.println("Player " + playerID + " can't move -- out of bounds."); + return; } - - - // Verify movement is allowed (does not exceed maximum movement) + + + // Verify movement is allowed (does not exceed maximum movement) if (Math.sqrt( - Math.pow( toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X) - - toolReadPlayerData(playerID, OFFSET_MOVE_TO_X), 2.0) - + - Math.pow( toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) - - toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y), 2.0) ) - <= MAX_MOVE_DISTANCE) + Math.pow(toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X) - + toolReadPlayerData(playerID, OFFSET_MOVE_TO_X), 2.0) + + + Math.pow(toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) - + toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y), 2.0)) + <= MAX_MOVE_DISTANCE) { - - // Write the new position of the player - toolWritePlayerData(playerID, OFFSET_WHERE_AM_I_X, - toolReadPlayerData(playerID, OFFSET_MOVE_TO_X)); - toolWritePlayerData(playerID, OFFSET_WHERE_AM_I_Y, - toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y)); - pd[playerID].setWhereAmI( toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X), - toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) ); - - // Write the new (reduced) energy of the player - // Policy: Constant ENERGY_PER_MOVE for any move regardless of length. - toolWritePlayerData(playerID, OFFSET_ENERGY, - toolReadPlayerData(playerID, OFFSET_ENERGY) - ENERGY_PER_MOVE); - pd[playerID].setEnergy( toolReadPlayerData(playerID, OFFSET_ENERGY) ); - - - // TBD FUTURE --- need to keep track of locations that the player has actually got to - // -- be able to tell that the player has reached a certain location - // -- be able to tell that the player has reached every location - for (int i = 0; i < NUM_LOCATIONS; i++) - { - if (toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X) == loc[i].X && - toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) == loc[i].Y ) - { - pd[playerID].setVisited(i); // Player has visited this location - } - } - - - // Write 0 to "move ready" location, signifying that the move request was processed - // Here we must write to the same location that we're now reading from, and we - // can't cause an infinite loop. Temporarily switch player ID to that of the administrator, - // and restore ID after the write. With the playerID set to administrator, the event - // caused by the write will not go through this same logic. - int tempPlayerID = playerID; - playerID = ADMINISTRATOR_ID; - toolWritePlayerData(tempPlayerID, OFFSET_MOVE_READY, 0); - playerID = tempPlayerID; - + + // Write the new position of the player + toolWritePlayerData(playerID, OFFSET_WHERE_AM_I_X, + toolReadPlayerData(playerID, OFFSET_MOVE_TO_X)); + toolWritePlayerData(playerID, OFFSET_WHERE_AM_I_Y, + toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y)); + pd[playerID].setWhereAmI(toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X), + toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y)); + + // Write the new (reduced) energy of the player + // Policy: Constant ENERGY_PER_MOVE for any move regardless of length. + toolWritePlayerData(playerID, OFFSET_ENERGY, + toolReadPlayerData(playerID, OFFSET_ENERGY) - ENERGY_PER_MOVE); + pd[playerID].setEnergy(toolReadPlayerData(playerID, OFFSET_ENERGY)); + + + // TBD FUTURE --- need to keep track of locations that the player has actually got to + // -- be able to tell that the player has reached a certain location + // -- be able to tell that the player has reached every location + for (int i = 0; i < NUM_LOCATIONS; i++) + { + if (toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X) == loc[i].X && + toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) == loc[i].Y) + { + pd[playerID].setVisited(i); // Player has visited this location + } + } + + + // Write 0 to "move ready" location, signifying that the move request was processed + // Here we must write to the same location that we're now reading from, and we + // can't cause an infinite loop. Temporarily switch player ID to that of the administrator, + // and restore ID after the write. With the playerID set to administrator, the event + // caused by the write will not go through this same logic. + int tempPlayerID = playerID; + playerID = ADMINISTRATOR_ID; + toolWritePlayerData(tempPlayerID, OFFSET_MOVE_READY, 0); + playerID = tempPlayerID; + } else { - System.out.println( "Player " + playerID + " can't move -- exceeded max. movement." ); - System.out.println(" Player is at (" + - toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X) + ", " + - toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) + "), wants to go to (" + - toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) + "," + - toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) + ")" ); - - return; + System.out.println("Player " + playerID + " can't move -- exceeded max. movement."); + System.out.println(" Player is at (" + + toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_X) + ", " + + toolReadPlayerData(playerID, OFFSET_WHERE_AM_I_Y) + "), wants to go to (" + + toolReadPlayerData(playerID, OFFSET_MOVE_TO_X) + "," + + toolReadPlayerData(playerID, OFFSET_MOVE_TO_Y) + ")"); + } - - } // end if Player wrote nonzero data to his/her assigned MoveReady location - - - else if (isWrite && - address == (ADDR_BASE + (playerID * MEM_PER_PLAYER) + OFFSET_TASK_COMPLETE) && - data != 0) // Player wrote data to his/her assigned TaskComplete location - { - - // System.out.println(" ******** ScavengerHunt.update: Player " + playerID + " requests more energy (task complete)" ); - - // Player indicates he/she has completed a task. Check to see if task is completed correctly. - // Task for this assignment: Numbers are sorted in ascending order. + + } // end if Player wrote nonzero data to his/her assigned MoveReady location + + + else if (isWrite && + address == (ADDR_BASE + (playerID * MEM_PER_PLAYER) + OFFSET_TASK_COMPLETE) && + data != 0) // Player wrote data to his/her assigned TaskComplete location + { + + // System.out.println(" ******** ScavengerHunt.update: Player " + playerID + " requests more energy (task complete)" ); + + // Player indicates he/she has completed a task. Check to see if task is completed correctly. + // Task for this assignment: Numbers are sorted in ascending order. int prevData, currentData; prevData = toolReadPlayerData(playerID, OFFSET_TASK_ARRAY); for (int i = 1; i < SIZE_OF_TASK; i++) { - currentData = toolReadPlayerData(playerID, OFFSET_TASK_ARRAY + (i*4)); - if (prevData > currentData) - { - // Task failure! Task not completed correctly! - System.out.println("Whoops! Player has NOT completed task correctly"); - return; - } - prevData = currentData; // update for next iteration - + currentData = toolReadPlayerData(playerID, OFFSET_TASK_ARRAY + (i * 4)); + if (prevData > currentData) + { + // Task failure! Task not completed correctly! + System.out.println("Whoops! Player has NOT completed task correctly"); + return; + } + prevData = currentData; // update for next iteration + } - - // If program flow has reached this point, the task is completed correctly. - // Award energy, reset TaskComplete, and set new task values for future use. + + // If program flow has reached this point, the task is completed correctly. + // Award energy, reset TaskComplete, and set new task values for future use. toolWritePlayerData(playerID, OFFSET_ENERGY, ENERGY_AWARD); toolWritePlayerData(playerID, OFFSET_TASK_COMPLETE, 0); for (int j = 0; j < SIZE_OF_TASK; j++) // Initialize the task data for this player { - toolWritePlayerData(playerID, OFFSET_TASK_ARRAY + (j * 4), - (int)(randomStream.nextDouble() * Integer.MAX_VALUE) ); // Set a random number for the task (sort them) + toolWritePlayerData(playerID, OFFSET_TASK_ARRAY + (j * 4), + (int) (randomStream.nextDouble() * Integer.MAX_VALUE)); // Set a random number for the task (sort them) } - // System.out.println("Player has completed task correctly and been awarded energy"); - pd[playerID].setEnergy( ENERGY_AWARD ); - - } // end if Player wrote nonzero data to his/her assigned TaskComplete location - - - else if (isWrite && - address == (ADDR_BASE + (playerID * MEM_PER_PLAYER) + OFFSET_PLAYER_COLOR)) - // Player wrote data to his/her assigned PlayerColor location - { - // PPlayer indicates he/she has changed the color of display - pd[playerID].setColor( toolReadPlayerData(playerID, OFFSET_PLAYER_COLOR) ); - } - - - // TBD TBD TBD - // TBD TBD TBD - // TBD TBD TBD - // Yet to be implemented: Enforce only one write of MoveRequest per player per turn - - - else if (isWrite && - address >= (ADDR_BASE + (playerID * MEM_PER_PLAYER)) && - address < (ADDR_BASE + ((playerID + 1) * MEM_PER_PLAYER)) ) - // Player wrote data elsewhere within his/her assigned location - { - // Player can write to any location within his/her assigned location - //System.out.println( "ScavengerHunt.update(): Player " + playerID + " wrote to valid location"); - } - else if (isWrite && playerID == ADMINISTRATOR_ID) - { - // ADMINISTRATOR_ID can write to any location, because it's trusted software - //System.out.println( "ScavengerHunt.update(): Administrator wrote to Mem[ " + - // Binary.intToHexString(address) + " ] == " + Binary.intToHexString(data) ); - } - else if (isWrite) - { - // This player is writing outside his/her assigned memory location + // System.out.println("Player has completed task correctly and been awarded energy"); + pd[playerID].setEnergy(ENERGY_AWARD); + + } // end if Player wrote nonzero data to his/her assigned TaskComplete location + + + else if (isWrite && + address == (ADDR_BASE + (playerID * MEM_PER_PLAYER) + OFFSET_PLAYER_COLOR)) + // Player wrote data to his/her assigned PlayerColor location + { + // PPlayer indicates he/she has changed the color of display + pd[playerID].setColor(toolReadPlayerData(playerID, OFFSET_PLAYER_COLOR)); + } + + + // TBD TBD TBD + // TBD TBD TBD + // TBD TBD TBD + // Yet to be implemented: Enforce only one write of MoveRequest per player per turn + + + else if (isWrite && + address >= (ADDR_BASE + (playerID * MEM_PER_PLAYER)) && + address < (ADDR_BASE + ((playerID + 1) * MEM_PER_PLAYER))) + // Player wrote data elsewhere within his/her assigned location + { + // Player can write to any location within his/her assigned location + //System.out.println( "ScavengerHunt.update(): Player " + playerID + " wrote to valid location"); + } + else if (isWrite && playerID == ADMINISTRATOR_ID) + { + // ADMINISTRATOR_ID can write to any location, because it's trusted software + //System.out.println( "ScavengerHunt.update(): Administrator wrote to Mem[ " + + // Binary.intToHexString(address) + " ] == " + Binary.intToHexString(data) ); + } + else if (isWrite) + { + // This player is writing outside his/her assigned memory location /* System.out.println("ScavengerHunt.update(): Player " + playerID + " writing outside assigned mem. loc. at address " + Binary.intToHexString(address) + " -- not implemented!"); */ - + JOptionPane.showMessageDialog(null, - "ScavengerHunt.update(): Player " + playerID + " writing outside assigned mem. loc. at address " + - Binary.intToHexString(address) + - " -- not implemented!"); - } - else if (isRead) - { + "ScavengerHunt.update(): Player " + playerID + " writing outside assigned mem. loc. at address " + + Binary.intToHexString(address) + + " -- not implemented!"); + } + else if (isRead) + { // Policy: anyone can read any location. - } - - - - } // end ScavengerHunt.update() - - - - + } + + + } // end ScavengerHunt.update() + /* ------------------------------------------------------------------------- */ + // Write one word to MIPS memory. This is a wrapper to isolate the try..catch blocks. - private void toolSetWord(int address, int data) - { - - if (KENVDEBUG) - { + private void toolSetWord(int address, int data) + { + + if (KENVDEBUG) + { System.out.println(" ScavengerHunt.toolSetWord: Setting MIPS Memory[" + - Binary.intToHexString(address) + "] to " + Binary.intToHexString(data) + " = " + data ); - } - SetWordCounter++; - - try { + Binary.intToHexString(address) + "] to " + Binary.intToHexString(data) + " = " + data); + } + SetWordCounter++; + + try + { Globals.memory.setWord(address, data); // Write - } - catch ( AddressErrorException e) { - System.out.println("ScavengerHunt.toolSetWord: deliberate exit on AEE exception."); - System.out.println(" SetWordCounter = " + SetWordCounter); - System.out.println(" address = " + Binary.intToHexString(address) ); - System.out.println(" data = " + data ); - System.exit(0); - } - catch ( Exception e) { - System.out.println("ScavengerHunt.toolSetWord: deliberate exit on " + e.getMessage() + " exception."); - System.out.println(" SetWordCounter = " + SetWordCounter); - System.out.println(" address = " + Binary.intToHexString(address) ); - System.out.println(" data = " + data ); - System.exit(0); - } - - if (KENVDEBUG) - { - // Verify data written correctly + } + catch (AddressErrorException e) + { + System.out.println("ScavengerHunt.toolSetWord: deliberate exit on AEE exception."); + System.out.println(" SetWordCounter = " + SetWordCounter); + System.out.println(" address = " + Binary.intToHexString(address)); + System.out.println(" data = " + data); + System.exit(0); + } + catch (Exception e) + { + System.out.println("ScavengerHunt.toolSetWord: deliberate exit on " + e.getMessage() + " exception."); + System.out.println(" SetWordCounter = " + SetWordCounter); + System.out.println(" address = " + Binary.intToHexString(address)); + System.out.println(" data = " + data); + System.exit(0); + } + + if (KENVDEBUG) + { + // Verify data written correctly int verifyData = toolGetWord(address); if (verifyData != data) { - System.out.println("\n\nScavengerHunt.toolSetWord: Can't verify data! Special exit."); - System.out.println(" address = " + Binary.intToHexString(address) ); - System.out.println(" data = " + data); - System.out.println(" verifyData = " + verifyData); - System.exit(0); + System.out.println("\n\nScavengerHunt.toolSetWord: Can't verify data! Special exit."); + System.out.println(" address = " + Binary.intToHexString(address)); + System.out.println(" data = " + data); + System.out.println(" verifyData = " + verifyData); + System.exit(0); } else - System.out.println(" ScavengerHunt.toolSetWord: Mem[" + - Binary.intToHexString(address) + - " verified as " + Binary.intToHexString(data)); - } - - } // end toolSetWord - - - - + { + System.out.println(" ScavengerHunt.toolSetWord: Mem[" + + Binary.intToHexString(address) + + " verified as " + Binary.intToHexString(data)); + } + } + + } // end toolSetWord + // Read one word from MIPS memory. This is a wrapper to isolate the try..catch blocks. - private int toolGetWord(int address) - { - - int returnValue; - + private int toolGetWord(int address) + { + + int returnValue; + //System.out.println("ScavengerHunt.toolGetWord: called with address " + // Binary.intToHexString(address) ); - - try { + + try + { /* System.out.println("ScavengerHunt.toolGetWord: returning " + Binary.intToHexString(Globals.memory.getWord(address)) + " which is at MIPS Memory[" + Binary.intToHexString(address) + "]" ); */ returnValue = Globals.memory.getWord(address); - + /* System.out.println("ScavengerHunt.toolGetWord: Mem[" + Binary.intToHexString(address) + "] = " + Binary.intToHexString(returnValue) + " --- returning normally"); */ - + return returnValue; - } - catch ( AddressErrorException e) { - System.out.println("ScavengerHunt.toolGetWord: deliberate exit on AEE exception."); - System.out.println(" SetWordCounter = " + SetWordCounter); - System.out.println(" address = " + Binary.intToHexString(address) ); - System.exit(0); - } - catch ( Exception e) { - System.out.println("ScavengerHunt.toolGetWord: deliberate exit on " + e.getMessage() + " exception."); - System.out.println(" SetWordCounter = " + SetWordCounter); - System.out.println(" address = " + Binary.intToHexString(address) ); - System.exit(0); - } - - return 0; // Must have return statement - } // end toolGetWord - - - - + } + catch (AddressErrorException e) + { + System.out.println("ScavengerHunt.toolGetWord: deliberate exit on AEE exception."); + System.out.println(" SetWordCounter = " + SetWordCounter); + System.out.println(" address = " + Binary.intToHexString(address)); + System.exit(0); + } + catch (Exception e) + { + System.out.println("ScavengerHunt.toolGetWord: deliberate exit on " + e.getMessage() + " exception."); + System.out.println(" SetWordCounter = " + SetWordCounter); + System.out.println(" address = " + Binary.intToHexString(address)); + System.exit(0); + } + + return 0; // Must have return statement + } // end toolGetWord + // Read player's data field. - private int toolReadPlayerData(int p, int offset) - { - - if (KENVDEBUG) - { - System.out.println("ScavengerHunt.toolReadPlayerData: called with player " + p + - ", offset = " + - Binary.intToHexString(offset) + " ---> address " + - Binary.intToHexString(ADDR_BASE + (p * MEM_PER_PLAYER) + offset) - ); - } - - int returnValue = toolGetWord( ADDR_BASE + (p * MEM_PER_PLAYER) + offset); - - if (KENVDEBUG) - { - //if ((ADDR_BASE + (p * MEM_PER_PLAYER) + offset) >= 0xffff8000 && - // (ADDR_BASE + (p * MEM_PER_PLAYER) + offset) < 0xffff8800) // Show debug for player 0 and 1 only - //{ - //System.out.println(" ScavengerHunt.toolReadPlayerData: Reading MIPS Memory[" + - // Binary.intToHexString(ADDR_BASE + (p * MEM_PER_PLAYER) + offset) + - // "] which is " + Binary.intToHexString(returnValue) + - // " = " + Binary.intToHexString( returnValue) ); - //} - + private int toolReadPlayerData(int p, int offset) + { + + if (KENVDEBUG) + { + System.out.println("ScavengerHunt.toolReadPlayerData: called with player " + p + + ", offset = " + + Binary.intToHexString(offset) + " ---> address " + + Binary.intToHexString(ADDR_BASE + (p * MEM_PER_PLAYER) + offset) + ); + } + + int returnValue = toolGetWord(ADDR_BASE + (p * MEM_PER_PLAYER) + offset); + + if (KENVDEBUG) + { + //if ((ADDR_BASE + (p * MEM_PER_PLAYER) + offset) >= 0xffff8000 && + // (ADDR_BASE + (p * MEM_PER_PLAYER) + offset) < 0xffff8800) // Show debug for player 0 and 1 only + //{ + //System.out.println(" ScavengerHunt.toolReadPlayerData: Reading MIPS Memory[" + + // Binary.intToHexString(ADDR_BASE + (p * MEM_PER_PLAYER) + offset) + + // "] which is " + Binary.intToHexString(returnValue) + + // " = " + Binary.intToHexString( returnValue) ); + //} + System.out.println("ScavengerHunt.toolReadPlayerData: Mem[" + - Binary.intToHexString(ADDR_BASE + (p * MEM_PER_PLAYER) + offset) + "] = " + - Binary.intToHexString(returnValue) + " --- returning normally"); - } - - return returnValue; - } // end toolReadPlayerData - + Binary.intToHexString(ADDR_BASE + (p * MEM_PER_PLAYER) + offset) + "] = " + + Binary.intToHexString(returnValue) + " --- returning normally"); + } + + return returnValue; + } // end toolReadPlayerData + // Write player's data field. - private void toolWritePlayerData(int p, int offset, int data) - { - - int address = ADDR_BASE + (p * MEM_PER_PLAYER) + offset; - - if (KENVDEBUG) - { - System.out.println("ScavengerHunt.toolWritePlayerData: called with player " + p + - ", offset = " + Binary.intToHexString(offset) + - ", data = " + Binary.intToHexString(data) ); - } - - toolSetWord( address, data); - - if (KENVDEBUG) - { + private void toolWritePlayerData(int p, int offset, int data) + { + + int address = ADDR_BASE + (p * MEM_PER_PLAYER) + offset; + + if (KENVDEBUG) + { + System.out.println("ScavengerHunt.toolWritePlayerData: called with player " + p + + ", offset = " + Binary.intToHexString(offset) + + ", data = " + Binary.intToHexString(data)); + } + + toolSetWord(address, data); + + if (KENVDEBUG) + { int verifyData = toolGetWord(address); if (data != verifyData) { - System.out.println("\n\nScavengerHunt.toolWritePlayerData: MAYDAY data not verified !"); - System.out.println(" requested data to be written was " + Binary.intToHexString(data)); - System.out.println(" actual data at that loc is " + Binary.intToHexString(toolGetWord(address))); - System.exit(0); + System.out.println("\n\nScavengerHunt.toolWritePlayerData: MAYDAY data not verified !"); + System.out.println(" requested data to be written was " + Binary.intToHexString(data)); + System.out.println(" actual data at that loc is " + Binary.intToHexString(toolGetWord(address))); + System.exit(0); } else - System.out.println(" ScavengerHunt.toolWritePlayerData: Mem[" + - Binary.intToHexString(address) + - " verified as " + Binary.intToHexString(data)); - } - - } // end toolWritePlayerData - - - private void initializeScavengerData() - { - //GameOn = false; // MIPS programs readiness - authenticationValue = 0; - playerID = ADMINISTRATOR_ID; - startTime = System.currentTimeMillis(); // Clock time for program run - - // This is a dubious use of the tool (this Java program) to initialize data values for the game. - // The administrator portion of the MIPS program should initialize all data for each - // player -- but it's easier to work with random numbers in the Java program - // Initialize the locations and task data for each player - randomStream = new Random(42); // TBD Use a seed for development. Remove seed for randomizing. - - for (int j = 0; j < NUM_LOCATIONS - 1; j++) // The first (n-1) locations are "random" - { // Two coordinates (x and y) for each location + { + System.out.println(" ScavengerHunt.toolWritePlayerData: Mem[" + + Binary.intToHexString(address) + + " verified as " + Binary.intToHexString(data)); + } + } + + } // end toolWritePlayerData + + private void initializeScavengerData() + { + //GameOn = false; // MIPS programs readiness + authenticationValue = 0; + playerID = ADMINISTRATOR_ID; + startTime = System.currentTimeMillis(); // Clock time for program run + + // This is a dubious use of the tool (this Java program) to initialize data values for the game. + // The administrator portion of the MIPS program should initialize all data for each + // player -- but it's easier to work with random numbers in the Java program + // Initialize the locations and task data for each player + randomStream = new Random(42); // TBD Use a seed for development. Remove seed for randomizing. + + for (int j = 0; j < NUM_LOCATIONS - 1; j++) // The first (n-1) locations are "random" + { // Two coordinates (x and y) for each location loc[j] = new Location(); // Initialize each location element - loc[j].X = (int)(randomStream.nextDouble() * GRAPHIC_WIDTH); // X coord. - loc[j].Y = (int)(randomStream.nextDouble() * (GRAPHIC_HEIGHT - 50)); // Y coord. (leave room for buttons in window) - + loc[j].X = (int) (randomStream.nextDouble() * GRAPHIC_WIDTH); // X coord. + loc[j].Y = (int) (randomStream.nextDouble() * (GRAPHIC_HEIGHT - 50)); // Y coord. (leave room for buttons in window) + /* System.out.println("ScavengerHunt.update(): set up a location at (" + loc[j].X + @@ -939,33 +643,429 @@ loc[j].Y + ")" ); */ - - } - loc[NUM_LOCATIONS - 1] = new Location(); // The last location is a return to the starting position - loc[NUM_LOCATIONS - 1].X = START_AND_END_LOCATION; - loc[NUM_LOCATIONS - 1].Y = START_AND_END_LOCATION; - - for (int i = 0; i < NUM_PLAYERS; i++) // Initialize data for each player - { - - //System.out.println("ScavengerHunt.update(): Player loop " + i); - + + } + loc[NUM_LOCATIONS - 1] = new Location(); // The last location is a return to the starting position + loc[NUM_LOCATIONS - 1].X = START_AND_END_LOCATION; + loc[NUM_LOCATIONS - 1].Y = START_AND_END_LOCATION; + + for (int i = 0; i < NUM_PLAYERS; i++) // Initialize data for each player + { + + //System.out.println("ScavengerHunt.update(): Player loop " + i); + pd[i] = new PlayerData(); // Initialize each player data structure. - + for (int j = 0; j < NUM_LOCATIONS; j++) // Initialize the locations this player goes to { - toolWritePlayerData(i, OFFSET_LOC_ARRAY + (j * 8) + 0, loc[j].X); // Set the same locations for each player - toolWritePlayerData(i, OFFSET_LOC_ARRAY + (j * 8) + 4, loc[j].Y); // Set the same locations for each player + toolWritePlayerData(i, OFFSET_LOC_ARRAY + (j * 8), loc[j].X); // Set the same locations for each player + toolWritePlayerData(i, OFFSET_LOC_ARRAY + (j * 8) + 4, loc[j].Y); // Set the same locations for each player } - + for (int j = 0; j < SIZE_OF_TASK; j++) // Initialize the task data for this player { - toolWritePlayerData(i, OFFSET_TASK_ARRAY + (j * 4), - (int)(randomStream.nextDouble() * Integer.MAX_VALUE) ); // Set a random number for the task (sort them) + toolWritePlayerData(i, OFFSET_TASK_ARRAY + (j * 4), + (int) (randomStream.nextDouble() * Integer.MAX_VALUE)); // Set a random number for the task (sort them) } - } - - } // end initializeScavengerData - - } // end ScavengerHunt + } + + } // end initializeScavengerData + + // Used to define (X,Y) coordinate of a location to which ScavengerHunt players + // will travel. + private class Location + { + public int X; + + public int Y; + } + + // private inner class to provide the data on each player needed for display + private class PlayerData + { + int whereAmIX = START_AND_END_LOCATION; // Read only. Memory Address: Base + + int whereAmIY = START_AND_END_LOCATION; // Read only. Memory Address: Base + 0x4 + + //int moveToX; // Memory Address: Base + 0x8 + //int moveToY; // Memory Address: Base + 0xc + //int goalX; // Read only. Memory Address: Base + 0x10 + //int goalY; // Read only. Memory Address: Base + 0x14 + int energy = 20; // Read only. Memory Address: Base + 0x18 + + int color = 0; // Memory Address: Base + 0x1c + + long finishTime; + + //int locID; // ID of the location to which ScavengerHunt players are headed. Not used by player. + boolean[] hasVisitedLoc = new boolean[NUM_LOCATIONS]; // boolean: player has visited each location + + boolean finis = false; + + // Class PlayerData has no constructor + + public void setWhereAmI(int gX, int gY) + { + whereAmIX = gX; + whereAmIY = gY; + } + + public int getWhereAmIX() + { + return whereAmIX; + } + + public int getWhereAmIY() + { + return whereAmIY; + } + + public int getColor() + { + return color; + } + + public void setColor(int c) + { + color = c; + } + + public boolean hasVisited(int i) + { + return hasVisitedLoc[i]; + } + + public void setVisited(int i) + { + hasVisitedLoc[i] = true; + } + + public void setFinished() + { + finis = true; + } + + public boolean isFinished() + { + return finis; + } + + public long getFinishTime() + { + return finishTime; + } + + public void setFinishTime(long t) + { + finishTime = t; + } + + public long getFinishMin() + { + return (finishTime / 60000); + } // Minutes portion of finishTime + + public long getFinishSec() + { + return (finishTime % 60000) / 1000; // Seconds portion of finishTime + } // Seconds portion of finishTime + + public long getFinishMillisec() + { + return (finishTime % 1000); + } // Millisec portion of finishTime + + //public int getGoalX() { return goalX; } + //public int getGoalY() { return goalY; } + //public int getMoveToX() { return moveToX; } + //public int getMoveToY() { return moveToY; } + public int getEnergy() + { + return energy; + } + + //public void setGoal(int gX, int gY) { goalX = gX; goalY = gY; } + public void setEnergy(int e) + { + energy = e; + } + //public int getLocationID() { return locID; } + } // end class PlayerData + + // private inner class + private class ScavengerHuntRunnable implements Runnable + { + JPanel panel; + + public ScavengerHuntRunnable() // constructor + { + // final JFrame frame = new JFrame("ScavengerHunt"); + // Recommended by Pete Sanderson, 2 Nov. 2006, so that the Tool window and + // MARS window can be on the screen at the same time. + final JDialog frame = new JDialog(Globals.getGui(), "ScavengerHunt"); + + // System.out.println("ScavengerHuntRunnable.constructor: starting...."); + + panel = new JPanel(new BorderLayout()); + graphicArea = new ScavengerHuntDisplay(GRAPHIC_WIDTH, GRAPHIC_HEIGHT); + JPanel buttonPanel = new JPanel(); + JButton resetButton = new JButton("Reset"); + resetButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + graphicArea.clear(); + + // TBD ------- TBD + // Reset actions here + initializeScavengerData(); + //JOptionPane.showMessageDialog(null, "Reset needs to be implemented!" ); + + } + + }); + buttonPanel.add(resetButton); + + + panel.add(graphicArea, BorderLayout.CENTER); + panel.add(buttonPanel, BorderLayout.SOUTH); + + + // Snippet by Pete Sanderson, 2 Nov. 2006, to be a window-closing sequence + frame.addWindowListener( + new WindowAdapter() + { + public void windowClosing(WindowEvent e) + { + frame.setVisible(false); + frame.dispose(); + } + }); + + frame.getContentPane().add(panel); + frame.setLocationRelativeTo(null); + frame.pack(); + frame.setVisible(true); + frame.setTitle(" This is the ScavengerHunt"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // TBD --- This should close only the Tool, not the entire MARS + frame.setPreferredSize(new Dimension(GRAPHIC_WIDTH, GRAPHIC_HEIGHT)); // TBD SIZE + frame.setVisible(true); // show(); + + } // end ScavengerHuntRunnable() constructor + + public void run() + { + + double tempAngle; + + // infinite loop: play the Scavenger Hunt game + do + { + + // Pause to slow down the redisplay of the game. This is separate from + // the execution speed of the MIPS program, so the display may lag behind + // the state of the MIPS program. + try + { + // System.out.println(" Hello from the ScavengerHuntRunnable runner, sleeping here ..."); + // System.out.print("."); + Thread.sleep(100); // millisec + } + catch (InterruptedException exception) + {// no action + } + + panel.repaint(); // show new ScavengerHunt position + } + while (true); + + } // end run method of ScavengerHuntRunnable class + + } // end ScavengerHuntRunnable class + + /** + * ScavengerHuntDisplay does not have access to the same MIPS Memory class object used by the ScavengerHunt class + * object. Need read-only access to a similar data structure maintained within ScavengerHunt. + */ + private class ScavengerHuntDisplay extends JPanel + { + private final int width; + + private final int height; + + private boolean clearTheDisplay = true; + + + public ScavengerHuntDisplay(int tw, int th) + { + // System.out.println("ScavengerHuntDisplay.constructor: starting...."); + width = tw; + height = th; + + } + + public void redraw() + { + repaint(); + } + + public void clear() + { + // clear the graphic display + clearTheDisplay = true; + //System.out.println("ScavengerHuntDisplay.clear: called to clear the display"); + repaint(); + } + + /** + * paintComponent does not have access to the same MIPS Memory class object used by the ScavengerHunt class + * object. Need read-only access to a similar data structure maintained within ScavengerHunt. + */ + public void paintComponent(Graphics g) + { + long tempN; + int xCoord; + int yCoord; + + // System.out.println("ScavengerHuntDisplay.paintComponent: I'm painting! n is " + n); + + + // Recover Graphics2D + Graphics2D g2 = (Graphics2D) g; + + if (!GameOn) // Make sure game is ready before continuing + { + g2.setColor(Color.lightGray); + g2.fillRect(0, 0, width - 1, height - 1); // Clear all previous drawn information + g2.setColor(Color.black); + g2.drawString(" ScavengerHunt not yet initialized by MIPS administrator program.", + 100, 200); + return; + } + + // Clear all previous drawn information + g2.setColor(Color.lightGray); + g2.fillRect(0, 0, width - 1, height - 1); + + + // Draw the locations to which the players will be moving + // All players have the same location data. + for (int i = 0; i < NUM_LOCATIONS; i++) + { + xCoord = loc[i].X; // toolReadPlayerData(0, OFFSET_LOC_ARRAY + (i*8) + 0); + yCoord = loc[i].Y; // toolReadPlayerData(0, OFFSET_LOC_ARRAY + (i*8) + 4); + g2.setColor(Color.blue); + g2.fillRect(xCoord, yCoord, 20, 20); // coord is upper left corner of oval + g2.setColor(Color.white); + g2.drawString(" " + i, xCoord + 4, yCoord + 15); // coord is lower left corner of string text box + + /* + System.out.println("ScavengerHuntDisplay.paintComponent: drew loc " + i + " at (" + + xCoord + + ", " + + yCoord + + ")" ); + */ + } + + //System.out.println("ScavengerHuntDisplay.paintComponent: special exit!"); + //System.exit(0); + + + // Draw scoreboard + g2.setColor(Color.black); + g2.drawString("Player", width - 160, 30); + g2.drawString("Locations", width - 110, 30); + g2.drawString("Energy", width - 50, 30); + g2.drawLine(width - 160, 35, width - 10, 35); // line under column headings + g2.drawLine(width - 120, 35, width - 120, 35 + (NUM_PLAYERS * 15)); // vertical line for location-visited marks + g2.drawLine(width - 50, 35, width - 50, 35 + (NUM_PLAYERS * 15)); // vertical line for location-visited marks + for (int i = 0; i < NUM_PLAYERS; i++) + { + // Draw player's symbol + g2.setColor(new Color(pd[i].getColor())); + // g2.setColor(Color.red); // TBD hardcoded + + + xCoord = pd[i].getWhereAmIX(); + yCoord = pd[i].getWhereAmIY(); + + // ystem.out.println("paintComponent loop " + i + ": Loc coord is (" + xCoord + ", " + yCoord); + + // Draw player symbol and label it with player ID number. + // Hardcoded size of location graphic and label. + g2.drawOval(xCoord, yCoord, 20, 20); // coord is upper left corner of oval + g2.drawString(" " + i, xCoord + 4, yCoord + 15); // coord is lower left corner of string text box + + // Draw player's info on scoreboard + g2.setColor(Color.black); + g2.drawString(" " + i, width - 150, 50 + (i * 15)); // Player's ID on scoreboard + g2.drawString(" " + pd[i].getEnergy(), width - 40, 50 + (i * 15)); // Player's energy on scoreboard + + // Display player's progress or finishing time, whichever is applicable + if (pd[i].isFinished()) + { + // Display finishing time + // This doesn't display leading zeroes to align time components (e.g. 27 millisec ought to print as 027) + g2.drawString(pd[i].getFinishMin() + ":" + // Minutes + pd[i].getFinishSec() + ":" + // Seconds + pd[i].getFinishMillisec(), // Milliseconds + width - 115, 50 + (i * 15)); + // System.out.println("Time is " + pd[i].getFinishTime()); + //g2.drawString (" " + pd[i].getFinishTime(), + // width - 255, 50 + (i*15)); + } + else // player either has not finished or is just now finishing + { + int visCount = 0; + for (int j = 0; j < NUM_LOCATIONS; j++) + { + if (pd[i].hasVisited(j)) // count number of locations that player has visited + { + visCount++; + } + } + if (visCount == NUM_LOCATIONS) // player has visited every location -- finished! + { + pd[i].setFinished(); + pd[i].setFinishTime(System.currentTimeMillis() - startTime); + + } + else // player has not yet visited every location + { + // Display locations that the player has actually visited + for (int j = 0; j < NUM_LOCATIONS; j++) + { + if (pd[i].hasVisited(j)) // Player has visited this location + { + g2.fillRect((width - 120) + (j * 10), 42 + (i * 15), 10, 8); + } + } + } + } // end player had not previously finished + + } // end display score/results for each player + + // System.out.println("paintComponent: Player " + 0 + " is at (" + pd[0].getWhereAmIX() + ", " + pd[0].getWhereAmIY() + ")" ); + + + + + /* + g2.setColor(Color.blue); + g2.setFont(new Font(g2.getFont().getName(), g2.getFont().getStyle(), 20) ); // same font and style in larger size + g2.drawOval( width/2 - 30, // TBD Hardcoded oval size + height/2 - 30, + 60, + 60); + g2.drawString(" " + n, width/2, height/2); + */ + + + } + + } // end private inner class ScavengerHuntDisplay + +} // end ScavengerHunt diff --git a/src/main/java/mars/tools/ScreenMagnifier.java b/src/main/java/mars/tools/ScreenMagnifier.java index 3718c73..2220a49 100644 --- a/src/main/java/mars/tools/ScreenMagnifier.java +++ b/src/main/java/mars/tools/ScreenMagnifier.java @@ -1,13 +1,13 @@ - package mars.tools; +package mars.tools; - import java.text.*; - import java.awt.*; - import java.awt.event.*; - import java.awt.image.*; - import java.awt.geom.*; - import javax.swing.*; - import javax.swing.event.*; - import javax.swing.border.*; +import javax.swing.*; +import javax.swing.border.TitledBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; +import java.awt.event.*; +import java.awt.geom.Line2D; +import java.awt.image.BufferedImage; /* Copyright (c) 2003-2007, Pete Sanderson and Kenneth Vollmar @@ -38,861 +38,991 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Handy little tool to magnify a selected section of the screen - * by a given scale and display it. The screen image snapshot - * will be of the screen pixels beneath the tool's frame. The - * scale can be adjusted. The image is displayed in the tool's - * scrollable panel. You can highlight items on the image using - * the scribbler (hold down mouse button and move it on the - * image). The magnification scale adjustment is on the tool's - * window, but other settings can be modified on a button- - * triggered dialog. It will capture the contents of the - * underlying MARS graphical user interface, but NOT the - * contents of other Mars Tools frames. - * @author Pete Sanderson - * @version 1.0. - * 9 July 2007. + * Interface to specify strategy for determining the size and location of the screen rectangle to capture. */ - public class ScreenMagnifier implements MarsTool { - - public String getName() { - return "Screen Magnifier"; - } - - public void action() { - Magnifier mag = new Magnifier(); - } - - // Permits stand-alone execution. - - public static void main(String[] args) { - new Thread( - new Runnable() { - public void run() { - new ScreenMagnifier().action(); - } - }).start(); - } - - - } +interface CaptureRectangleStrategy +{ + Rectangle getCaptureRectangle(Rectangle magnifierRectangle); +} /* Technique comes from the Javaworld article "Capture the Screen: Build a screen-capture utility based on Java's Robot class". By Jeff Friesen, JavaWorld.com, 4/24/06. http://www.javaworld.com/javaworld/jw-04-2006/jw-0424-funandgames.html */ - class Magnifier extends JFrame implements ComponentListener { - static Robot robot; - JButton close, capture, settings; - JSpinner scaleAdjuster; - JScrollPane view; - Dimension frameSize; - Dimension viewSize; - MagnifierImage magnifierImage; - ActionListener captureActionListener; - CaptureModel captureResize, captureMove, captureRescale; - CaptureModel captureDisplayCenter, captureDisplayUpperleft; - CaptureModel dialogDisplayCenter; - ScribblerSettings scribblerSettings; - static final double SCALE_MINIMUM = 1.0; - static final double SCALE_MAXIMUM = 4.0; - static final double SCALE_INCREMENT = 0.5; - static final double SCALE_DEFAULT = 2.0; - double scale = SCALE_DEFAULT; - CaptureDisplayAlignmentStrategy alignment; - CaptureRectangleStrategy captureLocationSize = new CaptureMagnifierRectangle(); - JFrame frame; - static final String CAPTURE_TOOLTIP_TEXT = "Capture, scale, and display pixels that lay beneath the Magnifier."; - static final String SETTINGS_TOOLTIP_TEXT = "Show dialog for changing tool settings."; - static final String SCALE_TOOLTIP_TEXT = "Magnification scale for captured image."; - static final String CLOSE_TOOLTIP_TEXT = "Exit the Screen Magnifier. Changed settings are NOT retained."; - - Magnifier() { - super("Screen Magnifier 1.0"); - frame = this; - createSettings(); - // If running withint MARS, set to its icon image; if not fuggetit. - try { +/** + * Interface to specify strategy for determining initial scrollbar settings when displaying captured and scaled image. + */ +interface CaptureDisplayAlignmentStrategy +{ + void setScrollBarValue(JScrollBar scrollBar); +} + +/** + * Handy little tool to magnify a selected section of the screen by a given scale and display it. The screen image + * snapshot will be of the screen pixels beneath the tool's frame. The scale can be adjusted. The image is displayed + * in the tool's scrollable panel. You can highlight items on the image using the scribbler (hold down mouse button and + * move it on the image). The magnification scale adjustment is on the tool's window, but other settings can be + * modified on a button- triggered dialog. It will capture the contents of the underlying MARS graphical user + * interface, but NOT the contents of other Mars Tools frames. + * + * @author Pete Sanderson + * @version 1.0. 9 July 2007. + */ +public class ScreenMagnifier implements MarsTool +{ + + public static void main(String[] args) + { + new Thread( + new Runnable() + { + public void run() + { + new ScreenMagnifier().action(); + } + }).start(); + } + + public String getName() + { + return "Screen Magnifier"; + } + + // Permits stand-alone execution. + + public void action() + { + Magnifier mag = new Magnifier(); + } + + +} + +class Magnifier extends JFrame implements ComponentListener +{ + static final double SCALE_MINIMUM = 1.0; + + static final double SCALE_MAXIMUM = 4.0; + + static final double SCALE_INCREMENT = 0.5; + + static final double SCALE_DEFAULT = 2.0; + + static final String CAPTURE_TOOLTIP_TEXT = "Capture, scale, and display pixels that lay beneath the Magnifier."; + + static final String SETTINGS_TOOLTIP_TEXT = "Show dialog for changing tool settings."; + + static final String SCALE_TOOLTIP_TEXT = "Magnification scale for captured image."; + + static final String CLOSE_TOOLTIP_TEXT = "Exit the Screen Magnifier. Changed settings are NOT retained."; + + static Robot robot; + + JButton close, capture, settings; + + JSpinner scaleAdjuster; + + JScrollPane view; + + Dimension frameSize; + + Dimension viewSize; + + MagnifierImage magnifierImage; + + ActionListener captureActionListener; + + CaptureModel captureResize, captureMove, captureRescale; + + CaptureModel captureDisplayCenter, captureDisplayUpperleft; + + CaptureModel dialogDisplayCenter; + + ScribblerSettings scribblerSettings; + + double scale = SCALE_DEFAULT; + + CaptureDisplayAlignmentStrategy alignment; + + CaptureRectangleStrategy captureLocationSize = new CaptureMagnifierRectangle(); + + JFrame frame; + + Magnifier() + { + super("Screen Magnifier 1.0"); + frame = this; + createSettings(); + // If running withint MARS, set to its icon image; if not fuggetit. + try + { this.setIconImage(mars.Globals.getGui().getIconImage()); - } - catch (Exception e) { } - getContentPane().setLayout(new BorderLayout()); - // Will capture an image each time frame is moved/resized. - addComponentListener(this); - try { - robot = new Robot (); - } - catch (AWTException e) { } - catch (SecurityException e) { } - - close = new JButton("Close"); - close.setToolTipText(CLOSE_TOOLTIP_TEXT); - close.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - setVisible(false); - } - }); - settings = new JButton("Settings..."); - settings.setToolTipText(SETTINGS_TOOLTIP_TEXT); - settings.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - new SettingsDialog(frame); - } - }); - magnifierImage = new MagnifierImage(this); - view = new JScrollPane(magnifierImage); - viewSize = new Dimension(200,150); - view.setSize(viewSize); - - capture = new JButton("Capture"); - capture.setToolTipText(CAPTURE_TOOLTIP_TEXT); - captureActionListener = - new ActionListener() { - public void actionPerformed (ActionEvent e) { - magnifierImage.setImage(MagnifierImage.getScaledImage(captureScreenSection(captureLocationSize.getCaptureRectangle(getFrameRectangle())), scale)); - alignment.setScrollBarValue(view.getHorizontalScrollBar()); - alignment.setScrollBarValue(view.getVerticalScrollBar()); - } + } + catch (Exception e) + { + } + getContentPane().setLayout(new BorderLayout()); + // Will capture an image each time frame is moved/resized. + addComponentListener(this); + try + { + robot = new Robot(); + } + catch (AWTException e) + { + } + catch (SecurityException e) + { + } + + close = new JButton("Close"); + close.setToolTipText(CLOSE_TOOLTIP_TEXT); + close.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + setVisible(false); + } + }); + settings = new JButton("Settings..."); + settings.setToolTipText(SETTINGS_TOOLTIP_TEXT); + settings.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + new SettingsDialog(frame); + } + }); + magnifierImage = new MagnifierImage(this); + view = new JScrollPane(magnifierImage); + viewSize = new Dimension(200, 150); + view.setSize(viewSize); + + capture = new JButton("Capture"); + capture.setToolTipText(CAPTURE_TOOLTIP_TEXT); + captureActionListener = + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + magnifierImage.setImage(MagnifierImage.getScaledImage(captureScreenSection(captureLocationSize.getCaptureRectangle(getFrameRectangle())), scale)); + alignment.setScrollBarValue(view.getHorizontalScrollBar()); + alignment.setScrollBarValue(view.getVerticalScrollBar()); + } }; - JLabel scaleLabel = new JLabel("Scale: "); - SpinnerModel scaleModel = new SpinnerNumberModel(SCALE_DEFAULT, SCALE_MINIMUM, SCALE_MAXIMUM, SCALE_INCREMENT); - scaleAdjuster = new JSpinner(scaleModel); - scaleAdjuster.setToolTipText(SCALE_TOOLTIP_TEXT); - JSpinner.NumberEditor scaleEditor = new JSpinner.NumberEditor(scaleAdjuster, "0.0"); - scaleEditor.getTextField().setEditable(false); - scaleAdjuster.setEditor(scaleEditor); - scaleAdjuster.addChangeListener( - new ChangeListener() { - public void stateChanged(ChangeEvent e) { - scale = ((Double) scaleAdjuster.getValue()).doubleValue(); - if (captureRescale.isEnabled()) { + JLabel scaleLabel = new JLabel("Scale: "); + SpinnerModel scaleModel = new SpinnerNumberModel(SCALE_DEFAULT, SCALE_MINIMUM, SCALE_MAXIMUM, SCALE_INCREMENT); + scaleAdjuster = new JSpinner(scaleModel); + scaleAdjuster.setToolTipText(SCALE_TOOLTIP_TEXT); + JSpinner.NumberEditor scaleEditor = new JSpinner.NumberEditor(scaleAdjuster, "0.0"); + scaleEditor.getTextField().setEditable(false); + scaleAdjuster.setEditor(scaleEditor); + scaleAdjuster.addChangeListener( + new ChangeListener() + { + public void stateChanged(ChangeEvent e) + { + scale = ((Double) scaleAdjuster.getValue()).doubleValue(); + if (captureRescale.isEnabled()) + { captureActionListener.actionPerformed( - new ActionEvent(frame, 0, "capture")); - } - } - }); - JPanel scalePanel = new JPanel(); - scalePanel.add(scaleLabel); - scalePanel.add(scaleAdjuster); - capture.addActionListener(captureActionListener); - Box buttonRow = Box.createHorizontalBox(); - buttonRow.add(Box.createHorizontalStrut(4)); - buttonRow.add(capture); - buttonRow.add(Box.createHorizontalGlue()); - buttonRow.add(settings); - buttonRow.add(scalePanel); - buttonRow.add(Box.createHorizontalGlue()); - buttonRow.add(getHelpButton()); - buttonRow.add(Box.createHorizontalGlue()); - buttonRow.add(close); - buttonRow.add(Box.createHorizontalStrut(4)); - getContentPane().add(view, BorderLayout.CENTER); - getContentPane().add(buttonRow, BorderLayout.SOUTH); - pack(); - setSize(500,400); - setLocationRelativeTo(null); // center on screen - setVisible(true); - // For some strange reason, the image has to be captured - // and displayed an extra time for the display justification - // to be recognized for the scrollbars. The first capture - // will justify left-center no matter what (scrollbar - // positions 0). - captureActionListener.actionPerformed( - new ActionEvent(frame, 0, "capture")); - captureActionListener.actionPerformed( - new ActionEvent(frame, 0, "capture")); - } - - /* - * Create the default Screen Magnifier tool settings. These can - * all be changed through the Settings dialog but are not persistent - * across activations of the tool. - */ - private void createSettings() { - // Which events will cause automatic re-capture? Pick any - // or all of these three: resize the frame, move the frame, - // change the magnification scale (using spinner). - captureResize = new CaptureModel(false); - captureMove = new CaptureModel(false); - captureRescale = new CaptureModel(true); - // When capture is taken, how shall it be displayed in the view - // panel? Scrollbars will be present since the displayed image - // has to be larger than the viewing panel. Display it either - // with scrollbars centered, or scrollbars at initial position - // (upper-left corner of image at upper-left corner of viewer). - alignment = new CaptureDisplayCentered();// CaptureDisplayUpperleft(); - // Once the alignment is set, these will correctly self-set. - captureDisplayCenter = new CaptureModel(alignment instanceof CaptureDisplayCentered); - captureDisplayUpperleft = new CaptureModel(alignment instanceof CaptureDisplayUpperleft); - // Scribbler has two settings: line width in pixels and line color. - scribblerSettings = new ScribblerSettings(2, Color.RED); - // Whether or not to center the Settings dialog over the Magnifier frame. - dialogDisplayCenter = new CaptureModel(true); - } - - // A simple explanation of what the tool does. - private JButton getHelpButton() { - final String helpContent = - "Use this utility tool to display a magnified image of a\n"+ - "screen section and highlight things on the image. This\n"+ - "will be of interest mainly to instructors.\n"+ - "\n"+ - "To capture an image, size and position the Screen Magnifier\n"+ - "over the screen segment to be magnified and click \"Capture\".\n"+ - "The pixels beneath the magnifier will be captured, scaled,\n"+ - "and displayed in a scrollable window.\n"+ - "\n"+ - "To highlight things in the image, just drag the mouse over\n"+ - "the image to make a scribble line. This line is ephemeral\n"+ - "(is not repainted if covered then uncovered).\n"+ - "\n"+ - "The magnification scale can be adjusted using the spinner.\n"+ - "Other settings can be adjusted through the Settings dialog.\n"+ - "Settings include: justification of displayed image, automatic\n"+ - "capture upon certain tool events, and the thickness and color\n"+ - "of the scribble line.\n"+ - "\n"+ - "LIMITS: The image is static; it is not updated when the\n"+ - "underlying pixels change. Scale changes do not take effect\n"+ - "until the next capture (but you can set auto-capture). The\n"+ - "Magnifier does not capture frame contents of other tools.\n"+ - "Setting changes are not saved when the tool is closed.\n"+ - "\n"+ - "Contact Pete Sanderson at psanderson@otterbein.edu with\n"+ - "questions or comments.\n"; - JButton help = new JButton("Help"); - help.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - JOptionPane.showMessageDialog(frame, helpContent); - } - }); - return help; - } - - - /** - * Capture the pixels of the specified screen rectangle into an ImageBuffer. - * The trick is the ScreenMagnifier's frame has to be made invisible first - * so it does not show up in the image. - * @param section A rectangle specifying the range of pixels to capture. - * @return A BufferedImage containing the captured pixel range. - */ - BufferedImage captureScreenSection(Rectangle section) { - // Hide Frame so that it does not appear in the screen capture. - setVisible (false); - // For some reason, the graphic extent vacated by the above call - // is not redrawn before the screen capture unless I explicitly - // force it to be redrawn by telling the Mars GUI to update. - // If this doesn't work, e.g. getGui() returns null, then there - // are no alternatives so just let what would happen, happen. - try { + new ActionEvent(frame, 0, "capture")); + } + } + }); + JPanel scalePanel = new JPanel(); + scalePanel.add(scaleLabel); + scalePanel.add(scaleAdjuster); + capture.addActionListener(captureActionListener); + Box buttonRow = Box.createHorizontalBox(); + buttonRow.add(Box.createHorizontalStrut(4)); + buttonRow.add(capture); + buttonRow.add(Box.createHorizontalGlue()); + buttonRow.add(settings); + buttonRow.add(scalePanel); + buttonRow.add(Box.createHorizontalGlue()); + buttonRow.add(getHelpButton()); + buttonRow.add(Box.createHorizontalGlue()); + buttonRow.add(close); + buttonRow.add(Box.createHorizontalStrut(4)); + getContentPane().add(view, BorderLayout.CENTER); + getContentPane().add(buttonRow, BorderLayout.SOUTH); + pack(); + setSize(500, 400); + setLocationRelativeTo(null); // center on screen + setVisible(true); + // For some strange reason, the image has to be captured + // and displayed an extra time for the display justification + // to be recognized for the scrollbars. The first capture + // will justify left-center no matter what (scrollbar + // positions 0). + captureActionListener.actionPerformed( + new ActionEvent(frame, 0, "capture")); + captureActionListener.actionPerformed( + new ActionEvent(frame, 0, "capture")); + } + + /* + * Create the default Screen Magnifier tool settings. These can + * all be changed through the Settings dialog but are not persistent + * across activations of the tool. + */ + private void createSettings() + { + // Which events will cause automatic re-capture? Pick any + // or all of these three: resize the frame, move the frame, + // change the magnification scale (using spinner). + captureResize = new CaptureModel(false); + captureMove = new CaptureModel(false); + captureRescale = new CaptureModel(true); + // When capture is taken, how shall it be displayed in the view + // panel? Scrollbars will be present since the displayed image + // has to be larger than the viewing panel. Display it either + // with scrollbars centered, or scrollbars at initial position + // (upper-left corner of image at upper-left corner of viewer). + alignment = new CaptureDisplayCentered();// CaptureDisplayUpperleft(); + // Once the alignment is set, these will correctly self-set. + captureDisplayCenter = new CaptureModel(alignment instanceof CaptureDisplayCentered); + captureDisplayUpperleft = new CaptureModel(alignment instanceof CaptureDisplayUpperleft); + // Scribbler has two settings: line width in pixels and line color. + scribblerSettings = new ScribblerSettings(2, Color.RED); + // Whether or not to center the Settings dialog over the Magnifier frame. + dialogDisplayCenter = new CaptureModel(true); + } + + // A simple explanation of what the tool does. + private JButton getHelpButton() + { + final String helpContent = + "Use this utility tool to display a magnified image of a\n" + + "screen section and highlight things on the image. This\n" + + "will be of interest mainly to instructors.\n" + + "\n" + + "To capture an image, size and position the Screen Magnifier\n" + + "over the screen segment to be magnified and click \"Capture\".\n" + + "The pixels beneath the magnifier will be captured, scaled,\n" + + "and displayed in a scrollable window.\n" + + "\n" + + "To highlight things in the image, just drag the mouse over\n" + + "the image to make a scribble line. This line is ephemeral\n" + + "(is not repainted if covered then uncovered).\n" + + "\n" + + "The magnification scale can be adjusted using the spinner.\n" + + "Other settings can be adjusted through the Settings dialog.\n" + + "Settings include: justification of displayed image, automatic\n" + + "capture upon certain tool events, and the thickness and color\n" + + "of the scribble line.\n" + + "\n" + + "LIMITS: The image is static; it is not updated when the\n" + + "underlying pixels change. Scale changes do not take effect\n" + + "until the next capture (but you can set auto-capture). The\n" + + "Magnifier does not capture frame contents of other tools.\n" + + "Setting changes are not saved when the tool is closed.\n" + + "\n" + + "Contact Pete Sanderson at psanderson@otterbein.edu with\n" + + "questions or comments.\n"; + JButton help = new JButton("Help"); + help.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + JOptionPane.showMessageDialog(frame, helpContent); + } + }); + return help; + } + + + /** + * Capture the pixels of the specified screen rectangle into an ImageBuffer. The trick is the ScreenMagnifier's + * frame has to be made invisible first so it does not show up in the image. + * + * @param section A rectangle specifying the range of pixels to capture. + * @return A BufferedImage containing the captured pixel range. + */ + BufferedImage captureScreenSection(Rectangle section) + { + // Hide Frame so that it does not appear in the screen capture. + setVisible(false); + // For some reason, the graphic extent vacated by the above call + // is not redrawn before the screen capture unless I explicitly + // force it to be redrawn by telling the Mars GUI to update. + // If this doesn't work, e.g. getGui() returns null, then there + // are no alternatives so just let what would happen, happen. + try + { mars.Globals.getGui().update(mars.Globals.getGui().getGraphics()); - } - catch (Exception e) { } - // Perform the screen capture. - BufferedImage imageOfSection; - imageOfSection = robot.createScreenCapture(section); - setVisible(true); - return imageOfSection; - } - - /** - * Place the current frame size and location into a Rectangle object. - * @return A Rectangle containing the ScreenMagnifier's location, plus - * width and height in pixels. - */ - Rectangle getFrameRectangle() { - return new Rectangle(getLocation().x, getLocation().y, - getSize().width, getSize().height); - } - - /** - * Place the current screen size and location into a Rectangle object. - * @return A Rectangle containing the current screen location (0,0), - * plus width and height in pixels. - */ - Rectangle getScreenRectangle() { - return new Rectangle (Toolkit.getDefaultToolkit ().getScreenSize ()); - } - - - ////////////////////////////////////////////////////////////////////// - ////// - ////// These four methods are required for ComponentListener interface. - ////// Note: due to single inheritance, I can't extend ComponentAdaptor. - ////// - ////////////////////////////////////////////////////////////////////// - /** - * Respond to frame movement event by showing new image based on - * the screen section of interest for this new position. This event - * may occur once at the end of the move or it may occur - * at multiple points during the move. The latter causes a flashing - * image, but I have not been able to determine how to limit it - * to one event occurance. - */ - - // Regarding the above comment, I want to have only one capture - // occur, at the end of the move. But the number and timing of - // the generated events seems to vary depending on what machine - // I am running the program on! I have seen a technique in which - // componentMoved would start up a timer to go off every 100 ms or - // so to determine if the frame location had changed since the - // previous timer check. If so, the frame is moving so do nothing. - // If not, the move is assumed to be over and the image is captured - // and the timer stopped. The latter would also handle the case - // where componentMoved is triggered only at the end of the move. - // componentMoved would also have to determine whether or not - // such a timer was already running (e.g. is this the - // first componentMoved event?) in case it is called multiple - // times throughout the move, not just at the end. - public void componentMoved(ComponentEvent e) { - if (captureMove.isEnabled()) { - captureActionListener.actionPerformed( - new ActionEvent(e.getComponent(), e.getID(), "capture")); - } - } - - /** - * Respond to frame resize event by showing new image based on - * the screen section of interest for this new size. This event - * may occur once at the end of the resize or it may occur - * at multiple points during the resize. The latter causes a flashing - * image, but I have not been able to determine how to limit it - * to one event occurance. - */ - // See comments above for possible technique for assuring only one - // capture at the end of the resize. - public void componentResized(ComponentEvent e) { - if (captureResize.isEnabled()) { - captureActionListener.actionPerformed( - new ActionEvent(e.getComponent(), e.getID(), "capture")); - } - } - - /** - * Invoked when setVisible(true) is called, do nothing. - */ - public void componentShown(ComponentEvent e) { } - - /** - * Invoked when setVisible(false) is called, do nothing. - */ - public void componentHidden(ComponentEvent e) { } - - } + } + catch (Exception e) + { + } + // Perform the screen capture. + BufferedImage imageOfSection; + imageOfSection = robot.createScreenCapture(section); + setVisible(true); + return imageOfSection; + } + + /** + * Place the current frame size and location into a Rectangle object. + * + * @return A Rectangle containing the ScreenMagnifier's location, plus width and height in pixels. + */ + Rectangle getFrameRectangle() + { + return new Rectangle(getLocation().x, getLocation().y, + getSize().width, getSize().height); + } + + /** + * Place the current screen size and location into a Rectangle object. + * + * @return A Rectangle containing the current screen location (0,0), plus width and height in pixels. + */ + Rectangle getScreenRectangle() + { + return new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); + } + + + ////////////////////////////////////////////////////////////////////// + ////// + ////// These four methods are required for ComponentListener interface. + ////// Note: due to single inheritance, I can't extend ComponentAdaptor. + ////// + ////////////////////////////////////////////////////////////////////// + + /** + * Respond to frame movement event by showing new image based on the screen section of interest for this new + * position. This event may occur once at the end of the move or it may occur at multiple points during the move. + * The latter causes a flashing image, but I have not been able to determine how to limit it to one event + * occurance. + */ + + // Regarding the above comment, I want to have only one capture + // occur, at the end of the move. But the number and timing of + // the generated events seems to vary depending on what machine + // I am running the program on! I have seen a technique in which + // componentMoved would start up a timer to go off every 100 ms or + // so to determine if the frame location had changed since the + // previous timer check. If so, the frame is moving so do nothing. + // If not, the move is assumed to be over and the image is captured + // and the timer stopped. The latter would also handle the case + // where componentMoved is triggered only at the end of the move. + // componentMoved would also have to determine whether or not + // such a timer was already running (e.g. is this the + // first componentMoved event?) in case it is called multiple + // times throughout the move, not just at the end. + public void componentMoved(ComponentEvent e) + { + if (captureMove.isEnabled()) + { + captureActionListener.actionPerformed( + new ActionEvent(e.getComponent(), e.getID(), "capture")); + } + } + + /** + * Respond to frame resize event by showing new image based on the screen section of interest for this new size. + * This event may occur once at the end of the resize or it may occur at multiple points during the resize. The + * latter causes a flashing image, but I have not been able to determine how to limit it to one event occurance. + */ + // See comments above for possible technique for assuring only one + // capture at the end of the resize. + public void componentResized(ComponentEvent e) + { + if (captureResize.isEnabled()) + { + captureActionListener.actionPerformed( + new ActionEvent(e.getComponent(), e.getID(), "capture")); + } + } + + /** + * Invoked when setVisible(true) is called, do nothing. + */ + public void componentShown(ComponentEvent e) + { + } + + /** + * Invoked when setVisible(false) is called, do nothing. + */ + public void componentHidden(ComponentEvent e) + { + } + +} - /** - * Specialized class for the magnifier's settings dialog. + * Specialized class for the magnifier's settings dialog. */ - - class SettingsDialog extends JDialog { - JButton applyButton, cancelButton; - JCheckBox captureResizeCheckBox, captureMoveCheckBox, captureRescaleCheckBox; - JRadioButton captureDisplayCenteredButton, captureDisplayUpperleftButton; - Integer[] scribblerLineWidthSettings = { new Integer(1), new Integer(2), - new Integer(3), new Integer(4), - new Integer(5), new Integer(6), - new Integer(7), new Integer(8) }; - JComboBox lineWidthSetting; - JButton lineColorSetting; - JCheckBox dialogCentered; // Whether or not dialog appears centered over the magnfier frame. - JDialog dialog; - // temporary storage until committed with "Apply". Needed because it is returned - // by same call that shows the color selection dialog, so cannot be retrieved - // later from the model (as you can with buttons, checkboxes, etc). - Color scribblerLineColorSetting; - // Text for tool tips. - static final String SETTINGS_APPLY_TOOLTIP_TEXT = "Apply current settings and close the dialog."; - static final String SETTINGS_CANCEL_TOOLTIP_TEXT = "Close the dialog without applying any setting changes."; - static final String SETTINGS_SCRIBBLER_WIDTH_TOOLTIP_TEXT = "Scribbler line thickness in pixels."; - static final String SETTINGS_SCRIBBLER_COLOR_TOOLTIP_TEXT = "Click here to change Scribbler line color."; - static final String SETTINGS_DIALOG_CENTERED_TOOLTIP_TEXT = "Whether to center this dialog over the Magnifier."; - - SettingsDialog(JFrame frame) { - super(frame, "Magnifier Tool Settings"); - dialog = this; - setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); - Container contentPane = this.getContentPane(); - contentPane.setLayout(new BorderLayout()); - JPanel settingsPanel = new JPanel(); - JPanel selectionsPanel = new JPanel(new GridLayout(2,1)); - selectionsPanel.add(getCaptureDisplayPanel()); - JPanel secondRow = new JPanel(new GridLayout(1,2)); - secondRow.add(getAutomaticCaptureSettingsPanel()); - secondRow.add(getScribblerPanel(this)); - selectionsPanel.add(secondRow); - contentPane.add(selectionsPanel); - contentPane.add(getButtonRowPanel(), BorderLayout.SOUTH); - pack(); - if (dialogCentered.isSelected()) { + +class SettingsDialog extends JDialog +{ + // Text for tool tips. + static final String SETTINGS_APPLY_TOOLTIP_TEXT = "Apply current settings and close the dialog."; + + static final String SETTINGS_CANCEL_TOOLTIP_TEXT = "Close the dialog without applying any setting changes."; + + static final String SETTINGS_SCRIBBLER_WIDTH_TOOLTIP_TEXT = "Scribbler line thickness in pixels."; + + static final String SETTINGS_SCRIBBLER_COLOR_TOOLTIP_TEXT = "Click here to change Scribbler line color."; + + static final String SETTINGS_DIALOG_CENTERED_TOOLTIP_TEXT = "Whether to center this dialog over the Magnifier."; + + JButton applyButton, cancelButton; + + JCheckBox captureResizeCheckBox, captureMoveCheckBox, captureRescaleCheckBox; + + JRadioButton captureDisplayCenteredButton, captureDisplayUpperleftButton; + + Integer[] scribblerLineWidthSettings = {Integer.valueOf(1), Integer.valueOf(2), + Integer.valueOf(3), Integer.valueOf(4), + Integer.valueOf(5), Integer.valueOf(6), + Integer.valueOf(7), Integer.valueOf(8)}; + + JComboBox lineWidthSetting; + + JButton lineColorSetting; + + JCheckBox dialogCentered; // Whether or not dialog appears centered over the magnfier frame. + + JDialog dialog; + + // temporary storage until committed with "Apply". Needed because it is returned + // by same call that shows the color selection dialog, so cannot be retrieved + // later from the model (as you can with buttons, checkboxes, etc). + Color scribblerLineColorSetting; + + SettingsDialog(JFrame frame) + { + super(frame, "Magnifier Tool Settings"); + dialog = this; + setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + Container contentPane = this.getContentPane(); + contentPane.setLayout(new BorderLayout()); + JPanel settingsPanel = new JPanel(); + JPanel selectionsPanel = new JPanel(new GridLayout(2, 1)); + selectionsPanel.add(getCaptureDisplayPanel()); + JPanel secondRow = new JPanel(new GridLayout(1, 2)); + secondRow.add(getAutomaticCaptureSettingsPanel()); + secondRow.add(getScribblerPanel(this)); + selectionsPanel.add(secondRow); + contentPane.add(selectionsPanel); + contentPane.add(getButtonRowPanel(), BorderLayout.SOUTH); + pack(); + if (dialogCentered.isSelected()) + { setLocationRelativeTo(frame); - } - setVisible(true); - } - - // This panel contains the control buttons for the Settings Dialog. - private JPanel getButtonRowPanel() { - JPanel buttonRow = new JPanel(); - applyButton = new JButton("Apply and Close"); - applyButton.setToolTipText(SETTINGS_APPLY_TOOLTIP_TEXT); - // Action to perform when APply button pressed: commit GUI settings to the models - applyButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - // commit settings - ((Magnifier)getOwner()).captureResize.setEnabled(captureResizeCheckBox.isSelected()); - ((Magnifier)getOwner()).captureMove.setEnabled(captureMoveCheckBox.isSelected()); - ((Magnifier)getOwner()).captureRescale.setEnabled(captureRescaleCheckBox.isSelected()); - ((Magnifier)getOwner()).captureDisplayCenter.setEnabled(captureDisplayCenteredButton.isSelected()); - ((Magnifier)getOwner()).captureDisplayUpperleft.setEnabled(captureDisplayUpperleftButton.isSelected()); - ((Magnifier)getOwner()).dialogDisplayCenter.setEnabled(dialogCentered.isSelected()); - if (captureDisplayCenteredButton.isSelected()) { - ((Magnifier)getOwner()).alignment = new CaptureDisplayCentered(); - } - else if (captureDisplayUpperleftButton.isSelected()) { - ((Magnifier)getOwner()).alignment = new CaptureDisplayUpperleft(); - } - ((Magnifier)getOwner()).scribblerSettings.setLineWidth(scribblerLineWidthSettings[lineWidthSetting.getSelectedIndex()].intValue()); - ((Magnifier)getOwner()).scribblerSettings.setLineColor(lineColorSetting.getBackground()); - dialog.dispose(); - } - }); - cancelButton = new JButton("Cancel"); - cancelButton.setToolTipText(SETTINGS_CANCEL_TOOLTIP_TEXT); - cancelButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - dialog.dispose(); - } - }); - // By default, display dialog centered over the Magnifier's frame. This - // can be changed however to display at upper left corner of screen. Why - // would you want to change this? So you can use the settings dialog to - // to change scribbler color and/or width without wiping out current - // scribbler marks. If the dialog is centered, its mere display may - // wipe out existing scribbler marks (because they are not repainted - // when the underlying image is repainted). - dialogCentered = new JCheckBox("Dialog centered", ((Magnifier)getOwner()).dialogDisplayCenter.isEnabled()); - dialogCentered.setToolTipText(SETTINGS_DIALOG_CENTERED_TOOLTIP_TEXT); - buttonRow.add(applyButton); - buttonRow.add(cancelButton); - buttonRow.add(dialogCentered); - return buttonRow; - } - - // Panel that contains settings for automatically performing an image - // capture. These are a convenience, as the image can always be - // manually captured by clicking the "Capture" button. - private JPanel getAutomaticCaptureSettingsPanel() { - JPanel automaticCaptureSettings = new JPanel(); - automaticCaptureSettings.setBorder(new TitledBorder("Automatic Capture")); - Box automaticCaptureSettingsBox = Box.createHorizontalBox(); - automaticCaptureSettings.add(automaticCaptureSettingsBox); - captureResizeCheckBox = new JCheckBox("Capture upon resize",((Magnifier)getOwner()).captureResize.isEnabled()); - captureMoveCheckBox = new JCheckBox("Capture upon move",((Magnifier)getOwner()).captureMove.isEnabled()); - captureRescaleCheckBox = new JCheckBox("Capture upon rescale",((Magnifier)getOwner()).captureRescale.isEnabled()); - JPanel checkboxColumn = new JPanel(new GridLayout(3,1)); - checkboxColumn.add(captureResizeCheckBox); - checkboxColumn.add(captureMoveCheckBox); - checkboxColumn.add(captureRescaleCheckBox); - automaticCaptureSettingsBox.add(checkboxColumn); - return automaticCaptureSettings; - } - - // Panel that contains settings for extent and display of image upon - // capture. In version 1.0, the extent is fixed; it is same as that - // of the tool's frame itself. The term "extent" refers to the location - // and dimension of the rectangle. - private JPanel getCaptureDisplayPanel() { - JPanel captureDisplaySetting = new JPanel(); - captureDisplaySetting.setBorder(new TitledBorder("Capture and Display")); - Box captureDisplaySettingsBox = Box.createHorizontalBox(); - captureDisplaySetting.add(captureDisplaySettingsBox); - captureDisplayCenteredButton = new JRadioButton("Capture area behind magnifier and display centered", - ((Magnifier)getOwner()).captureDisplayCenter.isEnabled()); - captureDisplayUpperleftButton = new JRadioButton("Capture area behind magnifier and display upper-left", - ((Magnifier)getOwner()).captureDisplayUpperleft.isEnabled()); - ButtonGroup displayButtonGroup = new ButtonGroup(); - displayButtonGroup.add(captureDisplayCenteredButton); - displayButtonGroup.add(captureDisplayUpperleftButton); - JPanel radioColumn = new JPanel(new GridLayout(2,1)); - radioColumn.add(captureDisplayCenteredButton); - radioColumn.add(captureDisplayUpperleftButton); - JPanel radioLabelColumn = new JPanel(new GridLayout(1,1)); - captureDisplaySettingsBox.add(radioColumn); - return captureDisplaySetting; - } - - // Panel that contains settings for the Scribbler part of the tool. - // The only settings here are choice of line width (thickness) in pixels - // and line color. - private JPanel getScribblerPanel(final JDialog dialog) { - JPanel scribblerSettings = new JPanel(); - scribblerSettings.setBorder(new TitledBorder("Scribbler")); - Box scribblerSettingsBox = Box.createHorizontalBox(); - scribblerSettings.add(scribblerSettingsBox); - lineWidthSetting = new JComboBox(scribblerLineWidthSettings); - lineWidthSetting.setToolTipText(SETTINGS_SCRIBBLER_WIDTH_TOOLTIP_TEXT); - lineWidthSetting.setSelectedIndex(((Magnifier)getOwner()).scribblerSettings.getLineWidth()-1); - lineColorSetting = new JButton(" "); - lineColorSetting.setToolTipText(SETTINGS_SCRIBBLER_COLOR_TOOLTIP_TEXT); - lineColorSetting.setBackground(((Magnifier)getOwner()).scribblerSettings.getLineColor()); - lineColorSetting.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - Color newColor = JColorChooser.showDialog(dialog, "Scribbler line color", lineColorSetting.getBackground()); - lineColorSetting.setBackground(newColor); - } - }); - scribblerLineColorSetting = lineColorSetting.getBackground(); - JPanel settingsColumn = new JPanel(new GridLayout(2,1,5,5)); - settingsColumn.add(lineWidthSetting); - settingsColumn.add(lineColorSetting); - JPanel labelColumn = new JPanel(new GridLayout(2,1,5,5)); - labelColumn.add(new JLabel("Line width ",SwingConstants.LEFT)); - labelColumn.add(new JLabel("Line color ",SwingConstants.LEFT)); - scribblerSettingsBox.add(labelColumn); - scribblerSettingsBox.add(settingsColumn); - return scribblerSettings; - } - - } + } + setVisible(true); + } - /** - * Represents an automatic capture "event" which is enabled - * if the capture operation is to be performed automatically - * upon occurance. - */ - class CaptureModel { - private boolean enabled; - /** - * Create a new CaptureModel. - * @param set True if capture is to be automatically performed when - * associated event occurs, false otherwise. - */ - public CaptureModel(boolean set) { - enabled = set; - } - /** - * Determine whether or not capture will automatically occur. - * @return True if automatic capture will occur, false otherwise. - */ - public boolean isEnabled() { - return enabled; - } - /** - * Specify whether or not capture will automatically occur. - * @param set True if capture is to be automatically performed when - * associated event occurs, false otherwise. - */ - public void setEnabled(boolean set) { - enabled = set; - } - - } + // This panel contains the control buttons for the Settings Dialog. + private JPanel getButtonRowPanel() + { + JPanel buttonRow = new JPanel(); + applyButton = new JButton("Apply and Close"); + applyButton.setToolTipText(SETTINGS_APPLY_TOOLTIP_TEXT); + // Action to perform when APply button pressed: commit GUI settings to the models + applyButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + // commit settings + ((Magnifier) getOwner()).captureResize.setEnabled(captureResizeCheckBox.isSelected()); + ((Magnifier) getOwner()).captureMove.setEnabled(captureMoveCheckBox.isSelected()); + ((Magnifier) getOwner()).captureRescale.setEnabled(captureRescaleCheckBox.isSelected()); + ((Magnifier) getOwner()).captureDisplayCenter.setEnabled(captureDisplayCenteredButton.isSelected()); + ((Magnifier) getOwner()).captureDisplayUpperleft.setEnabled(captureDisplayUpperleftButton.isSelected()); + ((Magnifier) getOwner()).dialogDisplayCenter.setEnabled(dialogCentered.isSelected()); + if (captureDisplayCenteredButton.isSelected()) + { + ((Magnifier) getOwner()).alignment = new CaptureDisplayCentered(); + } + else if (captureDisplayUpperleftButton.isSelected()) + { + ((Magnifier) getOwner()).alignment = new CaptureDisplayUpperleft(); + } + ((Magnifier) getOwner()).scribblerSettings.setLineWidth(scribblerLineWidthSettings[lineWidthSetting.getSelectedIndex()].intValue()); + ((Magnifier) getOwner()).scribblerSettings.setLineColor(lineColorSetting.getBackground()); + dialog.dispose(); + } + }); + cancelButton = new JButton("Cancel"); + cancelButton.setToolTipText(SETTINGS_CANCEL_TOOLTIP_TEXT); + cancelButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + dialog.dispose(); + } + }); + // By default, display dialog centered over the Magnifier's frame. This + // can be changed however to display at upper left corner of screen. Why + // would you want to change this? So you can use the settings dialog to + // to change scribbler color and/or width without wiping out current + // scribbler marks. If the dialog is centered, its mere display may + // wipe out existing scribbler marks (because they are not repainted + // when the underlying image is repainted). + dialogCentered = new JCheckBox("Dialog centered", ((Magnifier) getOwner()).dialogDisplayCenter.isEnabled()); + dialogCentered.setToolTipText(SETTINGS_DIALOG_CENTERED_TOOLTIP_TEXT); + buttonRow.add(applyButton); + buttonRow.add(cancelButton); + buttonRow.add(dialogCentered); + return buttonRow; + } + + // Panel that contains settings for automatically performing an image + // capture. These are a convenience, as the image can always be + // manually captured by clicking the "Capture" button. + private JPanel getAutomaticCaptureSettingsPanel() + { + JPanel automaticCaptureSettings = new JPanel(); + automaticCaptureSettings.setBorder(new TitledBorder("Automatic Capture")); + Box automaticCaptureSettingsBox = Box.createHorizontalBox(); + automaticCaptureSettings.add(automaticCaptureSettingsBox); + captureResizeCheckBox = new JCheckBox("Capture upon resize", ((Magnifier) getOwner()).captureResize.isEnabled()); + captureMoveCheckBox = new JCheckBox("Capture upon move", ((Magnifier) getOwner()).captureMove.isEnabled()); + captureRescaleCheckBox = new JCheckBox("Capture upon rescale", ((Magnifier) getOwner()).captureRescale.isEnabled()); + JPanel checkboxColumn = new JPanel(new GridLayout(3, 1)); + checkboxColumn.add(captureResizeCheckBox); + checkboxColumn.add(captureMoveCheckBox); + checkboxColumn.add(captureRescaleCheckBox); + automaticCaptureSettingsBox.add(checkboxColumn); + return automaticCaptureSettings; + } + + // Panel that contains settings for extent and display of image upon + // capture. In version 1.0, the extent is fixed; it is same as that + // of the tool's frame itself. The term "extent" refers to the location + // and dimension of the rectangle. + private JPanel getCaptureDisplayPanel() + { + JPanel captureDisplaySetting = new JPanel(); + captureDisplaySetting.setBorder(new TitledBorder("Capture and Display")); + Box captureDisplaySettingsBox = Box.createHorizontalBox(); + captureDisplaySetting.add(captureDisplaySettingsBox); + captureDisplayCenteredButton = new JRadioButton("Capture area behind magnifier and display centered", + ((Magnifier) getOwner()).captureDisplayCenter.isEnabled()); + captureDisplayUpperleftButton = new JRadioButton("Capture area behind magnifier and display upper-left", + ((Magnifier) getOwner()).captureDisplayUpperleft.isEnabled()); + ButtonGroup displayButtonGroup = new ButtonGroup(); + displayButtonGroup.add(captureDisplayCenteredButton); + displayButtonGroup.add(captureDisplayUpperleftButton); + JPanel radioColumn = new JPanel(new GridLayout(2, 1)); + radioColumn.add(captureDisplayCenteredButton); + radioColumn.add(captureDisplayUpperleftButton); + JPanel radioLabelColumn = new JPanel(new GridLayout(1, 1)); + captureDisplaySettingsBox.add(radioColumn); + return captureDisplaySetting; + } + + // Panel that contains settings for the Scribbler part of the tool. + // The only settings here are choice of line width (thickness) in pixels + // and line color. + private JPanel getScribblerPanel(final JDialog dialog) + { + JPanel scribblerSettings = new JPanel(); + scribblerSettings.setBorder(new TitledBorder("Scribbler")); + Box scribblerSettingsBox = Box.createHorizontalBox(); + scribblerSettings.add(scribblerSettingsBox); + lineWidthSetting = new JComboBox(scribblerLineWidthSettings); + lineWidthSetting.setToolTipText(SETTINGS_SCRIBBLER_WIDTH_TOOLTIP_TEXT); + lineWidthSetting.setSelectedIndex(((Magnifier) getOwner()).scribblerSettings.getLineWidth() - 1); + lineColorSetting = new JButton(" "); + lineColorSetting.setToolTipText(SETTINGS_SCRIBBLER_COLOR_TOOLTIP_TEXT); + lineColorSetting.setBackground(((Magnifier) getOwner()).scribblerSettings.getLineColor()); + lineColorSetting.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + Color newColor = JColorChooser.showDialog(dialog, "Scribbler line color", lineColorSetting.getBackground()); + lineColorSetting.setBackground(newColor); + } + }); + scribblerLineColorSetting = lineColorSetting.getBackground(); + JPanel settingsColumn = new JPanel(new GridLayout(2, 1, 5, 5)); + settingsColumn.add(lineWidthSetting); + settingsColumn.add(lineColorSetting); + JPanel labelColumn = new JPanel(new GridLayout(2, 1, 5, 5)); + labelColumn.add(new JLabel("Line width ", SwingConstants.LEFT)); + labelColumn.add(new JLabel("Line color ", SwingConstants.LEFT)); + scribblerSettingsBox.add(labelColumn); + scribblerSettingsBox.add(settingsColumn); + return scribblerSettings; + } + +} /** - * This class defines a specialized panel for displaying a captured image. + * Represents an automatic capture "event" which is enabled if the capture operation is to be performed automatically + * upon occurance. + */ +class CaptureModel +{ + private boolean enabled; + + /** + * Create a new CaptureModel. + * + * @param set True if capture is to be automatically performed when associated event occurs, false otherwise. + */ + public CaptureModel(boolean set) + { + enabled = set; + } + + /** + * Determine whether or not capture will automatically occur. + * + * @return True if automatic capture will occur, false otherwise. + */ + public boolean isEnabled() + { + return enabled; + } + + /** + * Specify whether or not capture will automatically occur. + * + * @param set True if capture is to be automatically performed when associated event occurs, false otherwise. + */ + public void setEnabled(boolean set) + { + enabled = set; + } + +} + +/** + * This class defines a specialized panel for displaying a captured image. */ - class MagnifierImage extends JPanel { - - // Enclosing JFrame for this panel -- the Screen Magnifier itself. - private Magnifier frame; - // Rectangle representing screen pixels to be magnified. - private Rectangle screenRectangle; - // Robot used to perform the screen capture. - private static Robot robot; - // Displayed image's Image object, which is actually a BufferedImage. - private Image image; - // Scribbler for highlighting image using mouse. - private Scribbler scribbler; - - - /** - * Construct an MagnifierImage component. - */ - - public MagnifierImage(Magnifier frame) { - this.frame = frame; - this.scribbler = new Scribbler(frame.scribblerSettings); - - addMouseListener ( - new MouseAdapter () { - public void mousePressed (MouseEvent e) { - scribbler.moveto(e.getX(), e.getY()); // Move to click position - } - }); - - // Install a mouse motion listener to draw the scribble. - addMouseMotionListener ( - new MouseMotionAdapter () { - public void mouseDragged (MouseEvent e) { - scribbler.lineto(e.getX(), e.getY(),(Graphics2D)getGraphics()); - } - }); - } - - - /** - * Return the current image. - * - * @return Image reference to current image - */ - - public Image getImage () { - return image; - } - - /** - * Repaint the MagnifierImage with the current image's pixels. - * - * @param g graphics context - */ - - public void paintComponent (Graphics g) { - // Repaint the component's background. - super.paintComponent (g); - // If an image has been defined, draw that image using the Component - // layer of this MagnifierImage object as the ImageObserver. - if (image != null) - g.drawImage (image, 0, 0, this); - } - - /** - * Establish a new image and update the display. - * - * @param image new image's Image reference - */ - - public void setImage (Image image) { - // Save the image for later repaint. - this.image = image; - // Set this panel's preferred size to the image's size, to influence the - // display of scrollbars. - setPreferredSize (new Dimension (image.getWidth (this), - image.getHeight (this))); - // Present scrollbars as necessary. - revalidate (); - // Update the image displayed on the panel. - repaint (); - } - - /** - * Get a scaled version of the image. Ignores scaling values between .99 and 1.01. - * @param image the original image - * @param scale the magnification scale as a double - * @param scaleAlgorithm Scaling algorithm to use: Image.SCALE_DEFAULT, - * Image.SCALE_FAST, Image.SCALE_SMOOTH. - */ - static Image getScaledImage(Image image, double scale, int scaleAlgorithm) { - // Don't bother if it is close to 1. I anticipate this will be used mainly to - // enlarge the image, so short circuit evalution will apply most of the time. - return (scale < 1.01 && scale > 0.99) - ? image - : image.getScaledInstance((int)(image.getWidth(null)*scale), - (int)(image.getHeight(null)*scale), - scaleAlgorithm); - } - - /** - * Get a scaled version of the image using default scaling algorithm. - * Ignores scaling values between .99 and 1.01. - * @param image the original image - * @param scale the magnification scale as a double - */ - static Image getScaledImage(Image image, double scale) { - return getScaledImage(image, scale, Image.SCALE_DEFAULT); - } - - /* - * Little class to allow user to scribble over the image using the - * mouse. The scribble is ephemeral; it is drawn but specifications - * are not saved. - */ - private class Scribbler { - private ScribblerSettings scribblerSettings; - private BasicStroke drawingStroke; - // coordinates of previous mouse position - protected int last_x, last_y; - - Scribbler(ScribblerSettings scribblerSettings) { +class MagnifierImage extends JPanel +{ + + // Robot used to perform the screen capture. + private static Robot robot; + + // Enclosing JFrame for this panel -- the Screen Magnifier itself. + private final Magnifier frame; + + // Rectangle representing screen pixels to be magnified. + private Rectangle screenRectangle; + + // Displayed image's Image object, which is actually a BufferedImage. + private Image image; + + // Scribbler for highlighting image using mouse. + private final Scribbler scribbler; + + + /** + * Construct an MagnifierImage component. + */ + + public MagnifierImage(Magnifier frame) + { + this.frame = frame; + this.scribbler = new Scribbler(frame.scribblerSettings); + + addMouseListener( + new MouseAdapter() + { + public void mousePressed(MouseEvent e) + { + scribbler.moveto(e.getX(), e.getY()); // Move to click position + } + }); + + // Install a mouse motion listener to draw the scribble. + addMouseMotionListener( + new MouseMotionAdapter() + { + public void mouseDragged(MouseEvent e) + { + scribbler.lineto(e.getX(), e.getY(), (Graphics2D) getGraphics()); + } + }); + } + + /** + * Get a scaled version of the image. Ignores scaling values between .99 and 1.01. + * + * @param image the original image + * @param scale the magnification scale as a double + * @param scaleAlgorithm Scaling algorithm to use: Image.SCALE_DEFAULT, Image.SCALE_FAST, Image.SCALE_SMOOTH. + */ + static Image getScaledImage(Image image, double scale, int scaleAlgorithm) + { + // Don't bother if it is close to 1. I anticipate this will be used mainly to + // enlarge the image, so short circuit evalution will apply most of the time. + return (scale < 1.01 && scale > 0.99) + ? image + : image.getScaledInstance((int) (image.getWidth(null) * scale), + (int) (image.getHeight(null) * scale), + scaleAlgorithm); + } + + /** + * Get a scaled version of the image using default scaling algorithm. Ignores scaling values between .99 and 1.01. + * + * @param image the original image + * @param scale the magnification scale as a double + */ + static Image getScaledImage(Image image, double scale) + { + return getScaledImage(image, scale, Image.SCALE_DEFAULT); + } + + /** + * Return the current image. + * + * @return Image reference to current image + */ + + public Image getImage() + { + return image; + } + + /** + * Establish a new image and update the display. + * + * @param image new image's Image reference + */ + + public void setImage(Image image) + { + // Save the image for later repaint. + this.image = image; + // Set this panel's preferred size to the image's size, to influence the + // display of scrollbars. + setPreferredSize(new Dimension(image.getWidth(this), + image.getHeight(this))); + // Present scrollbars as necessary. + revalidate(); + // Update the image displayed on the panel. + repaint(); + } + + /** + * Repaint the MagnifierImage with the current image's pixels. + * + * @param g graphics context + */ + + public void paintComponent(Graphics g) + { + // Repaint the component's background. + super.paintComponent(g); + // If an image has been defined, draw that image using the Component + // layer of this MagnifierImage object as the ImageObserver. + if (image != null) + { + g.drawImage(image, 0, 0, this); + } + } + + /* + * Little class to allow user to scribble over the image using the + * mouse. The scribble is ephemeral; it is drawn but specifications + * are not saved. + */ + private class Scribbler + { + // coordinates of previous mouse position + protected int last_x, last_y; + + private final ScribblerSettings scribblerSettings; + + private BasicStroke drawingStroke; + + Scribbler(ScribblerSettings scribblerSettings) + { this.scribblerSettings = scribblerSettings; drawingStroke = new BasicStroke(scribblerSettings.getLineWidth()); - } - - /** Get the scribbler's drawing color. */ - public Color getColor() { + } + + /** Get the scribbler's drawing color. */ + public Color getColor() + { return scribblerSettings.getLineColor();//color; - } - - /** Get the scribbler's line (stroke) width. */ - public int getLineWidth() { + } + + /** Set the scribbler's drawing color. */ + public void setColor(Color newColor) + { + scribblerSettings.setLineColor(newColor);// this.color = newColor; + } + + /** Get the scribbler's line (stroke) width. */ + public int getLineWidth() + { this.drawingStroke = new BasicStroke(scribblerSettings.getLineWidth()); return scribblerSettings.getLineWidth();//width; - } - - /** Set the scribbler's drawing color. */ - public void setColor(Color newColor) { - scribblerSettings.setLineColor(newColor);// this.color = newColor; - } - - /** Set the scribbler's line (stroke) width. */ - public void setLineWidth(int newWidth) { + } + + /** Set the scribbler's line (stroke) width. */ + public void setLineWidth(int newWidth) + { scribblerSettings.setLineWidth(newWidth);// this.width = newWidth; this.drawingStroke = new BasicStroke(newWidth); - } - - /** Get the scribbler's drawing (stroke) object. */ - private BasicStroke getStroke() { + } + + /** Get the scribbler's drawing (stroke) object. */ + private BasicStroke getStroke() + { return drawingStroke; - } - - /** Set the scribbler's drawing (stroke) object. */ - private void setStroke(BasicStroke newStroke) { + } + + /** Set the scribbler's drawing (stroke) object. */ + private void setStroke(BasicStroke newStroke) + { this.drawingStroke = newStroke; - } - - /** Remember the specified point */ - public void moveto(int x, int y) { + } + + /** Remember the specified point */ + public void moveto(int x, int y) + { last_x = x; last_y = y; - } - - /** Draw from the last point to this point, then remember new point */ - public void lineto(int x, int y, Graphics2D g2d) { - // System.out.println(drawingStroke.getLineWidth()); + } + + /** Draw from the last point to this point, then remember new point */ + public void lineto(int x, int y, Graphics2D g2d) + { + // System.out.println(drawingStroke.getLineWidth()); g2d.setStroke(new BasicStroke(scribblerSettings.getLineWidth())); g2d.setColor(scribblerSettings.getLineColor()); // Tell it what color to use g2d.draw(new Line2D.Float(last_x, last_y, x, y)); moveto(x, y); // Save the current point - } - } - - } - - - /** - * Class to represent current settings for the scribbler tool. - */ - class ScribblerSettings { - private int width; - private Color color; - - /** - * Build a new ScribblerSettings object. - */ - public ScribblerSettings(int width, Color color) { - this.width = width; - this.color = color; - } - - /** - * Fetch the current line width for the scribbler tool. - */ - public int getLineWidth() { - return width; - } - - /** - * Fetch the current line color for the scribbler tool. - */ - public Color getLineColor() { - return color; - } - - /** - * Set the current line width for the scribbler tool. - */ - public void setLineWidth(int newWidth) { - width = newWidth; - } - - /** - * Set the current line color for the scribbler tool. - */ - public void setLineColor(Color newColor) { - color = newColor; - } - } - - - /** - * Interface to specify strategy for determining the size and location - * of the screen rectangle to capture. - */ - interface CaptureRectangleStrategy { - public Rectangle getCaptureRectangle(Rectangle magnifierRectangle); - } - - /** - * Upon screen capture, capture the same rectangle as the magnifier - * itself. Pixels that lie directly beneath it. - */ - class CaptureMagnifierRectangle implements CaptureRectangleStrategy { - public Rectangle getCaptureRectangle(Rectangle magnifierRectangle) { - return magnifierRectangle; - } - } - /** - * Upon screen capture, capture a rectangle whose size and location is - * determined by the current magnification scale. For instance, if the - * scale is 2.0, the capture rectangle will have half the width and - * height of the magnifier and its location will be offset so it is - * centered within the magnifier rectangle -- except when the magnifier - * is near the edge of the screen in which case the location is adjusted - * such that the boundary pixels will be captured. - */ - class CaptureScaledRectangle implements CaptureRectangleStrategy { - public Rectangle getCaptureRectangle(Rectangle magnifierRectangle) { - throw new UnsupportedOperationException(); // This is not implementated yet! - //return new Rectangle(); - } - } - - - /** - * Interface to specify strategy for determining initial scrollbar settings - * when displaying captured and scaled image. - */ - interface CaptureDisplayAlignmentStrategy { - public void setScrollBarValue(JScrollBar scrollBar); - } - /** - * Captured and scaled image should be displayed centerd in the panel. - */ - class CaptureDisplayCentered implements CaptureDisplayAlignmentStrategy { - /** - * Set the scrollbar value to inticate the captured and scaled image - * is displayed centered in the panel. - * @param scrollBar The scrollbar to be adjusted. - */ - public void setScrollBarValue(JScrollBar scrollBar) { - scrollBar.setValue(( scrollBar.getModel().getMaximum() - - scrollBar.getModel().getMinimum() - - scrollBar.getModel().getExtent() - ) / 2 - ); - } - } - - /** - * Captured and scaled image should be displayed so that its upper left - * corner is initially displayed in the upper left corner of the panel. - */ - class CaptureDisplayUpperleft implements CaptureDisplayAlignmentStrategy { - /** - * Set the scrollbar value to inticate the captured and scaled image - * is displayed with the upper left corner initially visible in the panel. - * @param scrollBar The scrollbar to be adjusted. - */ - public void setScrollBarValue(JScrollBar scrollBar) { - scrollBar.setValue(0); - } - } + } + } + +} + +/** + * Class to represent current settings for the scribbler tool. + */ +class ScribblerSettings +{ + private int width; + + private Color color; + + /** + * Build a new ScribblerSettings object. + */ + public ScribblerSettings(int width, Color color) + { + this.width = width; + this.color = color; + } + + /** + * Fetch the current line width for the scribbler tool. + */ + public int getLineWidth() + { + return width; + } + + /** + * Set the current line width for the scribbler tool. + */ + public void setLineWidth(int newWidth) + { + width = newWidth; + } + + /** + * Fetch the current line color for the scribbler tool. + */ + public Color getLineColor() + { + return color; + } + + /** + * Set the current line color for the scribbler tool. + */ + public void setLineColor(Color newColor) + { + color = newColor; + } +} + +/** + * Upon screen capture, capture the same rectangle as the magnifier itself. Pixels that lie directly beneath it. + */ +class CaptureMagnifierRectangle implements CaptureRectangleStrategy +{ + public Rectangle getCaptureRectangle(Rectangle magnifierRectangle) + { + return magnifierRectangle; + } +} + +/** + * Upon screen capture, capture a rectangle whose size and location is determined by the current magnification scale. + * For instance, if the scale is 2.0, the capture rectangle will have half the width and height of the magnifier and its + * location will be offset so it is centered within the magnifier rectangle -- except when the magnifier is near the + * edge of the screen in which case the location is adjusted such that the boundary pixels will be captured. + */ +class CaptureScaledRectangle implements CaptureRectangleStrategy +{ + public Rectangle getCaptureRectangle(Rectangle magnifierRectangle) + { + throw new UnsupportedOperationException(); // This is not implementated yet! + //return new Rectangle(); + } +} + +/** + * Captured and scaled image should be displayed centerd in the panel. + */ +class CaptureDisplayCentered implements CaptureDisplayAlignmentStrategy +{ + /** + * Set the scrollbar value to inticate the captured and scaled image is displayed centered in the panel. + * + * @param scrollBar The scrollbar to be adjusted. + */ + public void setScrollBarValue(JScrollBar scrollBar) + { + scrollBar.setValue((scrollBar.getModel().getMaximum() + - scrollBar.getModel().getMinimum() + - scrollBar.getModel().getExtent() + ) / 2 + ); + } +} + +/** + * Captured and scaled image should be displayed so that its upper left corner is initially displayed in the upper left + * corner of the panel. + */ +class CaptureDisplayUpperleft implements CaptureDisplayAlignmentStrategy +{ + /** + * Set the scrollbar value to inticate the captured and scaled image is displayed with the upper left corner + * initially visible in the panel. + * + * @param scrollBar The scrollbar to be adjusted. + */ + public void setScrollBarValue(JScrollBar scrollBar) + { + scrollBar.setValue(0); + } +} diff --git a/src/main/java/mars/tools/UnitAnimation.java b/src/main/java/mars/tools/UnitAnimation.java index bdb5cac..131e0e8 100644 --- a/src/main/java/mars/tools/UnitAnimation.java +++ b/src/main/java/mars/tools/UnitAnimation.java @@ -1,23 +1,17 @@ package mars.tools; -import java.awt.Color; -import java.awt.Cursor; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.GraphicsConfiguration; -import java.awt.GraphicsDevice; -import java.awt.GraphicsEnvironment; -import java.awt.MouseInfo; -import java.awt.PointerInfo; -import java.awt.RenderingHints; +import mars.Globals; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import javax.imageio.ImageIO; +import javax.swing.*; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.font.FontRenderContext; -import java.awt.font.TextLayout; import java.awt.image.BufferedImage; import java.io.IOException; import java.text.DecimalFormat; @@ -25,803 +19,1026 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.Vector; -import javax.imageio.ImageIO; -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.Timer; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; - -import mars.Globals; - - - - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - class UnitAnimation extends JPanel -implements ActionListener { -/** - * - */ - private static final long serialVersionUID = -2681757800180958534L; + implements ActionListener +{ + /** + * + */ + private static final long serialVersionUID = -2681757800180958534L; - //config variables - private int PERIOD = 8; // velocity of frames in ms - private static final int PWIDTH = 1000; // size of this panel - private static final int PHEIGHT = 574; - private GraphicsConfiguration gc; - private GraphicsDevice gd; // for reporting accl. memory usage - private int accelMemory; - private DecimalFormat df; - - private int counter; //verify then remove. - private boolean justStarted; //flag to start movement + private static final int PWIDTH = 1000; // size of this panel + + private static final int PHEIGHT = 574; + + //config variables + private final int PERIOD = 8; // velocity of frames in ms + + private final GraphicsConfiguration gc; + + private final GraphicsDevice gd; // for reporting accl. memory usage + + private final int accelMemory; + + private final DecimalFormat df; + + private int counter; //verify then remove. + + private boolean justStarted; //flag to start movement - private int indexX; //counter of screen position - private int indexY; - private boolean xIsMoving, yIsMoving; //flag for mouse movement. + private int indexX; //counter of screen position -// private Vertex[][] inputGraph; - private Vector> outputGraph; - private ArrayList vertexList; - private ArrayList vertexTraversed; - //Screen Label variables - - private HashMap registerEquivalenceTable; + private int indexY; - private String instructionCode; - - private int countRegLabel; - private int countALULabel; - private int countPCLabel; - -private int register = 1; -private int control = 2; -private int aluControl = 3; -private int alu = 4; -private int datapatTypeUsed; - - private Boolean cursorInIM, cursorInALU, cursorInDataMem, cursorInReg; - - private Graphics2D g2d; - - private BufferedImage datapath; + private boolean xIsMoving, yIsMoving; //flag for mouse movement. - class Vertex { - private int numIndex; - private int init; - private int end; - private int current; - private String name; - public static final int movingUpside = 1; - public static final int movingDownside = 2; - public static final int movingLeft = 3; - public static final int movingRight = 4; - public int direction; - public int oppositeAxis; - private boolean isMovingXaxis; - private Color color; - private boolean first_interaction; - private boolean active; - private boolean isText; - private ArrayList targetVertex; - - public Vertex(int index, int init, int end, String name, int oppositeAxis, boolean isMovingXaxis, - String listOfColors, String listTargetVertex, boolean isText){ - this.numIndex = index; - this.init = init; - this.current = this.init; - this.end = end; - this.name = name; - this.oppositeAxis = oppositeAxis; - this.isMovingXaxis = isMovingXaxis; - this.first_interaction = true; - this.active = false; - this.isText = isText; - this.color = new Color(0,153,0); - if(isMovingXaxis == true){ - if( init < end) - direction = movingLeft; - else - direction = movingRight; - - } - else{ - if( init < end) - direction = movingUpside; - else - direction = movingDownside; - } - String[] list = listTargetVertex.split("#"); - targetVertex = new ArrayList(); - for(int i = 0; i < list.length; i++){ - targetVertex.add(Integer.parseInt(list[i])); - // System.out.println("Adding " + i + " " + Integer.parseInt(list[i])+ " in target"); - } - String[] listColor = listOfColors.split("#"); - this.color = new Color(Integer.parseInt(listColor[0]) , Integer.parseInt(listColor[1]), Integer.parseInt(listColor[2]) ); - } - - public int getDirection(){ - return direction; - } - - public boolean isText(){ - return this.isText; - } + // private Vertex[][] inputGraph; + private Vector> outputGraph; + + private final ArrayList vertexList; + + private ArrayList vertexTraversed; + //Screen Label variables + + private final HashMap registerEquivalenceTable; + + private String instructionCode; + + private final int countRegLabel; + + private final int countALULabel; + + private final int countPCLabel; + + private final int register = 1; + + private final int control = 2; + + private final int aluControl = 3; + + private final int alu = 4; + + private final int datapatTypeUsed; + + private final Boolean cursorInIM; + + private final Boolean cursorInALU; + + private final Boolean cursorInDataMem; + + private Boolean cursorInReg; + + private Graphics2D g2d; + + private BufferedImage datapath; + + public UnitAnimation(String instructionBinary, int datapathType) + { + datapatTypeUsed = datapathType; + cursorInIM = false; + cursorInALU = false; + cursorInDataMem = false; + df = new DecimalFormat("0.0"); // 1 dp + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + gd = ge.getDefaultScreenDevice(); + gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); + + accelMemory = gd.getAvailableAcceleratedMemory(); // in bytes + setBackground(Color.white); + setPreferredSize(new Dimension(PWIDTH, PHEIGHT)); + + // load and initialise the images + initImages(); + + vertexList = new ArrayList(); + counter = 0; + justStarted = true; + instructionCode = instructionBinary; + + //declaration of labels definition. + registerEquivalenceTable = new HashMap(); + + countRegLabel = 400; + countALULabel = 380; + countPCLabel = 380; + loadHashMapValues(); - public ArrayList getTargetVertex() { - return targetVertex; - } + } // end of ImagesTests() - public int getNumIndex() { - return numIndex; - } - public void setNumIndex(int numIndex) { - this.numIndex = numIndex; - } - public int getInit() { - return init; - } - public void setInit(int init) { - this.init = init; - } - public int getEnd() { - return end; - } - public void setEnd(int end) { - this.end = end; - } - public int getCurrent() { - return current; - } - public void setCurrent(int current) { - this.current = current; - } - public String getName() { - return name; - } - public void setName(String name) { - this.name = name; - } - public int getOppositeAxis() { - return oppositeAxis; - } - public void setOppositeAxis(int oppositeAxis) { - this.oppositeAxis = oppositeAxis; - } - public boolean isMovingXaxis() { - return isMovingXaxis; - } - public void setMovingXaxis(boolean isMovingXaxis) { - this.isMovingXaxis = isMovingXaxis; - } - public Color getColor() { - return color; - } - public void setColor(Color color) { - this.color = color; - } - public boolean isFirst_interaction() { - return first_interaction; - } - public void setFirst_interaction(boolean first_interaction) { - this.first_interaction = first_interaction; - } - public boolean isActive() { - return active; - } - public void setActive(boolean active) { - this.active = active; - } - } - - public UnitAnimation(String instructionBinary, int datapathType) - { - datapatTypeUsed = datapathType; - cursorInIM = false; - cursorInALU = false; - cursorInDataMem = false; - df = new DecimalFormat("0.0"); // 1 dp - GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); - gd = ge.getDefaultScreenDevice(); - gc = ge.getDefaultScreenDevice().getDefaultConfiguration(); + //set the binnary opcode value of the basic instructions of MIPS instruction set + public void loadHashMapValues() + { + if (datapatTypeUsed == register) + { + importXmlStringData("/registerDatapath.xml", registerEquivalenceTable, "register_equivalence", "bits", "mnemonic"); + importXmlDatapathMap("/registerDatapath.xml", "datapath_map"); + } + else if (datapatTypeUsed == control) + { + importXmlStringData("/controlDatapath.xml", registerEquivalenceTable, "register_equivalence", "bits", "mnemonic"); + importXmlDatapathMap("/controlDatapath.xml", "datapath_map"); + } - accelMemory = gd.getAvailableAcceleratedMemory(); // in bytes - setBackground(Color.white); - setPreferredSize( new Dimension(PWIDTH, PHEIGHT) ); + else if (datapatTypeUsed == aluControl) + { + importXmlStringData("/ALUcontrolDatapath.xml", registerEquivalenceTable, "register_equivalence", "bits", "mnemonic"); + importXmlDatapathMapAluControl("/ALUcontrolDatapath.xml", "datapath_map"); + } + } - // load and initialise the images - initImages(); - - vertexList = new ArrayList(); - counter = 0; - justStarted = true; - instructionCode = instructionBinary; - - //declaration of labels definition. - registerEquivalenceTable = new HashMap(); - - countRegLabel = 400; - countALULabel = 380; - countPCLabel = 380; - loadHashMapValues(); - + //import the list of opcodes of mips set of instructions + public void importXmlStringData(String xmlName, HashMap table, String elementTree, String tagId, String tagData) + { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(false); + DocumentBuilder docBuilder; + try + { + //System.out.println(); + docBuilder = dbf.newDocumentBuilder(); + Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); + Element root = doc.getDocumentElement(); + Element equivalenceItem; + NodeList bitsList, mnemonic; + NodeList equivalenceList = root.getElementsByTagName(elementTree); + for (int i = 0; i < equivalenceList.getLength(); i++) + { + equivalenceItem = (Element) equivalenceList.item(i); + bitsList = equivalenceItem.getElementsByTagName(tagId); + mnemonic = equivalenceItem.getElementsByTagName(tagData); + for (int j = 0; j < bitsList.getLength(); j++) + { + table.put(bitsList.item(j).getTextContent(), mnemonic.item(j).getTextContent()); + } + } + } + catch (Exception e) + { + e.printStackTrace(); + } + } - } // end of ImagesTests() + //import the parameters of the animation on datapath + public void importXmlDatapathMap(String xmlName, String elementTree) + { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(false); + DocumentBuilder docBuilder; + try + { + docBuilder = dbf.newDocumentBuilder(); + Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); + Element root = doc.getDocumentElement(); + Element datapath_mapItem; + NodeList index_vertex, name, init, end, color, other_axis, isMovingXaxis, targetVertex, sourceVertex, isText; + NodeList datapath_mapList = root.getElementsByTagName(elementTree); + for (int i = 0; i < datapath_mapList.getLength(); i++) + { //extract the vertex of the xml input and encapsulate into the vertex object + datapath_mapItem = (Element) datapath_mapList.item(i); + index_vertex = datapath_mapItem.getElementsByTagName("num_vertex"); + name = datapath_mapItem.getElementsByTagName("name"); + init = datapath_mapItem.getElementsByTagName("init"); + end = datapath_mapItem.getElementsByTagName("end"); + //definition of colors line - //set the binnary opcode value of the basic instructions of MIPS instruction set - public void loadHashMapValues(){ - if(datapatTypeUsed == register){ - importXmlStringData("/registerDatapath.xml", registerEquivalenceTable, "register_equivalence", "bits", "mnemonic"); - importXmlDatapathMap("/registerDatapath.xml", "datapath_map"); - } - else if(datapatTypeUsed == control){ - importXmlStringData("/controlDatapath.xml", registerEquivalenceTable, "register_equivalence", "bits", "mnemonic"); - importXmlDatapathMap("/controlDatapath.xml", "datapath_map"); - } - - else if(datapatTypeUsed == aluControl){ - importXmlStringData("/ALUcontrolDatapath.xml", registerEquivalenceTable, "register_equivalence", "bits", "mnemonic"); - importXmlDatapathMapAluControl("/ALUcontrolDatapath.xml", "datapath_map"); - } - } - - //import the list of opcodes of mips set of instructions - public void importXmlStringData(String xmlName, HashMap table, String elementTree, String tagId, String tagData){ - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(false); - DocumentBuilder docBuilder; - try { - //System.out.println(); - docBuilder = dbf.newDocumentBuilder(); - Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); - Element root = doc.getDocumentElement(); - Element equivalenceItem; - NodeList bitsList, mnemonic; - NodeList equivalenceList = root.getElementsByTagName(elementTree); - for(int i = 0; i < equivalenceList.getLength(); i++){ - equivalenceItem = (Element)equivalenceList.item(i); - bitsList = equivalenceItem.getElementsByTagName(tagId); - mnemonic = equivalenceItem.getElementsByTagName(tagData); - for(int j= 0; j < bitsList.getLength(); j++){ - table.put(bitsList.item(j).getTextContent(),mnemonic.item(j).getTextContent()); - } - } - } - catch (Exception e) { - e.printStackTrace(); - } - } - - //import the parameters of the animation on datapath - public void importXmlDatapathMap(String xmlName, String elementTree){ - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(false); - DocumentBuilder docBuilder; - try { - docBuilder = dbf.newDocumentBuilder(); - Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); - Element root = doc.getDocumentElement(); - Element datapath_mapItem; - NodeList index_vertex, name, init, end,color, other_axis, isMovingXaxis, targetVertex, sourceVertex, isText ; - NodeList datapath_mapList = root.getElementsByTagName(elementTree); - for(int i = 0; i < datapath_mapList.getLength(); i++){ //extract the vertex of the xml input and encapsulate into the vertex object - datapath_mapItem = (Element)datapath_mapList.item(i); - index_vertex = datapath_mapItem.getElementsByTagName("num_vertex"); - name = datapath_mapItem.getElementsByTagName("name"); - init = datapath_mapItem.getElementsByTagName("init"); - end = datapath_mapItem.getElementsByTagName("end"); - //definition of colors line - - if(instructionCode.substring(0,6).equals("000000")){//R-type instructions - color = datapath_mapItem.getElementsByTagName("color_Rtype"); - //System.out.println("rtype"); - } - else if(instructionCode.substring(0,6).matches("00001[0-1]")){ //J-type instructions - color = datapath_mapItem.getElementsByTagName("color_Jtype"); - //System.out.println("jtype"); - } - else if(instructionCode.substring(0,6).matches("100[0-1][0-1][0-1]")){ //LOAD type instructions - color = datapath_mapItem.getElementsByTagName("color_LOADtype"); - //System.out.println("load type"); - } - else if(instructionCode.substring(0,6).matches("101[0-1][0-1][0-1]")){ //LOAD type instructions - color = datapath_mapItem.getElementsByTagName("color_STOREtype"); - //System.out.println("store type"); - } - else if(instructionCode.substring(0,6).matches("0001[0-1][0-1]")){ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("color_BRANCHtype"); - //System.out.println("branch type"); - } - else{ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("color_Itype"); - //System.out.println("immediate type"); - } - - - other_axis = datapath_mapItem.getElementsByTagName("other_axis"); - isMovingXaxis = datapath_mapItem.getElementsByTagName("isMovingXaxis"); - targetVertex = datapath_mapItem.getElementsByTagName("target_vertex"); - isText = datapath_mapItem.getElementsByTagName("is_text"); - - for(int j= 0; j < index_vertex.getLength(); j++){ - Vertex vert = new Vertex(Integer.parseInt(index_vertex.item(j).getTextContent()), Integer.parseInt(init.item(j).getTextContent()), - Integer.parseInt(end.item(j).getTextContent()), name.item(j).getTextContent(), Integer.parseInt(other_axis.item(j).getTextContent()), - Boolean.parseBoolean(isMovingXaxis.item(j).getTextContent()), color.item(j).getTextContent(), targetVertex.item(j).getTextContent(), Boolean.parseBoolean(isText.item(j).getTextContent())); - vertexList.add(vert); - } - } - //loading matrix of control of vertex. - outputGraph = new Vector>(); - vertexTraversed = new ArrayList(); - int size = vertexList.size(); - Vertex vertex; - ArrayList targetList; - for(int i = 0; i < vertexList.size(); i++){ - vertex = vertexList.get(i); - targetList = vertex.getTargetVertex(); - Vector vertexOfTargets = new Vector(); - for(int k = 0; k < targetList.size(); k++){ - vertexOfTargets.add(vertexList.get(targetList.get(k))); - } - outputGraph.add(vertexOfTargets); - } - for(int i=0; i< outputGraph.size(); i++){ - Vector vert = outputGraph.get(i); - } - - vertexList.get(0).setActive(true); - vertexTraversed.add(vertexList.get(0)); - } - catch (Exception e) { - e.printStackTrace(); - } - - } - - - public void importXmlDatapathMapAluControl(String xmlName, String elementTree){ - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(false); - DocumentBuilder docBuilder; - try { - docBuilder = dbf.newDocumentBuilder(); - Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); - Element root = doc.getDocumentElement(); - Element datapath_mapItem; - NodeList index_vertex, name, init, end,color, other_axis, isMovingXaxis, targetVertex, sourceVertex, isText ; - NodeList datapath_mapList = root.getElementsByTagName(elementTree); - for(int i = 0; i < datapath_mapList.getLength(); i++){ //extract the vertex of the xml input and encapsulate into the vertex object - datapath_mapItem = (Element)datapath_mapList.item(i); - index_vertex = datapath_mapItem.getElementsByTagName("num_vertex"); - name = datapath_mapItem.getElementsByTagName("name"); - init = datapath_mapItem.getElementsByTagName("init"); - end = datapath_mapItem.getElementsByTagName("end"); - //definition of colors line - - if(instructionCode.substring(0,6).equals("000000")){//R-type instructions - if(instructionCode.substring(28,32).matches("0000")){ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("ALU_out010"); - System.out.println("ALU_out010 type " + instructionCode.substring(28,32)); - } - else if(instructionCode.substring(28,32).matches("0010")){ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("ALU_out110"); - System.out.println("ALU_out110 type " + instructionCode.substring(28,32)); - } - else if(instructionCode.substring(28,32).matches("0100")){ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("ALU_out000"); - System.out.println("ALU_out000 type " + instructionCode.substring(28,32)); - } - else if(instructionCode.substring(28,32).matches("0101")){ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("ALU_out001"); - System.out.println("ALU_out001 type " + instructionCode.substring(28,32)); - } - else{ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("ALU_out111"); - System.out.println("ALU_out111 type " + instructionCode.substring(28,32)); - } - } - else if(instructionCode.substring(0,6).matches("00001[0-1]")){ //J-type instructions - color = datapath_mapItem.getElementsByTagName("color_Jtype"); - System.out.println("jtype"); - } - else if(instructionCode.substring(0,6).matches("100[0-1][0-1][0-1]")){ //LOAD type instructions - color = datapath_mapItem.getElementsByTagName("color_LOADtype"); - System.out.println("load type"); - } - else if(instructionCode.substring(0,6).matches("101[0-1][0-1][0-1]")){ //LOAD type instructions - color = datapath_mapItem.getElementsByTagName("color_STOREtype"); - System.out.println("store type"); - } - else if(instructionCode.substring(0,6).matches("0001[0-1][0-1]")){ //BRANCH type instructions - color = datapath_mapItem.getElementsByTagName("color_BRANCHtype"); - System.out.println("branch type"); - } - else{ - color = datapath_mapItem.getElementsByTagName("color_Itype"); - System.out.println("immediate type"); - } - - - other_axis = datapath_mapItem.getElementsByTagName("other_axis"); - isMovingXaxis = datapath_mapItem.getElementsByTagName("isMovingXaxis"); - targetVertex = datapath_mapItem.getElementsByTagName("target_vertex"); - isText = datapath_mapItem.getElementsByTagName("is_text"); - - for(int j= 0; j < index_vertex.getLength(); j++){ - Vertex vert = new Vertex(Integer.parseInt(index_vertex.item(j).getTextContent()), Integer.parseInt(init.item(j).getTextContent()), - Integer.parseInt(end.item(j).getTextContent()), name.item(j).getTextContent(), Integer.parseInt(other_axis.item(j).getTextContent()), - Boolean.parseBoolean(isMovingXaxis.item(j).getTextContent()), color.item(j).getTextContent(), targetVertex.item(j).getTextContent(), Boolean.parseBoolean(isText.item(j).getTextContent())); - vertexList.add(vert); - } - } - //loading matrix of control of vertex. - outputGraph = new Vector>(); - vertexTraversed = new ArrayList(); - int size = vertexList.size(); - Vertex vertex; - ArrayList targetList; - for(int i = 0; i < vertexList.size(); i++){ - vertex = vertexList.get(i); - targetList = vertex.getTargetVertex(); - Vector vertexOfTargets = new Vector(); - for(int k = 0; k < targetList.size(); k++){ - vertexOfTargets.add(vertexList.get(targetList.get(k))); - } - outputGraph.add(vertexOfTargets); - } - for(int i=0; i< outputGraph.size(); i++){ - Vector vert = outputGraph.get(i); - } - - vertexList.get(0).setActive(true); - vertexTraversed.add(vertexList.get(0)); - } - catch (Exception e) { - e.printStackTrace(); - } - - } - -//set the initial state of the variables that controls the animation, and start the timer that triggers the animation. - public void startAnimation(String codeInstruction){ - instructionCode = codeInstruction; - new Timer(PERIOD, this).start(); // start timer - this.repaint(); - } - - //initialize the image of datapath. - private void initImages(){ - try { - BufferedImage im; - if(datapatTypeUsed == register){ - im = ImageIO.read( - getClass().getResource(Globals.imagesPath+"register.png") ); - } - else if(datapatTypeUsed == control){ - im = ImageIO.read( - getClass().getResource(Globals.imagesPath+"control.png") ); - } - else if(datapatTypeUsed == aluControl){ - im = ImageIO.read( - getClass().getResource(Globals.imagesPath+"ALUcontrol.png") ); - } - else{ - im = ImageIO.read( - getClass().getResource(Globals.imagesPath+"alu.png") ); - } - - int transparency = im.getColorModel().getTransparency(); - datapath = gc.createCompatibleImage( - im.getWidth(), im.getHeight(), - transparency ); - g2d = datapath.createGraphics(); - g2d.drawImage(im,0,0,null); - g2d.dispose(); - } - catch(IOException e) { - System.out.println("Load Image error for " + - getClass().getResource(Globals.imagesPath+"register.png") + ":\n" + e); - } - } - - public void updateDisplay(){ - this.repaint(); - } - - public void actionPerformed(ActionEvent e) - // triggered by the timer: update, repaint - { - if (justStarted) - justStarted = false; - if(xIsMoving) - indexX++; - if(yIsMoving) - indexY--; - repaint(); - } + if (instructionCode.startsWith("000000")) + {//R-type instructions + color = datapath_mapItem.getElementsByTagName("color_Rtype"); + //System.out.println("rtype"); + } + else if (instructionCode.substring(0, 6).matches("00001[0-1]")) + { //J-type instructions + color = datapath_mapItem.getElementsByTagName("color_Jtype"); + //System.out.println("jtype"); + } + else if (instructionCode.substring(0, 6).matches("100[0-1][0-1][0-1]")) + { //LOAD type instructions + color = datapath_mapItem.getElementsByTagName("color_LOADtype"); + //System.out.println("load type"); + } + else if (instructionCode.substring(0, 6).matches("101[0-1][0-1][0-1]")) + { //LOAD type instructions + color = datapath_mapItem.getElementsByTagName("color_STOREtype"); + //System.out.println("store type"); + } + else if (instructionCode.substring(0, 6).matches("0001[0-1][0-1]")) + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("color_BRANCHtype"); + //System.out.println("branch type"); + } + else + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("color_Itype"); + //System.out.println("immediate type"); + } - public void paintComponent(Graphics g) - { - super.paintComponent(g); - g2d = (Graphics2D)g; - // use antialiasing - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, - RenderingHints.VALUE_ANTIALIAS_ON); - // smoother (and slower) image transformations (e.g. for resizing) - g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2d = (Graphics2D)g; - drawImage(g2d, datapath, 0,0,null); - executeAnimation(g); - counter = (counter + 1)% 100; - g2d.dispose(); - - } + other_axis = datapath_mapItem.getElementsByTagName("other_axis"); + isMovingXaxis = datapath_mapItem.getElementsByTagName("isMovingXaxis"); + targetVertex = datapath_mapItem.getElementsByTagName("target_vertex"); + isText = datapath_mapItem.getElementsByTagName("is_text"); - private void drawImage(Graphics2D g2d, BufferedImage im, int x, int y,Color c){ - if (im == null) { - g2d.setColor(c); - g2d.fillOval(x, y, 20, 20); - g2d.setColor(Color.black); - g2d.drawString(" ", x, y); - } - else - g2d.drawImage(im, x, y, this); - } + for (int j = 0; j < index_vertex.getLength(); j++) + { + Vertex vert = new Vertex(Integer.parseInt(index_vertex.item(j).getTextContent()), Integer.parseInt(init.item(j).getTextContent()), + Integer.parseInt(end.item(j).getTextContent()), name.item(j).getTextContent(), Integer.parseInt(other_axis.item(j).getTextContent()), + Boolean.parseBoolean(isMovingXaxis.item(j).getTextContent()), color.item(j).getTextContent(), targetVertex.item(j).getTextContent(), Boolean.parseBoolean(isText.item(j).getTextContent())); + vertexList.add(vert); + } + } + //loading matrix of control of vertex. + outputGraph = new Vector>(); + vertexTraversed = new ArrayList(); + int size = vertexList.size(); + Vertex vertex; + ArrayList targetList; + for (int i = 0; i < vertexList.size(); i++) + { + vertex = vertexList.get(i); + targetList = vertex.getTargetVertex(); + Vector vertexOfTargets = new Vector(); + for (int k = 0; k < targetList.size(); k++) + { + vertexOfTargets.add(vertexList.get(targetList.get(k))); + } + outputGraph.add(vertexOfTargets); + } + for (int i = 0; i < outputGraph.size(); i++) + { + Vector vert = outputGraph.get(i); + } -//draw lines. - //method to draw the lines that run from left to right. - public void printTrackLtoR(Vertex v){ - int size; - int[] track; - size = v.getEnd() - v.getInit(); - track = new int[size]; - for(int i = 0; i < size; i++) - track[i] = v.getInit()+i; - if(v.isActive() == true){ - v.setFirst_interaction(false); - for(int i = 0; i < size; i++){ - if(track[i] <= v.getCurrent()){ - g2d.setColor(v.getColor()); - g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); - } - } - if (v.getCurrent() == track[size-1]) - v.setActive(false); - v.setCurrent(v.getCurrent()+1); - } - else if(v.isFirst_interaction() == false){ - for(int i = 0; i < size ; i++){ - g2d.setColor(v.getColor()); - g2d.fillRect(track[i],v.getOppositeAxis(), 3, 3); - } - } - - } + vertexList.get(0).setActive(true); + vertexTraversed.add(vertexList.get(0)); + } + catch (Exception e) + { + e.printStackTrace(); + } - //method to draw the lines that run from right to left. - //public boolean printTrackRtoL(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, -// boolean active, boolean firstInteraction){ -public void printTrackRtoL(Vertex v){ - int size; - int[] track; - size = v.getInit() - v.getEnd(); - track = new int[size]; - - for(int i = 0; i < size; i++) - track[i] = v.getInit()-i; + } - if(v.isActive() == true){ - v.setFirst_interaction(false); - for(int i = 0; i < size; i++){ - if(track[i] >= v.getCurrent()){ - g2d.setColor(v.getColor()); - g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); - } - } - if (v.getCurrent() == track[size-1]) - v.setActive(false); - - v.setCurrent(v.getCurrent()-1); - } - else if(v.isFirst_interaction() == false){ - for(int i = 0; i < size ; i++){ - g2d.setColor(v.getColor()); - g2d.fillRect(track[i],v.getOppositeAxis(), 3, 3); - } - } - } + public void importXmlDatapathMapAluControl(String xmlName, String elementTree) + { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(false); + DocumentBuilder docBuilder; + try + { + docBuilder = dbf.newDocumentBuilder(); + Document doc = docBuilder.parse(getClass().getResource(xmlName).toString()); + Element root = doc.getDocumentElement(); + Element datapath_mapItem; + NodeList index_vertex, name, init, end, color, other_axis, isMovingXaxis, targetVertex, sourceVertex, isText; + NodeList datapath_mapList = root.getElementsByTagName(elementTree); + for (int i = 0; i < datapath_mapList.getLength(); i++) + { //extract the vertex of the xml input and encapsulate into the vertex object + datapath_mapItem = (Element) datapath_mapList.item(i); + index_vertex = datapath_mapItem.getElementsByTagName("num_vertex"); + name = datapath_mapItem.getElementsByTagName("name"); + init = datapath_mapItem.getElementsByTagName("init"); + end = datapath_mapItem.getElementsByTagName("end"); + //definition of colors line - //method to draw the lines that run from down to top. -// public boolean printTrackDtoU(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, -// boolean active, boolean firstInteraction){ - public void printTrackDtoU(Vertex v){ - int size; - int[] track; - - if(v.getInit() > v.getEnd()){ - size = v.getInit() - v.getEnd(); - track = new int[size]; - for(int i = 0; i < size; i++) - track[i] = v.getInit()-i; - } - else{ - size = v.getEnd() - v.getInit(); - track = new int[size]; - for(int i = 0; i < size; i++) - track[i] = v.getInit()+i; - } - - if(v.isActive() == true){ - v.setFirst_interaction(false); - for(int i = 0; i < size; i++){ - if(track[i] >= v.getCurrent()){ - g2d.setColor(v.getColor()); - g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); - } - } - if (v.getCurrent() == track[size-1]) - v.setActive(false); - v.setCurrent(v.getCurrent()-1); - - } - else if(v.isFirst_interaction() == false){ - for(int i = 0; i < size; i++){ - g2d.setColor(v.getColor()); - g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); - } - } - } - //method to draw the lines that run from top to down. -// public boolean printTrackUtoD(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, -// boolean active, boolean firstInteraction){ - public void printTrackUtoD(Vertex v){ - - int size; - int[] track; - size = v.getEnd() - v.getInit(); - track = new int[size]; - - for(int i = 0; i < size; i++) - track[i] = v.getInit()+i; - - if(v.isActive() == true){ - v.setFirst_interaction(false); - for(int i = 0; i < size; i++){ - if(track[i] <= v.getCurrent()){ - g2d.setColor(v.getColor()); - g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); - } - - } - if (v.getCurrent() == track[size-1]) - v.setActive(false); - v.setCurrent(v.getCurrent()+1); - } - else if(v.isFirst_interaction() == false){ - for(int i = 0; i < size; i++){ - g2d.setColor(v.getColor()); - g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); - } - } - } + if (instructionCode.startsWith("000000")) + {//R-type instructions + if (instructionCode.substring(28, 32).matches("0000")) + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("ALU_out010"); + System.out.println("ALU_out010 type " + instructionCode.substring(28, 32)); + } + else if (instructionCode.substring(28, 32).matches("0010")) + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("ALU_out110"); + System.out.println("ALU_out110 type " + instructionCode.substring(28, 32)); + } + else if (instructionCode.substring(28, 32).matches("0100")) + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("ALU_out000"); + System.out.println("ALU_out000 type " + instructionCode.substring(28, 32)); + } + else if (instructionCode.substring(28, 32).matches("0101")) + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("ALU_out001"); + System.out.println("ALU_out001 type " + instructionCode.substring(28, 32)); + } + else + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("ALU_out111"); + System.out.println("ALU_out111 type " + instructionCode.substring(28, 32)); + } + } + else if (instructionCode.substring(0, 6).matches("00001[0-1]")) + { //J-type instructions + color = datapath_mapItem.getElementsByTagName("color_Jtype"); + System.out.println("jtype"); + } + else if (instructionCode.substring(0, 6).matches("100[0-1][0-1][0-1]")) + { //LOAD type instructions + color = datapath_mapItem.getElementsByTagName("color_LOADtype"); + System.out.println("load type"); + } + else if (instructionCode.substring(0, 6).matches("101[0-1][0-1][0-1]")) + { //LOAD type instructions + color = datapath_mapItem.getElementsByTagName("color_STOREtype"); + System.out.println("store type"); + } + else if (instructionCode.substring(0, 6).matches("0001[0-1][0-1]")) + { //BRANCH type instructions + color = datapath_mapItem.getElementsByTagName("color_BRANCHtype"); + System.out.println("branch type"); + } + else + { + color = datapath_mapItem.getElementsByTagName("color_Itype"); + System.out.println("immediate type"); + } - //convert binnary value to integer. - public String parseBinToInt(String code){ - int value = 0; + other_axis = datapath_mapItem.getElementsByTagName("other_axis"); + isMovingXaxis = datapath_mapItem.getElementsByTagName("isMovingXaxis"); + targetVertex = datapath_mapItem.getElementsByTagName("target_vertex"); + isText = datapath_mapItem.getElementsByTagName("is_text"); - for(int i =code.length()-1; i >= 0; i--){ - if("1".equals(code.substring(i,i+1))){ - value = value + (int)Math.pow(2,code.length()-i-1); - } - } - - return Integer.toString(value); - } - - //set and execute the information about the current position of each line of information in the animation, - //verifies the previous status of the animation and increment the position of each line that interconnect the unit function. - private void executeAnimation(Graphics g){ - g2d = (Graphics2D)g; - Vertex vert; - for(int i = 0; i < vertexTraversed.size(); i++){ - vert = vertexTraversed.get(i); - if(vert.isMovingXaxis == true){ - if(vert.getDirection() == vert.movingLeft){ - printTrackLtoR(vert); - if(vert.isActive() == false){ - int j = vert.getTargetVertex().size(); - Vertex tempVertex; - for(int k = 0; k < j; k++){ - tempVertex = outputGraph.get(vert.getNumIndex()).get(k); - Boolean hasThisVertex = false; - for(int m = 0 ; m < vertexTraversed.size(); m++){ - if(tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) - hasThisVertex = true; - } - if(hasThisVertex == false){ - outputGraph.get(vert.getNumIndex()).get(k).setActive(true); - vertexTraversed.add( outputGraph.get(vert.getNumIndex()).get(k)); - } - } - } - } - else{ - printTrackRtoL(vert); - if(vert.isActive() == false){ - int j = vert.getTargetVertex().size(); - Vertex tempVertex; - for(int k = 0; k < j; k++){ - tempVertex = outputGraph.get(vert.getNumIndex()).get(k); - Boolean hasThisVertex = false; - for(int m = 0 ; m < vertexTraversed.size(); m++){ - if(tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) - hasThisVertex = true; - } - if(hasThisVertex == false){ - outputGraph.get(vert.getNumIndex()).get(k).setActive(true); - vertexTraversed.add( outputGraph.get(vert.getNumIndex()).get(k)); - } - } - } - } - } //end of condition of X axis - else{ - if(vert.getDirection() == vert.movingDownside){ - if(vert.isText == true) - ; - else - printTrackDtoU(vert); - - if(vert.isActive() == false){ - int j = vert.getTargetVertex().size(); - Vertex tempVertex; - for(int k = 0; k < j; k++){ - tempVertex = outputGraph.get(vert.getNumIndex()).get(k); - Boolean hasThisVertex = false; - for(int m = 0 ; m < vertexTraversed.size(); m++){ - if(tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) - hasThisVertex = true; - } - if(hasThisVertex == false){ - outputGraph.get(vert.getNumIndex()).get(k).setActive(true); - vertexTraversed.add( outputGraph.get(vert.getNumIndex()).get(k)); - } - } - } - - } - else{ - printTrackUtoD(vert); - if(vert.isActive() == false){ - int j = vert.getTargetVertex().size(); - Vertex tempVertex; - for(int k = 0; k < j; k++){ - tempVertex = outputGraph.get(vert.getNumIndex()).get(k); - Boolean hasThisVertex = false; - for(int m = 0 ; m < vertexTraversed.size(); m++){ - if(tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) - hasThisVertex = true; - } - if(hasThisVertex == false){ - outputGraph.get(vert.getNumIndex()).get(k).setActive(true); - vertexTraversed.add( outputGraph.get(vert.getNumIndex()).get(k)); - } - } - } - } - } - } - } + for (int j = 0; j < index_vertex.getLength(); j++) + { + Vertex vert = new Vertex(Integer.parseInt(index_vertex.item(j).getTextContent()), Integer.parseInt(init.item(j).getTextContent()), + Integer.parseInt(end.item(j).getTextContent()), name.item(j).getTextContent(), Integer.parseInt(other_axis.item(j).getTextContent()), + Boolean.parseBoolean(isMovingXaxis.item(j).getTextContent()), color.item(j).getTextContent(), targetVertex.item(j).getTextContent(), Boolean.parseBoolean(isText.item(j).getTextContent())); + vertexList.add(vert); + } + } + //loading matrix of control of vertex. + outputGraph = new Vector>(); + vertexTraversed = new ArrayList(); + int size = vertexList.size(); + Vertex vertex; + ArrayList targetList; + for (int i = 0; i < vertexList.size(); i++) + { + vertex = vertexList.get(i); + targetList = vertex.getTargetVertex(); + Vector vertexOfTargets = new Vector(); + for (int k = 0; k < targetList.size(); k++) + { + vertexOfTargets.add(vertexList.get(targetList.get(k))); + } + outputGraph.add(vertexOfTargets); + } + for (int i = 0; i < outputGraph.size(); i++) + { + Vector vert = outputGraph.get(i); + } + + vertexList.get(0).setActive(true); + vertexTraversed.add(vertexList.get(0)); + } + catch (Exception e) + { + e.printStackTrace(); + } + + } + + //set the initial state of the variables that controls the animation, and start the timer that triggers the animation. + public void startAnimation(String codeInstruction) + { + instructionCode = codeInstruction; + new Timer(PERIOD, this).start(); // start timer + this.repaint(); + } + + //initialize the image of datapath. + private void initImages() + { + try + { + BufferedImage im; + if (datapatTypeUsed == register) + { + im = ImageIO.read( + getClass().getResource(Globals.imagesPath + "register.png")); + } + else if (datapatTypeUsed == control) + { + im = ImageIO.read( + getClass().getResource(Globals.imagesPath + "control.png")); + } + else if (datapatTypeUsed == aluControl) + { + im = ImageIO.read( + getClass().getResource(Globals.imagesPath + "ALUcontrol.png")); + } + else + { + im = ImageIO.read( + getClass().getResource(Globals.imagesPath + "alu.png")); + } + + int transparency = im.getColorModel().getTransparency(); + datapath = gc.createCompatibleImage( + im.getWidth(), im.getHeight(), + transparency); + g2d = datapath.createGraphics(); + g2d.drawImage(im, 0, 0, null); + g2d.dispose(); + } + catch (IOException e) + { + System.out.println("Load Image error for " + + getClass().getResource(Globals.imagesPath + "register.png") + ":\n" + e); + } + } + + public void updateDisplay() + { + this.repaint(); + } + + public void actionPerformed(ActionEvent e) + // triggered by the timer: update, repaint + { + if (justStarted) + { + justStarted = false; + } + if (xIsMoving) + { + indexX++; + } + if (yIsMoving) + { + indexY--; + } + repaint(); + } + + public void paintComponent(Graphics g) + { + super.paintComponent(g); + g2d = (Graphics2D) g; + // use antialiasing + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + // smoother (and slower) image transformations (e.g. for resizing) + g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2d = (Graphics2D) g; + drawImage(g2d, datapath, 0, 0, null); + executeAnimation(g); + counter = (counter + 1) % 100; + g2d.dispose(); + + } + + private void drawImage(Graphics2D g2d, BufferedImage im, int x, int y, Color c) + { + if (im == null) + { + g2d.setColor(c); + g2d.fillOval(x, y, 20, 20); + g2d.setColor(Color.black); + g2d.drawString(" ", x, y); + } + else + { + g2d.drawImage(im, x, y, this); + } + } + + //draw lines. + //method to draw the lines that run from left to right. + public void printTrackLtoR(Vertex v) + { + int size; + int[] track; + size = v.getEnd() - v.getInit(); + track = new int[size]; + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() + i; + } + if (v.isActive()) + { + v.setFirst_interaction(false); + for (int i = 0; i < size; i++) + { + if (track[i] <= v.getCurrent()) + { + g2d.setColor(v.getColor()); + g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); + } + } + if (v.getCurrent() == track[size - 1]) + { + v.setActive(false); + } + v.setCurrent(v.getCurrent() + 1); + } + else if (!v.isFirst_interaction()) + { + for (int i = 0; i < size; i++) + { + g2d.setColor(v.getColor()); + g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); + } + } + + } + + //method to draw the lines that run from right to left. + //public boolean printTrackRtoL(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, + // boolean active, boolean firstInteraction){ + public void printTrackRtoL(Vertex v) + { + int size; + int[] track; + size = v.getInit() - v.getEnd(); + track = new int[size]; + + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() - i; + } + + if (v.isActive()) + { + v.setFirst_interaction(false); + for (int i = 0; i < size; i++) + { + if (track[i] >= v.getCurrent()) + { + g2d.setColor(v.getColor()); + g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); + } + } + if (v.getCurrent() == track[size - 1]) + { + v.setActive(false); + } + + v.setCurrent(v.getCurrent() - 1); + } + else if (!v.isFirst_interaction()) + { + for (int i = 0; i < size; i++) + { + g2d.setColor(v.getColor()); + g2d.fillRect(track[i], v.getOppositeAxis(), 3, 3); + } + } + } + + //method to draw the lines that run from down to top. + // public boolean printTrackDtoU(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, + // boolean active, boolean firstInteraction){ + public void printTrackDtoU(Vertex v) + { + int size; + int[] track; + + if (v.getInit() > v.getEnd()) + { + size = v.getInit() - v.getEnd(); + track = new int[size]; + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() - i; + } + } + else + { + size = v.getEnd() - v.getInit(); + track = new int[size]; + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() + i; + } + } + + if (v.isActive()) + { + v.setFirst_interaction(false); + for (int i = 0; i < size; i++) + { + if (track[i] >= v.getCurrent()) + { + g2d.setColor(v.getColor()); + g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); + } + } + if (v.getCurrent() == track[size - 1]) + { + v.setActive(false); + } + v.setCurrent(v.getCurrent() - 1); + + } + else if (!v.isFirst_interaction()) + { + for (int i = 0; i < size; i++) + { + g2d.setColor(v.getColor()); + g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); + } + } + } + + //method to draw the lines that run from top to down. + // public boolean printTrackUtoD(int init, int end ,int currentIndex, Graphics2D g2d, Color color, int otherAxis, + // boolean active, boolean firstInteraction){ + public void printTrackUtoD(Vertex v) + { + + int size; + int[] track; + size = v.getEnd() - v.getInit(); + track = new int[size]; + + for (int i = 0; i < size; i++) + { + track[i] = v.getInit() + i; + } + + if (v.isActive()) + { + v.setFirst_interaction(false); + for (int i = 0; i < size; i++) + { + if (track[i] <= v.getCurrent()) + { + g2d.setColor(v.getColor()); + g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); + } + + } + if (v.getCurrent() == track[size - 1]) + { + v.setActive(false); + } + v.setCurrent(v.getCurrent() + 1); + } + else if (!v.isFirst_interaction()) + { + for (int i = 0; i < size; i++) + { + g2d.setColor(v.getColor()); + g2d.fillRect(v.getOppositeAxis(), track[i], 3, 3); + } + } + } + + //convert binnary value to integer. + public String parseBinToInt(String code) + { + int value = 0; + + for (int i = code.length() - 1; i >= 0; i--) + { + if ("1".equals(code.substring(i, i + 1))) + { + value = value + (int) Math.pow(2, code.length() - i - 1); + } + } + + return Integer.toString(value); + } + + //set and execute the information about the current position of each line of information in the animation, + //verifies the previous status of the animation and increment the position of each line that interconnect the unit function. + private void executeAnimation(Graphics g) + { + g2d = (Graphics2D) g; + Vertex vert; + for (int i = 0; i < vertexTraversed.size(); i++) + { + vert = vertexTraversed.get(i); + if (vert.isMovingXaxis) + { + if (vert.getDirection() == Vertex.movingLeft) + { + printTrackLtoR(vert); + if (!vert.isActive()) + { + int j = vert.getTargetVertex().size(); + Vertex tempVertex; + for (int k = 0; k < j; k++) + { + tempVertex = outputGraph.get(vert.getNumIndex()).get(k); + Boolean hasThisVertex = false; + for (int m = 0; m < vertexTraversed.size(); m++) + { + if (tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) + { + hasThisVertex = true; + break; + } + } + if (!hasThisVertex) + { + outputGraph.get(vert.getNumIndex()).get(k).setActive(true); + vertexTraversed.add(outputGraph.get(vert.getNumIndex()).get(k)); + } + } + } + } + else + { + printTrackRtoL(vert); + if (!vert.isActive()) + { + int j = vert.getTargetVertex().size(); + Vertex tempVertex; + for (int k = 0; k < j; k++) + { + tempVertex = outputGraph.get(vert.getNumIndex()).get(k); + Boolean hasThisVertex = false; + for (int m = 0; m < vertexTraversed.size(); m++) + { + if (tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) + { + hasThisVertex = true; + break; + } + } + if (!hasThisVertex) + { + outputGraph.get(vert.getNumIndex()).get(k).setActive(true); + vertexTraversed.add(outputGraph.get(vert.getNumIndex()).get(k)); + } + } + } + } + } //end of condition of X axis + else + { + if (vert.getDirection() == Vertex.movingDownside) + { + if (vert.isText) + ; + else + { + printTrackDtoU(vert); + } + + if (!vert.isActive()) + { + int j = vert.getTargetVertex().size(); + Vertex tempVertex; + for (int k = 0; k < j; k++) + { + tempVertex = outputGraph.get(vert.getNumIndex()).get(k); + Boolean hasThisVertex = false; + for (int m = 0; m < vertexTraversed.size(); m++) + { + if (tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) + { + hasThisVertex = true; + break; + } + } + if (!hasThisVertex) + { + outputGraph.get(vert.getNumIndex()).get(k).setActive(true); + vertexTraversed.add(outputGraph.get(vert.getNumIndex()).get(k)); + } + } + } + + } + else + { + printTrackUtoD(vert); + if (!vert.isActive()) + { + int j = vert.getTargetVertex().size(); + Vertex tempVertex; + for (int k = 0; k < j; k++) + { + tempVertex = outputGraph.get(vert.getNumIndex()).get(k); + Boolean hasThisVertex = false; + for (int m = 0; m < vertexTraversed.size(); m++) + { + if (tempVertex.getNumIndex() == vertexTraversed.get(m).getNumIndex()) + { + hasThisVertex = true; + break; + } + } + if (!hasThisVertex) + { + outputGraph.get(vert.getNumIndex()).get(k).setActive(true); + vertexTraversed.add(outputGraph.get(vert.getNumIndex()).get(k)); + } + } + } + } + } + } + } + + class Vertex + { + public static final int movingUpside = 1; + + public static final int movingDownside = 2; + + public static final int movingLeft = 3; + + public static final int movingRight = 4; + + public int direction; + + public int oppositeAxis; + + private int numIndex; + + private int init; + + private int end; + + private int current; + + private String name; + + private boolean isMovingXaxis; + + private Color color; + + private boolean first_interaction; + + private boolean active; + + private final boolean isText; + + private final ArrayList targetVertex; + + public Vertex(int index, int init, int end, String name, int oppositeAxis, boolean isMovingXaxis, + String listOfColors, String listTargetVertex, boolean isText) + { + this.numIndex = index; + this.init = init; + this.current = this.init; + this.end = end; + this.name = name; + this.oppositeAxis = oppositeAxis; + this.isMovingXaxis = isMovingXaxis; + this.first_interaction = true; + this.active = false; + this.isText = isText; + this.color = new Color(0, 153, 0); + if (isMovingXaxis) + { + if (init < end) + { + direction = movingLeft; + } + else + { + direction = movingRight; + } + + } + else + { + if (init < end) + { + direction = movingUpside; + } + else + { + direction = movingDownside; + } + } + String[] list = listTargetVertex.split("#"); + targetVertex = new ArrayList(); + for (int i = 0; i < list.length; i++) + { + targetVertex.add(Integer.parseInt(list[i])); + // System.out.println("Adding " + i + " " + Integer.parseInt(list[i])+ " in target"); + } + String[] listColor = listOfColors.split("#"); + this.color = new Color(Integer.parseInt(listColor[0]), Integer.parseInt(listColor[1]), Integer.parseInt(listColor[2])); + } + + public int getDirection() + { + return direction; + } + + public boolean isText() + { + return this.isText; + } + + + public ArrayList getTargetVertex() + { + return targetVertex; + } + + public int getNumIndex() + { + return numIndex; + } + + public void setNumIndex(int numIndex) + { + this.numIndex = numIndex; + } + + public int getInit() + { + return init; + } + + public void setInit(int init) + { + this.init = init; + } + + public int getEnd() + { + return end; + } + + public void setEnd(int end) + { + this.end = end; + } + + public int getCurrent() + { + return current; + } + + public void setCurrent(int current) + { + this.current = current; + } + + public String getName() + { + return name; + } + + public void setName(String name) + { + this.name = name; + } + + public int getOppositeAxis() + { + return oppositeAxis; + } + + public void setOppositeAxis(int oppositeAxis) + { + this.oppositeAxis = oppositeAxis; + } + + public boolean isMovingXaxis() + { + return isMovingXaxis; + } + + public void setMovingXaxis(boolean isMovingXaxis) + { + this.isMovingXaxis = isMovingXaxis; + } + + public Color getColor() + { + return color; + } + + public void setColor(Color color) + { + this.color = color; + } + + public boolean isFirst_interaction() + { + return first_interaction; + } + + public void setFirst_interaction(boolean first_interaction) + { + this.first_interaction = first_interaction; + } + + public boolean isActive() + { + return active; + } + + public void setActive(boolean active) + { + this.active = active; + } + } } diff --git a/src/main/java/mars/util/Binary.java b/src/main/java/mars/util/Binary.java index 6fde6a5..bd7a136 100644 --- a/src/main/java/mars/util/Binary.java +++ b/src/main/java/mars/util/Binary.java @@ -1,6 +1,8 @@ - package mars.util; - import mars.Globals; - import java.util.*; +package mars.util; + +import mars.Globals; + +import java.util.Arrays; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -29,358 +31,409 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + /** * Some utility methods for working with binary representations. - * + * * @author Pete Sanderson, Ken Vollmar, and Jason Bumgarner * @version July 2005 */ - public class Binary { - - // Using int value 0-15 as index, yields equivalent hex digit as char. - private static char[] chars = - {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; +public class Binary +{ + // Use this to produce String equivalent of unsigned int value (add it to int value, result is long) - private static final long UNSIGNED_BASE = (long)0x7FFFFFFF + (long)0x7FFFFFFF +(long)2; //0xFFFFFFFF+1 - + private static final long UNSIGNED_BASE = (long) 0x7FFFFFFF + (long) 0x7FFFFFFF + (long) 2; //0xFFFFFFFF+1 + + // Using int value 0-15 as index, yields equivalent hex digit as char. + private static final char[] chars = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + /** * Translate int value into a String consisting of '1's and '0's. - * + * * @param value The int value to convert. * @param length The number of bit positions, starting at least significant, to process. * @return String consisting of '1' and '0' characters corresponding to the requested binary sequence. **/ - - public static String intToBinaryString(int value, int length) { - char[] result = new char[length]; - int index = length-1; - for (int i=0; i= 0) { - if (value.charAt(position) == '1') - result = result + pow; - pow *= 2; - position--; - rep++; + while (rep < 4 && position >= 0) + { + if (value.charAt(position) == '1') + { + result = result + pow; + } + pow *= 2; + position--; + rep++; } - hexChars[digits-digs+1] = chars[result]; - } - return new String(hexChars); - } - + hexChars[digits - digs + 1] = chars[result]; + } + return new String(hexChars); + } + /** - * Translate String consisting of hexadecimal digits into String consisting of - * corresponding binary digits ('1's and '0's). No length limit. - * String position 0 will have most-significant bit, position length-1 has least-significant. - * - * @param value String containing '0', '1', ...'f' - * characters which form hexadecimal. Letters may be either upper or lower case. - * Works either with or without leading "Ox". + * Translate String consisting of hexadecimal digits into String consisting of corresponding binary digits ('1's and + * '0's). No length limit. String position 0 will have most-significant bit, position length-1 has + * least-significant. + * + * @param value String containing '0', '1', ...'f' characters which form hexadecimal. Letters may be either + * upper or lower case. Works either with or without leading "Ox". * @return String with equivalent value in binary. - **/ - public static String hexStringToBinaryString(String value) { - String result = ""; - // slice off leading Ox or 0X - if (value.indexOf("0x")==0 || value.indexOf("0X")==0) { + **/ + public static String hexStringToBinaryString(String value) + { + String result = ""; + // slice off leading Ox or 0X + if (value.indexOf("0x") == 0 || value.indexOf("0X") == 0) + { value = value.substring(2); - } - for (int digs = 0; digs < value.length(); digs++) { - switch (value.charAt(digs)) { - case '0' : result += "0000"; - break; - case '1' : result += "0001"; - break; - case '2' : result += "0010"; - break; - case '3' : result += "0011"; - break; - case '4' : result += "0100"; - break; - case '5' : result += "0101"; - break; - case '6' : result += "0110"; - break; - case '7' : result += "0111"; - break; - case '8' : result += "1000"; - break; - case '9' : result += "1001"; - break; - case 'a' : case 'A' : result += "1010"; - break; - case 'b' : case 'B' : result += "1011"; - break; - case 'c' : case 'C' : result += "1100"; - break; - case 'd' : case 'D' : result += "1101"; - break; - case 'e' : case 'E' : result += "1110"; - break; - case 'f' : case 'F' : result += "1111"; - break; + } + for (int digs = 0; digs < value.length(); digs++) + { + switch (value.charAt(digs)) + { + case '0': + result += "0000"; + break; + case '1': + result += "0001"; + break; + case '2': + result += "0010"; + break; + case '3': + result += "0011"; + break; + case '4': + result += "0100"; + break; + case '5': + result += "0101"; + break; + case '6': + result += "0110"; + break; + case '7': + result += "0111"; + break; + case '8': + result += "1000"; + break; + case '9': + result += "1001"; + break; + case 'a': + case 'A': + result += "1010"; + break; + case 'b': + case 'B': + result += "1011"; + break; + case 'c': + case 'C': + result += "1100"; + break; + case 'd': + case 'D': + result += "1101"; + break; + case 'e': + case 'E': + result += "1110"; + break; + case 'f': + case 'F': + result += "1111"; + break; } - } - return result; - } - + } + return result; + } + /** - * Translate String consisting of '1's and '0's into char equivalent of the corresponding - * hexadecimal digit. String limited to length 4. - * String position 0 has most-significant bit, position length-1 has least-significant. - * + * Translate String consisting of '1's and '0's into char equivalent of the corresponding hexadecimal digit. String + * limited to length 4. String position 0 has most-significant bit, position length-1 has least-significant. + * * @param value The String value to convert. - * @return char '0', '1', ...'F' which form hexadecimal equivalent of decoded String. - * If string length > 4, returns '0'. - **/ - - public static char binaryStringToHexDigit(String value) { - if (value.length() > 4) + * @return char '0', '1', ...'F' which form hexadecimal equivalent of decoded String. If string length > 4, returns + * '0'. + **/ + + public static char binaryStringToHexDigit(String value) + { + if (value.length() > 4) + { return '0'; - int result = 0; - int pow = 1; - for (int i=value.length()-1; i>=0; i--) { + } + int result = 0; + int pow = 1; + for (int i = value.length() - 1; i >= 0; i--) + { if (value.charAt(i) == '1') - result = result + pow; + { + result = result + pow; + } pow *= 2; - } - return chars[result]; - } - + } + return chars[result]; + } + /** - * Prefix a hexadecimal-indicating string "0x" to the string which is - * returned by the method "Integer.toHexString". Prepend leading zeroes - * to that string as necessary to make it always eight hexadecimal digits. + * Prefix a hexadecimal-indicating string "0x" to the string which is returned by the method "Integer.toHexString". + * Prepend leading zeroes to that string as necessary to make it always eight hexadecimal digits. * * @param d The int value to convert. * @return String containing '0', '1', ...'F' which form hexadecimal equivalent of int. */ - public static String intToHexString(int d) - { - String leadingZero = new String("0"); - String leadingX = new String("0x"); - String t = Integer.toHexString(d); - while (t.length() < 8) + public static String intToHexString(int d) + { + String leadingZero = "0"; + String leadingX = "0x"; + String t = Integer.toHexString(d); + while (t.length() < 8) + { t = leadingZero.concat(t); - - t = leadingX.concat(t); - return t; - } - + } + + t = leadingX.concat(t); + return t; + } + /** - * Returns a 6 character string representing the 16-bit hexadecimal equivalent of the - * given integer value. First two characters are "0x". It assumes value will "fit" - * in 16 bits. If non-negative, prepend leading zeroes to that string as necessary - * to make it always four hexadecimal digits. If negative, chop off the first - * four 'f' digits so result is always four hexadecimal digits + * Returns a 6 character string representing the 16-bit hexadecimal equivalent of the given integer value. First + * two characters are "0x". It assumes value will "fit" in 16 bits. If non-negative, prepend leading zeroes to + * that string as necessary to make it always four hexadecimal digits. If negative, chop off the first four 'f' + * digits so result is always four hexadecimal digits * * @param d The int value to convert. * @return String containing '0', '1', ...'F' which form hexadecimal equivalent of int. */ - public static String intToHalfHexString(int d) - { - String leadingZero = new String("0"); - String leadingX = new String("0x"); - String t = Integer.toHexString(d); - if (t.length() > 4) { - t = t.substring(t.length()-4, t.length()); - } - while (t.length() < 4) + public static String intToHalfHexString(int d) + { + String leadingZero = "0"; + String leadingX = "0x"; + String t = Integer.toHexString(d); + if (t.length() > 4) + { + t = t.substring(t.length() - 4); + } + while (t.length() < 4) + { t = leadingZero.concat(t); - - t = leadingX.concat(t); - return t; - } - - - + } + + t = leadingX.concat(t); + return t; + } + + /** - * Prefix a hexadecimal-indicating string "0x" to the string equivalent to the - * hexadecimal value in the long parameter. Prepend leading zeroes - * to that string as necessary to make it always sixteen hexadecimal digits. + * Prefix a hexadecimal-indicating string "0x" to the string equivalent to the hexadecimal value in the long + * parameter. Prepend leading zeroes to that string as necessary to make it always sixteen hexadecimal digits. * * @param value The long value to convert. * @return String containing '0', '1', ...'F' which form hexadecimal equivalent of long. */ - - public static String longToHexString(long value) { - return binaryStringToHexString(longToBinaryString(value)); - } - - + + public static String longToHexString(long value) + { + return binaryStringToHexString(longToBinaryString(value)); + } + + /** - * Produce String equivalent of integer value interpreting it as an unsigned integer. - * For instance, -1 (0xffffffff) produces "4294967295" instead of "-1". + * Produce String equivalent of integer value interpreting it as an unsigned integer. For instance, -1 (0xffffffff) + * produces "4294967295" instead of "-1". + * * @param d The int value to interpret. * @return String which forms unsigned 32 bit equivalent of int. */ - public static String unsignedIntToIntString(int d) { - return (d >= 0) ? Integer.toString(d) : Long.toString(UNSIGNED_BASE+d); - } - + public static String unsignedIntToIntString(int d) + { + return (d >= 0) ? Integer.toString(d) : Long.toString(UNSIGNED_BASE + d); + } + /** - * Produce ASCII string equivalent of integer value, interpreting it as 4 one-byte - * characters. If the value in a given byte does not correspond to a printable - * character, it will be assigned a default character (defined in config.properties) - * for a placeholder. - * + * Produce ASCII string equivalent of integer value, interpreting it as 4 one-byte characters. If the value in a + * given byte does not correspond to a printable character, it will be assigned a default character (defined in + * config.properties) for a placeholder. + * * @param d The int value to interpret * @return String that represents ASCII equivalent - */ - public static String intToAscii(int d) { - StringBuilder result = new StringBuilder(8); - for (int i=3; i>=0; i--) { + */ + public static String intToAscii(int d) + { + StringBuilder result = new StringBuilder(8); + for (int i = 3; i >= 0; i--) + { int byteValue = getByte(d, i); - result.append( (byteValue < Globals.ASCII_TABLE.length) ? Globals.ASCII_TABLE[byteValue] : Globals.ASCII_NON_PRINT ); - } - return result.toString(); - } - + result.append((byteValue < Globals.ASCII_TABLE.length) ? Globals.ASCII_TABLE[byteValue] : Globals.ASCII_NON_PRINT); + } + return result.toString(); + } + /** - * Attempt to validate given string whose characters represent a 32 bit integer. - * Integer.decode() is insufficient because it will not allow incorporation of - * hex two's complement (i.e. 0x80...0 through 0xff...f). Allows + * Attempt to validate given string whose characters represent a 32 bit integer. Integer.decode() is insufficient + * because it will not allow incorporation of hex two's complement (i.e. 0x80...0 through 0xff...f). Allows * optional negative (-) sign but no embedded spaces. * * @param s candidate string * @return returns int value represented by given string * @throws NumberFormatException if string cannot be translated into an int */ - - public static int stringToInt(String s) throws NumberFormatException { - String work = new String(s); - int result = 0; - // First, use Integer.decode(). This will validate most, but it flags - // valid hex two's complement values as exceptions. We'll catch those and - // do our own validation. - try { + + public static int stringToInt(String s) throws NumberFormatException + { + String work = s; + int result = 0; + // First, use Integer.decode(). This will validate most, but it flags + // valid hex two's complement values as exceptions. We'll catch those and + // do our own validation. + try + { result = Integer.decode(s).intValue(); - } - catch (NumberFormatException nfe) { + } + catch (NumberFormatException nfe) + { // Multistep process toward validation of hex two's complement. 3-step test: // (1) exactly 10 characters long, // (2) starts with Ox or 0X, // (3) last 8 characters are valid hex digits. - work = work.toLowerCase(); - if (work.length() == 10 && work.startsWith("0x")) { - String bitString = ""; - int index; - // while testing characters, build bit string to set up for binaryStringToInt - for (int i=2; i<10; i++) { - index = Arrays.binarySearch(chars, work.charAt(i)); - if (index < 0) { + work = work.toLowerCase(); + if (work.length() == 10 && work.startsWith("0x")) + { + String bitString = ""; + int index; + // while testing characters, build bit string to set up for binaryStringToInt + for (int i = 2; i < 10; i++) + { + index = Arrays.binarySearch(chars, work.charAt(i)); + if (index < 0) + { throw new NumberFormatException(); - } - bitString = bitString + intToBinaryString(index,4); - } - result = binaryStringToInt(bitString); - } + } + bitString = bitString + intToBinaryString(index, 4); + } + result = binaryStringToInt(bitString); + } /* The following "else" composed by Jose Baiocchi Paredes, Oct 2009. This new code will correctly translate a string representing an unsigned decimal (not hex) value whose signed value is negative. This is the decimal equivalent of the @@ -388,280 +441,313 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. when background highlighting of the Data Segment was added. Caused exceptions under certain conditions. */ - else if (!work.startsWith("0x")) { - result = 0; - for (int i=0; i> 32); // high order 32 bits - } - - - /** - * Returns int representing the bit values of the low order 32 bits of given - * 64 bit long value. - * @param longValue The long value from which to extract bits. - * @return int containing low order 32 bits of argument - **/ - public static int lowOrderLongToInt(long longValue) { - return (int) (longValue << 32 >> 32); // low order 32 bits - } - - /** - * Returns long (64 bit integer) combining the bit values of two given 32 bit - * integer values. - * @param highOrder Integer to form the high-order 32 bits of result. - * @param lowOrder Integer to form the high-order 32 bits of result. - * @return long containing concatenated 32 bit int values. - **/ - public static long twoIntsToLong(int highOrder, int lowOrder) { - return (((long)highOrder) << 32) | (((long)lowOrder) & 0xFFFFFFFFL); - } - - - - /** - * Returns the bit value of the given bit position of the given int value. - * @param value The value to read the bit from. - * @param bit bit position in range 0 (least significant) to 31 (most) - * @return 0 if the bit position contains 0, and 1 otherwise. - **/ - - public static int bitValue(int value, int bit) { - return 1 & (value >> bit); - } - - - /** - * Returns the bit value of the given bit position of the given long value. - * @param value The value to read the bit from. - * @param bit bit position in range 0 (least significant) to 63 (most) - * @return 0 if the bit position contains 0, and 1 otherwise. - **/ - - public static int bitValue(long value, int bit) { - - return (int) (1L & (value >> bit)); - } - - /** - * Sets the specified bit of the specified value to 1, and returns the result. - * @param value The value in which the bit is to be set. - * @param bit bit position in range 0 (least significant) to 31 (most) - * @return value possibly modified with given bit set to 1. - **/ - - public static int setBit(int value, int bit) { - return value | ( 1 << bit) ; - } - - - /** - * Sets the specified bit of the specified value to 0, and returns the result. - * @param value The value in which the bit is to be set. - * @param bit bit position in range 0 (least significant) to 31 (most) - * @return value possibly modified with given bit set to 0. - **/ - - public static int clearBit(int value, int bit) { - return value & ~(1 << bit); - } - - // setByte and getByte added by DPS on 12 July 2006 - - /** - * Sets the specified byte of the specified value to the low order 8 bits of - * specified replacement value, and returns the result. - * @param value The value in which the byte is to be set. - * @param bite byte position in range 0 (least significant) to 3 (most) - * @param replace value to place into that byte position - use low order 8 bits - * @return value modified value. - **/ - - public static int setByte(int value, int bite, int replace) { - return value & ~(0xFF << (bite<<3)) | ((replace & 0xFF) << (bite<<3)) ; - } - - - /** - * Gets the specified byte of the specified value. - * @param value The value in which the byte is to be retrieved. - * @param bite byte position in range 0 (least significant) to 3 (most) - * @return zero-extended byte value in low order byte. - **/ - - public static int getByte(int value, int bite) { - return value << ((3-bite)<<3) >>> 24; - } - - // KENV 1/4/05 - /** - * Parsing method to see if a string represents a hex number. - * As per http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Integer.html#decode(java.lang.String), - * a string represents a hex number if the string is in the forms: - * Signopt 0x HexDigits - * Signopt 0X HexDigits - * Signopt # HexDigits <---- Disallow this form since # is MIPS comment - * - * - * @param v String containing numeric digits (could be decimal, octal, or hex) - * - * @return Returns true if string represents a hex number, else returns false. - **/ - public static boolean isHex(String v) - { - try - { - // don't care about return value, just whether it threw exception. - // If value is EITHER a valid int OR a valid long, continue. - try { - Binary.stringToInt(v); - } - catch (NumberFormatException nfe) { - try { - Binary.stringToLong(v); - } - catch (NumberFormatException e) { - return false; // both failed; it is neither valid int nor long - } - } - + else + { + throw new NumberFormatException(); + } + } + return result; + } + + + /** + * Returns int representing the bit values of the high order 32 bits of given 64 bit long value. + * + * @param longValue The long value from which to extract bits. + * @return int containing high order 32 bits of argument + **/ + + public static int highOrderLongToInt(long longValue) + { + return (int) (longValue >> 32); // high order 32 bits + } + + + /** + * Returns int representing the bit values of the low order 32 bits of given 64 bit long value. + * + * @param longValue The long value from which to extract bits. + * @return int containing low order 32 bits of argument + **/ + public static int lowOrderLongToInt(long longValue) + { + return (int) (longValue << 32 >> 32); // low order 32 bits + } + + /** + * Returns long (64 bit integer) combining the bit values of two given 32 bit integer values. + * + * @param highOrder Integer to form the high-order 32 bits of result. + * @param lowOrder Integer to form the high-order 32 bits of result. + * @return long containing concatenated 32 bit int values. + **/ + public static long twoIntsToLong(int highOrder, int lowOrder) + { + return (((long) highOrder) << 32) | (((long) lowOrder) & 0xFFFFFFFFL); + } + + + /** + * Returns the bit value of the given bit position of the given int value. + * + * @param value The value to read the bit from. + * @param bit bit position in range 0 (least significant) to 31 (most) + * @return 0 if the bit position contains 0, and 1 otherwise. + **/ + + public static int bitValue(int value, int bit) + { + return 1 & (value >> bit); + } + + + /** + * Returns the bit value of the given bit position of the given long value. + * + * @param value The value to read the bit from. + * @param bit bit position in range 0 (least significant) to 63 (most) + * @return 0 if the bit position contains 0, and 1 otherwise. + **/ + + public static int bitValue(long value, int bit) + { + + return (int) (1L & (value >> bit)); + } + + /** + * Sets the specified bit of the specified value to 1, and returns the result. + * + * @param value The value in which the bit is to be set. + * @param bit bit position in range 0 (least significant) to 31 (most) + * @return value possibly modified with given bit set to 1. + **/ + + public static int setBit(int value, int bit) + { + return value | (1 << bit); + } + + + /** + * Sets the specified bit of the specified value to 0, and returns the result. + * + * @param value The value in which the bit is to be set. + * @param bit bit position in range 0 (least significant) to 31 (most) + * @return value possibly modified with given bit set to 0. + **/ + + public static int clearBit(int value, int bit) + { + return value & ~(1 << bit); + } + + // setByte and getByte added by DPS on 12 July 2006 + + /** + * Sets the specified byte of the specified value to the low order 8 bits of specified replacement value, and + * returns the result. + * + * @param value The value in which the byte is to be set. + * @param bite byte position in range 0 (least significant) to 3 (most) + * @param replace value to place into that byte position - use low order 8 bits + * @return value modified value. + **/ + + public static int setByte(int value, int bite, int replace) + { + return value & ~(0xFF << (bite << 3)) | ((replace & 0xFF) << (bite << 3)); + } + + + /** + * Gets the specified byte of the specified value. + * + * @param value The value in which the byte is to be retrieved. + * @param bite byte position in range 0 (least significant) to 3 (most) + * @return zero-extended byte value in low order byte. + **/ + + public static int getByte(int value, int bite) + { + return value << ((3 - bite) << 3) >>> 24; + } + + // KENV 1/4/05 + + /** + * Parsing method to see if a string represents a hex number. As per + * http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Integer.html#decode(java.lang.String), a string represents a + * hex number if the string is in the forms: Signopt 0x HexDigits Signopt 0X HexDigits Signopt # HexDigits <---- + * Disallow this form since # is MIPS comment + * + * @param v String containing numeric digits (could be decimal, octal, or hex) + * @return Returns true if string represents a hex number, else returns false. + **/ + public static boolean isHex(String v) + { + try + { + // don't care about return value, just whether it threw exception. + // If value is EITHER a valid int OR a valid long, continue. + try + { + Binary.stringToInt(v); + } + catch (NumberFormatException nfe) + { + try + { + Binary.stringToLong(v); + } + catch (NumberFormatException e) + { + return false; // both failed; it is neither valid int nor long + } + } + if ((v.charAt(0) == '-') && // sign is optional but if present can only be - - (v.charAt(1) == '0') && - (Character.toUpperCase( v.charAt(1) ) == 'X') ) - return true; // Form is Sign 0x.... and the entire string is parseable as a number - - else if ((v.charAt(0) == '0') && - (Character.toUpperCase( v.charAt(1) ) == 'X') ) - return true; // Form is 0x.... and the entire string is parseable as a number - - } - catch (StringIndexOutOfBoundsException e) + (v.charAt(1) == '0') && + (Character.toUpperCase(v.charAt(1)) == 'X')) { - return false; + return true; // Form is Sign 0x.... and the entire string is parseable as a number } - - return false; // default - } - - - - // KENV 1/4/05 - /** - * Parsing method to see if a string represents an octal number. - * As per http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Integer.html#decode(java.lang.String), - * a string represents an octal number if the string is in the forms: - * Signopt 0 OctalDigits - * - * @param v String containing numeric digits (could be decimal, octal, or hex) - * - * @return Returns true if string represents an octal number, else returns false. - **/ - public static boolean isOctal(String v) - { - // Don't mistake "0" or a string that starts "0x" for an octal string - try - { - // we don't care what value Binary.stringToInt(v) returns, just whether it threw exception + + else if ((v.charAt(0) == '0') && + (Character.toUpperCase(v.charAt(1)) == 'X')) + { + return true; // Form is 0x.... and the entire string is parseable as a number + } + + } + catch (StringIndexOutOfBoundsException e) + { + return false; + } + + return false; // default + } + + + // KENV 1/4/05 + + /** + * Parsing method to see if a string represents an octal number. As per + * http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Integer.html#decode(java.lang.String), a string represents an + * octal number if the string is in the forms: Signopt 0 OctalDigits + * + * @param v String containing numeric digits (could be decimal, octal, or hex) + * @return Returns true if string represents an octal number, else returns false. + **/ + public static boolean isOctal(String v) + { + // Don't mistake "0" or a string that starts "0x" for an octal string + try + { + // we don't care what value Binary.stringToInt(v) returns, just whether it threw exception int dontCare = Binary.stringToInt(v); - - if (isHex(v)) - return false; // String starts with "0" but continues "0x", so not octal - + + if (isHex(v)) + { + return false; // String starts with "0" but continues "0x", so not octal + } + if ((v.charAt(0) == '-') && // sign is optional but if present can only be - - (v.charAt(1) == '0') && - (v.length() > 1) ) // Has to have more digits than the leading zero - return true; // Form is Sign 0.... and the entire string is parseable as a number - + (v.charAt(1) == '0') && + (v.length() > 1)) // Has to have more digits than the leading zero + { + return true; // Form is Sign 0.... and the entire string is parseable as a number + } + else if ((v.charAt(0) == '0') && - (v.length() > 1) ) // Has to have more digits than the leading zero - return true; // Form is 0.... and the entire string is parseable as a number - - } - catch (StringIndexOutOfBoundsException e) + (v.length() > 1)) // Has to have more digits than the leading zero { - return false; + return true; // Form is 0.... and the entire string is parseable as a number } - catch (NumberFormatException e) - { - return false; - } - - return false; // default - } - - - - } + + } + catch (StringIndexOutOfBoundsException e) + { + return false; + } + catch (NumberFormatException e) + { + return false; + } + + return false; // default + } + + +} diff --git a/src/main/java/mars/util/EditorFont.java b/src/main/java/mars/util/EditorFont.java index ca623f0..47dbaf2 100644 --- a/src/main/java/mars/util/EditorFont.java +++ b/src/main/java/mars/util/EditorFont.java @@ -1,8 +1,9 @@ - package mars.util; - import mars.*; - import java.awt.*; - import java.util.*; - import java.awt.Font; +package mars.util; + +import mars.Globals; + +import java.awt.*; +import java.util.Arrays; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -31,199 +32,231 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + /** - * Specialized Font class designed to be used by both the - * settings menu methods and the Settings class. + * Specialized Font class designed to be used by both the settings menu methods and the Settings class. * * @author Pete Sanderson * @version July 2007 - */ - public class EditorFont { - - // Note: These are parallel arrays so corresponding elements must match up. - private static final String[] styleStrings = { "Plain", "Bold", "Italic", "Bold + Italic" }; - private static final int[] styleInts = { Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC }; - public static final String DEFAULT_STYLE_STRING = styleStrings[0]; - public static final int DEFAULT_STYLE_INT = styleInts[0]; - public static final int MIN_SIZE = 6; - public static final int MAX_SIZE = 72; - public static final int DEFAULT_SIZE = 12; - /* Fonts in 3 categories that are common to major Java platforms: Win, Mac, Linux. - * Monospace: Courier New and Lucida Sans Typewriter - * Serif: Georgia, Times New Roman - * Sans Serif: Ariel, Verdana - * This is according to lists published by www.codestyle.org. - */ - private static final String[] allCommonFamilies = { "Arial", "Courier New", "Georgia", - "Lucida Sans Typewriter", "Times New Roman", "Verdana" }; - - - /** - * Obtain an array of common font family names. These are guaranteed to - * be available at runtime, as they were checked against the local - * GraphicsEnvironment. - * @return Array of strings, each is a common and available font family name. - */ - - public static String[] getCommonFamilies() { - return commonFamilies; - } - - /** - * Obtain an array of all available font family names. These are guaranteed to - * be available at runtime, as they come from the local GraphicsEnvironment. - * @return Array of strings, each is an available font family name. - */ - - public static String[] getAllFamilies() { - return GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); - } - - /** - * Get array containing String values for font style names. - */ - public static String[] getFontStyleStrings() { - return styleStrings; - } - - - /** - * Given a string that represents a font style, returns the - * corresponding final int defined in Font: PLAIN, BOLD, ITALIC. It - * is not case-sensitive. - * @param style String representing the font style name - * @return The int value of the corresponding Font style constant. If the - * string does not match any style name, returns Font.PLAIN. - */ - public static int styleStringToStyleInt(String style) { - String styleLower = style.toLowerCase(); - for (int i=0; i < styleStrings.length; i++) { - if (styleLower.equals(styleStrings[i].toLowerCase())) { - return styleInts[i]; - } - } - return DEFAULT_STYLE_INT; - } - - /** - * Given an int that represents a font style from the Font class, - * returns the corresponding String. - * @param style Must be one of Font.PLAIN, Font.BOLD, Font.ITALIC. - * @return The String representation of that style. If the parameter - * is not one of the above, returns "Plain". - */ - public static String styleIntToStyleString(int style) { - for (int i=0; i < styleInts.length; i++) { - if (style == styleInts[i]) { - return styleStrings[i]; - } - } - return DEFAULT_STYLE_STRING; - } - - /** - * Given an int representing font size, returns corresponding string. - * @param size Int representing size. - * @return String value of parameter, unless it is less than MIN_SIZE (returns MIN_SIZE - * as String) or greater than MAX_SIZE (returns MAX_SIZE as String). - */ - public static String sizeIntToSizeString(int size) { - int result = (sizeMAX_SIZE) ? MAX_SIZE : size); - return String.valueOf(result); - } - - /** - * Given a String representing font size, returns corresponding int. - * @param size String representing size. - * @return int value of parameter, unless it is less than MIN_SIZE (returns - * MIN_SIZE) or greater than MAX_SIZE (returns MAX_SIZE). If the string - * cannot be parsed as a decimal integer, it returns DEFAULT_SIZE. - */ - public static int sizeStringToSizeInt(String size) { - int result = DEFAULT_SIZE; - try { - result = Integer.parseInt(size); - } - catch (NumberFormatException e) { } - return (resultMAX_SIZE) ? MAX_SIZE : result); - } - - /** - * Creates a new Font object based on the given String specifications. This - * is different than Font's constructor, which requires ints for style and size. - * It assures that defaults and size limits are applied when necessary. - * @param family String containing font family. - * @param style String containing font style. A list of available styles can - * be obtained from getFontStyleStrings(). The default of styleStringToStyleInt() - * is substituted if necessary. - * @param size String containing font size. The defaults and limits of - * sizeStringToSizeInt() are substituted if necessary. - */ - public static Font createFontFromStringValues(String family, String style, String size) { - return new Font(family, styleStringToStyleInt(style), sizeStringToSizeInt(size)); - } - - /** - * Handy utility to produce a string that substitutes spaces for all tab characters - * in the given string. The number of spaces generated is based on the position of - * the tab character and the editor's current tab size setting. - * @param string The original string - * @return New string in which spaces are substituted for tabs - * @throws NullPointerException if string is null - */ - private static final String TAB_STRING = "\t"; - private static final char TAB_CHAR = '\t'; - private static final String SPACES = " "; - - public static String substituteSpacesForTabs(String string) { - return substituteSpacesForTabs(string, Globals.getSettings().getEditorTabSize()); - } + */ +public class EditorFont +{ - /** - * Handy utility to produce a string that substitutes spaces for all tab characters - * in the given string. The number of spaces generated is based on the position of - * the tab character and the specified tab size. - * @param string The original string - * @param tabSize The number of spaces each tab character represents - * @return New string in which spaces are substituted for tabs - * @throws NullPointerException if string is null - */ - public static String substituteSpacesForTabs(String string, int tabSize) { - if (!string.contains(TAB_STRING)) + public static final int MIN_SIZE = 6; + + public static final int MAX_SIZE = 72; + + public static final int DEFAULT_SIZE = 12; + + // Note: These are parallel arrays so corresponding elements must match up. + private static final String[] styleStrings = {"Plain", "Bold", "Italic", "Bold + Italic"}; + + public static final String DEFAULT_STYLE_STRING = styleStrings[0]; + + private static final int[] styleInts = {Font.PLAIN, Font.BOLD, Font.ITALIC, Font.BOLD | Font.ITALIC}; + + public static final int DEFAULT_STYLE_INT = styleInts[0]; + + /* Fonts in 3 categories that are common to major Java platforms: Win, Mac, Linux. + * Monospace: Courier New and Lucida Sans Typewriter + * Serif: Georgia, Times New Roman + * Sans Serif: Ariel, Verdana + * This is according to lists published by www.codestyle.org. + */ + private static final String[] allCommonFamilies = {"Arial", "Courier New", "Georgia", + "Lucida Sans Typewriter", "Times New Roman", "Verdana"}; + + /** + * Handy utility to produce a string that substitutes spaces for all tab characters in the given string. The number + * of spaces generated is based on the position of the tab character and the editor's current tab size setting. + * + * @param string The original string + * @return New string in which spaces are substituted for tabs + * @throws NullPointerException if string is null + */ + private static final String TAB_STRING = "\t"; + + private static final char TAB_CHAR = '\t'; + + private static final String SPACES = " "; + + /* + * We want to vett the above list against the actual available families and give + * our client only those that are actually available. + */ + private static final String[] commonFamilies = actualCommonFamilies(); + + /** + * Obtain an array of common font family names. These are guaranteed to be available at runtime, as they were + * checked against the local GraphicsEnvironment. + * + * @return Array of strings, each is a common and available font family name. + */ + + public static String[] getCommonFamilies() + { + return commonFamilies; + } + + /** + * Obtain an array of all available font family names. These are guaranteed to be available at runtime, as they + * come from the local GraphicsEnvironment. + * + * @return Array of strings, each is an available font family name. + */ + + public static String[] getAllFamilies() + { + return GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); + } + + /** + * Get array containing String values for font style names. + */ + public static String[] getFontStyleStrings() + { + return styleStrings; + } + + /** + * Given a string that represents a font style, returns the corresponding final int defined in Font: PLAIN, BOLD, + * ITALIC. It is not case-sensitive. + * + * @param style String representing the font style name + * @return The int value of the corresponding Font style constant. If the string does not match any style name, + * returns Font.PLAIN. + */ + public static int styleStringToStyleInt(String style) + { + String styleLower = style.toLowerCase(); + for (int i = 0; i < styleStrings.length; i++) + { + if (styleLower.equals(styleStrings[i].toLowerCase())) + { + return styleInts[i]; + } + } + return DEFAULT_STYLE_INT; + } + + /** + * Given an int that represents a font style from the Font class, returns the corresponding String. + * + * @param style Must be one of Font.PLAIN, Font.BOLD, Font.ITALIC. + * @return The String representation of that style. If the parameter is not one of the above, returns "Plain". + */ + public static String styleIntToStyleString(int style) + { + for (int i = 0; i < styleInts.length; i++) + { + if (style == styleInts[i]) + { + return styleStrings[i]; + } + } + return DEFAULT_STYLE_STRING; + } + + /** + * Given an int representing font size, returns corresponding string. + * + * @param size Int representing size. + * @return String value of parameter, unless it is less than MIN_SIZE (returns MIN_SIZE as String) or greater than + * MAX_SIZE (returns MAX_SIZE as String). + */ + public static String sizeIntToSizeString(int size) + { + int result = (size < MIN_SIZE) ? MIN_SIZE : ((size > MAX_SIZE) ? MAX_SIZE : size); + return String.valueOf(result); + } + + /** + * Given a String representing font size, returns corresponding int. + * + * @param size String representing size. + * @return int value of parameter, unless it is less than MIN_SIZE (returns MIN_SIZE) or greater than MAX_SIZE + * (returns MAX_SIZE). If the string cannot be parsed as a decimal integer, it returns DEFAULT_SIZE. + */ + public static int sizeStringToSizeInt(String size) + { + int result = DEFAULT_SIZE; + try + { + result = Integer.parseInt(size); + } + catch (NumberFormatException e) + { + } + return (result < MIN_SIZE) ? MIN_SIZE : ((result > MAX_SIZE) ? MAX_SIZE : result); + } + + /** + * Creates a new Font object based on the given String specifications. This is different than Font's constructor, + * which requires ints for style and size. It assures that defaults and size limits are applied when necessary. + * + * @param family String containing font family. + * @param style String containing font style. A list of available styles can be obtained from + * getFontStyleStrings(). The default of styleStringToStyleInt() is substituted if necessary. + * @param size String containing font size. The defaults and limits of sizeStringToSizeInt() are substituted if + * necessary. + */ + public static Font createFontFromStringValues(String family, String style, String size) + { + return new Font(family, styleStringToStyleInt(style), sizeStringToSizeInt(size)); + } + + public static String substituteSpacesForTabs(String string) + { + return substituteSpacesForTabs(string, Globals.getSettings().getEditorTabSize()); + } + + /** + * Handy utility to produce a string that substitutes spaces for all tab characters in the given string. The number + * of spaces generated is based on the position of the tab character and the specified tab size. + * + * @param string The original string + * @param tabSize The number of spaces each tab character represents + * @return New string in which spaces are substituted for tabs + * @throws NullPointerException if string is null + */ + public static String substituteSpacesForTabs(String string, int tabSize) + { + if (!string.contains(TAB_STRING)) + { return string; - StringBuffer result = new StringBuffer(string); - for (int i=0; i= 0) { - result[k++] = allCommonFamilies[i]; + } + return result.toString(); + } + + private static String[] actualCommonFamilies() + { + String[] result = new String[allCommonFamilies.length]; + String[] availableFamilies = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); + Arrays.sort(availableFamilies); // not sure if necessary; is the list already alphabetical? + int k = 0; + for (int i = 0; i < allCommonFamilies.length; i++) + { + if (Arrays.binarySearch(availableFamilies, allCommonFamilies[i]) >= 0) + { + result[k++] = allCommonFamilies[i]; } - } - // If not all are found, creat a new array with only the ones that are. - if (k < allCommonFamilies.length) { + } + // If not all are found, creat a new array with only the ones that are. + if (k < allCommonFamilies.length) + { String[] temp = new String[k]; System.arraycopy(result, 0, temp, 0, k); result = temp; - } - return result; - } - } + } + return result; + } +} diff --git a/src/main/java/mars/util/FilenameFinder.java b/src/main/java/mars/util/FilenameFinder.java index 80c1470..717c859 100644 --- a/src/main/java/mars/util/FilenameFinder.java +++ b/src/main/java/mars/util/FilenameFinder.java @@ -1,16 +1,15 @@ - package mars.util; - import java.io.File; - import java.io.IOException; - import java.net.URI; - import java.net.URISyntaxException; - import java.net.URL; - import java.util.ArrayList; - import java.util.Enumeration; - import java.util.StringTokenizer; - import java.util.zip.ZipEntry; - import java.util.zip.ZipFile; +package mars.util; - import javax.swing.filechooser.FileFilter; +import javax.swing.filechooser.FileFilter; +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.StringTokenizer; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -39,103 +38,115 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + /** - * Utility class to perform necessary file-related search - * operations. One is to find file names in JAR file, - * another is to find names of files in given directory - * of normal file system. + * Utility class to perform necessary file-related search operations. One is to find file names in JAR file, another is + * to find names of files in given directory of normal file system. * * @author Pete Sanderson * @version October 2006 - */ - public class FilenameFinder - { - private static final String JAR_EXTENSION = ".jar"; - private static final String FILE_URL = "file:"; - private static final String JAR_URI_PREFIX = "jar:"; - private static final boolean NO_DIRECTORIES = false; - public static String MATCH_ALL_EXTENSIONS = "*"; - /** - * Locate files and return list of file names. Given a known relative directory path, - * it will locate it and build list of all names of files in that directory - * having the given file extension. If the "known file path" doesn't work - * because MARS is running from an executable JAR file, it will locate the - * directory in the JAR file and proceed from there. NOTE: since this uses - * the class loader to get the resource, the directory path needs to be - * relative to classpath, not absolute. To work with an arbitrary file system, - * use the other version of this overloaded method. Will NOT match directories - * that happen to have the desired extension. - * @param classLoader class loader to use - * @param directoryPath Search will be confined to this directory. Use "/" as - * separator but do NOT include starting or ending "/" (e.g. mars/tools) - * @param fileExtension Only files with this extension will be added - * to the list. Do NOT include the "." in extension. - * @return array list of matching file names as Strings. If none, list is empty. - */ - public static ArrayList getFilenameList(ClassLoader classLoader, - String directoryPath, - String fileExtension ) { - fileExtension = checkFileExtension(fileExtension); - ArrayList filenameList = new ArrayList(); - // Modified by DPS 10-July-2008 to better handle path containing space - // character (%20) and to hopefully handle path containing non-ASCII - // characters. The "toURI()" approach was suggested by MARS user - // Felipe Lessa and worked for him when running 'java Mars' but it did - // not work when executing from a jar file 'java -jar Mars.jar'. I - // took it from there and discovered that in the latter situation, - // "toURI()" created a URI prefixed with "jar:" and the "getPath()" in - // that case returns null! If you strip the "jar:" prefix and create a - // new URI from the resulting string, it works! Thanks Felipe! - // - // NOTE 5-Sep-2008: "toURI()" was introduced in Java 1.5. To maintain - // 1.4 compatibility, I need to change it to call URI constructor with - // string argument, as documented in Sun API. - // - // Modified by Ingo Kofler 24-Sept-2009 to handle multiple JAR files. - // This requires use of ClassLoader getResources() instead of - // getResource(). The former will look in all JAR files listed in - // in the java command. - // - URI uri; - try { + */ +public class FilenameFinder +{ + private static final String JAR_EXTENSION = ".jar"; + + private static final String FILE_URL = "file:"; + + private static final String JAR_URI_PREFIX = "jar:"; + + private static final boolean NO_DIRECTORIES = false; + + public static String MATCH_ALL_EXTENSIONS = "*"; + + /** + * Locate files and return list of file names. Given a known relative directory path, it will locate it and build + * list of all names of files in that directory having the given file extension. If the "known file path" doesn't + * work because MARS is running from an executable JAR file, it will locate the directory in the JAR file and + * proceed from there. NOTE: since this uses the class loader to get the resource, the directory path needs to be + * relative to classpath, not absolute. To work with an arbitrary file system, use the other version of this + * overloaded method. Will NOT match directories that happen to have the desired extension. + * + * @param classLoader class loader to use + * @param directoryPath Search will be confined to this directory. Use "/" as separator but do NOT include + * starting or ending "/" (e.g. mars/tools) + * @param fileExtension Only files with this extension will be added to the list. Do NOT include the "." in + * extension. + * @return array list of matching file names as Strings. If none, list is empty. + */ + public static ArrayList getFilenameList(ClassLoader classLoader, + String directoryPath, + String fileExtension) + { + fileExtension = checkFileExtension(fileExtension); + ArrayList filenameList = new ArrayList(); + // Modified by DPS 10-July-2008 to better handle path containing space + // character (%20) and to hopefully handle path containing non-ASCII + // characters. The "toURI()" approach was suggested by MARS user + // Felipe Lessa and worked for him when running 'java Mars' but it did + // not work when executing from a jar file 'java -jar Mars.jar'. I + // took it from there and discovered that in the latter situation, + // "toURI()" created a URI prefixed with "jar:" and the "getPath()" in + // that case returns null! If you strip the "jar:" prefix and create a + // new URI from the resulting string, it works! Thanks Felipe! + // + // NOTE 5-Sep-2008: "toURI()" was introduced in Java 1.5. To maintain + // 1.4 compatibility, I need to change it to call URI constructor with + // string argument, as documented in Sun API. + // + // Modified by Ingo Kofler 24-Sept-2009 to handle multiple JAR files. + // This requires use of ClassLoader getResources() instead of + // getResource(). The former will look in all JAR files listed in + // in the java command. + // + URI uri; + try + { Enumeration urls = classLoader.getResources(directoryPath); - - while (urls.hasMoreElements()) { - uri = new URI(urls.nextElement().toString()); - if (uri.toString().indexOf(JAR_URI_PREFIX)==0) { - uri = new URI(uri.toString().substring(JAR_URI_PREFIX.length())); - } - - File f = new File(uri.getPath()); - File[] files = f.listFiles(); - if (files == null) { - if (f.toString().toLowerCase().indexOf(JAR_EXTENSION)>0) { - // Must be running from a JAR file. Use ZipFile to find files and create list. - // Modified 12/28/09 by DPS to add results to existing filenameList instead of overwriting it. - filenameList.addAll(getListFromJar(extractJarFilename(f.toString()), directoryPath, fileExtension)); - } - } - else { // have array of File objects; convert to names and add to list - FileFilter filter = getFileFilter(fileExtension, "", NO_DIRECTORIES); - for (int i=0; i 0) + { + // Must be running from a JAR file. Use ZipFile to find files and create list. + // Modified 12/28/09 by DPS to add results to existing filenameList instead of overwriting it. + filenameList.addAll(getListFromJar(extractJarFilename(f.toString()), directoryPath, fileExtension)); + } + } + else + { // have array of File objects; convert to names and add to list + FileFilter filter = getFileFilter(fileExtension, "", NO_DIRECTORIES); + for (int i = 0; i < files.length; i++) + { + if (filter.accept(files[i])) + { + filenameList.add(files[i].getName()); + } + } + } } + return filenameList; + + } + catch (URISyntaxException e) + { + e.printStackTrace(); + return filenameList; + } + catch (IOException e) + { + e.printStackTrace(); + return filenameList; + } /* Original implementation URI uri; @@ -166,340 +177,391 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. } } return filenameList; */ - } - - - /** - * Locate files and return list of file names. Given a known relative directory path, - * it will locate it and build list of all names of files in that directory - * having the given file extension. If the "known file path" doesn't work - * because MARS is running from an executable JAR file, it will locate the - * directory in the JAR file and proceed from there. NOTE: since this uses - * the class loader to get the resource, the directory path needs to be - * relative to classpath, not absolute. To work with an arbitrary file system, - * use the other version of this overloaded method. - * @param classLoader class loader to use - * @param directoryPath Search will be confined to this directory. Use "/" as - * separator but do NOT include starting or ending "/" (e.g. mars/tools) - * @param fileExtensions ArrayList of Strings containing file extensions. - * Only files with an extension in this list will be added to the list. - * Do NOT include the ".", eg "class" not ".class". If Arraylist or - * extension null or empty, all files are added. - * @return array list of matching file names as Strings. If none, list is empty. - */ - public static ArrayList getFilenameList(ClassLoader classLoader, - String directoryPath, - ArrayList fileExtensions ) { - ArrayList filenameList = new ArrayList(); - String fileExtension; - if (fileExtensions==null || fileExtensions.size()==0) { - filenameList = getFilenameList(classLoader,directoryPath,""); - } - else { - for (int i=0; i 0 && i < s.length() - 1) { - ext = s.substring(i+1).toLowerCase(); - } - return ext; - } - - /** - * Get a FileFilter that will filter files based on the given list of filename extensions. - * @param extensions ArrayList of Strings, each string is acceptable filename extension. - * @param description String containing description to be added in parentheses after list of extensions. - * @param acceptDirectories boolean value true if directories are accepted by the filter, false otherwise. - * @return a FileFilter object that accepts files with given extensions, and directories if so indicated. - */ - - public static FileFilter getFileFilter(ArrayList extensions, String description, boolean acceptDirectories) { - return new MarsFileFilter(extensions, description, acceptDirectories); - } - - /** - * Get a FileFilter that will filter files based on the given list of filename extensions. - * All directories are accepted by the filter. - * @param extensions ArrayList of Strings, each string is acceptable filename extension - * @param description String containing description to be added in parentheses after list of extensions. - * @return a FileFilter object that accepts files with given extensions, and directories if so indicated. - */ - - public static FileFilter getFileFilter(ArrayList extensions, String description) { - return getFileFilter(extensions, description, true); - } - - /** - * Get a FileFilter that will filter files based on the given filename extension. - * @param extension String containing acceptable filename extension. - * @param description String containing description to be added in parentheses after list of extensions. - * @param acceptDirectories boolean value true if directories are accepted by the filter, false otherwise. - * @return a FileFilter object that accepts files with given extensions, and directories if so indicated. - */ - - public static FileFilter getFileFilter(String extension, String description, boolean acceptDirectories) { - ArrayList extensions = new ArrayList(); - extensions.add(extension); - return new MarsFileFilter(extensions, description, acceptDirectories); - } - - /** - * Get a FileFilter that will filter files based on the given filename extension. - * All directories are accepted by the filter. - * @param extension String containing acceptable filename extension - * @param description String containing description to be added in parentheses after list of extensions. - * @return a FileFilter object that accepts files with given extensions, and directories if so indicated. - */ - - public static FileFilter getFileFilter(String extension, String description) { - ArrayList extensions = new ArrayList(); - extensions.add(extension); - return getFileFilter(extensions, description, true); - } - - /** - * Determine if given filename ends with given extension. - * @param name A String containing the file name - * @param extension A String containing the file extension. Leading period is optional. - * @return Returns true if filename ends with given extension, false otherwise. - */ - // For assured results, make sure extension starts with "." (will add it if not there) - public static boolean fileExtensionMatch(String name, String extension) { - return (extension==null || extension.length()==0 || name.endsWith(((extension.startsWith("."))? "" : ".")+extension)); - } - - // return list of file names in specified folder inside JAR - private static ArrayList getListFromJar(String jarName, String directoryPath, String fileExtension) { - fileExtension = checkFileExtension(fileExtension); - ArrayList nameList = new ArrayList(); - if (jarName==null) { + } + return filenameList; + } + + + /** + * Return list of file names. Given a list of file names, it will return the list of all having the given file + * extension. If file extenion is null or empty, all filenames are returned. Returned list contains absolute + * filename paths. + * + * @param nameList ArrayList of String containing file names. + * @param fileExtensions ArrayList of Strings containing file extensions. Only files with an extension in this + * list will be added to the list. Do NOT include the "." in extensions. If Arraylist or extension null or + * empty, all files are added. + * @return array list of matching file names (absolute path). If none, list is empty. + */ + public static ArrayList getFilenameList(ArrayList nameList, ArrayList fileExtensions) + { + ArrayList filenameList = new ArrayList(); + String fileExtension; + if (fileExtensions == null || fileExtensions.size() == 0) + { + filenameList = getFilenameList(nameList, ""); + } + else + { + for (int i = 0; i < fileExtensions.size(); i++) + { + fileExtension = checkFileExtension((String) fileExtensions.get(i)); + filenameList.addAll(getFilenameList(nameList, fileExtension)); + } + } + return filenameList; + } + + /** + * Get the filename extension of the specified File. + * + * @param file the File object representing the file of interest + * @return The filename extension (everything that follows last '.' in filename) or null if none. + */ + // Source code from Sun Microsystems "The Java Tutorials : How To Use File Choosers" + public static String getExtension(File file) + { + String ext = null; + String s = file.getName(); + int i = s.lastIndexOf('.'); + if (i > 0 && i < s.length() - 1) + { + ext = s.substring(i + 1).toLowerCase(); + } + return ext; + } + + /** + * Get a FileFilter that will filter files based on the given list of filename extensions. + * + * @param extensions ArrayList of Strings, each string is acceptable filename extension. + * @param description String containing description to be added in parentheses after list of extensions. + * @param acceptDirectories boolean value true if directories are accepted by the filter, false otherwise. + * @return a FileFilter object that accepts files with given extensions, and directories if so indicated. + */ + + public static FileFilter getFileFilter(ArrayList extensions, String description, boolean acceptDirectories) + { + return new MarsFileFilter(extensions, description, acceptDirectories); + } + + /** + * Get a FileFilter that will filter files based on the given list of filename extensions. All directories are + * accepted by the filter. + * + * @param extensions ArrayList of Strings, each string is acceptable filename extension + * @param description String containing description to be added in parentheses after list of extensions. + * @return a FileFilter object that accepts files with given extensions, and directories if so indicated. + */ + + public static FileFilter getFileFilter(ArrayList extensions, String description) + { + return getFileFilter(extensions, description, true); + } + + /** + * Get a FileFilter that will filter files based on the given filename extension. + * + * @param extension String containing acceptable filename extension. + * @param description String containing description to be added in parentheses after list of extensions. + * @param acceptDirectories boolean value true if directories are accepted by the filter, false otherwise. + * @return a FileFilter object that accepts files with given extensions, and directories if so indicated. + */ + + public static FileFilter getFileFilter(String extension, String description, boolean acceptDirectories) + { + ArrayList extensions = new ArrayList(); + extensions.add(extension); + return new MarsFileFilter(extensions, description, acceptDirectories); + } + + /** + * Get a FileFilter that will filter files based on the given filename extension. All directories are accepted by + * the filter. + * + * @param extension String containing acceptable filename extension + * @param description String containing description to be added in parentheses after list of extensions. + * @return a FileFilter object that accepts files with given extensions, and directories if so indicated. + */ + + public static FileFilter getFileFilter(String extension, String description) + { + ArrayList extensions = new ArrayList(); + extensions.add(extension); + return getFileFilter(extensions, description, true); + } + + /** + * Determine if given filename ends with given extension. + * + * @param name A String containing the file name + * @param extension A String containing the file extension. Leading period is optional. + * @return Returns true if filename ends with given extension, false otherwise. + */ + // For assured results, make sure extension starts with "." (will add it if not there) + public static boolean fileExtensionMatch(String name, String extension) + { + return (extension == null || extension.length() == 0 || name.endsWith(((extension.startsWith(".")) ? "" : ".") + extension)); + } + + // return list of file names in specified folder inside JAR + private static ArrayList getListFromJar(String jarName, String directoryPath, String fileExtension) + { + fileExtension = checkFileExtension(fileExtension); + ArrayList nameList = new ArrayList(); + if (jarName == null) + { return nameList; - } - try { + } + try + { ZipFile zf = new ZipFile(new File(jarName)); Enumeration list = zf.entries(); - while (list.hasMoreElements()) { - ZipEntry ze = (ZipEntry) list.nextElement(); - if (ze.getName().startsWith(directoryPath+"/") && - fileExtensionMatch(ze.getName(),fileExtension)) { - nameList.add(ze.getName().substring(ze.getName().lastIndexOf('/')+1)); - } + while (list.hasMoreElements()) + { + ZipEntry ze = (ZipEntry) list.nextElement(); + if (ze.getName().startsWith(directoryPath + "/") && + fileExtensionMatch(ze.getName(), fileExtension)) + { + nameList.add(ze.getName().substring(ze.getName().lastIndexOf('/') + 1)); + } } - } - catch (Exception e) { - System.out.println("Exception occurred reading MarsTool list from JAR: "+e); - } - return nameList; - } - - // Given pathname, extract and return JAR file name (must be only element containing ".jar") - // 5 Dec 2007 DPS: Modified to return file path of JAR file, not just its name. This was - // by request of Zachary Kurmas of Grant Valley State, who got errors trying - // to run the Mars.jar file from a different working directory. He helpfully - // pointed out what the error is and where it occurs. Originally, it would - // work only if the JAR file was in the current working directory (as would - // be the case if executed from a GUI by double-clicking the jar icon). - private static String extractJarFilename(String path) { - StringTokenizer findTheJar = new StringTokenizer(path,"\\/"); - if (path.toLowerCase().startsWith(FILE_URL)) { + } + catch (Exception e) + { + System.out.println("Exception occurred reading MarsTool list from JAR: " + e); + } + return nameList; + } + + // Given pathname, extract and return JAR file name (must be only element containing ".jar") + // 5 Dec 2007 DPS: Modified to return file path of JAR file, not just its name. This was + // by request of Zachary Kurmas of Grant Valley State, who got errors trying + // to run the Mars.jar file from a different working directory. He helpfully + // pointed out what the error is and where it occurs. Originally, it would + // work only if the JAR file was in the current working directory (as would + // be the case if executed from a GUI by double-clicking the jar icon). + private static String extractJarFilename(String path) + { + StringTokenizer findTheJar = new StringTokenizer(path, "\\/"); + if (path.toLowerCase().startsWith(FILE_URL)) + { path = path.substring(FILE_URL.length()); - } - int jarPosition = path.toLowerCase().indexOf(JAR_EXTENSION); - return (jarPosition >= 0) ? path.substring(0,jarPosition+JAR_EXTENSION.length()) : path; - } - - // make sure file extension, if it is real, does not start with '.' -- remove it. - private static String checkFileExtension(String fileExtension) { - return (fileExtension==null || fileExtension.length()==0 || !fileExtension.startsWith(".")) - ? fileExtension - : fileExtension.substring(1); - } - - - /////////////////////////////////////////////////////////////////////////// - // FileFilter subclass to be instantiated by the getFileFilter method above. - // This extends javax.swing.filechooser.FileFilter - - private static class MarsFileFilter extends FileFilter { - - private ArrayList extensions; - private String fullDescription; - private boolean acceptDirectories; - - private MarsFileFilter(ArrayList extensions, String description, boolean acceptDirectories) { + } + int jarPosition = path.toLowerCase().indexOf(JAR_EXTENSION); + return (jarPosition >= 0) ? path.substring(0, jarPosition + JAR_EXTENSION.length()) : path; + } + + // make sure file extension, if it is real, does not start with '.' -- remove it. + private static String checkFileExtension(String fileExtension) + { + return (fileExtension == null || fileExtension.length() == 0 || !fileExtension.startsWith(".")) + ? fileExtension + : fileExtension.substring(1); + } + + + /////////////////////////////////////////////////////////////////////////// + // FileFilter subclass to be instantiated by the getFileFilter method above. + // This extends javax.swing.filechooser.FileFilter + + private static class MarsFileFilter extends FileFilter + { + + private final ArrayList extensions; + + private final String fullDescription; + + private final boolean acceptDirectories; + + private MarsFileFilter(ArrayList extensions, String description, boolean acceptDirectories) + { this.extensions = extensions; this.fullDescription = buildFullDescription(description, extensions); this.acceptDirectories = acceptDirectories; - } - - // User provides descriptive phrase to be parenthesized. - // We will attach it to description of the extensions. For example, if the extensions - // given are s and asm and the description is "Assembler Programs" the full description - // generated here will be "Assembler Programs (*.s; *.asm)" - private String buildFullDescription(String description, ArrayList extensions) { + } + + // User provides descriptive phrase to be parenthesized. + // We will attach it to description of the extensions. For example, if the extensions + // given are s and asm and the description is "Assembler Programs" the full description + // generated here will be "Assembler Programs (*.s; *.asm)" + private String buildFullDescription(String description, ArrayList extensions) + { String result = (description == null) ? "" : description; - if (extensions.size() > 0) { - result += " ("; + if (extensions.size() > 0) + { + result += " ("; } - for (int i=0; i 0) { - result += ((i==0)?"":"; ")+"*"+((extension.charAt(0)=='.')? "" : ".")+extension; - } + for (int i = 0; i < extensions.size(); i++) + { + String extension = (String) extensions.get(i); + if (extension != null && extension.length() > 0) + { + result += ((i == 0) ? "" : "; ") + "*" + ((extension.charAt(0) == '.') ? "" : ".") + extension; + } } - if (extensions.size() > 0) { - result += ")"; + if (extensions.size() > 0) + { + result += ")"; } return result; - } - - // required by the abstract superclass - public String getDescription() { + } + + // required by the abstract superclass + public String getDescription() + { return this.fullDescription; - } - - // required by the abstract superclass. - public boolean accept(File file) { - if (file.isDirectory()) { - return acceptDirectories; - } - String fileExtension = getExtension(file); - if (fileExtension != null) { - for (int i=0; i maxLength) { + catch (IOException e) + { + } + } + else + { + if (Globals.getSettings().getBooleanSetting(Settings.POPUP_SYSCALL_INPUT)) + { + input = Globals.getGui().getMessagesPane().getInputString( + "Enter a string of maximum length " + maxLength + + " (syscall " + serviceNumber + ")"); + } + else + { + input = Globals.getGui().getMessagesPane().getInputString(maxLength); + if (input.endsWith("\n")) + { + input = input.substring(0, input.length() - 1); + } + } + } + + if (input.length() > maxLength) + { // Modified DPS 13-July-2011. Originally: return input.substring(0, maxLength); return (maxLength <= 0) ? "" : input.substring(0, maxLength); - } - else { - return input; - } - } - - - /** Implements syscall having 12 in $v0, to read a char value. + } + else + { + return input; + } + } + + + /** + * Implements syscall having 12 in $v0, to read a char value. * * @param serviceNumber the number assigned to Read Char syscall (default 12) * @return int value with lowest byte corresponding to user input */ - public static int readChar(int serviceNumber) - { - String input = "0"; - int returnValue = 0; - if (Globals.getGui() == null) - { + public static int readChar(int serviceNumber) + { + String input = "0"; + int returnValue = 0; + if (Globals.getGui() == null) + { try { - input = getInputReader().readLine(); - } - catch (IOException e) - {} - } - else - { - if (Globals.getSettings().getBooleanSetting(Settings.POPUP_SYSCALL_INPUT)) { - input = Globals.getGui().getMessagesPane().getInputString( - "Enter a character value (syscall "+serviceNumber+")"); - } - else { - input = Globals.getGui().getMessagesPane().getInputString(1); + input = getInputReader().readLine(); } - } - // The whole try-catch is not really necessary in this case since I'm - // just propagating the runtime exception (the default behavior), but - // I want to make it explicit. The client needs to catch it. - try - { - returnValue = (int) (input.charAt(0)); // first character input - } - catch (IndexOutOfBoundsException e) // no chars present + catch (IOException e) { - throw e; // was: returnValue = 0; } - - return returnValue; - - } - - - /** Write bytes to file. + } + else + { + if (Globals.getSettings().getBooleanSetting(Settings.POPUP_SYSCALL_INPUT)) + { + input = Globals.getGui().getMessagesPane().getInputString( + "Enter a character value (syscall " + serviceNumber + ")"); + } + else + { + input = Globals.getGui().getMessagesPane().getInputString(1); + } + } + // The whole try-catch is not really necessary in this case since I'm + // just propagating the runtime exception (the default behavior), but + // I want to make it explicit. The client needs to catch it. + try + { + returnValue = input.charAt(0); // first character input + } + catch (IndexOutOfBoundsException e) // no chars present + { + throw e; // was: returnValue = 0; + } + + return returnValue; + + } + + + /** + * Write bytes to file. * * @param fd file descriptor * @param myBuffer byte array containing characters to write * @param lengthRequested number of bytes to write * @return number of bytes written, or -1 on error */ - - public static int writeToFile(int fd, byte[] myBuffer, int lengthRequested) - { - /////////////// DPS 8-Jan-2013 //////////////////////////////////////////////////// - /// Write to STDOUT or STDERR file descriptor while using IDE - write to Messages pane. - if ((fd==STDOUT || fd==STDERR) && Globals.getGui() != null) { + + public static int writeToFile(int fd, byte[] myBuffer, int lengthRequested) + { + /////////////// DPS 8-Jan-2013 //////////////////////////////////////////////////// + /// Write to STDOUT or STDERR file descriptor while using IDE - write to Messages pane. + if ((fd == STDOUT || fd == STDERR) && Globals.getGui() != null) + { String data = new String(myBuffer); Globals.getGui().getMessagesPane().postRunMessage(data); return data.length(); - } - /////////////////////////////////////////////////////////////////////////////////// - //// When running in command mode, code below works for either regular file or STDOUT/STDERR - - if (!FileIOData.fdInUse(fd, 1)) // Check the existence of the "write" fd - { - fileErrorString = new String( - "File descriptor " + fd + " is not open for writing"); + } + /////////////////////////////////////////////////////////////////////////////////// + //// When running in command mode, code below works for either regular file or STDOUT/STDERR + + if (!FileIOData.fdInUse(fd, 1)) // Check the existence of the "write" fd + { + fileErrorString = "File descriptor " + fd + " is not open for writing"; return -1; - } - // retrieve FileOutputStream from storage - OutputStream outputStream = (OutputStream) FileIOData.getStreamInUse(fd); - try - { + } + // retrieve FileOutputStream from storage + OutputStream outputStream = (OutputStream) FileIOData.getStreamInUse(fd); + try + { // Oct. 9 2005 Ken Vollmar // Observation: made a call to outputStream.write(myBuffer, 0, lengthRequested) // with myBuffer containing 6(ten) 32-bit-words <---> 24(ten) bytes, where the @@ -315,358 +343,365 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // by the return value.) // Writes up to lengthRequested bytes of data to this output stream from an array of bytes. // outputStream.write(myBuffer, 0, lengthRequested); // write is a void method -- no verification value returned - + // Oct. 9 2005 Ken Vollmar Force the write statement to write exactly // the number of bytes requested, even though those bytes include many ZERO values. for (int ii = 0; ii < lengthRequested; ii++) { - outputStream.write(myBuffer[ii]); + outputStream.write(myBuffer[ii]); } outputStream.flush();// DPS 7-Jan-2013 - } - catch (IOException e) - { - fileErrorString = new String( - "IO Exception on write of file with fd " + fd); - return -1; - } - catch (IndexOutOfBoundsException e) - { - fileErrorString = new String( - "IndexOutOfBoundsException on write of file with fd" + fd); - return -1; - } - - return lengthRequested; - - } // end writeToFile - - - /** Read bytes from file. + } + catch (IOException e) + { + fileErrorString = "IO Exception on write of file with fd " + fd; + return -1; + } + catch (IndexOutOfBoundsException e) + { + fileErrorString = "IndexOutOfBoundsException on write of file with fd" + fd; + return -1; + } + + return lengthRequested; + + } // end writeToFile + + + /** + * Read bytes from file. * * @param fd file descriptor * @param myBuffer byte array to contain bytes read * @param lengthRequested number of bytes to read * @return number of bytes read, 0 on EOF, or -1 on error */ - public static int readFromFile(int fd, byte[] myBuffer, int lengthRequested) - { - int retValue = -1; - /////////////// DPS 8-Jan-2013 ////////////////////////////////////////////////// - /// Read from STDIN file descriptor while using IDE - get input from Messages pane. - if (fd==STDIN && Globals.getGui() != null) { + public static int readFromFile(int fd, byte[] myBuffer, int lengthRequested) + { + int retValue = -1; + /////////////// DPS 8-Jan-2013 ////////////////////////////////////////////////// + /// Read from STDIN file descriptor while using IDE - get input from Messages pane. + if (fd == STDIN && Globals.getGui() != null) + { String input = Globals.getGui().getMessagesPane().getInputString(lengthRequested); - byte[] bytesRead = input.getBytes();; - for (int i=0; i < myBuffer.length; i++) { - myBuffer[i] = (i < bytesRead.length) ? bytesRead[i] : 0 ; + byte[] bytesRead = input.getBytes(); + for (int i = 0; i < myBuffer.length; i++) + { + myBuffer[i] = (i < bytesRead.length) ? bytesRead[i] : 0; } return Math.min(myBuffer.length, bytesRead.length); - } - //////////////////////////////////////////////////////////////////////////////////// - //// When running in command mode, code below works for either regular file or STDIN - - if (!FileIOData.fdInUse(fd, 0)) // Check the existence of the "read" fd - { - fileErrorString = new String( - "File descriptor " + fd + " is not open for reading"); + } + //////////////////////////////////////////////////////////////////////////////////// + //// When running in command mode, code below works for either regular file or STDIN + + if (!FileIOData.fdInUse(fd, 0)) // Check the existence of the "read" fd + { + fileErrorString = "File descriptor " + fd + " is not open for reading"; return -1; - } + } // retrieve FileInputStream from storage - InputStream InputStream = (InputStream) FileIOData.getStreamInUse(fd); - try - { + InputStream InputStream = (InputStream) FileIOData.getStreamInUse(fd); + try + { // Reads up to lengthRequested bytes of data from this Input stream into an array of bytes. retValue = InputStream.read(myBuffer, 0, lengthRequested); - // This method will return -1 upon EOF, but our spec says that negative - // value represents an error, so we return 0 for EOF. DPS 10-July-2008. - if (retValue == -1) { - retValue = 0; - } - } - catch (IOException e) + // This method will return -1 upon EOF, but our spec says that negative + // value represents an error, so we return 0 for EOF. DPS 10-July-2008. + if (retValue == -1) { - fileErrorString = new String( - "IO Exception on read of file with fd " + fd); - return -1; - } - catch (IndexOutOfBoundsException e) - { - fileErrorString = new String( - "IndexOutOfBoundsException on read of file with fd" + fd); - return -1; + retValue = 0; } - return retValue; - - } // end readFromFile - - - /** - * Open a file for either reading or writing. Note that read/write flag is NOT - * IMPLEMENTED. Also note that file permission modes are also NOT IMPLEMENTED. - * - * @param filename string containing filename - * @param flag 0 for read, 1 for write - * @return file descriptor in the range 0 to SYSCALL_MAXFILES-1, or -1 if error - * @author Ken Vollmar - */ - public static int openFile(String filename, int flags) - { + } + catch (IOException e) + { + fileErrorString = "IO Exception on read of file with fd " + fd; + return -1; + } + catch (IndexOutOfBoundsException e) + { + fileErrorString = "IndexOutOfBoundsException on read of file with fd" + fd; + return -1; + } + return retValue; + + } // end readFromFile + + + /** + * Open a file for either reading or writing. Note that read/write flag is NOT IMPLEMENTED. Also note that file + * permission modes are also NOT IMPLEMENTED. + * + * @param filename string containing filename + * @param flag 0 for read, 1 for write + * @return file descriptor in the range 0 to SYSCALL_MAXFILES-1, or -1 if error + * @author Ken Vollmar + */ + public static int openFile(String filename, int flags) + { // Internally, a "file descriptor" is an index into a table // of the filename, flag, and the File???putStream associated with // that file descriptor. - - int retValue = -1; - char ch[] = { ' '}; // Need an array to convert to String - FileInputStream inputStream; - FileOutputStream outputStream; - int fdToUse; - + + int retValue = -1; + char[] ch = {' '}; // Need an array to convert to String + FileInputStream inputStream; + FileOutputStream outputStream; + int fdToUse; + // Check internal plausibility of opening this file - fdToUse = FileIOData.nowOpening(filename, flags); - retValue = fdToUse; // return value is the fd - if (fdToUse < 0) - { + fdToUse = FileIOData.nowOpening(filename, flags); + retValue = fdToUse; // return value is the fd + if (fdToUse < 0) + { return -1; - } // fileErrorString would have been set - - - if (flags == O_RDONLY) // Open for reading only - { + } // fileErrorString would have been set + + + if (flags == O_RDONLY) // Open for reading only + { try { // Set up input stream from disk file - inputStream = new FileInputStream(filename); - FileIOData.setStreamInUse(fdToUse, inputStream); // Save stream for later use - } - catch (FileNotFoundException e) - { - fileErrorString = new String( - "File " + filename + " not found, open for input."); - retValue = -1; - } - } - else if ( (flags & O_WRONLY) != 0 ) // Open for writing only - { + inputStream = new FileInputStream(filename); + FileIOData.setStreamInUse(fdToUse, inputStream); // Save stream for later use + } + catch (FileNotFoundException e) + { + fileErrorString = "File " + filename + " not found, open for input."; + retValue = -1; + } + } + else if ((flags & O_WRONLY) != 0) // Open for writing only + { // Set up output stream to disk file try - { - outputStream = new FileOutputStream(filename, ((flags & O_APPEND) != 0) ); - FileIOData.setStreamInUse(fdToUse, outputStream); // Save stream for later use - } - catch (FileNotFoundException e) - { - fileErrorString = new String( - "File " + filename + " not found, open for output."); - retValue = -1; - } - } - return retValue; // return the "file descriptor" - - } - - /** Close the file with specified file descriptor + { + outputStream = new FileOutputStream(filename, ((flags & O_APPEND) != 0)); + FileIOData.setStreamInUse(fdToUse, outputStream); // Save stream for later use + } + catch (FileNotFoundException e) + { + fileErrorString = "File " + filename + " not found, open for output."; + retValue = -1; + } + } + return retValue; // return the "file descriptor" + + } + + /** + * Close the file with specified file descriptor * * @param fd the file descriptor of an open file */ - public static void closeFile(int fd) - { - FileIOData.close(fd); - } - - /** + public static void closeFile(int fd) + { + FileIOData.close(fd); + } + + /** * Reset all files -- clears out the file descriptor table. */ - public static void resetFiles() - { - FileIOData.resetFiles(); - } - - /** - * Retrieve file operation or error message - * - * @return string containing message - */ - public static String getFileErrorMessage() - { - return fileErrorString; - } - - /////////////////////////////////////////////////////////////////////// - // Private method to simply return the BufferedReader used for - // keyboard input, redirected input, or piped input. - // These are all equivalent in the eyes of the program because they are - // transparent to it. Lazy instantiation. DPS. 28 Feb 2008 - - private static BufferedReader getInputReader() { - if (inputReader == null) { - inputReader = new BufferedReader(new InputStreamReader(System.in)); - } - return inputReader; - } - - + public static void resetFiles() + { + FileIOData.resetFiles(); + } + + /** + * Retrieve file operation or error message + * + * @return string containing message + */ + public static String getFileErrorMessage() + { + return fileErrorString; + } + + /////////////////////////////////////////////////////////////////////// + // Private method to simply return the BufferedReader used for + // keyboard input, redirected input, or piped input. + // These are all equivalent in the eyes of the program because they are + // transparent to it. Lazy instantiation. DPS. 28 Feb 2008 + + private static BufferedReader getInputReader() + { + if (inputReader == null) + { + inputReader = new BufferedReader(new InputStreamReader(System.in)); + } + return inputReader; + } + + // ////////////////////////////////////////////////////////////////////////////// // Maintain information on files in use. The index to the arrays is the "file descriptor." // Ken Vollmar, August 2005 - - private static class FileIOData - { - private static String[] fileNames = new String[ SYSCALL_MAXFILES ]; // The filenames in use. Null if file descriptor i is not in use. - private static int[] fileFlags = new int[ SYSCALL_MAXFILES ]; // The flags of this file, 0=READ, 1=WRITE. Invalid if this file descriptor is not in use. - private static Object[] streams = new Object[SYSCALL_MAXFILES]; // The streams in use, associated with the filenames - + + private static class FileIOData + { + private static final String[] fileNames = new String[SYSCALL_MAXFILES]; // The filenames in use. Null if file descriptor i is not in use. + + private static final int[] fileFlags = new int[SYSCALL_MAXFILES]; // The flags of this file, 0=READ, 1=WRITE. Invalid if this file descriptor is not in use. + + private static final Object[] streams = new Object[SYSCALL_MAXFILES]; // The streams in use, associated with the filenames + // Reset all file information. Closes any open files and resets the arrays - private static void resetFiles() - { + private static void resetFiles() + { for (int i = 0; i < SYSCALL_MAXFILES; i++) { - close(i); + close(i); } setupStdio(); - } - // DPS 8-Jan-2013 - private static void setupStdio() { - fileNames[STDIN] = "STDIN"; + } + + // DPS 8-Jan-2013 + private static void setupStdio() + { + fileNames[STDIN] = "STDIN"; fileNames[STDOUT] = "STDOUT"; fileNames[STDERR] = "STDERR"; - fileFlags[STDIN] = SystemIO.O_RDONLY; + fileFlags[STDIN] = SystemIO.O_RDONLY; fileFlags[STDOUT] = SystemIO.O_WRONLY; fileFlags[STDERR] = SystemIO.O_WRONLY; - streams[STDIN] = System.in; + streams[STDIN] = System.in; streams[STDOUT] = System.out; streams[STDERR] = System.err; System.out.flush(); System.err.flush(); - } - + } + // Preserve a stream that is in use - private static void setStreamInUse(int fd, Object s) - { + private static void setStreamInUse(int fd, Object s) + { streams[fd] = s; - - } - + + } + // Retrieve a stream for use - private static Object getStreamInUse(int fd) - { + private static Object getStreamInUse(int fd) + { return streams[fd]; - - } - + + } + // Determine whether a given filename is already in use. - private static boolean filenameInUse(String requestedFilename) - { + private static boolean filenameInUse(String requestedFilename) + { for (int i = 0; i < SYSCALL_MAXFILES; i++) { - if (fileNames[i] != null - && fileNames[i].equals(requestedFilename)) - { + if (fileNames[i] != null + && fileNames[i].equals(requestedFilename)) + { // System.out.println("Mars.SystemIO.FileIOData.filenameInUse: rtng TRUE for " + requestedFilename); - return true; - } + return true; + } } - + // System.out.println("Mars.SystemIO.FileIOData.filenameInUse: rtng TRUE for " + requestedFilename); return false; - - } - + + } + // Determine whether a given fd is already in use with the given flag. - private static boolean fdInUse(int fd, int flag) - { + private static boolean fdInUse(int fd, int flag) + { if (fd < 0 || fd >= SYSCALL_MAXFILES) { - return false; - } - else if (fileNames[fd] != null && fileFlags[fd] == 0 && flag == 0) - { // O_RDONLY read-only - return true; + return false; } - else if (fileNames[fd] != null && ((fileFlags[fd] & flag & O_WRONLY) == O_WRONLY) ) - { // O_WRONLY write-only - return true; - } - return false; - - } - + else // O_WRONLY write-only + if (fileNames[fd] != null && fileFlags[fd] == 0 && flag == 0) + { // O_RDONLY read-only + return true; + } + else return fileNames[fd] != null && ((fileFlags[fd] & flag & O_WRONLY) == O_WRONLY); + + } + // Close the file with file descriptor fd. No errors are recoverable -- if the user's // made an error in the call, it will come back to him. - private static void close(int fd) - { + private static void close(int fd) + { // Can't close STDIN, STDOUT, STDERR, or invalid fd - if (fd <= STDERR || fd >= SYSCALL_MAXFILES) - return; - + if (fd <= STDERR || fd >= SYSCALL_MAXFILES) + { + return; + } + fileNames[fd] = null; - // All this code will be executed only if the descriptor is open. + // All this code will be executed only if the descriptor is open. if (streams[fd] != null) { - int keepFlag = fileFlags[fd]; - Object keepStream = streams[fd]; - fileFlags[fd] = -1; - streams[fd] = null; - try { - if (keepFlag == O_RDONLY) - ((FileInputStream)keepStream).close(); - else - ((FileOutputStream)keepStream).close(); - } - catch (IOException ioe) { - // not concerned with this exception - } - } - else { - fileFlags[fd] = -1; // just to be sure... streams[fd] known to be null + int keepFlag = fileFlags[fd]; + Object keepStream = streams[fd]; + fileFlags[fd] = -1; + streams[fd] = null; + try + { + if (keepFlag == O_RDONLY) + { + ((FileInputStream) keepStream).close(); + } + else + { + ((FileOutputStream) keepStream).close(); + } + } + catch (IOException ioe) + { + // not concerned with this exception + } } - } - + else + { + fileFlags[fd] = -1; // just to be sure... streams[fd] known to be null + } + } + // Attempt to open a new file with the given flag, using the lowest available file descriptor. // Check that filename is not in use, flag is reasonable, and there is an available file descriptor. // Return: file descriptor in 0...(SYSCALL_MAXFILES-1), or -1 if error - private static int nowOpening(String filename, int flag) - { + private static int nowOpening(String filename, int flag) + { int i = 0; if (filenameInUse(filename)) { - fileErrorString = new String( - "File name " + filename + " is already open."); - return -1; + fileErrorString = "File name " + filename + " is already open."; + return -1; } - - if (flag != O_RDONLY && flag != O_WRONLY && flag != (O_WRONLY | O_APPEND) ) // Only read and write are implemented + + if (flag != O_RDONLY && flag != O_WRONLY && flag != (O_WRONLY | O_APPEND)) // Only read and write are implemented { - fileErrorString = new String( - "File name " + filename - + " has unknown requested opening flag"); - return -1; + fileErrorString = "File name " + filename + + " has unknown requested opening flag"; + return -1; } - + while (fileNames[i] != null && i < SYSCALL_MAXFILES) { - i++; + i++; } // Attempt to find available file descriptor - + if (i >= SYSCALL_MAXFILES) // no available file descriptors { - fileErrorString = new String( - "File name " + filename - + " exceeds maximum open file limit of " - + SYSCALL_MAXFILES); - return -1; - } - - // Must be OK -- put filename in table - fileNames[i] = new String(filename); // our table has its own copy of filename - fileFlags[i] = flag; - fileErrorString = new String("File operation OK"); - return i; - - } - - } // end private class FileIOData - //////////////////////////////////////////////////////////////////////////////// - - - } + fileErrorString = "File name " + filename + + " exceeds maximum open file limit of " + + SYSCALL_MAXFILES; + return -1; + } + + // Must be OK -- put filename in table + fileNames[i] = filename; // our table has its own copy of filename + fileFlags[i] = flag; + fileErrorString = "File operation OK"; + return i; + + } + + } // end private class FileIOData + //////////////////////////////////////////////////////////////////////////////// + + +} diff --git a/src/main/java/mars/venus/AbstractFontSettingDialog.java b/src/main/java/mars/venus/AbstractFontSettingDialog.java index 1143355..ca46d92 100644 --- a/src/main/java/mars/venus/AbstractFontSettingDialog.java +++ b/src/main/java/mars/venus/AbstractFontSettingDialog.java @@ -1,15 +1,18 @@ - package mars.venus; - import mars.simulator.*; - import mars.*; - import mars.util.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.text.*; - import javax.swing.border.*; - import javax.swing.event.*; - import java.io.*; +package mars.venus; + +import mars.util.EditorFont; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.border.LineBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.util.Vector; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -38,250 +41,284 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Abstract class for a font selection dialog. - */ - public abstract class AbstractFontSettingDialog extends JDialog { - - JDialog editorDialog; - JComboBox fontFamilySelector, fontStyleSelector; - JSlider fontSizeSelector; - JSpinner fontSizeSpinSelector; - JLabel fontSample; - protected Font currentFont; - - // Used to determine upon OK, whether or not anything has changed. - String initialFontFamily, initialFontStyle, initialFontSize; - - /** - * Create a new font chooser. Has pertinent JDialog parameters. - * Will do everything except make it visible. - */ - public AbstractFontSettingDialog(Frame owner, String title, boolean modality, Font currentFont) { - super(owner, title, modality); - this.currentFont = currentFont; - JPanel overallPanel = new JPanel(new BorderLayout()); - overallPanel.setBorder(new EmptyBorder(10,10,10,10)); - overallPanel.add(buildDialogPanel(), BorderLayout.CENTER); - overallPanel.add(buildControlPanel(), BorderLayout.SOUTH); - this.setContentPane(overallPanel); - this.setDefaultCloseOperation( - JDialog.DO_NOTHING_ON_CLOSE); - this.addWindowListener( - new WindowAdapter() { - public void windowClosing(WindowEvent we) { - closeDialog(); - } - }); - this.pack(); - this.setLocationRelativeTo(owner); - } - - // The dialog area, not including control buttons at bottom - protected JPanel buildDialogPanel() { - JPanel contents = new JPanel(new BorderLayout(20,20)); - contents.setBorder(new EmptyBorder(10,10,10,10)); - - //Font currentFont = Globals.getSettings().getEditorFont(); - initialFontFamily = currentFont.getFamily(); - initialFontStyle = EditorFont.styleIntToStyleString(currentFont.getStyle()); - initialFontSize = EditorFont.sizeIntToSizeString(currentFont.getSize()); - String[] commonFontFamilies = EditorFont.getCommonFamilies(); - String[] allFontFamilies = EditorFont.getAllFamilies(); - // The makeVectorData() method will combine these two into one Vector - // with a horizontal line separating the two groups. - String[][] fullList = { commonFontFamilies, allFontFamilies }; - - fontFamilySelector = new JComboBox(makeVectorData(fullList)); - fontFamilySelector.setRenderer(new ComboBoxRenderer()); - fontFamilySelector.addActionListener(new BlockComboListener(fontFamilySelector)); - fontFamilySelector.setSelectedItem(currentFont.getFamily()); - fontFamilySelector.setEditable(false); - fontFamilySelector.setMaximumRowCount(commonFontFamilies.length); - fontFamilySelector.setToolTipText("Short list of common font families followed by complete list."); - - String[] fontStyles = EditorFont.getFontStyleStrings(); - fontStyleSelector = new JComboBox(fontStyles); - fontStyleSelector.setSelectedItem(EditorFont.styleIntToStyleString(currentFont.getStyle())); - fontStyleSelector.setEditable(false); - fontStyleSelector.setToolTipText("List of available font styles."); - - fontSizeSelector = new JSlider(EditorFont.MIN_SIZE, EditorFont.MAX_SIZE, currentFont.getSize()); - fontSizeSelector.setToolTipText("Use slider to select font size from "+EditorFont.MIN_SIZE+" to "+EditorFont.MAX_SIZE+"."); - fontSizeSelector.addChangeListener( - new ChangeListener() { - public void stateChanged(ChangeEvent e) { - Integer value = new Integer(((JSlider)e.getSource()).getValue()); - fontSizeSpinSelector.setValue(value); - fontSample.setFont(getFont()); - } - }); - SpinnerNumberModel fontSizeSpinnerModel = new SpinnerNumberModel(currentFont.getSize(), EditorFont.MIN_SIZE, EditorFont.MAX_SIZE, 1); - fontSizeSpinSelector = new JSpinner(fontSizeSpinnerModel); - fontSizeSpinSelector.setToolTipText("Current font size in points."); - fontSizeSpinSelector.addChangeListener( - new ChangeListener() { - public void stateChanged(ChangeEvent e) { - Object value = ((JSpinner)e.getSource()).getValue(); - fontSizeSelector.setValue(((Integer)value).intValue()); - fontSample.setFont(getFont()); - } - }); - // Action listener to update sample when family or style selected - ActionListener updateSample = - new ActionListener() { - public void actionPerformed(ActionEvent e) { - fontSample.setFont(getFont()); - } - }; - fontFamilySelector.addActionListener(updateSample); - fontStyleSelector.addActionListener(updateSample); - - JPanel familyStyleComponents = new JPanel(new GridLayout(2,2,4,4)); - familyStyleComponents.add(new JLabel("Font Family")); - familyStyleComponents.add(new JLabel("Font Style")); - familyStyleComponents.add(fontFamilySelector); - familyStyleComponents.add(fontStyleSelector); - - fontSample = new JLabel("Sample of this font", SwingConstants.CENTER); - fontSample.setBorder(new LineBorder(Color.BLACK)); - fontSample.setFont(getFont()); - fontSample.setToolTipText("Dynamically updated font sample based on current settings"); - JPanel sizeComponents = new JPanel(); - sizeComponents.add(new JLabel("Font Size ")); - sizeComponents.add(fontSizeSelector); - sizeComponents.add(fontSizeSpinSelector); - JPanel sizeAndSample = new JPanel(new GridLayout(2,1,4,8)); - sizeAndSample.add(sizeComponents); - sizeAndSample.add(fontSample); - contents.add(familyStyleComponents, BorderLayout.NORTH); - contents.add(sizeAndSample, BorderLayout.CENTER); - return contents; - } - // Build component containing the buttons for dialog control - // Such as OK, Cancel, Reset, Apply, etc. These may vary - // by application - protected abstract Component buildControlPanel(); +/** + * Abstract class for a font selection dialog. + */ +public abstract class AbstractFontSettingDialog extends JDialog +{ - - public Font getFont() { - return EditorFont.createFontFromStringValues( - (String) fontFamilySelector.getSelectedItem(), - (String) fontStyleSelector.getSelectedItem(), - fontSizeSpinSelector.getValue().toString() ); - } - - // User has clicked "Apply" or "Apply and Close" button. - protected void performApply() { - apply(this.getFont()); - } - - // We're finished with this modal dialog. - protected void closeDialog() { - this.setVisible(false); - this.dispose(); - } - - // Reset font to its initial setting - protected void reset() { - fontFamilySelector.setSelectedItem(initialFontFamily); - fontStyleSelector.setSelectedItem(initialFontStyle); - fontSizeSelector.setValue(EditorFont.sizeStringToSizeInt(initialFontSize)); - fontSizeSpinSelector.setValue(new Integer(EditorFont.sizeStringToSizeInt(initialFontSize))); - } - - /** - * Apply the given font. Left for the client to define. - * - * @param font a font to be applied by the client. - */ - protected abstract void apply(Font font); - - - ///////////////////////////////////////////////////////////////////// - // - // Method and two classes to permit one or more horizontal separators - // within a combo box list. I obtained this code on 13 July 2007 - // from http://www.codeguru.com/java/articles/164.shtml. Author - // is listed: Nobuo Tamemasa. Code is old, 1999, but fine for this. - // I will use it to separate the short list of "common" font - // families from the very long list of all font families. No attempt - // to keep a list of recently-used fonts like Word does. The list - // of common font families is static. - // - ///////////////////////////////////////////////////////////////////// - - private static String SEPARATOR = "___SEPARATOR____"; - - // Given an array of string arrays, will produce a Vector contenating - // the arrays with a separator between each. - private Vector makeVectorData(String[][] str) { - boolean needSeparator = false; - Vector data = new Vector(); - for (int i=0;i=0 && thisDistance < shortDistance) { + } + int shortDistance = 0x7fffffff; + int thisDistance; + // Check distance from .extern base. Cannot be below it + thisDistance = address - Memory.externBaseAddress; + if (thisDistance >= 0 && thisDistance < shortDistance) + { shortDistance = thisDistance; desiredComboBoxIndex = EXTERN_BASE_ADDRESS_INDEX; - } - // Check distance from global pointer; can be either side of it... - thisDistance = Math.abs(address - RegisterFile.getValue(RegisterFile.GLOBAL_POINTER_REGISTER)); // distance from global pointer - if (thisDistance < shortDistance) { + } + // Check distance from global pointer; can be either side of it... + thisDistance = Math.abs(address - RegisterFile.getValue(RegisterFile.GLOBAL_POINTER_REGISTER)); // distance from global pointer + if (thisDistance < shortDistance) + { shortDistance = thisDistance; desiredComboBoxIndex = GLOBAL_POINTER_ADDRESS_INDEX; - } - // Check distance from .data base. Cannot be below it - thisDistance = address - Memory.dataBaseAddress; - if (thisDistance >=0 && thisDistance < shortDistance) { + } + // Check distance from .data base. Cannot be below it + thisDistance = address - Memory.dataBaseAddress; + if (thisDistance >= 0 && thisDistance < shortDistance) + { shortDistance = thisDistance; desiredComboBoxIndex = DATA_BASE_ADDRESS_INDEX; - } - // Check distance from heap base. Cannot be below it - thisDistance = address - Memory.heapBaseAddress; - if (thisDistance >=0 && thisDistance < shortDistance) { + } + // Check distance from heap base. Cannot be below it + thisDistance = address - Memory.heapBaseAddress; + if (thisDistance >= 0 && thisDistance < shortDistance) + { shortDistance = thisDistance; desiredComboBoxIndex = HEAP_BASE_ADDRESS_INDEX; - } - // Check distance from stack pointer. Can be on either side of it... - thisDistance = Math.abs(address - RegisterFile.getValue(RegisterFile.STACK_POINTER_REGISTER)); - if (thisDistance < shortDistance) { + } + // Check distance from stack pointer. Can be on either side of it... + thisDistance = Math.abs(address - RegisterFile.getValue(RegisterFile.STACK_POINTER_REGISTER)); + if (thisDistance < shortDistance) + { shortDistance = thisDistance; desiredComboBoxIndex = STACK_POINTER_BASE_ADDRESS_INDEX; - } - return desiredComboBoxIndex; - } - - - //////////////////////////////////////////////////////////////////////////////// - // Generates the Address/Data part of the Data Segment window. - // Returns the JScrollPane for the Address/Data part of the Data Segment window. - private JScrollPane generateDataPanel(){ - dataData = new Object[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS]; - int valueBase = Globals.getGui().getMainPane().getExecutePane().getValueDisplayBase(); - int addressBase = Globals.getGui().getMainPane().getExecutePane().getAddressDisplayBase(); - int address = this.homeAddress; - for(int row=0; row=0. DPS 23 jan 2009 - addressColumn = -1; - } - - - private int getValueDisplayFormat() { - return (asciiDisplay) ? NumberDisplayBaseChooser.ASCII : + dataTable.getColumnModel().getColumn(i).setCellRenderer(addressCellRenderer); + } + dataTableScroller = new JScrollPane(dataTable, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + return dataTableScroller; + } + + // Little helper. Is called when headers set up and each time number base changes. + private String getHeaderStringForColumn(int i, int base) + { + return (i == ADDRESS_COLUMN) ? "Address" : "Value (+" + Integer.toString((i - 1) * BYTES_PER_VALUE, base) + ")"; + } + + + /** + * Generates and displays fresh table, typically done upon successful assembly. + */ + public void setupTable() + { + tablePanel.removeAll(); + tablePanel.add(generateDataPanel()); + contentPane.add(tablePanel); + enableAllButtons(); + } + + /** + * Removes the table from its frame, typically done when a file is closed. + */ + public void clearWindow() + { + tablePanel.removeAll(); + disableAllButtons(); + } + + /** + * Clear highlight background color from any cell currently highlighted. + */ + public void clearHighlighting() + { + addressHighlighting = false; + dataTable.tableChanged(new TableModelEvent(dataTable.getModel(), 0, dataData.length - 1)); + // The below addresses situation in which addressRow and addressColum hold their + // values across assemble operations. Whereupon at the first step of the next + // run the last cells from the previous run are highlighted! This method is called + // after each successful assemble (or reset, which just re-assembles). The + // assignment below assures the highlighting condition column==addressColumn will be + // initially false since column>=0. DPS 23 jan 2009 + addressColumn = -1; + } + + + private int getValueDisplayFormat() + { + return (asciiDisplay) ? NumberDisplayBaseChooser.ASCII : Globals.getGui().getMainPane().getExecutePane().getValueDisplayBase(); - } - - /** - * Update table model with contents of new memory "chunk". Mars supports megabytes of - * data segment space so we only plug a "chunk" at a time into the table. - * @param firstAddr the first address in the memory range to be placed in the model. - */ - - public void updateModelForMemoryRange(int firstAddr) { - if (tablePanel.getComponentCount() == 0) + } + + /** + * Update table model with contents of new memory "chunk". Mars supports megabytes of data segment space so we only + * plug a "chunk" at a time into the table. + * + * @param firstAddr the first address in the memory range to be placed in the model. + */ + + public void updateModelForMemoryRange(int firstAddr) + { + if (tablePanel.getComponentCount() == 0) + { return; // ignore if no content to change - int valueBase = getValueDisplayFormat(); - int addressBase = Globals.getGui().getMainPane().getExecutePane().getAddressDisplayBase(); - int address = firstAddr; - TableModel dataModel = dataTable.getModel(); - for (int row=0; row=MEMORY_CHUNK_SIZE) { // out of range + } + } + + /** + * Update data display to show this value (I'm not sure it is being called). + */ + + public void updateCell(int address, int value) + { + int offset = address - this.firstAddress; + if (offset < 0 || offset >= MEMORY_CHUNK_SIZE) + { // out of range return; - } - int row = offset/BYTES_PER_ROW; - int column = (offset % BYTES_PER_ROW)/BYTES_PER_VALUE + 1; // column 0 reserved for address - int valueBase = Globals.getGui().getMainPane().getExecutePane().getValueDisplayBase(); - ((DataTableModel)dataTable.getModel()).setDisplayAndModelValueAt(NumberDisplayBaseChooser.formatNumber(value, valueBase), - row,column); - } - - /** - * Redisplay the addresses. This should only be done when address display base is - * modified (e.g. between base 16, hex, and base 10, dec). - */ - public void updateDataAddresses() { - if (tablePanel.getComponentCount() == 0) + } + int row = offset / BYTES_PER_ROW; + int column = (offset % BYTES_PER_ROW) / BYTES_PER_VALUE + 1; // column 0 reserved for address + int valueBase = Globals.getGui().getMainPane().getExecutePane().getValueDisplayBase(); + ((DataTableModel) dataTable.getModel()).setDisplayAndModelValueAt(NumberDisplayBaseChooser.formatNumber(value, valueBase), + row, column); + } + + /** + * Redisplay the addresses. This should only be done when address display base is modified (e.g. between base 16, + * hex, and base 10, dec). + */ + public void updateDataAddresses() + { + if (tablePanel.getComponentCount() == 0) + { return; // ignore if no content to change - int addressBase = Globals.getGui().getMainPane().getExecutePane().getAddressDisplayBase(); - int address = this.firstAddress; - String formattedAddress; - for(int i=0; i= highLimit - MEMORY_CHUNK_SIZE) { + } + if (lowAddress >= highLimit - MEMORY_CHUNK_SIZE) + { lowAddress = highLimit - MEMORY_CHUNK_SIZE + 1; nextButton.setEnabled(false); - } - else { + } + else + { nextButton.setEnabled(true); - } - return lowAddress; - } - - - /** Required by Observer interface. Called when notified by an Observable that we are registered with. - * Observables include: - * The Simulator object, which lets us know when it starts and stops running - * A delegate of the Memory object, which lets us know of memory operations - * The Simulator keeps us informed of when simulated MIPS execution is active. - * This is the only time we care about memory operations. - * @param observable The Observable object who is notifying us - * @param obj Auxiliary object with additional information. - */ - - public void update(Observable observable, Object obj) { - if (observable == mars.simulator.Simulator.getInstance()) { + } + return lowAddress; + } + + + /** + * Required by Observer interface. Called when notified by an Observable that we are registered with. Observables + * include: The Simulator object, which lets us know when it starts and stops running A delegate of the Memory + * object, which lets us know of memory operations The Simulator keeps us informed of when simulated MIPS execution + * is active. This is the only time we care about memory operations. + * + * @param observable The Observable object who is notifying us + * @param obj Auxiliary object with additional information. + */ + + public void update(Observable observable, Object obj) + { + if (observable == mars.simulator.Simulator.getInstance()) + { SimulatorNotice notice = (SimulatorNotice) obj; - if (notice.getAction()==SimulatorNotice.SIMULATOR_START) { - - // Simulated MIPS execution starts. Respond to memory changes if running in timed - // or stepped mode. - if (notice.getRunSpeed() != RunSpeedPanel.UNLIMITED_SPEED || notice.getMaxSteps()==1) { - Memory.getInstance().addObserver(this); - addressHighlighting = true; - } - } - else { - // Simulated MIPS execution stops. Stop responding. - Memory.getInstance().deleteObserver(this); + if (notice.getAction() == SimulatorNotice.SIMULATOR_START) + { + + // Simulated MIPS execution starts. Respond to memory changes if running in timed + // or stepped mode. + if (notice.getRunSpeed() != RunSpeedPanel.UNLIMITED_SPEED || notice.getMaxSteps() == 1) + { + Memory.getInstance().addObserver(this); + addressHighlighting = true; + } } - } - else if (observable == settings) { + else + { + // Simulated MIPS execution stops. Stop responding. + Memory.getInstance().deleteObserver(this); + } + } + else if (observable == settings) + { // Suspended work in progress. Intended to disable combobox item for text segment. DPS 9-July-2013. //baseAddressSelector.getModel().getElementAt(TEXT_BASE_ADDRESS_INDEX) //*.setEnabled(settings.getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)); - } - else if (obj instanceof MemoryAccessNotice) { // NOTE: observable != Memory.getInstance() because Memory class delegates notification duty. + } + else if (obj instanceof MemoryAccessNotice) + { // NOTE: observable != Memory.getInstance() because Memory class delegates notification duty. MemoryAccessNotice access = (MemoryAccessNotice) obj; - if (access.getAccessType()==AccessNotice.WRITE) { - int address = access.getAddress(); - // Use the same highlighting technique as for Text Segment -- see - // AddressCellRenderer class below. - this.highlightCellForAddress(address); + if (access.getAccessType() == AccessNotice.WRITE) + { + int address = access.getAddress(); + // Use the same highlighting technique as for Text Segment -- see + // AddressCellRenderer class below. + this.highlightCellForAddress(address); } - } - } - - - /////////////////////////////////////////////////////////////////////////////// - // Class defined to address apparent Javax.swing.JComboBox bug: when selection is - // is set programmatically using setSelectedIndex() rather than by user-initiated - // event (such as mouse click), the text displayed in the JComboBox is not always - // updated correctly. Sometimes it is, sometimes updated to incorrect value. - // No pattern that I can detect. Google search yielded many forums addressing - // this problem. One suggested solution, a JComboBox superclass overriding - // setSelectedIndex to also call selectedItemChanged() did not help. Only this - // solution to extend the model class to call the protected - // "fireContentsChanged()" method worked. DPS 25-Jan-2009 - private class CustomComboBoxModel extends DefaultComboBoxModel { - public CustomComboBoxModel(Object[] list) { + } + } + + + /////////////////////////////////////////////////////////////////////////////// + // Class defined to address apparent Javax.swing.JComboBox bug: when selection is + // is set programmatically using setSelectedIndex() rather than by user-initiated + // event (such as mouse click), the text displayed in the JComboBox is not always + // updated correctly. Sometimes it is, sometimes updated to incorrect value. + // No pattern that I can detect. Google search yielded many forums addressing + // this problem. One suggested solution, a JComboBox superclass overriding + // setSelectedIndex to also call selectedItemChanged() did not help. Only this + // solution to extend the model class to call the protected + // "fireContentsChanged()" method worked. DPS 25-Jan-2009 + private class CustomComboBoxModel extends DefaultComboBoxModel + { + public CustomComboBoxModel(Object[] list) + { super(list); - } - private void forceComboBoxUpdate(int index) { - super.fireContentsChanged(this,index,index); - } - } - - - - //////////////////////////////////////////////////////////////////////// - // Class representing memory data table data - - class DataTableModel extends AbstractTableModel { - String[] columnNames; - Object[][] data; - - public DataTableModel(Object[][] d, String [] n){ - data=d; - columnNames= n; - } - - public int getColumnCount() { + } + + private void forceComboBoxUpdate(int index) + { + super.fireContentsChanged(this, index, index); + } + } + + + //////////////////////////////////////////////////////////////////////// + // Class representing memory data table data + + class DataTableModel extends AbstractTableModel + { + String[] columnNames; + + Object[][] data; + + public DataTableModel(Object[][] d, String[] n) + { + data = d; + columnNames = n; + } + + public int getColumnCount() + { return columnNames.length; - } - - public int getRowCount() { + } + + public int getRowCount() + { return data.length; - } - - public String getColumnName(int col) { + } + + public String getColumnName(int col) + { return columnNames[col]; - } - - public Object getValueAt(int row, int col) { + } + + public Object getValueAt(int row, int col) + { return data[row][col]; - } - + } + /* - * The cells in the Address column are not editable. - * Value cells are editable except when displayed - * in ASCII view - don't want to give the impression - * that ASCII text can be entered directly because - * it can't. It is possible but not worth the - * effort to implement. + * The cells in the Address column are not editable. + * Value cells are editable except when displayed + * in ASCII view - don't want to give the impression + * that ASCII text can be entered directly because + * it can't. It is possible but not worth the + * effort to implement. */ - public boolean isCellEditable(int row, int col) { + public boolean isCellEditable(int row, int col) + { //Note that the data/cell address is constant, //no matter where the cell appears onscreen. - if (col != ADDRESS_COLUMN && !asciiDisplay) { - return true; - } - else { - return false; - } - } - - - + return col != ADDRESS_COLUMN && !asciiDisplay; + } + + /* * JTable uses this method to determine the default renderer/ - * editor for each cell. + * editor for each cell. */ - public Class getColumnClass(int c) { + public Class getColumnClass(int c) + { return getValueAt(0, c).getClass(); - } - - + } + + /* * Update cell contents in table model. This method should be called - * only when user edits cell, so input validation has to be done. If - * value is valid, MIPS memory is updated. + * only when user edits cell, so input validation has to be done. If + * value is valid, MIPS memory is updated. */ - public void setValueAt(Object value, int row, int col) { - int val=0; - int address=0; - try { - val = Binary.stringToInt((String) value); + public void setValueAt(Object value, int row, int col) + { + int val = 0; + int address = 0; + try + { + val = Binary.stringToInt((String) value); } - catch (NumberFormatException nfe) { - data[row][col] = "INVALID"; - fireTableCellUpdated(row, col); - return; - } - - // calculate address from row and column - try { - address = Binary.stringToInt((String)data[row][ADDRESS_COLUMN]) + (col-1)*BYTES_PER_VALUE; // KENV 1/6/05 + catch (NumberFormatException nfe) + { + data[row][col] = "INVALID"; + fireTableCellUpdated(row, col); + return; } - catch (NumberFormatException nfe) { - // can't really happen since memory addresses are completely under - // the control of my software. - } - // Assures that if changed during MIPS program execution, the update will - // occur only between MIPS instructions. - synchronized (Globals.memoryAndRegistersLock) { - try { - Globals.memory.setRawWord(address,val); - } + + // calculate address from row and column + try + { + address = Binary.stringToInt((String) data[row][ADDRESS_COLUMN]) + (col - 1) * BYTES_PER_VALUE; // KENV 1/6/05 + } + catch (NumberFormatException nfe) + { + // can't really happen since memory addresses are completely under + // the control of my software. + } + // Assures that if changed during MIPS program execution, the update will + // occur only between MIPS instructions. + synchronized (Globals.memoryAndRegistersLock) + { + try + { + Globals.memory.setRawWord(address, val); + } // somehow, user was able to display out-of-range address. Most likely to occur between // stack base and Kernel. Also text segment with self-modifying-code setting off. - catch (AddressErrorException aee) { - return; - } + catch (AddressErrorException aee) + { + return; + } }// end synchronized block int valueBase = Globals.getGui().getMainPane().getExecutePane().getValueDisplayBase(); - data[row][col] = NumberDisplayBaseChooser.formatNumber(val, valueBase); + data[row][col] = NumberDisplayBaseChooser.formatNumber(val, valueBase); fireTableCellUpdated(row, col); - return; - } - - + } + + /* * Update cell contents in table model. Does not affect MIPS memory. */ - private void setDisplayAndModelValueAt(Object value, int row, int col) { + private void setDisplayAndModelValueAt(Object value, int row, int col) + { data[row][col] = value; fireTableCellUpdated(row, col); - } - - private void printDebugData() { + } + + private void printDebugData() + { int numRows = getRowCount(); int numCols = getColumnCount(); - - for (int i=0; i < numRows; i++) { - System.out.print(" row " + i + ":"); - for (int j=0; j < numCols; j++) { - System.out.print(" " + data[i][j]); - } - System.out.println(); + + for (int i = 0; i < numRows; i++) + { + System.out.print(" row " + i + ":"); + for (int j = 0; j < numCols; j++) + { + System.out.print(" " + data[i][j]); + } + System.out.println(); } System.out.println("--------------------------"); - } - } - - - // Special renderer capable of highlighting cells by changing background color. - // Will set background to highlight color if certain conditions met. - - class AddressCellRenderer extends DefaultTableCellRenderer { - - public Component getTableCellRendererComponent(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - JLabel cell = (JLabel) super.getTableCellRendererComponent(table, value, - isSelected, hasFocus, row, column); - + } + } + + + // Special renderer capable of highlighting cells by changing background color. + // Will set background to highlight color if certain conditions met. + + class AddressCellRenderer extends DefaultTableCellRenderer + { + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel cell = (JLabel) super.getTableCellRendererComponent(table, value, + isSelected, hasFocus, row, column); + cell.setHorizontalAlignment(SwingConstants.RIGHT); - int rowFirstAddress = Binary.stringToInt(table.getValueAt(row,ADDRESS_COLUMN).toString()); - if (settings.getDataSegmentHighlighting() && addressHighlighting && rowFirstAddress==addressRowFirstAddress && column==addressColumn) { - cell.setBackground( settings.getColorSettingByPosition(Settings.DATASEGMENT_HIGHLIGHT_BACKGROUND) ); - cell.setForeground( settings.getColorSettingByPosition(Settings.DATASEGMENT_HIGHLIGHT_FOREGROUND) ); - cell.setFont( settings.getFontByPosition(Settings.DATASEGMENT_HIGHLIGHT_FONT) ); - } - else if (row%2==0) { - cell.setBackground( settings.getColorSettingByPosition(Settings.EVEN_ROW_BACKGROUND) ); - cell.setForeground( settings.getColorSettingByPosition(Settings.EVEN_ROW_FOREGROUND) ); - cell.setFont( settings.getFontByPosition(Settings.EVEN_ROW_FONT) ); - } - else { - cell.setBackground( settings.getColorSettingByPosition(Settings.ODD_ROW_BACKGROUND) ); - cell.setForeground( settings.getColorSettingByPosition(Settings.ODD_ROW_FOREGROUND) ); - cell.setFont( settings.getFontByPosition(Settings.ODD_ROW_FONT) ); + int rowFirstAddress = Binary.stringToInt(table.getValueAt(row, ADDRESS_COLUMN).toString()); + if (settings.getDataSegmentHighlighting() && addressHighlighting && rowFirstAddress == addressRowFirstAddress && column == addressColumn) + { + cell.setBackground(settings.getColorSettingByPosition(Settings.DATASEGMENT_HIGHLIGHT_BACKGROUND)); + cell.setForeground(settings.getColorSettingByPosition(Settings.DATASEGMENT_HIGHLIGHT_FOREGROUND)); + cell.setFont(settings.getFontByPosition(Settings.DATASEGMENT_HIGHLIGHT_FONT)); + } + else if (row % 2 == 0) + { + cell.setBackground(settings.getColorSettingByPosition(Settings.EVEN_ROW_BACKGROUND)); + cell.setForeground(settings.getColorSettingByPosition(Settings.EVEN_ROW_FOREGROUND)); + cell.setFont(settings.getFontByPosition(Settings.EVEN_ROW_FONT)); + } + else + { + cell.setBackground(settings.getColorSettingByPosition(Settings.ODD_ROW_BACKGROUND)); + cell.setForeground(settings.getColorSettingByPosition(Settings.ODD_ROW_FOREGROUND)); + cell.setFont(settings.getFontByPosition(Settings.ODD_ROW_FONT)); } return cell; - } - - } - /////////////////////////////////////////////////////////////////// - // - // JTable subclass to provide custom tool tips for each of the - // text table column headers. From Sun's JTable tutorial. - // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html - // - private class MyTippedJTable extends JTable { - MyTippedJTable(DataTableModel m) { + } + + } + + /////////////////////////////////////////////////////////////////// + // + // JTable subclass to provide custom tool tips for each of the + // text table column headers. From Sun's JTable tutorial. + // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html + // + private class MyTippedJTable extends JTable + { + private final String[] columnToolTips = { + /* address */ "Base MIPS memory address for this row of the table.", + /* value +0 */ "32-bit value stored at base address for its row.", + /* value +n */ "32-bit value stored ", + /* value +n */ " bytes beyond base address for its row." + }; + + MyTippedJTable(DataTableModel m) + { super(m); - } - - private String[] columnToolTips = { - /* address */ "Base MIPS memory address for this row of the table.", - /* value +0 */ "32-bit value stored at base address for its row.", - /* value +n */ "32-bit value stored ", - /* value +n */ " bytes beyond base address for its row." - }; - - //Implement table header tool tips. - protected JTableHeader createDefaultTableHeader() { - return - new JTableHeader(columnModel) { - public String getToolTipText(MouseEvent e) { - String tip = null; - java.awt.Point p = e.getPoint(); - int index = columnModel.getColumnIndexAtX(p.x); - int realIndex = columnModel.getColumn(index).getModelIndex(); - return (realIndex < 2) ? columnToolTips[realIndex] - : columnToolTips[2]+((realIndex-1)*4)+columnToolTips[3]; - } - }; - } - } - - /////////////////////////////////////////////////////////////////////// - // - // The Prev button (left arrow) scrolls downward through the - // selected address range. It is a RepeatButton, which means - // if the mouse is held down on the button, it will repeatedly - // fire after an initial delay. Allows rapid scrolling. - // DPS 20 July 2008 - private class PrevButton extends RepeatButton { - public PrevButton(Icon ico) { + } + + //Implement table header tool tips. + protected JTableHeader createDefaultTableHeader() + { + return + new JTableHeader(columnModel) + { + public String getToolTipText(MouseEvent e) + { + String tip = null; + java.awt.Point p = e.getPoint(); + int index = columnModel.getColumnIndexAtX(p.x); + int realIndex = columnModel.getColumn(index).getModelIndex(); + return (realIndex < 2) ? columnToolTips[realIndex] + : columnToolTips[2] + ((realIndex - 1) * 4) + columnToolTips[3]; + } + }; + } + } + + /////////////////////////////////////////////////////////////////////// + // + // The Prev button (left arrow) scrolls downward through the + // selected address range. It is a RepeatButton, which means + // if the mouse is held down on the button, it will repeatedly + // fire after an initial delay. Allows rapid scrolling. + // DPS 20 July 2008 + private class PrevButton extends RepeatButton + { + public PrevButton(Icon ico) + { super(ico); this.setInitialDelay(500); // 500 milliseconds hold-down before firing this.setDelay(60); // every 60 milliseconds after that this.addActionListener(this); - } - // This one will respond when either timer goes off or button lifted. - public void actionPerformed(ActionEvent ae) { + } + + // This one will respond when either timer goes off or button lifted. + public void actionPerformed(ActionEvent ae) + { firstAddress -= PREV_NEXT_CHUNK_SIZE; firstAddress = setFirstAddressAndPrevNextButtonEnableStatus(firstAddress); updateModelForMemoryRange(firstAddress); - } - }////////////////////////////////////////////////////////////////////// - - /////////////////////////////////////////////////////////////////////// - // - // The Next button (right arrow) scrolls upward through the - // selected address range. It is a RepeatButton, which means - // if the mouse is held down on the button, it will repeatedly - // fire after an initial delay. Allows rapid scrolling. - // DPS 20 July 2008 - private class NextButton extends RepeatButton { - public NextButton(Icon ico) { + } + }////////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////////// + // + // The Next button (right arrow) scrolls upward through the + // selected address range. It is a RepeatButton, which means + // if the mouse is held down on the button, it will repeatedly + // fire after an initial delay. Allows rapid scrolling. + // DPS 20 July 2008 + private class NextButton extends RepeatButton + { + public NextButton(Icon ico) + { super(ico); this.setInitialDelay(500); // 500 milliseconds hold-down before firing this.setDelay(60); // every 60 milliseconds after that this.addActionListener(this); - } - // This one will respond when either timer goes off or button lifted. - public void actionPerformed(ActionEvent ae) { + } + + // This one will respond when either timer goes off or button lifted. + public void actionPerformed(ActionEvent ae) + { firstAddress += PREV_NEXT_CHUNK_SIZE; firstAddress = setFirstAddressAndPrevNextButtonEnableStatus(firstAddress); updateModelForMemoryRange(firstAddress); - } - }////////////////////////////////////////////////////////////////////// - - } + } + }////////////////////////////////////////////////////////////////////// + +} diff --git a/src/main/java/mars/venus/EditCopyAction.java b/src/main/java/mars/venus/EditCopyAction.java index 57d265a..ab2dc10 100644 --- a/src/main/java/mars/venus/EditCopyAction.java +++ b/src/main/java/mars/venus/EditCopyAction.java @@ -1,8 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,18 +30,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the Edit -> Copy menu item - */ - public class EditCopyAction extends GuiAction { - - public EditCopyAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e){ - mainUI.getMainPane().getEditPane().copyText(); - } - } \ No newline at end of file + +/** + * Action for the Edit -> Copy menu item + */ +public class EditCopyAction extends GuiAction +{ + + public EditCopyAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + mainUI.getMainPane().getEditPane().copyText(); + } +} diff --git a/src/main/java/mars/venus/EditCutAction.java b/src/main/java/mars/venus/EditCutAction.java index c9ea645..8ce7d92 100644 --- a/src/main/java/mars/venus/EditCutAction.java +++ b/src/main/java/mars/venus/EditCutAction.java @@ -1,8 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,18 +30,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the Edit -> Cut menu item - */ - public class EditCutAction extends GuiAction { - - public EditCutAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e){ - mainUI.getMainPane().getEditPane().cutText(); - } - } \ No newline at end of file + +/** + * Action for the Edit -> Cut menu item + */ +public class EditCutAction extends GuiAction +{ + + public EditCutAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + mainUI.getMainPane().getEditPane().cutText(); + } +} diff --git a/src/main/java/mars/venus/EditFindReplaceAction.java b/src/main/java/mars/venus/EditFindReplaceAction.java index d69ba6c..6a61ae2 100644 --- a/src/main/java/mars/venus/EditFindReplaceAction.java +++ b/src/main/java/mars/venus/EditFindReplaceAction.java @@ -1,9 +1,14 @@ - package mars.venus; - import mars.*; - import mars.venus.editors.MARSTextEditingArea; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import mars.Globals; +import mars.venus.editors.MARSTextEditingArea; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -32,156 +37,193 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the Edit -> Find/Replace menu item - */ - public class EditFindReplaceAction extends GuiAction { - private static String searchString = ""; - private static boolean caseSensitivity = true; - private static final String DIALOG_TITLE = "Find and Replace"; - JDialog findReplaceDialog; - public EditFindReplaceAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e){ - findReplaceDialog = new FindReplaceDialog(Globals.getGui(), DIALOG_TITLE, false ); - findReplaceDialog.setVisible(true); - } - - ////////////////////////////////////////////////////////////////////////////// - // - // Private class to do all the work! - // - private class FindReplaceDialog extends JDialog { - JButton findButton, replaceButton, replaceAllButton, closeButton; - JTextField findInputField, replaceInputField; - JCheckBox caseSensitiveCheckBox; - JRadioButton linearFromStart, circularFromCursor; - private JLabel resultsLabel; - - public static final String FIND_TOOL_TIP_TEXT = "Find next occurrence of given text; wraps around at end"; - public static final String REPLACE_TOOL_TIP_TEXT = "Replace current occurrence of text then find next"; - public static final String REPLACE_ALL_TOOL_TIP_TEXT = "Replace all occurrences of text"; - public static final String CLOSE_TOOL_TIP_TEXT = "Close the dialog"; - public static final String RESULTS_TOOL_TIP_TEXT = "Outcome of latest operation (button click)"; - - public static final String RESULTS_TEXT_FOUND = "Text found"; - public static final String RESULTS_TEXT_NOT_FOUND = "Text not found"; - public static final String RESULTS_TEXT_REPLACED = "Text replaced and found next"; - public static final String RESULTS_TEXT_REPLACED_LAST = "Text replaced; last occurrence"; - public static final String RESULTS_TEXT_REPLACED_ALL = "Replaced"; - public static final String RESULTS_NO_TEXT_TO_FIND = "No text to find"; - - public FindReplaceDialog(Frame owner, String title, boolean modality) { + +/** + * Action for the Edit -> Find/Replace menu item + */ +public class EditFindReplaceAction extends GuiAction +{ + private static final String DIALOG_TITLE = "Find and Replace"; + + private static String searchString = ""; + + private static boolean caseSensitivity = true; + + JDialog findReplaceDialog; + + public EditFindReplaceAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + findReplaceDialog = new FindReplaceDialog(Globals.getGui(), DIALOG_TITLE, false); + findReplaceDialog.setVisible(true); + } + + ////////////////////////////////////////////////////////////////////////////// + // + // Private class to do all the work! + // + private class FindReplaceDialog extends JDialog + { + public static final String FIND_TOOL_TIP_TEXT = "Find next occurrence of given text; wraps around at end"; + + public static final String REPLACE_TOOL_TIP_TEXT = "Replace current occurrence of text then find next"; + + public static final String REPLACE_ALL_TOOL_TIP_TEXT = "Replace all occurrences of text"; + + public static final String CLOSE_TOOL_TIP_TEXT = "Close the dialog"; + + public static final String RESULTS_TOOL_TIP_TEXT = "Outcome of latest operation (button click)"; + + public static final String RESULTS_TEXT_FOUND = "Text found"; + + public static final String RESULTS_TEXT_NOT_FOUND = "Text not found"; + + public static final String RESULTS_TEXT_REPLACED = "Text replaced and found next"; + + public static final String RESULTS_TEXT_REPLACED_LAST = "Text replaced; last occurrence"; + + public static final String RESULTS_TEXT_REPLACED_ALL = "Replaced"; + + public static final String RESULTS_NO_TEXT_TO_FIND = "No text to find"; + + JButton findButton, replaceButton, replaceAllButton, closeButton; + + JTextField findInputField, replaceInputField; + + JCheckBox caseSensitiveCheckBox; + + JRadioButton linearFromStart, circularFromCursor; + + private JLabel resultsLabel; + + public FindReplaceDialog(Frame owner, String title, boolean modality) + { super(owner, title, modality); this.setContentPane(buildDialogPanel()); this.setDefaultCloseOperation( - JDialog.DO_NOTHING_ON_CLOSE); + JDialog.DO_NOTHING_ON_CLOSE); this.addWindowListener( - new WindowAdapter() { - public void windowClosing(WindowEvent we) { + new WindowAdapter() + { + public void windowClosing(WindowEvent we) + { performClose(); - } - }); + } + }); this.pack(); this.setLocationRelativeTo(owner); - } - - // Constructs the dialog's main panel. - private JPanel buildDialogPanel() { + } + + // Constructs the dialog's main panel. + private JPanel buildDialogPanel() + { JPanel dialogPanel = new JPanel(new BorderLayout()); - dialogPanel.setBorder(new javax.swing.border.EmptyBorder(10,10,10,10)); + dialogPanel.setBorder(new javax.swing.border.EmptyBorder(10, 10, 10, 10)); dialogPanel.add(buildInputPanel(), BorderLayout.NORTH); dialogPanel.add(buildOptionsPanel()); - dialogPanel.add(buildControlPanel(),BorderLayout.SOUTH); + dialogPanel.add(buildControlPanel(), BorderLayout.SOUTH); return dialogPanel; - } - - // Top part of the dialog, to contain the two input text fields. - private Component buildInputPanel() { + } + + // Top part of the dialog, to contain the two input text fields. + private Component buildInputPanel() + { findInputField = new JTextField(30); - if (searchString.length() > 0) { - findInputField.setText(searchString); - findInputField.selectAll(); + if (searchString.length() > 0) + { + findInputField.setText(searchString); + findInputField.selectAll(); } replaceInputField = new JTextField(30); JPanel inputPanel = new JPanel(); - JPanel labelsPanel = new JPanel(new GridLayout(2,1,5,5)); - JPanel fieldsPanel = new JPanel(new GridLayout(2,1,5,5)); + JPanel labelsPanel = new JPanel(new GridLayout(2, 1, 5, 5)); + JPanel fieldsPanel = new JPanel(new GridLayout(2, 1, 5, 5)); labelsPanel.add(new JLabel("Find what:")); labelsPanel.add(new JLabel("Replace with:")); fieldsPanel.add(findInputField); fieldsPanel.add(replaceInputField); - + Box columns = Box.createHorizontalBox(); columns.add(labelsPanel); columns.add(Box.createHorizontalStrut(6)); columns.add(fieldsPanel); inputPanel.add(columns); - return inputPanel; - } - - // Center part of the dialog, which contains the check box - // for case sensitivity along with a label to display the - // outcome of each operation. - private Component buildOptionsPanel() { + return inputPanel; + } + + // Center part of the dialog, which contains the check box + // for case sensitivity along with a label to display the + // outcome of each operation. + private Component buildOptionsPanel() + { Box optionsPanel = Box.createHorizontalBox(); - caseSensitiveCheckBox = new JCheckBox("Case Sensitive",caseSensitivity); - JPanel casePanel = new JPanel(new GridLayout(2,1)); + caseSensitiveCheckBox = new JCheckBox("Case Sensitive", caseSensitivity); + JPanel casePanel = new JPanel(new GridLayout(2, 1)); casePanel.add(caseSensitiveCheckBox); casePanel.setMaximumSize(casePanel.getPreferredSize()); optionsPanel.add(casePanel); optionsPanel.add(Box.createHorizontalStrut(5)); - JPanel resultsPanel = new JPanel(new GridLayout(1,1)); + JPanel resultsPanel = new JPanel(new GridLayout(1, 1)); resultsPanel.setBorder(BorderFactory.createTitledBorder("Outcome")); resultsLabel = new JLabel(""); resultsLabel.setForeground(Color.RED); resultsLabel.setToolTipText(RESULTS_TOOL_TIP_TEXT); - resultsPanel.add(resultsLabel); + resultsPanel.add(resultsLabel); optionsPanel.add(resultsPanel); return optionsPanel; - } - - - // Row of control buttons to be placed along the button of the dialog - private Component buildControlPanel() { + } + + + // Row of control buttons to be placed along the button of the dialog + private Component buildControlPanel() + { Box controlPanel = Box.createHorizontalBox(); - controlPanel.setBorder(BorderFactory.createEmptyBorder(6,0,0,0)); + controlPanel.setBorder(BorderFactory.createEmptyBorder(6, 0, 0, 0)); findButton = new JButton("Find"); findButton.setToolTipText(FIND_TOOL_TIP_TEXT); findButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performFind(); - } - }); + } + }); replaceButton = new JButton("Replace then Find"); replaceButton.setToolTipText(REPLACE_TOOL_TIP_TEXT); replaceButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performReplace(); - } - }); + } + }); replaceAllButton = new JButton("Replace all"); replaceAllButton.setToolTipText(REPLACE_ALL_TOOL_TIP_TEXT); replaceAllButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performReplaceAll(); - } - }); + } + }); closeButton = new JButton("Close"); closeButton.setToolTipText(CLOSE_TOOL_TIP_TEXT); closeButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performClose(); - } - }); + } + }); controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(findButton); controlPanel.add(Box.createHorizontalGlue()); @@ -190,110 +232,128 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. controlPanel.add(replaceAllButton); controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(closeButton); - controlPanel.add(Box.createHorizontalGlue()); + controlPanel.add(Box.createHorizontalGlue()); return controlPanel; - } - - //////////////////////////////////////////////////////////////////////// - // - // Private methods to carry out the button actions - - // Performs a find. The operation starts at the current cursor position - // which is not known to this object but is maintained by the EditPane - // object. The operation will wrap around when it reaches the end of the - // document. If found, the matching text is selected. - private void performFind() { + } + + //////////////////////////////////////////////////////////////////////// + // + // Private methods to carry out the button actions + + // Performs a find. The operation starts at the current cursor position + // which is not known to this object but is maintained by the EditPane + // object. The operation will wrap around when it reaches the end of the + // document. If found, the matching text is selected. + private void performFind() + { resultsLabel.setText(""); - if (findInputField.getText().length() > 0) { - EditPane editPane = mainUI.getMainPane().getEditPane(); - // Being cautious. Should not be null because find/replace tool button disabled if no file open - if (editPane != null) { - searchString = findInputField.getText(); - int posn = editPane.doFindText(searchString, caseSensitiveCheckBox.isSelected()); - if (posn == MARSTextEditingArea.TEXT_NOT_FOUND) { - resultsLabel.setText(findButton.getText()+": "+RESULTS_TEXT_NOT_FOUND); - } - else { - resultsLabel.setText(findButton.getText()+": "+RESULTS_TEXT_FOUND); - } - } - } - else { - resultsLabel.setText(findButton.getText()+": "+RESULTS_NO_TEXT_TO_FIND); + if (findInputField.getText().length() > 0) + { + EditPane editPane = mainUI.getMainPane().getEditPane(); + // Being cautious. Should not be null because find/replace tool button disabled if no file open + if (editPane != null) + { + searchString = findInputField.getText(); + int posn = editPane.doFindText(searchString, caseSensitiveCheckBox.isSelected()); + if (posn == MARSTextEditingArea.TEXT_NOT_FOUND) + { + resultsLabel.setText(findButton.getText() + ": " + RESULTS_TEXT_NOT_FOUND); + } + else + { + resultsLabel.setText(findButton.getText() + ": " + RESULTS_TEXT_FOUND); + } + } } - } - - // Performs a replace-and-find. If the matched text is current selected with cursor at - // its end, the replace happens immediately followed by a find for the next occurrence. - // Otherwise, it performs a find. This will select the matching text so the next press - // of Replace will do the replace. This is apparently common behavior for replace - // buttons of different apps I've checked. - private void performReplace() { - resultsLabel.setText(""); - if (findInputField.getText().length() > 0) { - EditPane editPane = mainUI.getMainPane().getEditPane(); - // Being cautious. Should not be null b/c find/replace tool button disabled if no file open - if (editPane != null) { - searchString = findInputField.getText(); - int posn = editPane.doReplace(searchString, replaceInputField.getText(), caseSensitiveCheckBox.isSelected()); - String result = replaceButton.getText()+": "; - switch (posn) { - - case MARSTextEditingArea.TEXT_NOT_FOUND : - result += RESULTS_TEXT_NOT_FOUND; - break; - case MARSTextEditingArea.TEXT_FOUND : - result += RESULTS_TEXT_FOUND; - break; - case MARSTextEditingArea.TEXT_REPLACED_NOT_FOUND_NEXT : - result += RESULTS_TEXT_REPLACED_LAST; - break; - case MARSTextEditingArea.TEXT_REPLACED_FOUND_NEXT : - result += RESULTS_TEXT_REPLACED; - break; - } - resultsLabel.setText(result); - } - } - else { - resultsLabel.setText(replaceButton.getText()+": "+RESULTS_NO_TEXT_TO_FIND); + else + { + resultsLabel.setText(findButton.getText() + ": " + RESULTS_NO_TEXT_TO_FIND); } - - } - - // Performs a replace-all. Makes one pass through the document starting at - // position 0. - private void performReplaceAll() { + } + + // Performs a replace-and-find. If the matched text is current selected with cursor at + // its end, the replace happens immediately followed by a find for the next occurrence. + // Otherwise, it performs a find. This will select the matching text so the next press + // of Replace will do the replace. This is apparently common behavior for replace + // buttons of different apps I've checked. + private void performReplace() + { resultsLabel.setText(""); - if (findInputField.getText().length() > 0) { - EditPane editPane = mainUI.getMainPane().getEditPane(); - // Being cautious. Should not be null b/c find/replace tool button disabled if no file open - if (editPane != null) { - searchString = findInputField.getText(); - int replaceCount = editPane.doReplaceAll(searchString, replaceInputField.getText(), caseSensitiveCheckBox.isSelected()); - if (replaceCount == 0) { - resultsLabel.setText(replaceAllButton.getText()+": "+RESULTS_TEXT_NOT_FOUND); - } - else { - resultsLabel.setText(replaceAllButton.getText()+": "+RESULTS_TEXT_REPLACED_ALL + " "+replaceCount+" occurrence"+(replaceCount==1 ? "" : "s")); - } - } - } - else { - resultsLabel.setText(replaceAllButton.getText()+": "+RESULTS_NO_TEXT_TO_FIND); - } - } - - // Performs the close operation. Records the current state of the case-sensitivity - // checkbox into a static variable so it will be remembered across invocations within - // the session. This also happens with the contents of the "find" text field. - private void performClose() { - caseSensitivity = caseSensitiveCheckBox.isSelected(); + if (findInputField.getText().length() > 0) + { + EditPane editPane = mainUI.getMainPane().getEditPane(); + // Being cautious. Should not be null b/c find/replace tool button disabled if no file open + if (editPane != null) + { + searchString = findInputField.getText(); + int posn = editPane.doReplace(searchString, replaceInputField.getText(), caseSensitiveCheckBox.isSelected()); + String result = replaceButton.getText() + ": "; + switch (posn) + { + + case MARSTextEditingArea.TEXT_NOT_FOUND: + result += RESULTS_TEXT_NOT_FOUND; + break; + case MARSTextEditingArea.TEXT_FOUND: + result += RESULTS_TEXT_FOUND; + break; + case MARSTextEditingArea.TEXT_REPLACED_NOT_FOUND_NEXT: + result += RESULTS_TEXT_REPLACED_LAST; + break; + case MARSTextEditingArea.TEXT_REPLACED_FOUND_NEXT: + result += RESULTS_TEXT_REPLACED; + break; + } + resultsLabel.setText(result); + } + } + else + { + resultsLabel.setText(replaceButton.getText() + ": " + RESULTS_NO_TEXT_TO_FIND); + } + + } + + // Performs a replace-all. Makes one pass through the document starting at + // position 0. + private void performReplaceAll() + { + resultsLabel.setText(""); + if (findInputField.getText().length() > 0) + { + EditPane editPane = mainUI.getMainPane().getEditPane(); + // Being cautious. Should not be null b/c find/replace tool button disabled if no file open + if (editPane != null) + { + searchString = findInputField.getText(); + int replaceCount = editPane.doReplaceAll(searchString, replaceInputField.getText(), caseSensitiveCheckBox.isSelected()); + if (replaceCount == 0) + { + resultsLabel.setText(replaceAllButton.getText() + ": " + RESULTS_TEXT_NOT_FOUND); + } + else + { + resultsLabel.setText(replaceAllButton.getText() + ": " + RESULTS_TEXT_REPLACED_ALL + " " + replaceCount + " occurrence" + (replaceCount == 1 ? "" : "s")); + } + } + } + else + { + resultsLabel.setText(replaceAllButton.getText() + ": " + RESULTS_NO_TEXT_TO_FIND); + } + } + + // Performs the close operation. Records the current state of the case-sensitivity + // checkbox into a static variable so it will be remembered across invocations within + // the session. This also happens with the contents of the "find" text field. + private void performClose() + { + caseSensitivity = caseSensitiveCheckBox.isSelected(); this.setVisible(false); this.dispose(); - } - // - //////////////////////////////////////////////////////////////////////////////// - } - - } \ No newline at end of file + } + // + //////////////////////////////////////////////////////////////////////////////// + } + +} diff --git a/src/main/java/mars/venus/EditPane.java b/src/main/java/mars/venus/EditPane.java index ad9cdbd..0d50be7 100644 --- a/src/main/java/mars/venus/EditPane.java +++ b/src/main/java/mars/venus/EditPane.java @@ -1,16 +1,24 @@ - package mars.venus; - import mars.*; - import mars.venus.editors.MARSTextEditingArea; - import mars.venus.editors.generic.GenericTextArea; - import mars.venus.editors.jeditsyntax.JEditBasedTextArea; - import javax.swing.*; - import javax.swing.event.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.undo.*; - import java.text.*; - import java.util.*; - import java.io.*; +package mars.venus; + +import mars.Globals; +import mars.Settings; +import mars.venus.editors.MARSTextEditingArea; +import mars.venus.editors.generic.GenericTextArea; +import mars.venus.editors.jeditsyntax.JEditBasedTextArea; + +import javax.swing.*; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoManager; +import java.awt.*; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.Observable; +import java.util.Observer; /* Copyright (c) 2003-2011, Pete Sanderson and Kenneth Vollmar @@ -40,312 +48,352 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - /** - * Represents one file opened for editing. Maintains required internal structures. - * Before Mars 4.0, there was only one editor pane, a tab, and only one file could - * be open at a time. With 4.0 came the multifile (pane, tab) editor, and existing - * duties were split between EditPane and the new EditTabbedPane class. - * @author Sanderson and Bumgarner - */ +/** + * Represents one file opened for editing. Maintains required internal structures. Before Mars 4.0, there was only one + * editor pane, a tab, and only one file could be open at a time. With 4.0 came the multifile (pane, tab) editor, and + * existing duties were split between EditPane and the new EditTabbedPane class. + * + * @author Sanderson and Bumgarner + */ - public class EditPane extends JPanel implements Observer { - - private MARSTextEditingArea sourceCode; - private VenusUI mainUI; - private String currentDirectoryPath; - private JLabel caretPositionLabel; - private JCheckBox showLineNumbers; - private JLabel lineNumbers; - private static int count = 0; - private boolean isCompoundEdit = false; - private CompoundEdit compoundEdit; - private FileStatus fileStatus; - - /** - * Constructor for the EditPane class. - */ - - public EditPane(VenusUI appFrame){ - super(new BorderLayout()); - this.mainUI = appFrame; - // user.dir, user's current working directory, is guaranteed to have a value - currentDirectoryPath = System.getProperty("user.dir"); - // mainUI.editor = new Editor(mainUI); - // We want to be notified of editor font changes! See update() below. - Globals.getSettings().addObserver(this); - this.fileStatus = new FileStatus(); - lineNumbers = new JLabel(); - - if (Globals.getSettings().getBooleanSetting(Settings.GENERIC_TEXT_EDITOR)) { +public class EditPane extends JPanel implements Observer +{ + + /** + * Form string with source code line numbers. Resulting string is HTML, for which JLabel will happily honor
to + * do multiline label (it ignores '\n'). The line number list is a JLabel with one line number per line. + */ + private static final String spaces = "         "; + + /** + * Given byte stream position in text being edited, calculate its column and line number coordinates. + * + * @param stream position of character + * @return position Its column and line number coordinate as a Point. + */ + private static final char newline = '\n'; + + private static final int count = 0; + + private final MARSTextEditingArea sourceCode; + + private final VenusUI mainUI; + + private final String currentDirectoryPath; + + private final JLabel caretPositionLabel; + + private final JCheckBox showLineNumbers; + + private final JLabel lineNumbers; + + private final boolean isCompoundEdit = false; + + private CompoundEdit compoundEdit; + + private final FileStatus fileStatus; + + /** + * Constructor for the EditPane class. + */ + + public EditPane(VenusUI appFrame) + { + super(new BorderLayout()); + this.mainUI = appFrame; + // user.dir, user's current working directory, is guaranteed to have a value + currentDirectoryPath = System.getProperty("user.dir"); + // mainUI.editor = new Editor(mainUI); + // We want to be notified of editor font changes! See update() below. + Globals.getSettings().addObserver(this); + this.fileStatus = new FileStatus(); + lineNumbers = new JLabel(); + + if (Globals.getSettings().getBooleanSetting(Settings.GENERIC_TEXT_EDITOR)) + { this.sourceCode = new GenericTextArea(this, lineNumbers); - } - else { + } + else + { this.sourceCode = new JEditBasedTextArea(this, lineNumbers); - } - // sourceCode is responsible for its own scrolling - this.add(this.sourceCode.getOuterComponent(), BorderLayout.CENTER); - - // If source code is modified, will set flag to trigger/request file save. - sourceCode.getDocument().addDocumentListener( - new DocumentListener() { - public void insertUpdate(DocumentEvent evt) { - // IF statement added DPS 9-Aug-2011 - // This method is triggered when file contents added to document - // upon opening, even though not edited by user. The IF - // statement will sense this situation and immediately return. - if (FileStatus.get() == FileStatus.OPENING) { - setFileStatus(FileStatus.NOT_EDITED); + } + // sourceCode is responsible for its own scrolling + this.add(this.sourceCode.getOuterComponent(), BorderLayout.CENTER); + + // If source code is modified, will set flag to trigger/request file save. + sourceCode.getDocument().addDocumentListener( + new DocumentListener() + { + public void insertUpdate(DocumentEvent evt) + { + // IF statement added DPS 9-Aug-2011 + // This method is triggered when file contents added to document + // upon opening, even though not edited by user. The IF + // statement will sense this situation and immediately return. + if (FileStatus.get() == FileStatus.OPENING) + { + setFileStatus(FileStatus.NOT_EDITED); FileStatus.set(FileStatus.NOT_EDITED); - if (showingLineNumbers()) { - lineNumbers.setText(getLineNumbersList(sourceCode.getDocument())); + if (showingLineNumbers()) + { + lineNumbers.setText(getLineNumbersList(sourceCode.getDocument())); } return; - } - // End of 9-Aug-2011 modification. - if (getFileStatus() == FileStatus.NEW_NOT_EDITED) { + } + // End of 9-Aug-2011 modification. + if (getFileStatus() == FileStatus.NEW_NOT_EDITED) + { setFileStatus(FileStatus.NEW_EDITED); - } - if (getFileStatus() == FileStatus.NOT_EDITED) { + } + if (getFileStatus() == FileStatus.NOT_EDITED) + { setFileStatus(FileStatus.EDITED); - } - if (getFileStatus() == FileStatus.NEW_EDITED) { - mainUI.editor.setTitle( "", getFilename(), getFileStatus()); - } - else { + } + if (getFileStatus() == FileStatus.NEW_EDITED) + { + mainUI.editor.setTitle("", getFilename(), getFileStatus()); + } + else + { mainUI.editor.setTitle(getPathname(), getFilename(), getFileStatus()); - } - - FileStatus.setEdited(true); - switch (FileStatus.get()) { + } + + FileStatus.setEdited(true); + switch (FileStatus.get()) + { case FileStatus.NEW_NOT_EDITED: - FileStatus.set(FileStatus.NEW_EDITED); - break; + FileStatus.set(FileStatus.NEW_EDITED); + break; case FileStatus.NEW_EDITED: - break; + break; default: - FileStatus.set(FileStatus.EDITED); - } - - Globals.getGui().getMainPane().getExecutePane().clearPane(); // DPS 9-Aug-2011 - - if (showingLineNumbers()) { + FileStatus.set(FileStatus.EDITED); + } + + Globals.getGui().getMainPane().getExecutePane().clearPane(); // DPS 9-Aug-2011 + + if (showingLineNumbers()) + { lineNumbers.setText(getLineNumbersList(sourceCode.getDocument())); - } - } - public void removeUpdate(DocumentEvent evt) { - this.insertUpdate(evt); - } - public void changedUpdate(DocumentEvent evt) { - this.insertUpdate(evt); - } - }); - - showLineNumbers = new JCheckBox("Show Line Numbers"); - showLineNumbers.setToolTipText("If checked, will display line number for each line of text."); - showLineNumbers.setEnabled(false); - // Show line numbers by default. - showLineNumbers.setSelected(Globals.getSettings().getEditorLineNumbersDisplayed()); - - this.setSourceCode("",false); - - lineNumbers.setFont(getLineNumberFont(sourceCode.getFont())); - lineNumbers.setVerticalAlignment(JLabel.TOP); - lineNumbers.setText(""); - lineNumbers.setVisible(true); - - // Listener fires when "Show Line Numbers" check box is clicked. - showLineNumbers.addItemListener( - new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if (showLineNumbers.isSelected()) { + } + } + + public void removeUpdate(DocumentEvent evt) + { + this.insertUpdate(evt); + } + + public void changedUpdate(DocumentEvent evt) + { + this.insertUpdate(evt); + } + }); + + showLineNumbers = new JCheckBox("Show Line Numbers"); + showLineNumbers.setToolTipText("If checked, will display line number for each line of text."); + showLineNumbers.setEnabled(false); + // Show line numbers by default. + showLineNumbers.setSelected(Globals.getSettings().getEditorLineNumbersDisplayed()); + + this.setSourceCode("", false); + + lineNumbers.setFont(getLineNumberFont(sourceCode.getFont())); + lineNumbers.setVerticalAlignment(JLabel.TOP); + lineNumbers.setText(""); + lineNumbers.setVisible(true); + + // Listener fires when "Show Line Numbers" check box is clicked. + showLineNumbers.addItemListener( + new ItemListener() + { + public void itemStateChanged(ItemEvent e) + { + if (showLineNumbers.isSelected()) + { lineNumbers.setText(getLineNumbersList(sourceCode.getDocument())); lineNumbers.setVisible(true); - } - else { + } + else + { lineNumbers.setText(""); lineNumbers.setVisible(false); - } - sourceCode.revalidate(); // added 16 Jan 2012 to assure label redrawn. - Globals.getSettings().setEditorLineNumbersDisplayed(showLineNumbers.isSelected()); - // needed because caret disappears when checkbox clicked - sourceCode.setCaretVisible(true); - sourceCode.requestFocusInWindow(); - } - }); - - JPanel editInfo = new JPanel(new BorderLayout()); - caretPositionLabel = new JLabel(); - caretPositionLabel.setToolTipText("Tracks the current position of the text editing cursor."); - displayCaretPosition(new Point()); - editInfo.add(caretPositionLabel,BorderLayout.WEST); - editInfo.add(showLineNumbers,BorderLayout.CENTER); - this.add(editInfo,BorderLayout.SOUTH); - } - - - /** - * For initalizing the source code when opening an ASM file - * @param s String containing text - * @param editable set true if code is editable else false - */ - - public void setSourceCode(String s, boolean editable){ - sourceCode.setSourceCode(s, editable); - } - - /** - * Get rid of any accumulated undoable edits. It is useful to call - * this method after opening a file into the text area. The - * act of setting its text content upon reading the file will generate - * an undoable edit. Normally you don't want a freshly-opened file - * to appear with its Undo action enabled. But it will unless you - * call this after setting the text. - */ - public void discardAllUndoableEdits() { - sourceCode.discardAllUndoableEdits(); - } - - /** Form string with source code line numbers. - * Resulting string is HTML, for which JLabel will happily honor
to do - * multiline label (it ignores '\n'). The line number list is a JLabel with - * one line number per line. - */ - private static final String spaces = "         "; - public String getLineNumbersList(javax.swing.text.Document doc) { - StringBuffer lineNumberList = new StringBuffer(""); - int lineCount = doc.getDefaultRootElement().getElementCount(); //this.getSourceLineCount(); - int digits = Integer.toString(lineCount).length(); - for (int i=1; i<=lineCount;i++) { + } + sourceCode.revalidate(); // added 16 Jan 2012 to assure label redrawn. + Globals.getSettings().setEditorLineNumbersDisplayed(showLineNumbers.isSelected()); + // needed because caret disappears when checkbox clicked + sourceCode.setCaretVisible(true); + sourceCode.requestFocusInWindow(); + } + }); + + JPanel editInfo = new JPanel(new BorderLayout()); + caretPositionLabel = new JLabel(); + caretPositionLabel.setToolTipText("Tracks the current position of the text editing cursor."); + displayCaretPosition(new Point()); + editInfo.add(caretPositionLabel, BorderLayout.WEST); + editInfo.add(showLineNumbers, BorderLayout.CENTER); + this.add(editInfo, BorderLayout.SOUTH); + } + + /** + * For initalizing the source code when opening an ASM file + * + * @param s String containing text + * @param editable set true if code is editable else false + */ + + public void setSourceCode(String s, boolean editable) + { + sourceCode.setSourceCode(s, editable); + } + + /** + * Get rid of any accumulated undoable edits. It is useful to call this method after opening a file into the text + * area. The act of setting its text content upon reading the file will generate an undoable edit. Normally you + * don't want a freshly-opened file to appear with its Undo action enabled. But it will unless you call this after + * setting the text. + */ + public void discardAllUndoableEdits() + { + sourceCode.discardAllUndoableEdits(); + } + + public String getLineNumbersList(javax.swing.text.Document doc) + { + StringBuffer lineNumberList = new StringBuffer(""); + int lineCount = doc.getDefaultRootElement().getElementCount(); //this.getSourceLineCount(); + int digits = Integer.toString(lineCount).length(); + for (int i = 1; i <= lineCount; i++) + { String lineStr = Integer.toString(i); - int leadingSpaces = digits-lineStr.length(); - if (leadingSpaces == 0) { - lineNumberList.append(lineStr+" 
"); - } - else { - lineNumberList.append(spaces.substring(0,leadingSpaces*6)+lineStr+" 
"); + int leadingSpaces = digits - lineStr.length(); + if (leadingSpaces == 0) + { + lineNumberList.append(lineStr + " 
"); } - } - lineNumberList.append("
"); - return lineNumberList.toString(); - } - - - /** Calculate and return number of lines in source code text. - * Do this by counting newline characters then adding one if last line does - * not end with newline character. - */ - - /* IMPLEMENTATION NOTE: - * Tried repeatedly to use StringTokenizer to count lines but got bad results - * on empty lines (consecutive delimiters) even when returning delimiter as token. - * BufferedReader on StringReader seems to work better. - */ - public int getSourceLineCount() { - BufferedReader bufStringReader = new BufferedReader(new StringReader(sourceCode.getText())); - int lineNums = 0; - try { - while (bufStringReader.readLine() != null) { - lineNums++; + else + { + lineNumberList.append(spaces.substring(0, leadingSpaces * 6) + lineStr + " 
"); } - } - catch (IOException e) { + } + lineNumberList.append("
"); + return lineNumberList.toString(); + } + + /** + * Calculate and return number of lines in source code text. Do this by counting newline characters then adding one + * if last line does not end with newline character. + */ + + /* IMPLEMENTATION NOTE: + * Tried repeatedly to use StringTokenizer to count lines but got bad results + * on empty lines (consecutive delimiters) even when returning delimiter as token. + * BufferedReader on StringReader seems to work better. + */ + public int getSourceLineCount() + { + BufferedReader bufStringReader = new BufferedReader(new StringReader(sourceCode.getText())); + int lineNums = 0; + try + { + while (bufStringReader.readLine() != null) + { + lineNums++; } - return lineNums; - } - - /** - * Get source code text - * - * @return Sting containing source code - */ - public String getSource(){ - return sourceCode.getText(); - } - - - /** - * Set the editing status for this EditPane's associated document. - * For the argument, use one of the constants from class FileStatus. - * - * @param FileStatus the status constant from class FileStatus - */ - public void setFileStatus(int fileStatus) { - this.fileStatus.setFileStatus(fileStatus); - } - - - /** - * Get the editing status for this EditPane's associated document. - * This will be one of the constants from class FileStatus. - */ - - public int getFileStatus() { - return this.fileStatus.getFileStatus(); - } - - /** - * Delegates to corresponding FileStatus method - */ - public String getFilename() { - return this.fileStatus.getFilename(); - } - - - /** - * Delegates to corresponding FileStatus method - */ - public String getPathname() { - return this.fileStatus.getPathname(); - } - - - /** - * Delegates to corresponding FileStatus method - */ - public void setPathname(String pathname) { - this.fileStatus.setPathname(pathname); - } - - /** - * Delegates to corresponding FileStatus method - */ - public boolean hasUnsavedEdits() { - return this.fileStatus.hasUnsavedEdits(); - } - - - /** - * Delegates to corresponding FileStatus method - */ - public boolean isNew() { - return this.fileStatus.isNew(); - } - - - /** - * Delegates to text area's requestFocusInWindow method. - */ - - public void tellEditingComponentToRequestFocusInWindow() { - this.sourceCode.requestFocusInWindow(); - } - - - /** - * Delegates to corresponding FileStatus method - */ - public void updateStaticFileStatus() { - fileStatus.updateStaticFileStatus(); - } - - - /** - * get the manager in charge of Undo and Redo operations - * @return the UnDo manager - */ - public UndoManager getUndoManager() { - return sourceCode.getUndoManager(); - } + } + catch (IOException e) + { + } + return lineNums; + } + + /** + * Get source code text + * + * @return Sting containing source code + */ + public String getSource() + { + return sourceCode.getText(); + } + + /** + * Get the editing status for this EditPane's associated document. This will be one of the constants from class + * FileStatus. + */ + + public int getFileStatus() + { + return this.fileStatus.getFileStatus(); + } + + /** + * Set the editing status for this EditPane's associated document. For the argument, use one of the constants from + * class FileStatus. + * + * @param FileStatus the status constant from class FileStatus + */ + public void setFileStatus(int fileStatus) + { + this.fileStatus.setFileStatus(fileStatus); + } + + /** + * Delegates to corresponding FileStatus method + */ + public String getFilename() + { + return this.fileStatus.getFilename(); + } + + /** + * Delegates to corresponding FileStatus method + */ + public String getPathname() + { + return this.fileStatus.getPathname(); + } + + /** + * Delegates to corresponding FileStatus method + */ + public void setPathname(String pathname) + { + this.fileStatus.setPathname(pathname); + } + + /** + * Delegates to corresponding FileStatus method + */ + public boolean hasUnsavedEdits() + { + return this.fileStatus.hasUnsavedEdits(); + } + + /** + * Delegates to corresponding FileStatus method + */ + public boolean isNew() + { + return this.fileStatus.isNew(); + } + + /** + * Delegates to text area's requestFocusInWindow method. + */ + + public void tellEditingComponentToRequestFocusInWindow() + { + this.sourceCode.requestFocusInWindow(); + } + + /** + * Delegates to corresponding FileStatus method + */ + public void updateStaticFileStatus() + { + fileStatus.updateStaticFileStatus(); + } /* Note: these are invoked only when copy/cut/paste are used from the toolbar or menu or the defined menu Alt codes. When @@ -356,270 +404,306 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. to disappear! This does not happen when using menu selection or Ctrl-C/X/V */ - /** - * copy currently-selected text into clipboard - */ - public void copyText() { - sourceCode.copy(); - sourceCode.setCaretVisible(true); - sourceCode.setSelectionVisible(true); - } - - /** - * cut currently-selected text into clipboard - */ - public void cutText() { - sourceCode.cut(); - sourceCode.setCaretVisible(true); - } - /** - * paste clipboard contents at cursor position - */ - public void pasteText() { - sourceCode.paste(); - sourceCode.setCaretVisible(true); - } - /** - * select all text - */ - public void selectAllText() { - sourceCode.selectAll(); - sourceCode.setCaretVisible(true); - sourceCode.setSelectionVisible(true); - } - - /** - * Undo previous edit - */ - public void undo() { - sourceCode.undo(); - } - - /** - * Redo previous edit - */ - public void redo() { - sourceCode.redo(); - } - - /** - * Update state of Edit menu's Undo menu item. - */ - public void updateUndoState() { - mainUI.editUndoAction.updateUndoState(); - } - - /** - * Update state of Edit menu's Redo menu item. - */ - public void updateRedoState() { - mainUI.editRedoAction.updateRedoState(); - } - - /** - * get editor's line number display status - * - * @return true if editor is current displaying line numbers, false otherwise. - */ - public boolean showingLineNumbers() { - return showLineNumbers.isSelected(); - } - - /** - * enable or disable checkbox that controls display of line numbers - * - * @param enable True to enable box, false to disable. - */ - public void setShowLineNumbersEnabled(boolean enabled) { - showLineNumbers.setEnabled(enabled); - //showLineNumbers.setSelected(false); // set off, whether closing or opening - } - - /** - * Update the caret position label on the editor's border to - * display the current line and column. The position is given - * as text stream offset and will be converted into line and column. - * @param pos Offset into the text stream of caret. - */ - public void displayCaretPosition(int pos) { - displayCaretPosition(convertStreamPositionToLineColumn(pos)); - } - /** - * Display cursor coordinates - * - * @param p Point object with x-y (column, line number) coordinates of cursor - */ - public void displayCaretPosition(Point p) { - caretPositionLabel.setText("Line: "+ p.y + " Column: "+ p.x); - } - - /** - * Given byte stream position in text being edited, calculate its column and line - * number coordinates. - * - * @param stream position of character - * @return position Its column and line number coordinate as a Point. - */ - private static final char newline = '\n'; - public Point convertStreamPositionToLineColumn(int position) { - String textStream = sourceCode.getText(); - int line = 1; - int column = 1; - for (int i=0; i 0) { - int lineStartPosition = convertLineColumnToStreamPosition(line,1); - int lineEndPosition = convertLineColumnToStreamPosition(line+1,1)-1; - if (lineEndPosition < 0) { // DPS 19 Sept 2012. Happens if "line" is last line of file. - - lineEndPosition = sourceCode.getText().length()-1; + if (textStream.charAt(i) == newline) + { + textLine++; + textColumn = 1; } - if (lineStartPosition >= 0) { - sourceCode.select(lineStartPosition, lineEndPosition); - sourceCode.setSelectionVisible(true); - } - } - } - - - /** - * Select the specified editor text line. Lines are numbered starting with 1, consistent - * with line numbers displayed by the editor. - * @param line The desired line number of this TextPane's text. Numbering starts at 1, and - * nothing will happen if the parameter value is less than 1 - * @param column Desired column at which to place the cursor. - */ - public void selectLine(int line, int column) { - selectLine(line); - // Made one attempt at setting cursor; didn't work but here's the attempt - // (imagine using it in the one-parameter overloaded method above) - //sourceCode.setCaretPosition(lineStartPosition+column-1); - } - - /** Finds next occurrence of text in a forward search of a string. Search begins - * at the current cursor location, and wraps around when the end of the string - * is reached. - * @param find the text to locate in the string + else + { + textColumn++; + } + } + return -1; + } + + /** + * Select the specified editor text line. Lines are numbered starting with 1, consistent with line numbers + * displayed by the editor. + * + * @param line The desired line number of this TextPane's text. Numbering starts at 1, and nothing will happen + * if the parameter value is less than 1 + */ + public void selectLine(int line) + { + if (line > 0) + { + int lineStartPosition = convertLineColumnToStreamPosition(line, 1); + int lineEndPosition = convertLineColumnToStreamPosition(line + 1, 1) - 1; + if (lineEndPosition < 0) + { // DPS 19 Sept 2012. Happens if "line" is last line of file. + + lineEndPosition = sourceCode.getText().length() - 1; + } + if (lineStartPosition >= 0) + { + sourceCode.select(lineStartPosition, lineEndPosition); + sourceCode.setSelectionVisible(true); + } + } + } + + + /** + * Select the specified editor text line. Lines are numbered starting with 1, consistent with line numbers + * displayed by the editor. + * + * @param line The desired line number of this TextPane's text. Numbering starts at 1, and nothing will happen + * if the parameter value is less than 1 + * @param column Desired column at which to place the cursor. + */ + public void selectLine(int line, int column) + { + selectLine(line); + // Made one attempt at setting cursor; didn't work but here's the attempt + // (imagine using it in the one-parameter overloaded method above) + //sourceCode.setCaretPosition(lineStartPosition+column-1); + } + + /** + * Finds next occurrence of text in a forward search of a string. Search begins at the current cursor location, and + * wraps around when the end of the string is reached. + * + * @param find the text to locate in the string * @param caseSensitive true if search is to be case-sensitive, false otherwise * @return TEXT_FOUND or TEXT_NOT_FOUND, depending on the result. */ - public int doFindText(String find, boolean caseSensitive) { - return sourceCode.doFindText(find, caseSensitive); - } - - /** Finds and replaces next occurrence of text in a string in a forward search. - * If cursor is initially at end - * of matching selection, will immediately replace then find and select the - * next occurrence if any. Otherwise it performs a find operation. The replace - * can be undone with one undo operation. + public int doFindText(String find, boolean caseSensitive) + { + return sourceCode.doFindText(find, caseSensitive); + } + + /** + * Finds and replaces next occurrence of text in a string in a forward search. If cursor is initially at end of + * matching selection, will immediately replace then find and select the next occurrence if any. Otherwise it + * performs a find operation. The replace can be undone with one undo operation. * * @param find the text to locate in the string * @param replace the text to replace the find text with - if the find text exists * @param caseSensitive true for case sensitive. false to ignore case - * @return Returns TEXT_FOUND if not initially at end of selected match and matching - * occurrence is found. Returns TEXT_NOT_FOUND if the text is not matched. - * Returns TEXT_REPLACED_NOT_FOUND_NEXT if replacement is successful but there are - * no additional matches. Returns TEXT_REPLACED_FOUND_NEXT if reaplacement is - * successful and there is at least one additional match. - */ - public int doReplace(String find, String replace, boolean caseSensitive) { - return sourceCode.doReplace(find, replace, caseSensitive); - } - - - - /** Finds and replaces ALL occurrences of text in a string in a forward search. - * All replacements are bundled into one CompoundEdit, so one Undo operation will - * undo all of them. - * @param find the text to locate in the string - * @param replace the text to replace the find text with - if the find text exists - * @param caseSensitive true for case sensitive. false to ignore case - * @return the number of occurrences that were matched and replaced. - */ - public int doReplaceAll(String find, String replace, boolean caseSensitive) { - return sourceCode.doReplaceAll(find, replace, caseSensitive); - } - - - - /** - * Update, if source code is visible, when Font setting changes. - * This method is specified by the Observer interface. - */ - public void update(Observable fontChanger, Object arg) { - sourceCode.setFont(Globals.getSettings().getEditorFont()); - sourceCode.setLineHighlightEnabled(Globals.getSettings().getBooleanSetting(Settings.EDITOR_CURRENT_LINE_HIGHLIGHTING)); - sourceCode.setCaretBlinkRate(Globals.getSettings().getCaretBlinkRate()); - sourceCode.setTabSize(Globals.getSettings().getEditorTabSize()); - sourceCode.updateSyntaxStyles(); - sourceCode.revalidate(); - // We want line numbers to be displayed same size but always PLAIN style. - // Easiest way to get same pixel height as source code is to set to same - // font family as the source code! It can get a bit complicated otherwise - // because different fonts will render the same font size in different - // pixel heights. This is a factor because the line numbers as displayed - // in the editor form a separate column from the source code and if the - // pixel height is not the same then the numbers will not line up with - // the source lines. - lineNumbers.setFont(getLineNumberFont(sourceCode.getFont())); - lineNumbers.revalidate(); - } - - - /* Private helper method. - * Determine font to use for editor line number display, given current - * font for source code. - */ - private Font getLineNumberFont(Font sourceFont) { - return (sourceCode.getFont().getStyle() == Font.PLAIN) - ? sourceFont - : new Font(sourceFont.getFamily(), Font.PLAIN, sourceFont.getSize()); - } - - - } \ No newline at end of file + * @return Returns TEXT_FOUND if not initially at end of selected match and matching occurrence is found. Returns + * TEXT_NOT_FOUND if the text is not matched. Returns TEXT_REPLACED_NOT_FOUND_NEXT if replacement is successful + * but there are no additional matches. Returns TEXT_REPLACED_FOUND_NEXT if reaplacement is successful and + * there is at least one additional match. + */ + public int doReplace(String find, String replace, boolean caseSensitive) + { + return sourceCode.doReplace(find, replace, caseSensitive); + } + + + /** + * Finds and replaces ALL occurrences of text in a string in a forward search. All replacements are bundled + * into one CompoundEdit, so one Undo operation will undo all of them. + * + * @param find the text to locate in the string + * @param replace the text to replace the find text with - if the find text exists + * @param caseSensitive true for case sensitive. false to ignore case + * @return the number of occurrences that were matched and replaced. + */ + public int doReplaceAll(String find, String replace, boolean caseSensitive) + { + return sourceCode.doReplaceAll(find, replace, caseSensitive); + } + + + /** + * Update, if source code is visible, when Font setting changes. This method is specified by the Observer + * interface. + */ + public void update(Observable fontChanger, Object arg) + { + sourceCode.setFont(Globals.getSettings().getEditorFont()); + sourceCode.setLineHighlightEnabled(Globals.getSettings().getBooleanSetting(Settings.EDITOR_CURRENT_LINE_HIGHLIGHTING)); + sourceCode.setCaretBlinkRate(Globals.getSettings().getCaretBlinkRate()); + sourceCode.setTabSize(Globals.getSettings().getEditorTabSize()); + sourceCode.updateSyntaxStyles(); + sourceCode.revalidate(); + // We want line numbers to be displayed same size but always PLAIN style. + // Easiest way to get same pixel height as source code is to set to same + // font family as the source code! It can get a bit complicated otherwise + // because different fonts will render the same font size in different + // pixel heights. This is a factor because the line numbers as displayed + // in the editor form a separate column from the source code and if the + // pixel height is not the same then the numbers will not line up with + // the source lines. + lineNumbers.setFont(getLineNumberFont(sourceCode.getFont())); + lineNumbers.revalidate(); + } + + + /* Private helper method. + * Determine font to use for editor line number display, given current + * font for source code. + */ + private Font getLineNumberFont(Font sourceFont) + { + return (sourceCode.getFont().getStyle() == Font.PLAIN) + ? sourceFont + : new Font(sourceFont.getFamily(), Font.PLAIN, sourceFont.getSize()); + } + + +} diff --git a/src/main/java/mars/venus/EditPasteAction.java b/src/main/java/mars/venus/EditPasteAction.java index 41523fd..a38584f 100644 --- a/src/main/java/mars/venus/EditPasteAction.java +++ b/src/main/java/mars/venus/EditPasteAction.java @@ -1,8 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,18 +30,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the Edit -> Paste menu item - */ - public class EditPasteAction extends GuiAction { - - public EditPasteAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e){ - mainUI.getMainPane().getEditPane().pasteText(); - } - } \ No newline at end of file + +/** + * Action for the Edit -> Paste menu item + */ +public class EditPasteAction extends GuiAction +{ + + public EditPasteAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + mainUI.getMainPane().getEditPane().pasteText(); + } +} diff --git a/src/main/java/mars/venus/EditRedoAction.java b/src/main/java/mars/venus/EditRedoAction.java index c17bda6..b762f9b 100644 --- a/src/main/java/mars/venus/EditRedoAction.java +++ b/src/main/java/mars/venus/EditRedoAction.java @@ -1,9 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.undo.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,33 +30,38 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Action for the Edit -> Redo menu item + */ +public class EditRedoAction extends GuiAction +{ + + public EditRedoAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + setEnabled(false); + } + /** - * Action for the Edit -> Redo menu item - */ - public class EditRedoAction extends GuiAction { - - public EditRedoAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - setEnabled(false); - } - /** - * Adapted from TextComponentDemo.java in the - * Java Tutorial "Text Component Features" - */ - public void actionPerformed(ActionEvent e) { - EditPane editPane = mainUI.getMainPane().getEditPane(); - if (editPane != null) { + * Adapted from TextComponentDemo.java in the Java Tutorial "Text Component Features" + */ + public void actionPerformed(ActionEvent e) + { + EditPane editPane = mainUI.getMainPane().getEditPane(); + if (editPane != null) + { editPane.redo(); updateRedoState(); mainUI.editUndoAction.updateUndoState(); - } - } - - void updateRedoState() { - EditPane editPane = mainUI.getMainPane().getEditPane(); - setEnabled(editPane != null && editPane.getUndoManager().canRedo()); - } - } + } + } + + void updateRedoState() + { + EditPane editPane = mainUI.getMainPane().getEditPane(); + setEnabled(editPane != null && editPane.getUndoManager().canRedo()); + } +} diff --git a/src/main/java/mars/venus/EditSelectAllAction.java b/src/main/java/mars/venus/EditSelectAllAction.java index 5df72c1..b1241d3 100644 --- a/src/main/java/mars/venus/EditSelectAllAction.java +++ b/src/main/java/mars/venus/EditSelectAllAction.java @@ -1,8 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar @@ -31,18 +30,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the Edit -> Copy menu item - */ - public class EditSelectAllAction extends GuiAction { - - public EditSelectAllAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e){ - mainUI.getMainPane().getEditPane().selectAllText(); - } - } \ No newline at end of file + +/** + * Action for the Edit -> Copy menu item + */ +public class EditSelectAllAction extends GuiAction +{ + + public EditSelectAllAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + mainUI.getMainPane().getEditPane().selectAllText(); + } +} diff --git a/src/main/java/mars/venus/EditTabbedPane.java b/src/main/java/mars/venus/EditTabbedPane.java index 821e2db..efcc00b 100644 --- a/src/main/java/mars/venus/EditTabbedPane.java +++ b/src/main/java/mars/venus/EditTabbedPane.java @@ -1,17 +1,22 @@ - package mars.venus; - import mars.mips.hardware.*; - import mars.util.*; - import mars.*; - import javax.swing.*; - import javax.swing.event.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.undo.*; - import java.text.*; - import java.util.*; - import java.io.*; - import java.beans.PropertyChangeListener; - import javax.swing.filechooser.FileFilter; +package mars.venus; + +import mars.Globals; +import mars.MIPSprogram; +import mars.ProcessingException; +import mars.mips.hardware.RegisterFile; +import mars.util.FilenameFinder; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.filechooser.FileFilter; +import java.awt.*; +import java.beans.PropertyChangeListener; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; /* Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar @@ -41,277 +46,321 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - /** - * Tabbed pane for the editor. Each of its tabs represents an open file. - * @author Sanderson - **/ +/** + * Tabbed pane for the editor. Each of its tabs represents an open file. + * + * @author Sanderson + **/ + +public class EditTabbedPane extends JTabbedPane +{ + EditPane editTab; + + MainPane mainPane; + + private final VenusUI mainUI; + + private final Editor editor; + + private final FileOpener fileOpener; - public class EditTabbedPane extends JTabbedPane{ - EditPane editTab; - MainPane mainPane; - - private VenusUI mainUI; - private Editor editor; - private FileOpener fileOpener; - /** - * Constructor for the EditTabbedPane class. - **/ - - public EditTabbedPane(VenusUI appFrame, Editor editor, MainPane mainPane){ - super(); - this.mainUI = appFrame; - this.editor = editor; - this.fileOpener = new FileOpener(editor); - this.mainPane = mainPane; - this.editor.setEditTabbedPane(this); - this.addChangeListener( - new ChangeListener() { - public void stateChanged(ChangeEvent e) { - EditPane editPane = (EditPane) getSelectedComponent(); - if (editPane != null) { + * Constructor for the EditTabbedPane class. + **/ + + public EditTabbedPane(VenusUI appFrame, Editor editor, MainPane mainPane) + { + super(); + this.mainUI = appFrame; + this.editor = editor; + this.fileOpener = new FileOpener(editor); + this.mainPane = mainPane; + this.editor.setEditTabbedPane(this); + this.addChangeListener( + new ChangeListener() + { + public void stateChanged(ChangeEvent e) + { + EditPane editPane = (EditPane) getSelectedComponent(); + if (editPane != null) + { // New IF statement to permit free traversal of edit panes w/o invalidating - // assembly if assemble-all is selected. DPS 9-Aug-2011 - if (Globals.getSettings().getBooleanSetting(mars.Settings.ASSEMBLE_ALL_ENABLED)) { - EditTabbedPane.this.updateTitles(editPane); - } - else { - EditTabbedPane.this.updateTitlesAndMenuState(editPane); - EditTabbedPane.this.mainPane.getExecutePane().clearPane(); + // assembly if assemble-all is selected. DPS 9-Aug-2011 + if (Globals.getSettings().getBooleanSetting(mars.Settings.ASSEMBLE_ALL_ENABLED)) + { + EditTabbedPane.this.updateTitles(editPane); + } + else + { + EditTabbedPane.this.updateTitlesAndMenuState(editPane); + EditTabbedPane.this.mainPane.getExecutePane().clearPane(); } editPane.tellEditingComponentToRequestFocusInWindow(); - } - } - }); - } - - /** - * The current EditPane representing a file. Returns null if - * no files open. - * - * @return the current editor pane - */ - public EditPane getCurrentEditTab() { - return (EditPane)this.getSelectedComponent(); - } - - /** - * Select the specified EditPane to be the current tab. - * - * @param editPane The EditPane tab to become current. - */ - public void setCurrentEditTab(EditPane editPane) { - this.setSelectedComponent(editPane); - } - - /** - * If the given file is open in the tabbed pane, make it the - * current tab. If not opened, open it in a new tab and make - * it the current tab. If file is unable to be opened, - * leave current tab as is. - * @param file File object for the desired file. - * @return EditPane for the specified file, or null if file is unable to be opened in an EditPane - */ - public EditPane getCurrentEditTabForFile(File file) { - EditPane result = null; - EditPane tab = getEditPaneForFile(file.getPath()); - if (tab != null) { - if (tab != getCurrentEditTab()) { - setCurrentEditTab(tab); - } - return tab; - } - // If no return yet, then file is not open. Try to open it. - if (openFile(file)) { - result = getCurrentEditTab(); - } - return result; - } - - /** - * Carries out all necessary operations to implement - * the New operation from the File menu. - */ - public void newFile() { - EditPane editPane = new EditPane(this.mainUI); - editPane.setSourceCode("", true); - editPane.setShowLineNumbersEnabled(true); - editPane.setFileStatus(FileStatus.NEW_NOT_EDITED); - String name = editor.getNextDefaultFilename(); - editPane.setPathname(name); - this.addTab(name, editPane); - - FileStatus.reset(); - FileStatus.setName(name); - FileStatus.set(FileStatus.NEW_NOT_EDITED); - - RegisterFile.resetRegisters(); - mainUI.setReset(true); - mainPane.getExecutePane().clearPane(); - mainPane.setSelectedComponent(this); - editPane.displayCaretPosition(new Point(1,1)); - this.setSelectedComponent(editPane); - updateTitlesAndMenuState(editPane); - editPane.tellEditingComponentToRequestFocusInWindow(); - } - - - /** - * Carries out all necessary operations to implement - * the Open operation from the File menu. This - * begins with an Open File dialog. - * @return true if file was opened, false otherwise. - */ - public boolean openFile() { - return fileOpener.openFile(); - } - + } + } + }); + } + /** - * Carries out all necessary operations to open the - * specified file in the editor. - * @return true if file was opened, false otherwise. + * The current EditPane representing a file. Returns null if no files open. + * + * @return the current editor pane */ - public boolean openFile(File file) { - return fileOpener.openFile(file); - } - - - /** - * Carries out all necessary operations to implement - * the Close operation from the File menu. May return - * false, for instance when file has unsaved changes - * and user selects Cancel from the warning dialog. - * @return true if file was closed, false otherwise. - */ - public boolean closeCurrentFile() { - EditPane editPane = getCurrentEditTab(); - if (editPane != null) { - if (editsSavedOrAbandoned()) { - this.remove(editPane); - mainPane.getExecutePane().clearPane(); - mainPane.setSelectedComponent(this); - } - else { - return false; + public EditPane getCurrentEditTab() + { + return (EditPane) this.getSelectedComponent(); + } + + /** + * Select the specified EditPane to be the current tab. + * + * @param editPane The EditPane tab to become current. + */ + public void setCurrentEditTab(EditPane editPane) + { + this.setSelectedComponent(editPane); + } + + /** + * If the given file is open in the tabbed pane, make it the current tab. If not opened, open it in a new tab and + * make it the current tab. If file is unable to be opened, leave current tab as is. + * + * @param file File object for the desired file. + * @return EditPane for the specified file, or null if file is unable to be opened in an EditPane + */ + public EditPane getCurrentEditTabForFile(File file) + { + EditPane result = null; + EditPane tab = getEditPaneForFile(file.getPath()); + if (tab != null) + { + if (tab != getCurrentEditTab()) + { + setCurrentEditTab(tab); } - } - return true; - } - - /** - * Carries out all necessary operations to implement - * the Close All operation from the File menu. - * @return true if files closed, false otherwise. - */ - public boolean closeAllFiles() { - boolean result = true; - boolean unsavedChanges = false; - int tabCount = getTabCount(); - if (tabCount > 0) { + return tab; + } + // If no return yet, then file is not open. Try to open it. + if (openFile(file)) + { + result = getCurrentEditTab(); + } + return result; + } + + /** + * Carries out all necessary operations to implement the New operation from the File menu. + */ + public void newFile() + { + EditPane editPane = new EditPane(this.mainUI); + editPane.setSourceCode("", true); + editPane.setShowLineNumbersEnabled(true); + editPane.setFileStatus(FileStatus.NEW_NOT_EDITED); + String name = editor.getNextDefaultFilename(); + editPane.setPathname(name); + this.addTab(name, editPane); + + FileStatus.reset(); + FileStatus.setName(name); + FileStatus.set(FileStatus.NEW_NOT_EDITED); + + RegisterFile.resetRegisters(); + VenusUI.setReset(true); + mainPane.getExecutePane().clearPane(); + mainPane.setSelectedComponent(this); + editPane.displayCaretPosition(new Point(1, 1)); + this.setSelectedComponent(editPane); + updateTitlesAndMenuState(editPane); + editPane.tellEditingComponentToRequestFocusInWindow(); + } + + + /** + * Carries out all necessary operations to implement the Open operation from the File menu. This begins with an + * Open File dialog. + * + * @return true if file was opened, false otherwise. + */ + public boolean openFile() + { + return fileOpener.openFile(); + } + + /** + * Carries out all necessary operations to open the specified file in the editor. + * + * @return true if file was opened, false otherwise. + */ + public boolean openFile(File file) + { + return fileOpener.openFile(file); + } + + + /** + * Carries out all necessary operations to implement the Close operation from the File menu. May return false, for + * instance when file has unsaved changes and user selects Cancel from the warning dialog. + * + * @return true if file was closed, false otherwise. + */ + public boolean closeCurrentFile() + { + EditPane editPane = getCurrentEditTab(); + if (editPane != null) + { + if (editsSavedOrAbandoned()) + { + this.remove(editPane); + mainPane.getExecutePane().clearPane(); + mainPane.setSelectedComponent(this); + } + else + { + return false; + } + } + return true; + } + + /** + * Carries out all necessary operations to implement the Close All operation from the File menu. + * + * @return true if files closed, false otherwise. + */ + public boolean closeAllFiles() + { + boolean result = true; + boolean unsavedChanges = false; + int tabCount = getTabCount(); + if (tabCount > 0) + { mainPane.getExecutePane().clearPane(); mainPane.setSelectedComponent(this); EditPane[] tabs = new EditPane[tabCount]; - for (int i=0; i < tabCount; i++) { - tabs[i] = (EditPane)getComponentAt(i); - if ( tabs[i].hasUnsavedEdits() ) { - unsavedChanges = true; - } + for (int i = 0; i < tabCount; i++) + { + tabs[i] = (EditPane) getComponentAt(i); + if (tabs[i].hasUnsavedEdits()) + { + unsavedChanges = true; + } } - if (unsavedChanges) { - switch (confirm("one or more files")) { - case JOptionPane.YES_OPTION : - boolean removedAll = true; - for (int i=0; i < tabCount; i++) { - if ( tabs[i].hasUnsavedEdits() ) { - setSelectedComponent(tabs[i]); - boolean saved = saveCurrentFile(); - if (saved) { - this.remove(tabs[i]); - } - else { - removedAll = false; - } - } - else { - this.remove(tabs[i]); + if (unsavedChanges) + { + switch (confirm("one or more files")) + { + case JOptionPane.YES_OPTION: + boolean removedAll = true; + for (int i = 0; i < tabCount; i++) + { + if (tabs[i].hasUnsavedEdits()) + { + setSelectedComponent(tabs[i]); + boolean saved = saveCurrentFile(); + if (saved) + { + this.remove(tabs[i]); + } + else + { + removedAll = false; + } + } + else + { + this.remove(tabs[i]); + } } - } - return removedAll; - case JOptionPane.NO_OPTION : - for (int i=0; i < tabCount; i++) { - this.remove(tabs[i]); - } - return true; - case JOptionPane.CANCEL_OPTION : - return false; - default : // should never occur - return false; - } - } - else { - for (int i=0; i < tabCount; i++) { - this.remove(tabs[i]); - } + return removedAll; + case JOptionPane.NO_OPTION: + for (int i = 0; i < tabCount; i++) + { + this.remove(tabs[i]); + } + return true; + case JOptionPane.CANCEL_OPTION: + return false; + default: // should never occur + return false; + } } - } - return result; - } - - /** - * Saves file under existing name. If no name, will invoke Save As. - * @return true if the file was actually saved. - */ - public boolean saveCurrentFile() { - EditPane editPane = getCurrentEditTab(); - if (saveFile(editPane)) { + else + { + for (int i = 0; i < tabCount; i++) + { + this.remove(tabs[i]); + } + } + } + return result; + } + + /** + * Saves file under existing name. If no name, will invoke Save As. + * + * @return true if the file was actually saved. + */ + public boolean saveCurrentFile() + { + EditPane editPane = getCurrentEditTab(); + if (saveFile(editPane)) + { FileStatus.setSaved(true); FileStatus.setEdited(false); FileStatus.set(FileStatus.NOT_EDITED); editPane.setFileStatus(FileStatus.NOT_EDITED); updateTitlesAndMenuState(editPane); return true; - } - return false; - } - - // Save file associatd with specified edit pane. - // Returns true if save operation worked, else false. - private boolean saveFile(EditPane editPane) { - if (editPane != null) { - if (editPane.isNew()) { - File theFile = saveAsFile(editPane); - if (theFile != null) { - editPane.setPathname(theFile.getPath()); - } - return (theFile != null); + } + return false; + } + + // Save file associatd with specified edit pane. + // Returns true if save operation worked, else false. + private boolean saveFile(EditPane editPane) + { + if (editPane != null) + { + if (editPane.isNew()) + { + File theFile = saveAsFile(editPane); + if (theFile != null) + { + editPane.setPathname(theFile.getPath()); + } + return (theFile != null); } File theFile = new File(editPane.getPathname()); - try { - BufferedWriter outFileStream = new BufferedWriter(new FileWriter(theFile)); - outFileStream.write(editPane.getSource(), 0, editPane.getSource().length()); - outFileStream.close(); - } - catch(java.io.IOException c) { - JOptionPane.showMessageDialog(null,"Save operation could not be completed due to an error:\n"+c, - "Save Operation Failed", JOptionPane.ERROR_MESSAGE); - return false; - } + try + { + BufferedWriter outFileStream = new BufferedWriter(new FileWriter(theFile)); + outFileStream.write(editPane.getSource(), 0, editPane.getSource().length()); + outFileStream.close(); + } + catch (java.io.IOException c) + { + JOptionPane.showMessageDialog(null, "Save operation could not be completed due to an error:\n" + c, + "Save Operation Failed", JOptionPane.ERROR_MESSAGE); + return false; + } return true; - } - return false; - } - - - - /** - * Pops up a dialog box to do "Save As" operation. If necessary - * an additional overwrite dialog is performed. - * @return true if the file was actually saved. - */ - public boolean saveAsCurrentFile() { - EditPane editPane = getCurrentEditTab(); - File theFile = saveAsFile(editPane); - if (theFile != null) { + } + return false; + } + + + /** + * Pops up a dialog box to do "Save As" operation. If necessary an additional overwrite dialog is performed. + * + * @return true if the file was actually saved. + */ + public boolean saveAsCurrentFile() + { + EditPane editPane = getCurrentEditTab(); + File theFile = saveAsFile(editPane); + if (theFile != null) + { FileStatus.setFile(theFile); FileStatus.setName(theFile.getPath()); FileStatus.setSaved(true); @@ -321,413 +370,480 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. editPane.setPathname(theFile.getPath()); editPane.setFileStatus(FileStatus.NOT_EDITED); updateTitlesAndMenuState(editPane); - return true; - } - return false; - } - - // perform Save As for selected edit pane. If the save is performed, - // return its File object. Otherwise return null. - private File saveAsFile(EditPane editPane) { - File theFile = null; - if (editPane != null) { + return true; + } + return false; + } + + // perform Save As for selected edit pane. If the save is performed, + // return its File object. Otherwise return null. + private File saveAsFile(EditPane editPane) + { + File theFile = null; + if (editPane != null) + { JFileChooser saveDialog = null; boolean operationOK = false; - while (!operationOK) { - // Set Save As dialog directory in a logical way. If file in - // edit pane had been previously saved, default to its directory. - // If a new file (mipsN.asm), default to current save directory. - // DPS 13-July-2011 - if (editPane.isNew()) { - saveDialog = new JFileChooser(editor.getCurrentSaveDirectory()); - } - else { - File f = new File(editPane.getPathname()); - if (f != null) { - saveDialog = new JFileChooser(f.getParent()); - } - else { - saveDialog = new JFileChooser(editor.getCurrentSaveDirectory()); - } - } - String paneFile = editPane.getFilename(); - if (paneFile != null) saveDialog.setSelectedFile(new File(paneFile)); - // end of 13-July-2011 code. - saveDialog.setDialogTitle("Save As"); - - int decision = saveDialog.showSaveDialog(mainUI); - if (decision != JFileChooser.APPROVE_OPTION) { - return null; - } - theFile = saveDialog.getSelectedFile(); - operationOK = true; - if (theFile.exists()) { - int overwrite = JOptionPane.showConfirmDialog(mainUI, - "File "+theFile.getName()+" already exists. Do you wish to overwrite it?", - "Overwrite existing file?", - JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); - switch (overwrite) { - case JOptionPane.YES_OPTION : - operationOK = true; - break; - case JOptionPane.NO_OPTION : - operationOK = false; - break; - case JOptionPane.CANCEL_OPTION : - return null; - default : // should never occur - return null; - } - } + while (!operationOK) + { + // Set Save As dialog directory in a logical way. If file in + // edit pane had been previously saved, default to its directory. + // If a new file (mipsN.asm), default to current save directory. + // DPS 13-July-2011 + if (editPane.isNew()) + { + saveDialog = new JFileChooser(editor.getCurrentSaveDirectory()); + } + else + { + File f = new File(editPane.getPathname()); + if (f != null) + { + saveDialog = new JFileChooser(f.getParent()); + } + else + { + saveDialog = new JFileChooser(editor.getCurrentSaveDirectory()); + } + } + String paneFile = editPane.getFilename(); + if (paneFile != null) + { + saveDialog.setSelectedFile(new File(paneFile)); + } + // end of 13-July-2011 code. + saveDialog.setDialogTitle("Save As"); + + int decision = saveDialog.showSaveDialog(mainUI); + if (decision != JFileChooser.APPROVE_OPTION) + { + return null; + } + theFile = saveDialog.getSelectedFile(); + operationOK = true; + if (theFile.exists()) + { + int overwrite = JOptionPane.showConfirmDialog(mainUI, + "File " + theFile.getName() + " already exists. Do you wish to overwrite it?", + "Overwrite existing file?", + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); + switch (overwrite) + { + case JOptionPane.YES_OPTION: + operationOK = true; + break; + case JOptionPane.NO_OPTION: + operationOK = false; + break; + case JOptionPane.CANCEL_OPTION: + return null; + default: // should never occur + return null; + } + } } // Either file with selected name does not exist or user wants to - // overwrite it, so go for it! - try { - BufferedWriter outFileStream = new BufferedWriter(new FileWriter(theFile)); - outFileStream.write(editPane.getSource(), 0, editPane.getSource().length()); - outFileStream.close(); - } - catch(java.io.IOException c) { - JOptionPane.showMessageDialog(null,"Save As operation could not be completed due to an error:\n"+c, - "Save As Operation Failed", JOptionPane.ERROR_MESSAGE); - return null; - } - } - return theFile; - } - - - - - /** - * Saves all files currently open in the editor. - * - * @return true if operation succeeded otherwise false. - */ - public boolean saveAllFiles() { - boolean result = false; - int tabCount = getTabCount(); - if (tabCount > 0) { - + // overwrite it, so go for it! + try + { + BufferedWriter outFileStream = new BufferedWriter(new FileWriter(theFile)); + outFileStream.write(editPane.getSource(), 0, editPane.getSource().length()); + outFileStream.close(); + } + catch (java.io.IOException c) + { + JOptionPane.showMessageDialog(null, "Save As operation could not be completed due to an error:\n" + c, + "Save As Operation Failed", JOptionPane.ERROR_MESSAGE); + return null; + } + } + return theFile; + } + + + /** + * Saves all files currently open in the editor. + * + * @return true if operation succeeded otherwise false. + */ + public boolean saveAllFiles() + { + boolean result = false; + int tabCount = getTabCount(); + if (tabCount > 0) + { + result = true; EditPane[] tabs = new EditPane[tabCount]; EditPane savedPane = getCurrentEditTab(); - for (int i=0; i < tabCount; i++) { - tabs[i] = (EditPane)getComponentAt(i); - if ( tabs[i].hasUnsavedEdits() ) { - setCurrentEditTab(tabs[i]); - if (saveFile(tabs[i])) { - tabs[i].setFileStatus(FileStatus.NOT_EDITED); - editor.setTitle(tabs[i].getPathname(), tabs[i].getFilename(), tabs[i].getFileStatus()); - } - else { - result = false; - } - } + for (int i = 0; i < tabCount; i++) + { + tabs[i] = (EditPane) getComponentAt(i); + if (tabs[i].hasUnsavedEdits()) + { + setCurrentEditTab(tabs[i]); + if (saveFile(tabs[i])) + { + tabs[i].setFileStatus(FileStatus.NOT_EDITED); + editor.setTitle(tabs[i].getPathname(), tabs[i].getFilename(), tabs[i].getFileStatus()); + } + else + { + result = false; + } + } } setCurrentEditTab(savedPane); - if (result) { - EditPane editPane = getCurrentEditTab(); - FileStatus.setSaved(true); - FileStatus.setEdited(false); - FileStatus.set(FileStatus.NOT_EDITED); - editPane.setFileStatus(FileStatus.NOT_EDITED); - updateTitlesAndMenuState(editPane); - } - } - return result; - } - - - - /** - * Remove the pane and update menu status - */ - public void remove(EditPane editPane) { - super.remove(editPane); - editPane = getCurrentEditTab(); // is now next tab or null - if (editPane == null) { + if (result) + { + EditPane editPane = getCurrentEditTab(); + FileStatus.setSaved(true); + FileStatus.setEdited(false); + FileStatus.set(FileStatus.NOT_EDITED); + editPane.setFileStatus(FileStatus.NOT_EDITED); + updateTitlesAndMenuState(editPane); + } + } + return result; + } + + + /** + * Remove the pane and update menu status + */ + public void remove(EditPane editPane) + { + super.remove(editPane); + editPane = getCurrentEditTab(); // is now next tab or null + if (editPane == null) + { FileStatus.set(FileStatus.NO_FILE); - this.editor.setTitle("","",FileStatus.NO_FILE); + this.editor.setTitle("", "", FileStatus.NO_FILE); Globals.getGui().setMenuState(FileStatus.NO_FILE); - } - else { + } + else + { FileStatus.set(editPane.getFileStatus()); updateTitlesAndMenuState(editPane); - } - // When last file is closed, menu is unable to respond to mnemonics - // and accelerators. Let's have it request focus so it may do so. - if (getTabCount()==0) mainUI.haveMenuRequestFocus(); - } - - - // Handy little utility to update the title on the current tab and the frame title bar - // and also to update the MARS menu state (controls which actions are enabled). - private void updateTitlesAndMenuState(EditPane editPane) { - editor.setTitle(editPane.getPathname(), editPane.getFilename(), editPane.getFileStatus()); - editPane.updateStaticFileStatus(); // for legacy code that depends on the static FileStatus (pre 4.0) - Globals.getGui().setMenuState(editPane.getFileStatus()); - } - - // Handy little utility to update the title on the current tab and the frame title bar - // and also to update the MARS menu state (controls which actions are enabled). - // DPS 9-Aug-2011 - private void updateTitles(EditPane editPane) { - editor.setTitle(editPane.getPathname(), editPane.getFilename(), editPane.getFileStatus()); - boolean assembled = FileStatus.isAssembled(); - editPane.updateStaticFileStatus(); // for legacy code that depends on the static FileStatus (pre 4.0) - FileStatus.setAssembled(assembled); - } - - /** - * If there is an EditPane for the given file pathname, return it else return null. - * - * @param pathname Pathname for desired file - * @return the EditPane for this file if it is open in the editor, or null if not. - */ - public EditPane getEditPaneForFile(String pathname) { - EditPane openPane = null; - for (int i=0; iAssemble's // actionPerformed() method. - if (theFile.canRead() && Globals.getSettings().getAssembleOnOpenEnabled()) { - mainUI.getRunAssembleAction().actionPerformed(null); - } + if (theFile.canRead() && Globals.getSettings().getAssembleOnOpenEnabled()) + { + mainUI.getRunAssembleAction().actionPerformed(null); + } + } + return true; + } + + /* + * Open the specified file. Return true if file opened, false otherwise + */ + + private boolean openFile(File theFile) + { + try + { + theFile = theFile.getCanonicalFile(); + } + catch (IOException ioe) + { + // nothing to do, theFile will keep current value } - return true; - } - - /* - * Open the specified file. Return true if file opened, false otherwise - */ - - private boolean openFile(File theFile) { - try { - theFile = theFile.getCanonicalFile(); - } - catch (IOException ioe) { - // nothing to do, theFile will keep current value - } String currentFilePath = theFile.getPath(); - // If this file is currently already open, then simply select its tab + // If this file is currently already open, then simply select its tab EditPane editPane = getEditPaneForFile(currentFilePath); - if (editPane != null) { - setSelectedComponent(editPane); - //updateTitlesAndMenuState(editPane); - updateTitles(editPane); - return false; - } - else { - editPane = new EditPane(mainUI); + if (editPane != null) + { + setSelectedComponent(editPane); + //updateTitlesAndMenuState(editPane); + updateTitles(editPane); + return false; + } + else + { + editPane = new EditPane(mainUI); } editPane.setPathname(currentFilePath); //FileStatus.reset(); FileStatus.setName(currentFilePath); FileStatus.setFile(theFile); FileStatus.set(FileStatus.OPENING);// DPS 9-Aug-2011 - if (theFile.canRead()) { - Globals.program = new MIPSprogram(); - try { - Globals.program.readSource(currentFilePath); - } - catch (ProcessingException pe) { - } - // DPS 1 Nov 2006. Defined a StringBuffer to receive all file contents, - // one line at a time, before adding to the Edit pane with one setText. - // StringBuffer is preallocated to full filelength to eliminate dynamic - // expansion as lines are added to it. Previously, each line was appended - // to the Edit pane as it was read, way slower due to dynamic string alloc. - StringBuffer fileContents = new StringBuffer((int)theFile.length()); - int lineNumber = 1; - String line = Globals.program.getSourceLine(lineNumber++); - while (line != null) { - fileContents.append(line+"\n"); - line = Globals.program.getSourceLine(lineNumber++); - } - editPane.setSourceCode(fileContents.toString(), true); - // The above operation generates an undoable edit, setting the initial - // text area contents, that should not be seen as undoable by the Undo - // action. Let's get rid of it. - editPane.discardAllUndoableEdits(); - editPane.setShowLineNumbersEnabled(true); - editPane.setFileStatus(FileStatus.NOT_EDITED); - - addTab(editPane.getFilename(), editPane); - setToolTipTextAt(indexOfComponent(editPane), editPane.getPathname()); - setSelectedComponent(editPane); - FileStatus.setSaved(true); - FileStatus.setEdited(false); - FileStatus.set(FileStatus.NOT_EDITED); - - // If assemble-all, then allow opening of any file w/o invalidating assembly. - // DPS 9-Aug-2011 - if (Globals.getSettings().getBooleanSetting(mars.Settings.ASSEMBLE_ALL_ENABLED)) { - updateTitles(editPane); - } - else {// this was the original code... - updateTitlesAndMenuState(editPane); - mainPane.getExecutePane().clearPane(); - } - - mainPane.setSelectedComponent(EditTabbedPane.this); - editPane.tellEditingComponentToRequestFocusInWindow(); - mostRecentlyOpenedFile = theFile; + if (theFile.canRead()) + { + Globals.program = new MIPSprogram(); + try + { + Globals.program.readSource(currentFilePath); + } + catch (ProcessingException pe) + { + } + // DPS 1 Nov 2006. Defined a StringBuffer to receive all file contents, + // one line at a time, before adding to the Edit pane with one setText. + // StringBuffer is preallocated to full filelength to eliminate dynamic + // expansion as lines are added to it. Previously, each line was appended + // to the Edit pane as it was read, way slower due to dynamic string alloc. + StringBuffer fileContents = new StringBuffer((int) theFile.length()); + int lineNumber = 1; + String line = Globals.program.getSourceLine(lineNumber++); + while (line != null) + { + fileContents.append(line + "\n"); + line = Globals.program.getSourceLine(lineNumber++); + } + editPane.setSourceCode(fileContents.toString(), true); + // The above operation generates an undoable edit, setting the initial + // text area contents, that should not be seen as undoable by the Undo + // action. Let's get rid of it. + editPane.discardAllUndoableEdits(); + editPane.setShowLineNumbersEnabled(true); + editPane.setFileStatus(FileStatus.NOT_EDITED); + + addTab(editPane.getFilename(), editPane); + setToolTipTextAt(indexOfComponent(editPane), editPane.getPathname()); + setSelectedComponent(editPane); + FileStatus.setSaved(true); + FileStatus.setEdited(false); + FileStatus.set(FileStatus.NOT_EDITED); + + // If assemble-all, then allow opening of any file w/o invalidating assembly. + // DPS 9-Aug-2011 + if (Globals.getSettings().getBooleanSetting(mars.Settings.ASSEMBLE_ALL_ENABLED)) + { + updateTitles(editPane); + } + else + {// this was the original code... + updateTitlesAndMenuState(editPane); + mainPane.getExecutePane().clearPane(); + } + + mainPane.setSelectedComponent(EditTabbedPane.this); + editPane.tellEditingComponentToRequestFocusInWindow(); + mostRecentlyOpenedFile = theFile; } return true; - } - - // Private method to generate the file chooser's list of choosable file filters. - // It is called when the file chooser is created, and called again each time the Open - // dialog is activated. We do this because the user may have added a new filter - // during the previous dialog. This can be done by entering e.g. *.txt in the file - // name text field. Java is funny, however, in that if the user does this then - // cancels the dialog, the new filter will remain in the list BUT if the user does - // this then ACCEPTS the dialog, the new filter will NOT remain in the list. However - // the act of entering it causes a property change event to occur, and we have a - // handler that will add the new filter to our internal filter list and "restore" it - // the next time this method is called. Strangely, if the user then similarly - // adds yet another new filter, the new one becomes simply a description change - // to the previous one, the previous object is modified AND NO PROPERTY CHANGE EVENT - // IS FIRED! I could obviously deal with this situation if I wanted to, but enough - // is enough. The limit will be one alternative filter at a time. - // DPS... 9 July 2008 - - private void setChoosableFileFilters() { - // See if a new filter has been added to the master list. If so, - // regenerate the fileChooser list from the master list. - if (fileFilterCount < fileFilterList.size() || - fileFilterList.size() != fileChooser.getChoosableFileFilters().length) { - fileFilterCount = fileFilterList.size(); - // First, "deactivate" the listener, because our addChoosableFileFilter - // calls would otherwise activate it! We want it to be triggered only - // by MARS user action. - boolean activeListener = false; - if (fileChooser.getPropertyChangeListeners().length > 0) { - fileChooser.removePropertyChangeListener(listenForUserAddedFileFilter); - activeListener = true; // we'll note this, for re-activation later - } - // clear out the list and populate from our own ArrayList. - // Last one added becomes the default. - fileChooser.resetChoosableFileFilters(); - for (int i=0; i < fileFilterList.size(); i++) { - fileChooser.addChoosableFileFilter((FileFilter)fileFilterList.get(i)); - } - // Restore listener. - if (activeListener) { - fileChooser.addPropertyChangeListener(listenForUserAddedFileFilter); - } + } + + // Private method to generate the file chooser's list of choosable file filters. + // It is called when the file chooser is created, and called again each time the Open + // dialog is activated. We do this because the user may have added a new filter + // during the previous dialog. This can be done by entering e.g. *.txt in the file + // name text field. Java is funny, however, in that if the user does this then + // cancels the dialog, the new filter will remain in the list BUT if the user does + // this then ACCEPTS the dialog, the new filter will NOT remain in the list. However + // the act of entering it causes a property change event to occur, and we have a + // handler that will add the new filter to our internal filter list and "restore" it + // the next time this method is called. Strangely, if the user then similarly + // adds yet another new filter, the new one becomes simply a description change + // to the previous one, the previous object is modified AND NO PROPERTY CHANGE EVENT + // IS FIRED! I could obviously deal with this situation if I wanted to, but enough + // is enough. The limit will be one alternative filter at a time. + // DPS... 9 July 2008 + + private void setChoosableFileFilters() + { + // See if a new filter has been added to the master list. If so, + // regenerate the fileChooser list from the master list. + if (fileFilterCount < fileFilterList.size() || + fileFilterList.size() != fileChooser.getChoosableFileFilters().length) + { + fileFilterCount = fileFilterList.size(); + // First, "deactivate" the listener, because our addChoosableFileFilter + // calls would otherwise activate it! We want it to be triggered only + // by MARS user action. + boolean activeListener = false; + if (fileChooser.getPropertyChangeListeners().length > 0) + { + fileChooser.removePropertyChangeListener(listenForUserAddedFileFilter); + activeListener = true; // we'll note this, for re-activation later + } + // clear out the list and populate from our own ArrayList. + // Last one added becomes the default. + fileChooser.resetChoosableFileFilters(); + for (int i = 0; i < fileFilterList.size(); i++) + { + fileChooser.addChoosableFileFilter((FileFilter) fileFilterList.get(i)); + } + // Restore listener. + if (activeListener) + { + fileChooser.addPropertyChangeListener(listenForUserAddedFileFilter); + } } - }////////////////////////////////////////////////////////////////////////////////// - // Private inner class for special property change listener. DPS 9 July 2008. - // If user adds a file filter, e.g. by typing *.txt into the file text field then pressing - // Enter, then it is automatically added to the array of choosable file filters. BUT, unless you - // Cancel out of the Open dialog, it is then REMOVED from the list automatically also. Here - // we will achieve a sort of persistence at least through the current activation of MARS. - - private class ChoosableFileFilterChangeListener implements PropertyChangeListener { - public void propertyChange(java.beans.PropertyChangeEvent e) { - if (e.getPropertyName()==JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) { - FileFilter[] newFilters = (FileFilter[]) e.getNewValue(); - FileFilter[] oldFilters = (FileFilter[]) e.getOldValue(); - if (newFilters.length > fileFilterList.size()) { - // new filter added, so add to end of master list. - fileFilterList.add(newFilters[newFilters.length-1]); - } - } - } - } - - - } - - } \ No newline at end of file + }////////////////////////////////////////////////////////////////////////////////// + // Private inner class for special property change listener. DPS 9 July 2008. + // If user adds a file filter, e.g. by typing *.txt into the file text field then pressing + // Enter, then it is automatically added to the array of choosable file filters. BUT, unless you + // Cancel out of the Open dialog, it is then REMOVED from the list automatically also. Here + // we will achieve a sort of persistence at least through the current activation of MARS. + + private class ChoosableFileFilterChangeListener implements PropertyChangeListener + { + public void propertyChange(java.beans.PropertyChangeEvent e) + { + if (e.getPropertyName() == JFileChooser.CHOOSABLE_FILE_FILTER_CHANGED_PROPERTY) + { + FileFilter[] newFilters = (FileFilter[]) e.getNewValue(); + FileFilter[] oldFilters = (FileFilter[]) e.getOldValue(); + if (newFilters.length > fileFilterList.size()) + { + // new filter added, so add to end of master list. + fileFilterList.add(newFilters[newFilters.length - 1]); + } + } + } + } + + + } + +} diff --git a/src/main/java/mars/venus/EditUndoAction.java b/src/main/java/mars/venus/EditUndoAction.java index 9eb2297..4243fb4 100644 --- a/src/main/java/mars/venus/EditUndoAction.java +++ b/src/main/java/mars/venus/EditUndoAction.java @@ -1,9 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.undo.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,33 +30,38 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Action for the Edit -> Undo menu item + */ +public class EditUndoAction extends GuiAction +{ + + public EditUndoAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + setEnabled(false); + } + /** - * Action for the Edit -> Undo menu item - */ - public class EditUndoAction extends GuiAction { - - public EditUndoAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - setEnabled(false); - } - /** - * Adapted from TextComponentDemo.java in the - * Java Tutorial "Text Component Features" - */ - public void actionPerformed(ActionEvent e) { - EditPane editPane = mainUI.getMainPane().getEditPane(); - if (editPane != null) { + * Adapted from TextComponentDemo.java in the Java Tutorial "Text Component Features" + */ + public void actionPerformed(ActionEvent e) + { + EditPane editPane = mainUI.getMainPane().getEditPane(); + if (editPane != null) + { editPane.undo(); updateUndoState(); mainUI.editRedoAction.updateRedoState(); - } - } - - void updateUndoState() { - EditPane editPane = mainUI.getMainPane().getEditPane(); - setEnabled(editPane != null && editPane.getUndoManager().canUndo()); - //new Throwable("update undo state: "+(editPane != null && editPane.getUndoManager().canUndo())).printStackTrace(); - } - } \ No newline at end of file + } + } + + void updateUndoState() + { + EditPane editPane = mainUI.getMainPane().getEditPane(); + setEnabled(editPane != null && editPane.getUndoManager().canUndo()); + //new Throwable("update undo state: "+(editPane != null && editPane.getUndoManager().canUndo())).printStackTrace(); + } +} diff --git a/src/main/java/mars/venus/Editor.java b/src/main/java/mars/venus/Editor.java index 8e43771..ab5dc26 100644 --- a/src/main/java/mars/venus/Editor.java +++ b/src/main/java/mars/venus/Editor.java @@ -1,7 +1,6 @@ - package mars.venus; - import mars.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import java.io.File; /* Copyright (c) 2003-2007, Pete Sanderson and Kenneth Vollmar @@ -30,228 +29,258 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Manage the file being edited. - * Currently only manages one file at a time, but can be expanded. - */ - - public class Editor { - - public static final int MIN_TAB_SIZE = 1; - public static final int MAX_TAB_SIZE = 32; - public static final int MIN_BLINK_RATE = 0; // no flashing - public static final int MAX_BLINK_RATE = 1000; // once per second - - private VenusUI mainUI; - private EditTabbedPane editTabbedPane; - private String mainUIbaseTitle; + +/** + * Manage the file being edited. Currently only manages one file at a time, but can be expanded. + */ + +public class Editor +{ + + public static final int MIN_TAB_SIZE = 1; + + public static final int MAX_TAB_SIZE = 32; + + public static final int MIN_BLINK_RATE = 0; // no flashing + + public static final int MAX_BLINK_RATE = 1000; // once per second + + private final VenusUI mainUI; + + private EditTabbedPane editTabbedPane; + + private final String mainUIbaseTitle; + /* number of times File->New has been selected. Used to generate * default filename until first Save or Save As. */ - private int newUsageCount; - // Current Directory for Open operation, same for Save operation - // Values will mainly be set by the EditTabbedPane as Open/Save operations occur. - private String defaultOpenDirectory, currentOpenDirectory; - private String defaultSaveDirectory, currentSaveDirectory; - + private int newUsageCount; + + // Current Directory for Open operation, same for Save operation + // Values will mainly be set by the EditTabbedPane as Open/Save operations occur. + private final String defaultOpenDirectory; + + private String currentOpenDirectory; + + private final String defaultSaveDirectory; + + private String currentSaveDirectory; + /** * Create editor. * * @param ui the GUI that owns this editor */ - public Editor(VenusUI ui) { - mainUI = ui; - FileStatus.reset(); - mainUIbaseTitle = mainUI.getTitle(); - newUsageCount = 0; - // Directory from which MARS was launched. Guaranteed to have a value. - defaultOpenDirectory = System.getProperty("user.dir"); - defaultSaveDirectory = System.getProperty("user.dir"); - currentOpenDirectory = defaultOpenDirectory; - currentSaveDirectory = defaultSaveDirectory; - } - + public Editor(VenusUI ui) + { + mainUI = ui; + FileStatus.reset(); + mainUIbaseTitle = mainUI.getTitle(); + newUsageCount = 0; + // Directory from which MARS was launched. Guaranteed to have a value. + defaultOpenDirectory = System.getProperty("user.dir"); + defaultSaveDirectory = System.getProperty("user.dir"); + currentOpenDirectory = defaultOpenDirectory; + currentSaveDirectory = defaultSaveDirectory; + } + /** - * Set associated EditTabbedPane. This is container for any/all open files. + * Set associated EditTabbedPane. This is container for any/all open files. * * @param editTabbedPane an existing editTabbedPane object - */ - public void setEditTabbedPane(EditTabbedPane editTabbedPane) { - this.editTabbedPane = editTabbedPane; - } - - /** - * Get name of current directory for Open operation. - * - * @return String containing directory pathname. Returns null if there is - * no EditTabbedPane. Returns default, directory MARS is launched from, if - * no Opens have been performed. - */ - - public String getCurrentOpenDirectory() { - return currentOpenDirectory; - } - - /** - * Set name of current directory for Open operation. The contents of this directory will - * be displayed when Open dialog is launched. - * - * @param currentOpenDirectory String containing pathname for current Open directory. If - * it does not exist or is not a directory, the default (MARS launch directory) will be used. - */ - - void setCurrentOpenDirectory(String currentOpenDirectory) { - File file = new File(currentOpenDirectory); - if ( !file.exists() || !file.isDirectory() ) { + */ + public void setEditTabbedPane(EditTabbedPane editTabbedPane) + { + this.editTabbedPane = editTabbedPane; + } + + /** + * Get name of current directory for Open operation. + * + * @return String containing directory pathname. Returns null if there is no EditTabbedPane. Returns default, + * directory MARS is launched from, if no Opens have been performed. + */ + + public String getCurrentOpenDirectory() + { + return currentOpenDirectory; + } + + /** + * Set name of current directory for Open operation. The contents of this directory will be displayed when Open + * dialog is launched. + * + * @param currentOpenDirectory String containing pathname for current Open directory. If it does not exist or is + * not a directory, the default (MARS launch directory) will be used. + */ + + void setCurrentOpenDirectory(String currentOpenDirectory) + { + File file = new File(currentOpenDirectory); + if (!file.exists() || !file.isDirectory()) + { this.currentOpenDirectory = defaultOpenDirectory; - } - else { + } + else + { this.currentOpenDirectory = currentOpenDirectory; - } - } - - - - /** - * Get name of current directory for Save or Save As operation. - * - * @return String containing directory pathname. Returns null if there is - * no EditTabbedPane. Returns default, directory MARS is launched from, if - * no Save or Save As operations have been performed. - */ - - public String getCurrentSaveDirectory() { - return currentSaveDirectory; - } - - /** - * Set name of current directory for Save operation. The contents of this directory will - * be displayed when Save dialog is launched. - * - * @param currentSaveDirectory String containing pathname for current Save directory. If - * it does not exist or is not a directory, the default (MARS launch directory) will be used. - */ - - void setCurrentSaveDirectory(String currentSaveDirectory) { - File file = new File(currentSaveDirectory); - if ( !file.exists() || !file.isDirectory() ) { + } + } + + + /** + * Get name of current directory for Save or Save As operation. + * + * @return String containing directory pathname. Returns null if there is no EditTabbedPane. Returns default, + * directory MARS is launched from, if no Save or Save As operations have been performed. + */ + + public String getCurrentSaveDirectory() + { + return currentSaveDirectory; + } + + /** + * Set name of current directory for Save operation. The contents of this directory will be displayed when Save + * dialog is launched. + * + * @param currentSaveDirectory String containing pathname for current Save directory. If it does not exist or is + * not a directory, the default (MARS launch directory) will be used. + */ + + void setCurrentSaveDirectory(String currentSaveDirectory) + { + File file = new File(currentSaveDirectory); + if (!file.exists() || !file.isDirectory()) + { this.currentSaveDirectory = defaultSaveDirectory; - } - else { + } + else + { this.currentSaveDirectory = currentSaveDirectory; - } - } - - - /** - * Generates a default file name - * - * @return returns string mipsN.asm, where N is 1,2,3,... - */ - public String getNextDefaultFilename() { - newUsageCount++; - return "mips"+newUsageCount+".asm"; - } - - - /** Places name of file currently being edited into its edit tab and - * the application's title bar. The edit tab will contain only - * the filename, the title bar will contain full pathname. - * If file has been modified since created, opened or saved, as - * indicated by value of the status parameter, the name and path - * will be followed with an '*'. If newly-created file has not - * yet been saved, the title bar will show (temporary) file name - * but not path. - * - * @param path Full pathname for file - * @param name Name of file (last component of path) - * @param status Edit status of file. See FileStatus static constants. - */ - public void setTitle(String path, String name, int status) { - if (status == FileStatus.NO_FILE || name==null || name.length()==0) { + } + } + + + /** + * Generates a default file name + * + * @return returns string mipsN.asm, where N is 1,2,3,... + */ + public String getNextDefaultFilename() + { + newUsageCount++; + return "mips" + newUsageCount + ".asm"; + } + + + /** + * Places name of file currently being edited into its edit tab and the application's title bar. The edit tab will + * contain only the filename, the title bar will contain full pathname. If file has been modified since created, + * opened or saved, as indicated by value of the status parameter, the name and path will be followed with an '*'. + * If newly-created file has not yet been saved, the title bar will show (temporary) file name but not path. + * + * @param path Full pathname for file + * @param name Name of file (last component of path) + * @param status Edit status of file. See FileStatus static constants. + */ + public void setTitle(String path, String name, int status) + { + if (status == FileStatus.NO_FILE || name == null || name.length() == 0) + { mainUI.setTitle(mainUIbaseTitle); - } - else { - String edited = (status==FileStatus.NEW_EDITED || status==FileStatus.EDITED) ? "*" : " "; - String titleName = (status==FileStatus.NEW_EDITED || status==FileStatus.NEW_NOT_EDITED) ? name : path; + } + else + { + String edited = (status == FileStatus.NEW_EDITED || status == FileStatus.EDITED) ? "*" : " "; + String titleName = (status == FileStatus.NEW_EDITED || status == FileStatus.NEW_NOT_EDITED) ? name : path; mainUI.setTitle(titleName + edited + " - " + mainUIbaseTitle); editTabbedPane.setTitleAt(editTabbedPane.getSelectedIndex(), name + edited); - } - } - - - - /** - * Perform "new" operation to create an empty tab. - */ - public void newFile() { - editTabbedPane.newFile(); - } - - /** - * Perform "close" operation on current tab's file. - * @return true if succeeded, else false. - */ - public boolean close() { - return editTabbedPane.closeCurrentFile(); - } - - /** - * Close all currently open files. - * @return true if succeeded, else false. - */ - public boolean closeAll() { - return editTabbedPane.closeAllFiles(); - } - - /** - * Perform "save" operation on current tab's file. - * @return true if succeeded, else false. - */ - public boolean save() { - return editTabbedPane.saveCurrentFile(); - } - - /** - * Perform "save as" operation on current tab's file. - * @return true if succeeded, else false. - */ - public boolean saveAs() { - return editTabbedPane.saveAsCurrentFile(); - } - - /** - * Perform save operation on all open files (tabs). - * @return true if succeeded, else false. - */ - public boolean saveAll() { - return editTabbedPane.saveAllFiles(); - } - - /** - * Open file in a new tab. - * @return true if succeeded, else false. - */ - public boolean open() { - return editTabbedPane.openFile(); - } - - - - /** - * Called by several of the Action objects when there is potential - * loss of editing changes. Specifically: if there is a current - * file open for editing and its modify flag is true, then give user - * a dialog box with choice to save, discard edits, or cancel and - * carry out the decision. This applies to File->New, File->Open, - * File->Close, and File->Exit. - * - * @return false means user selected Cancel so caller should do that. - * Return of true means caller can proceed (edits were saved or discarded). - */ - public boolean editsSavedOrAbandoned() { - return editTabbedPane.editsSavedOrAbandoned(); - } - - } \ No newline at end of file + } + } + + + /** + * Perform "new" operation to create an empty tab. + */ + public void newFile() + { + editTabbedPane.newFile(); + } + + /** + * Perform "close" operation on current tab's file. + * + * @return true if succeeded, else false. + */ + public boolean close() + { + return editTabbedPane.closeCurrentFile(); + } + + /** + * Close all currently open files. + * + * @return true if succeeded, else false. + */ + public boolean closeAll() + { + return editTabbedPane.closeAllFiles(); + } + + /** + * Perform "save" operation on current tab's file. + * + * @return true if succeeded, else false. + */ + public boolean save() + { + return editTabbedPane.saveCurrentFile(); + } + + /** + * Perform "save as" operation on current tab's file. + * + * @return true if succeeded, else false. + */ + public boolean saveAs() + { + return editTabbedPane.saveAsCurrentFile(); + } + + /** + * Perform save operation on all open files (tabs). + * + * @return true if succeeded, else false. + */ + public boolean saveAll() + { + return editTabbedPane.saveAllFiles(); + } + + /** + * Open file in a new tab. + * + * @return true if succeeded, else false. + */ + public boolean open() + { + return editTabbedPane.openFile(); + } + + + /** + * Called by several of the Action objects when there is potential loss of editing changes. Specifically: if there + * is a current file open for editing and its modify flag is true, then give user a dialog box with choice to save, + * discard edits, or cancel and carry out the decision. This applies to File->New, File->Open, File->Close, and + * File->Exit. + * + * @return false means user selected Cancel so caller should do that. Return of true means caller can proceed (edits + * were saved or discarded). + */ + public boolean editsSavedOrAbandoned() + { + return editTabbedPane.editsSavedOrAbandoned(); + } + +} diff --git a/src/main/java/mars/venus/ExecutePane.java b/src/main/java/mars/venus/ExecutePane.java index d2b67a5..f309bde 100644 --- a/src/main/java/mars/venus/ExecutePane.java +++ b/src/main/java/mars/venus/ExecutePane.java @@ -1,8 +1,9 @@ - package mars.venus; - import mars.*; - import javax.swing.*; - import java.awt.*; - import java.util.*; +package mars.venus; + +import mars.Globals; + +import javax.swing.*; +import java.awt.*; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,230 +33,262 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - /** - * Container for the execution-related windows. Currently displayed as a tabbed pane. - * @author Sanderson and Team JSpim - **/ +/** + * Container for the execution-related windows. Currently displayed as a tabbed pane. + * + * @author Sanderson and Team JSpim + **/ - public class ExecutePane extends JDesktopPane { - private RegistersWindow registerValues; - private Coprocessor1Window coprocessor1Values; - private Coprocessor0Window coprocessor0Values; - private DataSegmentWindow dataSegment; - private TextSegmentWindow textSegment; - private LabelsWindow labelValues; - private VenusUI mainUI; - private NumberDisplayBaseChooser valueDisplayBase; - private NumberDisplayBaseChooser addressDisplayBase; - private boolean labelWindowVisible; - - /** - * initialize the Execute pane with major components - * - * @param mainUI the parent GUI - * @param regs window containing integer register set - * @param cop1Regs window containing Coprocessor 1 register set - * @param cop0Regs window containing Coprocessor 0 register set - */ - - public ExecutePane(VenusUI mainUI, RegistersWindow regs, Coprocessor1Window cop1Regs, Coprocessor0Window cop0Regs) { - this.mainUI = mainUI; - // Although these are displayed in Data Segment, they apply to all three internal - // windows within the Execute pane. So they will be housed here. - addressDisplayBase = new NumberDisplayBaseChooser("Hexadecimal Addresses", - Globals.getSettings().getDisplayAddressesInHex()); - valueDisplayBase = new NumberDisplayBaseChooser("Hexadecimal Values", - Globals.getSettings().getDisplayValuesInHex());//VenusUI.DEFAULT_NUMBER_BASE); - addressDisplayBase.setToolTipText("If checked, displays all memory addresses in hexadecimal. Otherwise, decimal."); - valueDisplayBase.setToolTipText("If checked, displays all memory and register contents in hexadecimal. Otherwise, decimal."); - NumberDisplayBaseChooser[] choosers = { addressDisplayBase, valueDisplayBase }; - registerValues = regs; - coprocessor1Values = cop1Regs; - coprocessor0Values = cop0Regs; - textSegment = new TextSegmentWindow(); - dataSegment = new DataSegmentWindow(choosers); - labelValues = new LabelsWindow(); - labelWindowVisible = Globals.getSettings().getLabelWindowVisibility(); - this.add(textSegment); // these 3 LOC moved up. DPS 3-Sept-2014 - this.add(dataSegment); - this.add(labelValues); - textSegment.pack(); // these 3 LOC added. DPS 3-Sept-2014 - dataSegment.pack(); - labelValues.pack(); - textSegment.setVisible(true); - dataSegment.setVisible(true); - labelValues.setVisible(labelWindowVisible); +public class ExecutePane extends JDesktopPane +{ + private final RegistersWindow registerValues; - } - - /** - * This method will set the bounds of this JDesktopPane's internal windows - * relative to the current size of this JDesktopPane. Such an operation - * cannot be adequately done at constructor time because the actual - * size of the desktop pane window is not yet established. Layout manager - * is not a good option here because JDesktopPane does not work well with - * them (the whole idea of using JDesktopPane with internal frames is to - * have mini-frames that you can resize, move around, minimize, etc). This - * method should be invoked only once: the first time the Execute tab is - * selected (a change listener invokes it). We do not want it invoked - * on subsequent tab selections; otherwise, user manipulations of the - * internal frames would be lost the next time execute tab is selected. - */ - public void setWindowBounds() { - - int fullWidth = this.getSize().width - this.getInsets().left - this.getInsets().right; - int fullHeight = this.getSize().height - this.getInsets().top - this.getInsets().bottom; - int halfHeight = fullHeight/2; - Dimension textDim = new Dimension((int)(fullWidth*.75),halfHeight); - Dimension dataDim = new Dimension((int)(fullWidth),halfHeight); - Dimension lablDim = new Dimension((int)(fullWidth*.25), halfHeight); - Dimension textFullDim = new Dimension((int)(fullWidth), halfHeight); - dataSegment.setBounds(0,textDim.height+1, dataDim.width, dataDim.height); - if (labelWindowVisible) { + private final Coprocessor1Window coprocessor1Values; + + private final Coprocessor0Window coprocessor0Values; + + private final DataSegmentWindow dataSegment; + + private final TextSegmentWindow textSegment; + + private final LabelsWindow labelValues; + + private final VenusUI mainUI; + + private final NumberDisplayBaseChooser valueDisplayBase; + + private final NumberDisplayBaseChooser addressDisplayBase; + + private boolean labelWindowVisible; + + /** + * initialize the Execute pane with major components + * + * @param mainUI the parent GUI + * @param regs window containing integer register set + * @param cop1Regs window containing Coprocessor 1 register set + * @param cop0Regs window containing Coprocessor 0 register set + */ + + public ExecutePane(VenusUI mainUI, RegistersWindow regs, Coprocessor1Window cop1Regs, Coprocessor0Window cop0Regs) + { + this.mainUI = mainUI; + // Although these are displayed in Data Segment, they apply to all three internal + // windows within the Execute pane. So they will be housed here. + addressDisplayBase = new NumberDisplayBaseChooser("Hexadecimal Addresses", + Globals.getSettings().getDisplayAddressesInHex()); + valueDisplayBase = new NumberDisplayBaseChooser("Hexadecimal Values", + Globals.getSettings().getDisplayValuesInHex());//VenusUI.DEFAULT_NUMBER_BASE); + addressDisplayBase.setToolTipText("If checked, displays all memory addresses in hexadecimal. Otherwise, decimal."); + valueDisplayBase.setToolTipText("If checked, displays all memory and register contents in hexadecimal. Otherwise, decimal."); + NumberDisplayBaseChooser[] choosers = {addressDisplayBase, valueDisplayBase}; + registerValues = regs; + coprocessor1Values = cop1Regs; + coprocessor0Values = cop0Regs; + textSegment = new TextSegmentWindow(); + dataSegment = new DataSegmentWindow(choosers); + labelValues = new LabelsWindow(); + labelWindowVisible = Globals.getSettings().getLabelWindowVisibility(); + this.add(textSegment); // these 3 LOC moved up. DPS 3-Sept-2014 + this.add(dataSegment); + this.add(labelValues); + textSegment.pack(); // these 3 LOC added. DPS 3-Sept-2014 + dataSegment.pack(); + labelValues.pack(); + textSegment.setVisible(true); + dataSegment.setVisible(true); + labelValues.setVisible(labelWindowVisible); + + } + + /** + * This method will set the bounds of this JDesktopPane's internal windows relative to the current size of this + * JDesktopPane. Such an operation cannot be adequately done at constructor time because the actual size of the + * desktop pane window is not yet established. Layout manager is not a good option here because JDesktopPane does + * not work well with them (the whole idea of using JDesktopPane with internal frames is to have mini-frames that + * you can resize, move around, minimize, etc). This method should be invoked only once: the first time the Execute + * tab is selected (a change listener invokes it). We do not want it invoked on subsequent tab selections; + * otherwise, user manipulations of the internal frames would be lost the next time execute tab is selected. + */ + public void setWindowBounds() + { + + int fullWidth = this.getSize().width - this.getInsets().left - this.getInsets().right; + int fullHeight = this.getSize().height - this.getInsets().top - this.getInsets().bottom; + int halfHeight = fullHeight / 2; + Dimension textDim = new Dimension((int) (fullWidth * .75), halfHeight); + Dimension dataDim = new Dimension(fullWidth, halfHeight); + Dimension lablDim = new Dimension((int) (fullWidth * .25), halfHeight); + Dimension textFullDim = new Dimension(fullWidth, halfHeight); + dataSegment.setBounds(0, textDim.height + 1, dataDim.width, dataDim.height); + if (labelWindowVisible) + { textSegment.setBounds(0, 0, textDim.width, textDim.height); - labelValues.setBounds(textDim.width+1, 0, lablDim.width, lablDim.height); - } - else { + labelValues.setBounds(textDim.width + 1, 0, lablDim.width, lablDim.height); + } + else + { textSegment.setBounds(0, 0, textFullDim.width, textFullDim.height); - labelValues.setBounds(0, 0, 0, 0); - } - } - - /** - * Show or hide the label window (symbol table). If visible, it is displayed - * to the right of the text segment and the latter is shrunk accordingly. - * @param visibility set to true or false - */ - - public void setLabelWindowVisibility(boolean visibility) { - if (!visibility && labelWindowVisible) { + labelValues.setBounds(0, 0, 0, 0); + } + } + + /** + * Show or hide the label window (symbol table). If visible, it is displayed to the right of the text segment and + * the latter is shrunk accordingly. + * + * @param visibility set to true or false + */ + + public void setLabelWindowVisibility(boolean visibility) + { + if (!visibility && labelWindowVisible) + { labelWindowVisible = false; textSegment.setVisible(false); labelValues.setVisible(false); setWindowBounds(); textSegment.setVisible(true); - } - else - if (visibility && !labelWindowVisible) { - labelWindowVisible = true; - textSegment.setVisible(false); - setWindowBounds(); - textSegment.setVisible(true); - labelValues.setVisible(true); - } - } - - /** Clears out all components of the Execute tab: text segment - * display, data segment display, label display and register display. - * This will typically be done upon File->Close, Open, New. - */ - - public void clearPane() { - this.getTextSegmentWindow().clearWindow(); - this.getDataSegmentWindow().clearWindow(); - this.getRegistersWindow().clearWindow(); - this.getCoprocessor1Window().clearWindow(); - this.getCoprocessor0Window().clearWindow(); - this.getLabelsWindow().clearWindow(); - // seems to be required, to display cleared Execute tab contents... - if (mainUI.getMainPane().getSelectedComponent()== this) { + } + else if (visibility && !labelWindowVisible) + { + labelWindowVisible = true; + textSegment.setVisible(false); + setWindowBounds(); + textSegment.setVisible(true); + labelValues.setVisible(true); + } + } + + /** + * Clears out all components of the Execute tab: text segment display, data segment display, label display and + * register display. This will typically be done upon File->Close, Open, New. + */ + + public void clearPane() + { + this.getTextSegmentWindow().clearWindow(); + this.getDataSegmentWindow().clearWindow(); + this.getRegistersWindow().clearWindow(); + this.getCoprocessor1Window().clearWindow(); + this.getCoprocessor0Window().clearWindow(); + this.getLabelsWindow().clearWindow(); + // seems to be required, to display cleared Execute tab contents... + if (mainUI.getMainPane().getSelectedComponent() == this) + { mainUI.getMainPane().setSelectedComponent(mainUI.getMainPane().getEditTabbedPane()); mainUI.getMainPane().setSelectedComponent(this); - } - } - - /** - * Access the text segment window. - */ - public TextSegmentWindow getTextSegmentWindow() { - return textSegment; - } - - /** - * Access the data segment window. - */ - public DataSegmentWindow getDataSegmentWindow() { - return dataSegment; - } - - /** - * Access the register values window. - */ - public RegistersWindow getRegistersWindow() { - return registerValues; - } - - /** - * Access the coprocessor1 values window. - */ - public Coprocessor1Window getCoprocessor1Window() { - return coprocessor1Values; - } - - /** - * Access the coprocessor0 values window. - */ - public Coprocessor0Window getCoprocessor0Window() { - return coprocessor0Values; - } - - /** - * Access the label values window. - */ - public LabelsWindow getLabelsWindow() { - return labelValues; - } - /** - * Retrieve the number system base for displaying values (mem/register contents) - */ - public int getValueDisplayBase() { - return valueDisplayBase.getBase(); - } - - /** - * Retrieve the number system base for displaying memory addresses - */ - public int getAddressDisplayBase() { - return addressDisplayBase.getBase(); - } - - /** - * Retrieve component used to set numerical base (10 or 16) of data value display. - * @return the chooser - */ - public NumberDisplayBaseChooser getValueDisplayBaseChooser() { - return valueDisplayBase; - } - - /** - * Retrieve component used to set numerical base (10 or 16) of address display. - * @return the chooser - */ - public NumberDisplayBaseChooser getAddressDisplayBaseChooser() { - return addressDisplayBase; - } - - /** - * Update display of columns based on state of given chooser. Normally - * called only by the chooser's ItemListener. - * @param chooser the GUI object manipulated by the user to change number base - */ - public void numberDisplayBaseChanged(NumberDisplayBaseChooser chooser) { - if (chooser == valueDisplayBase) { - // Have all internal windows update their value columns + } + } + + /** + * Access the text segment window. + */ + public TextSegmentWindow getTextSegmentWindow() + { + return textSegment; + } + + /** + * Access the data segment window. + */ + public DataSegmentWindow getDataSegmentWindow() + { + return dataSegment; + } + + /** + * Access the register values window. + */ + public RegistersWindow getRegistersWindow() + { + return registerValues; + } + + /** + * Access the coprocessor1 values window. + */ + public Coprocessor1Window getCoprocessor1Window() + { + return coprocessor1Values; + } + + /** + * Access the coprocessor0 values window. + */ + public Coprocessor0Window getCoprocessor0Window() + { + return coprocessor0Values; + } + + /** + * Access the label values window. + */ + public LabelsWindow getLabelsWindow() + { + return labelValues; + } + + /** + * Retrieve the number system base for displaying values (mem/register contents) + */ + public int getValueDisplayBase() + { + return valueDisplayBase.getBase(); + } + + /** + * Retrieve the number system base for displaying memory addresses + */ + public int getAddressDisplayBase() + { + return addressDisplayBase.getBase(); + } + + /** + * Retrieve component used to set numerical base (10 or 16) of data value display. + * + * @return the chooser + */ + public NumberDisplayBaseChooser getValueDisplayBaseChooser() + { + return valueDisplayBase; + } + + /** + * Retrieve component used to set numerical base (10 or 16) of address display. + * + * @return the chooser + */ + public NumberDisplayBaseChooser getAddressDisplayBaseChooser() + { + return addressDisplayBase; + } + + /** + * Update display of columns based on state of given chooser. Normally called only by the chooser's ItemListener. + * + * @param chooser the GUI object manipulated by the user to change number base + */ + public void numberDisplayBaseChanged(NumberDisplayBaseChooser chooser) + { + if (chooser == valueDisplayBase) + { + // Have all internal windows update their value columns registerValues.updateRegisters(); coprocessor1Values.updateRegisters(); coprocessor0Values.updateRegisters(); dataSegment.updateValues(); textSegment.updateBasicStatements(); - } - else { // addressDisplayBase - // Have all internal windows update their address columns + } + else + { // addressDisplayBase + // Have all internal windows update their address columns dataSegment.updateDataAddresses(); labelValues.updateLabelAddresses(); textSegment.updateCodeAddresses(); textSegment.updateBasicStatements(); - } - } - - } \ No newline at end of file + } + } + +} diff --git a/src/main/java/mars/venus/FileCloseAction.java b/src/main/java/mars/venus/FileCloseAction.java index fa6f0f4..83766af 100644 --- a/src/main/java/mars/venus/FileCloseAction.java +++ b/src/main/java/mars/venus/FileCloseAction.java @@ -1,8 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,18 +30,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the File -> Close menu item - */ - public class FileCloseAction extends GuiAction { - - public FileCloseAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e){ - mainUI.editor.close(); - } - } \ No newline at end of file + +/** + * Action for the File -> Close menu item + */ +public class FileCloseAction extends GuiAction +{ + + public FileCloseAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + mainUI.editor.close(); + } +} diff --git a/src/main/java/mars/venus/FileCloseAllAction.java b/src/main/java/mars/venus/FileCloseAllAction.java index 92df132..f439309 100644 --- a/src/main/java/mars/venus/FileCloseAllAction.java +++ b/src/main/java/mars/venus/FileCloseAllAction.java @@ -1,8 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,18 +30,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the File -> Close All menu item - */ - public class FileCloseAllAction extends GuiAction { - - public FileCloseAllAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e){ - mainUI.editor.closeAll(); - } - } \ No newline at end of file + +/** + * Action for the File -> Close All menu item + */ +public class FileCloseAllAction extends GuiAction +{ + + public FileCloseAllAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + mainUI.editor.closeAll(); + } +} diff --git a/src/main/java/mars/venus/FileDumpMemoryAction.java b/src/main/java/mars/venus/FileDumpMemoryAction.java index 153d80e..905a9f3 100644 --- a/src/main/java/mars/venus/FileDumpMemoryAction.java +++ b/src/main/java/mars/venus/FileDumpMemoryAction.java @@ -1,15 +1,24 @@ - package mars.venus; - import mars.*; - import mars.util.*; - import mars.mips.dump.*; - import mars.mips.hardware.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.border.*; - import java.io.*; - import java.util.*; - import javax.swing.plaf.basic.*; +package mars.venus; + +import mars.Globals; +import mars.mips.dump.DumpFormat; +import mars.mips.dump.DumpFormatLoader; +import mars.mips.hardware.AddressErrorException; +import mars.mips.hardware.Memory; +import mars.util.Binary; +import mars.util.MemoryDump; + +import javax.swing.*; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.basic.BasicComboBoxRenderer; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -38,255 +47,298 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the File -> Save For Dump Memory menu item - */ - public class FileDumpMemoryAction extends GuiAction { - - private JDialog dumpDialog; - private static final String title = "Dump Memory To File"; - - // A series of parallel arrays representing the memory segments that can be dumped. - private String[] segmentArray; - private int[] baseAddressArray; - private int[] limitAddressArray; - private int[] highAddressArray; - // These three are allocated and filled by buildDialogPanel() and used by action listeners. - private String[] segmentListArray; - private int[] segmentListBaseArray; - private int[] segmentListHighArray; - - private JComboBox segmentListSelector; - private JComboBox formatListSelector; - public FileDumpMemoryAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - - } - - - public void actionPerformed(ActionEvent e){ - dumpMemory(); - } - - /* Save the memory segment in a supported format. - */ - private boolean dumpMemory() { - dumpDialog = createDumpDialog(); - dumpDialog.pack(); - dumpDialog.setLocationRelativeTo(Globals.getGui()); - dumpDialog.setVisible(true); - return true; - ///////////////////////////////////////////////////////////////////// - } - - - // The dump dialog that appears when menu item is selected. - private JDialog createDumpDialog() { - JDialog dumpDialog = new JDialog(Globals.getGui(), title, true); - dumpDialog.setContentPane(buildDialogPanel()); - dumpDialog.setDefaultCloseOperation( - JDialog.DO_NOTHING_ON_CLOSE); - dumpDialog.addWindowListener( - new WindowAdapter() { - public void windowClosing(WindowEvent we) { - closeDialog(); - } - }); - return dumpDialog; - } - - // Set contents of dump dialog. - private JPanel buildDialogPanel() { - JPanel contents = new JPanel(new BorderLayout(20,20)); - contents.setBorder(new EmptyBorder(10,10,10,10)); - - segmentArray = MemoryDump.getSegmentNames(); - baseAddressArray = MemoryDump.getBaseAddresses(segmentArray); - limitAddressArray = MemoryDump.getLimitAddresses(segmentArray); - highAddressArray = new int[segmentArray.length]; - - - - segmentListArray = new String[segmentArray.length]; - segmentListBaseArray = new int[segmentArray.length]; - segmentListHighArray = new int[segmentArray.length]; - - // Calculate the actual highest address to be dumped. For text segment, this depends on the - // program length (number of machine code instructions). For data segment, this depends on - // how many MARS 4K word blocks have been referenced during assembly and/or execution. - // Then generate label from concatentation of segmentArray[i], baseAddressArray[i] - // and highAddressArray[i]. This lets user know exactly what range will be dumped. Initially not - // editable but maybe add this later. - // If there is nothing to dump (e.g. address of first null == base address), then - // the segment will not be listed. - int segmentCount = 0; - - for (int i=0; i Save For Dump Memory menu item + */ +public class FileDumpMemoryAction extends GuiAction +{ + + private static final String title = "Dump Memory To File"; + + private JDialog dumpDialog; + + // A series of parallel arrays representing the memory segments that can be dumped. + private String[] segmentArray; + + private int[] baseAddressArray; + + private int[] limitAddressArray; + + private int[] highAddressArray; + + // These three are allocated and filled by buildDialogPanel() and used by action listeners. + private String[] segmentListArray; + + private int[] segmentListBaseArray; + + private int[] segmentListHighArray; + + private JComboBox segmentListSelector; + + private JComboBox formatListSelector; + + public FileDumpMemoryAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + + } + + + public void actionPerformed(ActionEvent e) + { + dumpMemory(); + } + + /* Save the memory segment in a supported format. + */ + private boolean dumpMemory() + { + dumpDialog = createDumpDialog(); + dumpDialog.pack(); + dumpDialog.setLocationRelativeTo(Globals.getGui()); + dumpDialog.setVisible(true); + return true; + ///////////////////////////////////////////////////////////////////// + } + + + // The dump dialog that appears when menu item is selected. + private JDialog createDumpDialog() + { + JDialog dumpDialog = new JDialog(Globals.getGui(), title, true); + dumpDialog.setContentPane(buildDialogPanel()); + dumpDialog.setDefaultCloseOperation( + JDialog.DO_NOTHING_ON_CLOSE); + dumpDialog.addWindowListener( + new WindowAdapter() + { + public void windowClosing(WindowEvent we) + { + closeDialog(); + } + }); + return dumpDialog; + } + + // Set contents of dump dialog. + private JPanel buildDialogPanel() + { + JPanel contents = new JPanel(new BorderLayout(20, 20)); + contents.setBorder(new EmptyBorder(10, 10, 10, 10)); + + segmentArray = MemoryDump.getSegmentNames(); + baseAddressArray = MemoryDump.getBaseAddresses(segmentArray); + limitAddressArray = MemoryDump.getLimitAddresses(segmentArray); + highAddressArray = new int[segmentArray.length]; + + + segmentListArray = new String[segmentArray.length]; + segmentListBaseArray = new int[segmentArray.length]; + segmentListHighArray = new int[segmentArray.length]; + + // Calculate the actual highest address to be dumped. For text segment, this depends on the + // program length (number of machine code instructions). For data segment, this depends on + // how many MARS 4K word blocks have been referenced during assembly and/or execution. + // Then generate label from concatentation of segmentArray[i], baseAddressArray[i] + // and highAddressArray[i]. This lets user know exactly what range will be dumped. Initially not + // editable but maybe add this later. + // If there is nothing to dump (e.g. address of first null == base address), then + // the segment will not be listed. + int segmentCount = 0; + + for (int i = 0; i < segmentArray.length; i++) + { + try + { + highAddressArray[i] = Globals.memory.getAddressOfFirstNull(baseAddressArray[i], limitAddressArray[i]) - Memory.WORD_LENGTH_BYTES; + } // Exception will not happen since the Memory base and limit addresses are on word boundaries! - catch (AddressErrorException aee) { - highAddressArray[i] = baseAddressArray[i] - Memory.WORD_LENGTH_BYTES; - } - if (highAddressArray[i] >= baseAddressArray[i]) { - segmentListBaseArray[segmentCount] = baseAddressArray[i]; - segmentListHighArray[segmentCount] = highAddressArray[i]; - segmentListArray[segmentCount] = - segmentArray[i] + " (" + Binary.intToHexString(baseAddressArray[i]) + - " - " + Binary.intToHexString(highAddressArray[i]) + ")"; - segmentCount++; + catch (AddressErrorException aee) + { + highAddressArray[i] = baseAddressArray[i] - Memory.WORD_LENGTH_BYTES; } - } - - // It is highly unlikely that no segments remain after the null check, since - // there will always be at least one instruction (.text segment has one non-null). - // But just in case... - if (segmentCount == 0) { + if (highAddressArray[i] >= baseAddressArray[i]) + { + segmentListBaseArray[segmentCount] = baseAddressArray[i]; + segmentListHighArray[segmentCount] = highAddressArray[i]; + segmentListArray[segmentCount] = + segmentArray[i] + " (" + Binary.intToHexString(baseAddressArray[i]) + + " - " + Binary.intToHexString(highAddressArray[i]) + ")"; + segmentCount++; + } + } + + // It is highly unlikely that no segments remain after the null check, since + // there will always be at least one instruction (.text segment has one non-null). + // But just in case... + if (segmentCount == 0) + { contents.add(new Label("There is nothing to dump!"), BorderLayout.NORTH); JButton OKButton = new JButton("OK"); OKButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { closeDialog(); - } - }); + } + }); contents.add(OKButton, BorderLayout.SOUTH); return contents; - } - - // This is needed to assure no null array elements in ComboBox list. - if (segmentCount < segmentListArray.length) { + } + + // This is needed to assure no null array elements in ComboBox list. + if (segmentCount < segmentListArray.length) + { String[] tempArray = new String[segmentCount]; System.arraycopy(segmentListArray, 0, tempArray, 0, segmentCount); segmentListArray = tempArray; - } - - // Create segment selector. First element selected by default. - segmentListSelector = new JComboBox(segmentListArray); - segmentListSelector.setSelectedIndex(0); - JPanel segmentPanel = new JPanel(new BorderLayout()); - segmentPanel.add(new Label("Memory Segment"), BorderLayout.NORTH); - segmentPanel.add(segmentListSelector); - contents.add(segmentPanel, BorderLayout.WEST); - - // Next, create list of all available dump formats. - ArrayList dumpFormats = (new DumpFormatLoader()).loadDumpFormats(); - formatListSelector = new JComboBox(dumpFormats.toArray()); - formatListSelector.setRenderer(new DumpFormatComboBoxRenderer(formatListSelector)); - formatListSelector.setSelectedIndex(0); - JPanel formatPanel = new JPanel(new BorderLayout()); - formatPanel.add(new Label("Dump Format"), BorderLayout.NORTH); - formatPanel.add(formatListSelector); - contents.add(formatPanel, BorderLayout.EAST); - - // Bottom row - the control buttons for Dump and Cancel - Box controlPanel = Box.createHorizontalBox(); - JButton dumpButton = new JButton("Dump To File..."); - dumpButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - if (performDump(segmentListBaseArray[segmentListSelector.getSelectedIndex()], - segmentListHighArray[segmentListSelector.getSelectedIndex()], - (DumpFormat)formatListSelector.getSelectedItem())) { + } + + // Create segment selector. First element selected by default. + segmentListSelector = new JComboBox(segmentListArray); + segmentListSelector.setSelectedIndex(0); + JPanel segmentPanel = new JPanel(new BorderLayout()); + segmentPanel.add(new Label("Memory Segment"), BorderLayout.NORTH); + segmentPanel.add(segmentListSelector); + contents.add(segmentPanel, BorderLayout.WEST); + + // Next, create list of all available dump formats. + ArrayList dumpFormats = (new DumpFormatLoader()).loadDumpFormats(); + formatListSelector = new JComboBox(dumpFormats.toArray()); + formatListSelector.setRenderer(new DumpFormatComboBoxRenderer(formatListSelector)); + formatListSelector.setSelectedIndex(0); + JPanel formatPanel = new JPanel(new BorderLayout()); + formatPanel.add(new Label("Dump Format"), BorderLayout.NORTH); + formatPanel.add(formatListSelector); + contents.add(formatPanel, BorderLayout.EAST); + + // Bottom row - the control buttons for Dump and Cancel + Box controlPanel = Box.createHorizontalBox(); + JButton dumpButton = new JButton("Dump To File..."); + dumpButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + if (performDump(segmentListBaseArray[segmentListSelector.getSelectedIndex()], + segmentListHighArray[segmentListSelector.getSelectedIndex()], + (DumpFormat) formatListSelector.getSelectedItem())) + { closeDialog(); - } - } - }); - JButton cancelButton = new JButton("Cancel"); - cancelButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - closeDialog(); - } - }); - controlPanel.add(Box.createHorizontalGlue()); - controlPanel.add(dumpButton); - controlPanel.add(Box.createHorizontalGlue()); - controlPanel.add(cancelButton); - controlPanel.add(Box.createHorizontalGlue()); - contents.add(controlPanel,BorderLayout.SOUTH); - return contents; - } - - // User has clicked "Dump" button, so launch a file chooser then get - // segment (memory range) and format selections and save to the file. - private boolean performDump(int firstAddress, int lastAddress, DumpFormat format) { - File theFile = null; - JFileChooser saveDialog = null; - boolean operationOK = false; - - saveDialog = new JFileChooser(mainUI.getEditor().getCurrentSaveDirectory()); - saveDialog.setDialogTitle(title); - while (!operationOK) { + } + } + }); + JButton cancelButton = new JButton("Cancel"); + cancelButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + closeDialog(); + } + }); + controlPanel.add(Box.createHorizontalGlue()); + controlPanel.add(dumpButton); + controlPanel.add(Box.createHorizontalGlue()); + controlPanel.add(cancelButton); + controlPanel.add(Box.createHorizontalGlue()); + contents.add(controlPanel, BorderLayout.SOUTH); + return contents; + } + + // User has clicked "Dump" button, so launch a file chooser then get + // segment (memory range) and format selections and save to the file. + private boolean performDump(int firstAddress, int lastAddress, DumpFormat format) + { + File theFile = null; + JFileChooser saveDialog = null; + boolean operationOK = false; + + saveDialog = new JFileChooser(mainUI.getEditor().getCurrentSaveDirectory()); + saveDialog.setDialogTitle(title); + while (!operationOK) + { int decision = saveDialog.showSaveDialog(mainUI); - if (decision != JFileChooser.APPROVE_OPTION) { - return false; + if (decision != JFileChooser.APPROVE_OPTION) + { + return false; } theFile = saveDialog.getSelectedFile(); operationOK = true; - if (theFile.exists()) { - int overwrite = JOptionPane.showConfirmDialog(mainUI, - "File "+theFile.getName()+" already exists. Do you wish to overwrite it?", - "Overwrite existing file?", - JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); - switch (overwrite) { - case JOptionPane.YES_OPTION : - operationOK = true; - break; - case JOptionPane.NO_OPTION : - operationOK = false; - break; - case JOptionPane.CANCEL_OPTION : - return false; - default : // should never occur - return false; - } + if (theFile.exists()) + { + int overwrite = JOptionPane.showConfirmDialog(mainUI, + "File " + theFile.getName() + " already exists. Do you wish to overwrite it?", + "Overwrite existing file?", + JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE); + switch (overwrite) + { + case JOptionPane.YES_OPTION: + operationOK = true; + break; + case JOptionPane.NO_OPTION: + operationOK = false; + break; + case JOptionPane.CANCEL_OPTION: + return false; + default: // should never occur + return false; + } } - if (operationOK) { - try { - format.dumpMemoryRange(theFile, firstAddress, lastAddress); - } - catch (AddressErrorException aee) { - - } - catch (IOException ioe) { - } + if (operationOK) + { + try + { + format.dumpMemoryRange(theFile, firstAddress, lastAddress); + } + catch (AddressErrorException aee) + { + + } + catch (IOException ioe) + { + } } - } - return true; - } - - // We're finished with this modal dialog. - private void closeDialog() { - dumpDialog.setVisible(false); - dumpDialog.dispose(); - } - - - // Display tool tip for dump format list items. Got the technique from - // http://forum.java.sun.com/thread.jspa?threadID=488762&messageID=2292482 - - private class DumpFormatComboBoxRenderer extends BasicComboBoxRenderer { - private JComboBox myMaster; - - public DumpFormatComboBoxRenderer(JComboBox myMaster) { + } + return true; + } + + // We're finished with this modal dialog. + private void closeDialog() + { + dumpDialog.setVisible(false); + dumpDialog.dispose(); + } + + + // Display tool tip for dump format list items. Got the technique from + // http://forum.java.sun.com/thread.jspa?threadID=488762&messageID=2292482 + + private class DumpFormatComboBoxRenderer extends BasicComboBoxRenderer + { + private final JComboBox myMaster; + + public DumpFormatComboBoxRenderer(JComboBox myMaster) + { super(); this.myMaster = myMaster; - } - - public Component getListCellRendererComponent( JList list, Object value, int index, - boolean isSelected, boolean cellHasFocus) { - super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); + } + + public Component getListCellRendererComponent(JList list, Object value, int index, + boolean isSelected, boolean cellHasFocus) + { + super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); setToolTipText(value.toString()); - if (index >=0 && ((DumpFormat)(myMaster.getItemAt(index))).getDescription() != null) { - setToolTipText(((DumpFormat)(myMaster.getItemAt(index))).getDescription()); + if (index >= 0 && ((DumpFormat) (myMaster.getItemAt(index))).getDescription() != null) + { + setToolTipText(((DumpFormat) (myMaster.getItemAt(index))).getDescription()); } return this; - } - } - - - } \ No newline at end of file + } + } + + +} diff --git a/src/main/java/mars/venus/FileExitAction.java b/src/main/java/mars/venus/FileExitAction.java index 5c28821..bfb27fc 100644 --- a/src/main/java/mars/venus/FileExitAction.java +++ b/src/main/java/mars/venus/FileExitAction.java @@ -1,8 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,23 +30,27 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Action for the File -> Exit menu item + */ +public class FileExitAction extends GuiAction +{ + + public FileExitAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + /** - * Action for the File -> Exit menu item - */ - public class FileExitAction extends GuiAction { - - public FileExitAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - /** - * Exit MARS, unless one or more files have unsaved edits and user cancels. - */ - public void actionPerformed(ActionEvent e){ - if (mainUI.editor.closeAll()) { - System.exit(0); - } - } - } + * Exit MARS, unless one or more files have unsaved edits and user cancels. + */ + public void actionPerformed(ActionEvent e) + { + if (mainUI.editor.closeAll()) + { + System.exit(0); + } + } +} diff --git a/src/main/java/mars/venus/FileNewAction.java b/src/main/java/mars/venus/FileNewAction.java index f6a5110..7d3f381 100644 --- a/src/main/java/mars/venus/FileNewAction.java +++ b/src/main/java/mars/venus/FileNewAction.java @@ -1,8 +1,7 @@ - package mars.venus; - import mars.mips.hardware.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,24 +30,27 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Action for the File -> New menu item + */ +public class FileNewAction extends GuiAction +{ + + public FileNewAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + /** - * Action for the File -> New menu item - */ - public class FileNewAction extends GuiAction { - - public FileNewAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - /** - * Code to create a new document. It clears the source code window. - * - * @param e component triggering this call - */ - public void actionPerformed(ActionEvent e) { - mainUI.editor.newFile(); - } - } + * Code to create a new document. It clears the source code window. + * + * @param e component triggering this call + */ + public void actionPerformed(ActionEvent e) + { + mainUI.editor.newFile(); + } +} diff --git a/src/main/java/mars/venus/FileOpenAction.java b/src/main/java/mars/venus/FileOpenAction.java index 3301ba8..a5a0225 100644 --- a/src/main/java/mars/venus/FileOpenAction.java +++ b/src/main/java/mars/venus/FileOpenAction.java @@ -1,13 +1,10 @@ - package mars.venus; - import mars.*; - import mars.util.*; - import java.util.ArrayList; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.filechooser.FileFilter; - import java.io.*; - import java.beans.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.util.ArrayList; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -36,30 +33,37 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Action for the File -> Open menu item + */ +public class FileOpenAction extends GuiAction +{ + + private File mostRecentlyOpenedFile; + + private JFileChooser fileChooser; + + private int fileFilterCount; + + private ArrayList fileFilterList; + + private PropertyChangeListener listenForUserAddedFileFilter; + + public FileOpenAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + /** - * Action for the File -> Open menu item - */ - public class FileOpenAction extends GuiAction { - - private File mostRecentlyOpenedFile; - private JFileChooser fileChooser; - private int fileFilterCount; - private ArrayList fileFilterList; - private PropertyChangeListener listenForUserAddedFileFilter; - - public FileOpenAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - /** - * Launch a file chooser for name of file to open - * - * @param e component triggering this call - */ - public void actionPerformed(ActionEvent e) { - mainUI.editor.open(); - } - - } + * Launch a file chooser for name of file to open + * + * @param e component triggering this call + */ + public void actionPerformed(ActionEvent e) + { + mainUI.editor.open(); + } + +} diff --git a/src/main/java/mars/venus/FilePrintAction.java b/src/main/java/mars/venus/FilePrintAction.java index c191388..379b102 100644 --- a/src/main/java/mars/venus/FilePrintAction.java +++ b/src/main/java/mars/venus/FilePrintAction.java @@ -1,11 +1,10 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; - import java.awt.print.*; - import java.util.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -34,63 +33,75 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Action for the File -> Print menu item + */ +public class FilePrintAction extends GuiAction +{ + + public FilePrintAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + /** - * Action for the File -> Print menu item - */ - public class FilePrintAction extends GuiAction { - - public FilePrintAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - /** Uses the HardcopyWriter class developed by David Flanagan for the book - * "Java Examples in a Nutshell". It will do basic printing of multipage - * text documents. It displays a print dialog but does not act on any - * changes the user may have specified there, such as number of copies. - * - * @param e component triggering this call - */ - - public void actionPerformed(ActionEvent e) { - EditPane editPane = mainUI.getMainPane().getEditPane(); - if (editPane == null) return; - int fontsize = 10; // fixed at 10 point - double margins = .5; // all margins (left,right,top,bottom) fixed at .5" - HardcopyWriter out; - try { - out = new HardcopyWriter(mainUI,editPane.getFilename(), - fontsize, margins, margins, margins, margins); - } - catch (HardcopyWriter.PrintCanceledException pce) { - return; - } - BufferedReader in = new BufferedReader(new StringReader(editPane.getSource())); - int lineNumberDigits = new Integer(editPane.getSourceLineCount()).toString().length(); - String line; - String lineNumberString = ""; - int lineNumber = 0; - int numchars; - try { + * Uses the HardcopyWriter class developed by David Flanagan for the book "Java Examples in a Nutshell". It will do + * basic printing of multipage text documents. It displays a print dialog but does not act on any changes the user + * may have specified there, such as number of copies. + * + * @param e component triggering this call + */ + + public void actionPerformed(ActionEvent e) + { + EditPane editPane = mainUI.getMainPane().getEditPane(); + if (editPane == null) + { + return; + } + int fontsize = 10; // fixed at 10 point + double margins = .5; // all margins (left,right,top,bottom) fixed at .5" + HardcopyWriter out; + try + { + out = new HardcopyWriter(mainUI, editPane.getFilename(), + fontsize, margins, margins, margins, margins); + } + catch (HardcopyWriter.PrintCanceledException pce) + { + return; + } + BufferedReader in = new BufferedReader(new StringReader(editPane.getSource())); + int lineNumberDigits = Integer.valueOf(editPane.getSourceLineCount()).toString().length(); + String line; + String lineNumberString = ""; + int lineNumber = 0; + int numchars; + try + { line = in.readLine(); - while(line != null) { - if (editPane.showingLineNumbers()) { - lineNumber++; - lineNumberString = new Integer(lineNumber).toString() + ": "; - while (lineNumberString.length() < lineNumberDigits) { - lineNumberString = lineNumberString + " "; - } - } - line = lineNumberString + line + "\n"; - out.write(line.toCharArray(), 0, line.length()); - line = in.readLine(); + while (line != null) + { + if (editPane.showingLineNumbers()) + { + lineNumber++; + lineNumberString = Integer.valueOf(lineNumber) + ": "; + while (lineNumberString.length() < lineNumberDigits) + { + lineNumberString = lineNumberString + " "; + } + } + line = lineNumberString + line + "\n"; + out.write(line.toCharArray(), 0, line.length()); + line = in.readLine(); } - in.close( ); - out.close( ); - } - catch (IOException ioe) { - } - return; - } - } + in.close(); + out.close(); + } + catch (IOException ioe) + { + } + } +} diff --git a/src/main/java/mars/venus/FileSaveAction.java b/src/main/java/mars/venus/FileSaveAction.java index ec104fc..ef6e05e 100644 --- a/src/main/java/mars/venus/FileSaveAction.java +++ b/src/main/java/mars/venus/FileSaveAction.java @@ -1,9 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,23 +30,26 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Action for the File -> Save menu item + */ +public class FileSaveAction extends GuiAction +{ + + public FileSaveAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + /** - * Action for the File -> Save menu item - */ - public class FileSaveAction extends GuiAction { - - public FileSaveAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - /** - * saves the file, if not alredy saved it will do a saveAs - */ - - public void actionPerformed(ActionEvent e){ - mainUI.editor.save(); - } - - } + * saves the file, if not alredy saved it will do a saveAs + */ + + public void actionPerformed(ActionEvent e) + { + mainUI.editor.save(); + } + +} diff --git a/src/main/java/mars/venus/FileSaveAllAction.java b/src/main/java/mars/venus/FileSaveAllAction.java index 5bc5575..0dd2583 100644 --- a/src/main/java/mars/venus/FileSaveAllAction.java +++ b/src/main/java/mars/venus/FileSaveAllAction.java @@ -1,8 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,18 +30,21 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the File -> Close All menu item - */ - public class FileSaveAllAction extends GuiAction { - - public FileSaveAllAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e){ - mainUI.editor.saveAll(); - } - } \ No newline at end of file + +/** + * Action for the File -> Close All menu item + */ +public class FileSaveAllAction extends GuiAction +{ + + public FileSaveAllAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + mainUI.editor.saveAll(); + } +} diff --git a/src/main/java/mars/venus/FileSaveAsAction.java b/src/main/java/mars/venus/FileSaveAsAction.java index d6d824b..6ed2374 100644 --- a/src/main/java/mars/venus/FileSaveAsAction.java +++ b/src/main/java/mars/venus/FileSaveAsAction.java @@ -1,9 +1,7 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,19 +30,22 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the File -> Save As menu item - */ - public class FileSaveAsAction extends GuiAction { - - public FileSaveAsAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - - public void actionPerformed(ActionEvent e){ - mainUI.editor.saveAs(); - } - } \ No newline at end of file + +/** + * Action for the File -> Save As menu item + */ +public class FileSaveAsAction extends GuiAction +{ + + public FileSaveAsAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + + public void actionPerformed(ActionEvent e) + { + mainUI.editor.saveAs(); + } +} diff --git a/src/main/java/mars/venus/FileStatus.java b/src/main/java/mars/venus/FileStatus.java index a817f02..92f66d2 100644 --- a/src/main/java/mars/venus/FileStatus.java +++ b/src/main/java/mars/venus/FileStatus.java @@ -1,6 +1,8 @@ - package mars.venus; - import mars.*; - import java.io.*; +package mars.venus; + +import mars.Globals; + +import java.io.File; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -30,297 +32,347 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - /** - * Used to store and return information on the status of the current ASM file that - * is being edited in the program. - * @author Team JSpim - **/ - - public class FileStatus{ - /** initial state or after close */ - public static final int NO_FILE = 0; - /** New edit window with no edits */ - public static final int NEW_NOT_EDITED = 1; - /** New edit window with unsaved edits */ - public static final int NEW_EDITED = 2; - /** open/saved edit window with no edits */ - public static final int NOT_EDITED = 3; - /** open/saved edit window with unsaved edits */ - public static final int EDITED = 4; - /** successful assembly */ - public static final int RUNNABLE = 5; - /** execution is under way */ - public static final int RUNNING = 6; - /** execution terminated */ - public static final int TERMINATED = 7; - /** file is being opened. DPS 9-Aug-2011 */ - public static final int OPENING = 8; - - - /////////////////////////////////////////////////////////////////// - // // - // The static part. Legacy code from original student team's // - // 2003 Practicum project through MARS 3.8, when the editor // - // was limited to one file. The status of that file became // - // the de facto status of the system. Should have used a // - // singleton class but in 2003 did not know what that was! // - // My plan is to phase out all statics but the constants // - // in MARS 4.0 but will keep it in place while at the same time // - // defining non-static members for use by individual files // - // currently opened in the editor. DPS, 9 April 2010. // - // // - /////////////////////////////////////////////////////////////////// - - private static int systemStatus; // set to one of the above - private static boolean systemAssembled; - private static boolean systemSaved; - private static boolean systemEdited; - private static String systemName; - private static File systemFile; - - /** - * Set file status. Also updates menu state accordingly. - * - * @param newStatus New status: EDITED, RUNNABLE, etc, see list above. - */ - public static void set(int newStatus) { - systemStatus = newStatus; - Globals.getGui().setMenuState(systemStatus); - } - - /** - * Get file status - * - * @return file status EDITED, RUNNABLE, etc, see list above - */ - public static int get() { - return systemStatus; - } - - /** - * Changes the value of assenbked to the parameter given. - * @param b boolean variable that tells what to set assembled to. - */ - public static void setAssembled(boolean b){ - systemAssembled= b; - } - - /** - * Changes the value of saved to the parameter given. - * @param b boolean variable that tells what to set saved to . - */ - public static void setSaved(boolean b){ - systemSaved= b; - } - - /** - * Changes the value of edited to the parameter given. - * @param b boolean variable that tells what to set edited to. - */ - public static void setEdited(boolean b){ - systemEdited= b; - } - - /** - * Changes the value of name to the parameter given. - * @param s string variable tells what to set the name of the file to . - */ - public static void setName(String s){ - systemName = s; - } - - /** - * Sets the file to the ASM file passed. - * @param f file object variable that stores the ASM file. - */ - public static void setFile(File f){ - systemFile = f; - } - - /** - * Returns the ASM file. - * @return The ASM file. - */ - public static File getFile(){ - return systemFile; - } - - /** - * Returns the name of the file. - * @return The name of the ASM file. - */ - public static String getName(){ - return systemName; - } - - /** - * Tells whether the file has been assembled. - * @return Boolean value that is true if the ASM file has been assembled. - */ - public static boolean isAssembled(){ - return systemAssembled; - } - - /** - * Tells whether the file has been saved. - * @return Boolean variable that is true if the ASM file has been saved - */ - public static boolean isSaved(){ - return systemSaved; - } - - /** - * Tells whether the file has been edited since it has been saved. - * @return Boolean value that returns true if the ASM file has been edited. - */ - public static boolean isEdited(){ - return systemEdited; - } - - /** - * Resets all the values in FileStatus - */ - public static void reset(){ - systemStatus = NO_FILE; - systemName= ""; - systemAssembled= false; - systemSaved= false; - systemEdited= false; - systemFile= null; - } - - ///////////////////// END OF STATIC PART /////////////////////// - /////////////////////////////////////////////////////////////////// - - - // Remaining members are of instantiable class that can be used by - // every file that is currently open in the editor. - - - private int status; - private File file; - - /** - * Create a FileStatus object with FileStatis.NO_FILE for status and null for file getters. - */ - public FileStatus() { - this(FileStatus.NO_FILE,null); - } - - /** - * Create a FileStatus object with given status and file pathname. - * - * @param status Initial file status. See FileStatus static constants. - * @param pathname Full file pathname. See setPathname(String newPath) below. - */ - public FileStatus(int status, String pathname) { - this.status = status; - if (pathname==null) { +/** + * Used to store and return information on the status of the current ASM file that is being edited in the program. + * + * @author Team JSpim + **/ + +public class FileStatus +{ + /** initial state or after close */ + public static final int NO_FILE = 0; + + /** New edit window with no edits */ + public static final int NEW_NOT_EDITED = 1; + + /** New edit window with unsaved edits */ + public static final int NEW_EDITED = 2; + + /** open/saved edit window with no edits */ + public static final int NOT_EDITED = 3; + + /** open/saved edit window with unsaved edits */ + public static final int EDITED = 4; + + /** successful assembly */ + public static final int RUNNABLE = 5; + + /** execution is under way */ + public static final int RUNNING = 6; + + /** execution terminated */ + public static final int TERMINATED = 7; + + /** file is being opened. DPS 9-Aug-2011 */ + public static final int OPENING = 8; + + + /////////////////////////////////////////////////////////////////// + // // + // The static part. Legacy code from original student team's // + // 2003 Practicum project through MARS 3.8, when the editor // + // was limited to one file. The status of that file became // + // the de facto status of the system. Should have used a // + // singleton class but in 2003 did not know what that was! // + // My plan is to phase out all statics but the constants // + // in MARS 4.0 but will keep it in place while at the same time // + // defining non-static members for use by individual files // + // currently opened in the editor. DPS, 9 April 2010. // + // // + /////////////////////////////////////////////////////////////////// + + private static int systemStatus; // set to one of the above + + private static boolean systemAssembled; + + private static boolean systemSaved; + + private static boolean systemEdited; + + private static String systemName; + + private static File systemFile; + + private int status; + + private File file; + + /** + * Create a FileStatus object with FileStatis.NO_FILE for status and null for file getters. + */ + public FileStatus() + { + this(FileStatus.NO_FILE, null); + } + + /** + * Create a FileStatus object with given status and file pathname. + * + * @param status Initial file status. See FileStatus static constants. + * @param pathname Full file pathname. See setPathname(String newPath) below. + */ + public FileStatus(int status, String pathname) + { + this.status = status; + if (pathname == null) + { this.file = null; - } - else { + } + else + { setPathname(pathname); - } - } - - /** - * Set editing status of this file. See FileStatus static constants. - * - * @param newStatus the new status - */ - public void setFileStatus(int newStatus) { - this.status = newStatus; - } - - /** - * Get editing status of this file. - * - * @return current editing status. See FileStatus static constants. - */ - public int getFileStatus() { - return this.status; - } - - /** - * Determine if file is "new", which means created using New but not yet saved. - * If created using Open, it is not new. - * - * @return true if file was created using New and has not yet been saved, false otherwise. - */ - public boolean isNew() { - return status == FileStatus.NEW_NOT_EDITED || status == FileStatus.NEW_EDITED; - } - - /** - * Determine if file has been modified since last save or, if not yet saved, since - * being created using New or Open. - * - * @return true if file has been modified since save or creation, false otherwise. - */ - public boolean hasUnsavedEdits() { - return status == FileStatus.NEW_EDITED || status == FileStatus.EDITED; - } - - /** - * Set full file pathname. See java.io.File(String pathname) for parameter specs. - * - * @param newPath the new pathname. If no directory path, getParent() will return null. - */ - public void setPathname(String newPath) { - this.file = new File(newPath); - } - - /** - * Set full file pathname. See java.io.File(String parent, String child) for parameter specs. - * - * @param parent the parent directory of the file. If null, getParent() will return null. - * @param name the name of the file (no directory path) - */ - public void setPathname(String parent, String name) { - this.file = new File(parent,name); - } - - /** - * Get full file pathname. See java.io.File.getPath() - * - * @return full pathname as a String. Null if - */ - public String getPathname() { - return (this.file==null) ? null : this.file.getPath(); - } - - /** - * Get file name with no path information. See java.io.File.getName() - * - * @return filename as a String - */ - public String getFilename() { - return (this.file==null) ? null : this.file.getName(); - } - - /** - * Get file parent pathname. See java.io.File.getParent() - * - * @return parent full pathname as a String - */ - public String getParent() { - return (this.file==null) ? null : this.file.getParent(); - } - - - /** - * Update static FileStatus fields with values from this FileStatus object - * To support legacy code that depends on the static. - */ - - public void updateStaticFileStatus() { - systemStatus = this.status; - systemName= this.file.getPath(); - systemAssembled= false; - systemSaved= (status==NOT_EDITED || status==RUNNABLE || status==RUNNING || status==TERMINATED); - systemEdited= (status==NEW_EDITED || status==EDITED); - systemFile= this.file; - - } - - } \ No newline at end of file + } + } + + /** + * Set file status. Also updates menu state accordingly. + * + * @param newStatus New status: EDITED, RUNNABLE, etc, see list above. + */ + public static void set(int newStatus) + { + systemStatus = newStatus; + Globals.getGui().setMenuState(systemStatus); + } + + /** + * Get file status + * + * @return file status EDITED, RUNNABLE, etc, see list above + */ + public static int get() + { + return systemStatus; + } + + /** + * Returns the ASM file. + * + * @return The ASM file. + */ + public static File getFile() + { + return systemFile; + } + + /** + * Sets the file to the ASM file passed. + * + * @param f file object variable that stores the ASM file. + */ + public static void setFile(File f) + { + systemFile = f; + } + + /** + * Returns the name of the file. + * + * @return The name of the ASM file. + */ + public static String getName() + { + return systemName; + } + + /** + * Changes the value of name to the parameter given. + * + * @param s string variable tells what to set the name of the file to . + */ + public static void setName(String s) + { + systemName = s; + } + + /** + * Tells whether the file has been assembled. + * + * @return Boolean value that is true if the ASM file has been assembled. + */ + public static boolean isAssembled() + { + return systemAssembled; + } + + /** + * Changes the value of assenbked to the parameter given. + * + * @param b boolean variable that tells what to set assembled to. + */ + public static void setAssembled(boolean b) + { + systemAssembled = b; + } + + /** + * Tells whether the file has been saved. + * + * @return Boolean variable that is true if the ASM file has been saved + */ + public static boolean isSaved() + { + return systemSaved; + } + + ///////////////////// END OF STATIC PART /////////////////////// + /////////////////////////////////////////////////////////////////// + + + // Remaining members are of instantiable class that can be used by + // every file that is currently open in the editor. + + /** + * Changes the value of saved to the parameter given. + * + * @param b boolean variable that tells what to set saved to . + */ + public static void setSaved(boolean b) + { + systemSaved = b; + } + + /** + * Tells whether the file has been edited since it has been saved. + * + * @return Boolean value that returns true if the ASM file has been edited. + */ + public static boolean isEdited() + { + return systemEdited; + } + + /** + * Changes the value of edited to the parameter given. + * + * @param b boolean variable that tells what to set edited to. + */ + public static void setEdited(boolean b) + { + systemEdited = b; + } + + /** + * Resets all the values in FileStatus + */ + public static void reset() + { + systemStatus = NO_FILE; + systemName = ""; + systemAssembled = false; + systemSaved = false; + systemEdited = false; + systemFile = null; + } + + /** + * Get editing status of this file. + * + * @return current editing status. See FileStatus static constants. + */ + public int getFileStatus() + { + return this.status; + } + + /** + * Set editing status of this file. See FileStatus static constants. + * + * @param newStatus the new status + */ + public void setFileStatus(int newStatus) + { + this.status = newStatus; + } + + /** + * Determine if file is "new", which means created using New but not yet saved. If created using Open, it is not + * new. + * + * @return true if file was created using New and has not yet been saved, false otherwise. + */ + public boolean isNew() + { + return status == FileStatus.NEW_NOT_EDITED || status == FileStatus.NEW_EDITED; + } + + /** + * Determine if file has been modified since last save or, if not yet saved, since being created using New or Open. + * + * @return true if file has been modified since save or creation, false otherwise. + */ + public boolean hasUnsavedEdits() + { + return status == FileStatus.NEW_EDITED || status == FileStatus.EDITED; + } + + /** + * Set full file pathname. See java.io.File(String parent, String child) for parameter specs. + * + * @param parent the parent directory of the file. If null, getParent() will return null. + * @param name the name of the file (no directory path) + */ + public void setPathname(String parent, String name) + { + this.file = new File(parent, name); + } + + /** + * Get full file pathname. See java.io.File.getPath() + * + * @return full pathname as a String. Null if + */ + public String getPathname() + { + return (this.file == null) ? null : this.file.getPath(); + } + + /** + * Set full file pathname. See java.io.File(String pathname) for parameter specs. + * + * @param newPath the new pathname. If no directory path, getParent() will return null. + */ + public void setPathname(String newPath) + { + this.file = new File(newPath); + } + + /** + * Get file name with no path information. See java.io.File.getName() + * + * @return filename as a String + */ + public String getFilename() + { + return (this.file == null) ? null : this.file.getName(); + } + + /** + * Get file parent pathname. See java.io.File.getParent() + * + * @return parent full pathname as a String + */ + public String getParent() + { + return (this.file == null) ? null : this.file.getParent(); + } + + + /** + * Update static FileStatus fields with values from this FileStatus object To support legacy code that depends on + * the static. + */ + + public void updateStaticFileStatus() + { + systemStatus = this.status; + systemName = this.file.getPath(); + systemAssembled = false; + systemSaved = (status == NOT_EDITED || status == RUNNABLE || status == RUNNING || status == TERMINATED); + systemEdited = (status == NEW_EDITED || status == EDITED); + systemFile = this.file; + + } + +} diff --git a/src/main/java/mars/venus/GuiAction.java b/src/main/java/mars/venus/GuiAction.java index a277387..afd3c24 100644 --- a/src/main/java/mars/venus/GuiAction.java +++ b/src/main/java/mars/venus/GuiAction.java @@ -1,6 +1,7 @@ - package mars.venus; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -29,27 +30,30 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * parent class for Action subclasses to be defined for every menu/toolbar - * option. - */ - - public class GuiAction extends AbstractAction { - protected VenusUI mainUI; - - protected GuiAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon); - putValue(SHORT_DESCRIPTION, descrip); - putValue(MNEMONIC_KEY, mnemonic); - putValue(ACCELERATOR_KEY, accel); - mainUI = gui; - } - /** - * does nothing by default. Should be over-ridden by subclass - */ - public void actionPerformed(ActionEvent e) { - - } - } \ No newline at end of file + +/** + * parent class for Action subclasses to be defined for every menu/toolbar option. + */ + +public class GuiAction extends AbstractAction +{ + protected VenusUI mainUI; + + protected GuiAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon); + putValue(SHORT_DESCRIPTION, descrip); + putValue(MNEMONIC_KEY, mnemonic); + putValue(ACCELERATOR_KEY, accel); + mainUI = gui; + } + + /** + * does nothing by default. Should be over-ridden by subclass + */ + public void actionPerformed(ActionEvent e) + { + + } +} diff --git a/src/main/java/mars/venus/HardcopyWriter.java b/src/main/java/mars/venus/HardcopyWriter.java index 7727109..3cce962 100644 --- a/src/main/java/mars/venus/HardcopyWriter.java +++ b/src/main/java/mars/venus/HardcopyWriter.java @@ -1,322 +1,427 @@ /* HardcopyWriter class comes from the book "Java Examples in a Nutshell, - * 3rd Edition" by David Flanagan. Publisher is O'Reilly, ISBN is - * 0-596-00620-9. Published Jan 2004. Web page for the book is: + * 3rd Edition" by David Flanagan. Publisher is O'Reilly, ISBN is + * 0-596-00620-9. Published Jan 2004. Web page for the book is: * http://www.oreilly.com/catalog/jenut3/ - * + * * Concerning my use of their code, the O'Reilly policy is described at: * http://www.oreilly.com/pub/a/oreilly/ask_tim/2001/codepolicy.html * * Here is a quote copied from that web page: * - * "What is our policy with regard to programmers incorporating code - * examples from books into their work? I get asked this all the time." - * The short answer is this: - * You can use and redistribute example code from our books for any - * non-commercial purpose (and most commercial purposes) as long as + * "What is our policy with regard to programmers incorporating code + * examples from books into their work? I get asked this all the time." + * The short answer is this: + * You can use and redistribute example code from our books for any + * non-commercial purpose (and most commercial purposes) as long as * you acknowledge their source and authorship. The source of the code - * should be noted in any documentation as well as in the program code - * itself (as a comment). The attribution should include author, title, - * publisher, and ISBN. + * should be noted in any documentation as well as in the program code + * itself (as a comment). The attribution should include author, title, + * publisher, and ISBN. * * I have made some minor adjustments to the code to fit my needs in - * regards to font sizing and spacing. This code will be used to print - * multi-page plain text documents, specifically listings of MIPS assembler + * regards to font sizing and spacing. This code will be used to print + * multi-page plain text documents, specifically listings of MIPS assembler * source code. I am extremely grateful for this generous code use policy! * * Pete Sanderson, August 2004 */ - package mars.venus; - import java.awt.*; - import java.awt.event.*; - import java.io.*; - import java.text.*; - import java.util.*; +package mars.venus; + +import java.awt.*; +import java.io.FileReader; +import java.io.Writer; +import java.text.DateFormat; +import java.util.Date; +import java.util.Properties; +import java.util.TimeZone; /* HardcopyWriter class from the book "Java Examples in a Nutshell, - * 3rd Edition" by David Flanagan. Publisher is O'Reilly, ISBN is - * 0-596-00620-9. Published Jan 2004. Web page for the book is: + * 3rd Edition" by David Flanagan. Publisher is O'Reilly, ISBN is + * 0-596-00620-9. Published Jan 2004. Web page for the book is: * http://www.oreilly.com/catalog/jenut3/ * * A character output stream that sends output to a printer. I made only * a couple minor changes -- Pete Sanderson **/ - public class HardcopyWriter extends Writer { - // These are the instance variables for the class - protected PrintJob job; // The PrintJob object in use - protected Graphics page; // Graphics object for current page - protected String jobname; // The name of the print job - protected int fontsize; // Point size of the font - protected String time; // Current time (appears in header) - protected Dimension pagesize; // Size of the page (in dots) - protected int pagedpi; // Page resolution in dots per inch - protected Font font, headerfont; // Body font and header font - protected FontMetrics metrics; // Metrics for the body font - protected FontMetrics headermetrics; // Metrics for the header font - protected int x0, y0; // Upper-left corner inside margin - protected int width, height; // Size (in dots) inside margins - protected int headery; // Baseline of the page header - protected int charwidth; // The width of each character - protected int lineheight; // The height of each line - protected int lineascent; // Offset of font baseline - protected int chars_per_line; // Number of characters per line - protected int lines_per_page; // Number of lines per page - protected int chars_per_tab = 4; // Added by Pete Sanderson 8-17-04 - protected int charnum = 0, linenum = 0; // Current column and line position - protected int pagenum = 0; // Current page number - // A field to save state between invocations of the write( ) method - private boolean last_char_was_return = false; - // A static variable that holds user preferences between print jobs - protected static Properties printprops = new Properties( ); - /** - * The constructor for this class has a bunch of arguments: - * The frame argument is required for all printing in Java. - * The jobname appears left justified at the top of each printed page. - * The font size is specified in points, as on-screen font sizes are. - * The margins are specified in inches (or fractions of inches). - **/ - public HardcopyWriter(Frame frame, String jobname, int fontsize, - double leftmargin, double rightmargin, - double topmargin, double bottommargin) - throws HardcopyWriter.PrintCanceledException - { - // Get the PrintJob object with which we'll do all the printing. - // The call is synchronized on the static printprops object, which - // means that only one print dialog can be popped up at a time. - // If the user clicks Cancel in the print dialog, throw an exception. - Toolkit toolkit = frame.getToolkit( ); // get Toolkit from Frame - synchronized(printprops) { +public class HardcopyWriter extends Writer +{ + // A static variable that holds user preferences between print jobs + protected static Properties printprops = new Properties(); + + // These are the instance variables for the class + protected PrintJob job; // The PrintJob object in use + + protected Graphics page; // Graphics object for current page + + protected String jobname; // The name of the print job + + protected int fontsize; // Point size of the font + + protected String time; // Current time (appears in header) + + protected Dimension pagesize; // Size of the page (in dots) + + protected int pagedpi; // Page resolution in dots per inch + + protected Font font, headerfont; // Body font and header font + + protected FontMetrics metrics; // Metrics for the body font + + protected FontMetrics headermetrics; // Metrics for the header font + + protected int x0, y0; // Upper-left corner inside margin + + protected int width, height; // Size (in dots) inside margins + + protected int headery; // Baseline of the page header + + protected int charwidth; // The width of each character + + protected int lineheight; // The height of each line + + protected int lineascent; // Offset of font baseline + + protected int chars_per_line; // Number of characters per line + + protected int lines_per_page; // Number of lines per page + + protected int chars_per_tab = 4; // Added by Pete Sanderson 8-17-04 + + protected int charnum = 0, linenum = 0; // Current column and line position + + protected int pagenum = 0; // Current page number + + // A field to save state between invocations of the write( ) method + private boolean last_char_was_return = false; + + /** + * The constructor for this class has a bunch of arguments: The frame argument is required for all printing in Java. + * The jobname appears left justified at the top of each printed page. The font size is specified in points, as + * on-screen font sizes are. The margins are specified in inches (or fractions of inches). + **/ + public HardcopyWriter(Frame frame, String jobname, int fontsize, + double leftmargin, double rightmargin, + double topmargin, double bottommargin) + throws HardcopyWriter.PrintCanceledException + { + // Get the PrintJob object with which we'll do all the printing. + // The call is synchronized on the static printprops object, which + // means that only one print dialog can be popped up at a time. + // If the user clicks Cancel in the print dialog, throw an exception. + Toolkit toolkit = frame.getToolkit(); // get Toolkit from Frame + synchronized (printprops) + { //job = toolkit.getPrintJob(frame, jobname, printprops); - //******************************************* - // SANDERSON MOD 8-17-2004: - // Currently we will ignore user specifications from Print dialog - // such as page ranges and number of copies. But ja and pa can be - // queried to get and act upon them (in future release). + //******************************************* + // SANDERSON MOD 8-17-2004: + // Currently we will ignore user specifications from Print dialog + // such as page ranges and number of copies. But ja and pa can be + // queried to get and act upon them (in future release). JobAttributes ja = new JobAttributes(); PageAttributes pa = new PageAttributes(); job = toolkit.getPrintJob(frame, jobname, ja, pa); - //******************************************* - } - if (job == null) + //******************************************* + } + if (job == null) + { throw new PrintCanceledException("User cancelled print request"); - /******************************************************* - SANDERSON OVERRIDE 8-17-2004: - I didn't like the results produced by the code below, so am commenting - it out and just setting pagedpi to 72. This assures, among other things, - that the client asking for 10 point font will really get 10 point font! - - - pagesize = job.getPageDimension( ); // query the page size - pagedpi = job.getPageResolution( ); // query the page resolution - // Bug Workaround: - // On Windows, getPageDimension( ) and getPageResolution don't work, so - // we've got to fake them. + } + /******************************************************* + SANDERSON OVERRIDE 8-17-2004: + I didn't like the results produced by the code below, so am commenting + it out and just setting pagedpi to 72. This assures, among other things, + that the client asking for 10 point font will really get 10 point font! + + + pagesize = job.getPageDimension( ); // query the page size + pagedpi = job.getPageResolution( ); // query the page resolution + // Bug Workaround: + // On Windows, getPageDimension( ) and getPageResolution don't work, so + // we've got to fake them. if (System.getProperty("os.name").regionMatches(true,0,"windows",0,7)){ // Use screen dpi, which is what the PrintJob tries to emulate - pagedpi = toolkit.getScreenResolution( ); + pagedpi = toolkit.getScreenResolution( ); // Assume a 8.5" x 11" page size. A4 paper users must change this. - pagesize = new Dimension((int)(8.5 * pagedpi), 11*pagedpi); + pagesize = new Dimension((int)(8.5 * pagedpi), 11*pagedpi); // We also have to adjust the fontsize. It is specified in points, // (1 point = 1/72 of an inch) but Windows measures it in pixels. - fontsize = fontsize * pagedpi / 72; - } - ***********************************/ - - pagedpi = 72; - pagesize = new Dimension((int)(8.5 * pagedpi), 11*pagedpi); fontsize = fontsize * pagedpi / 72; - - - // Compute coordinates of the upper-left corner of the page. - // I.e. the coordinates of (leftmargin, topmargin). Also compute - // the width and height inside of the margins. - x0 = (int)(leftmargin * pagedpi); - y0 = (int)(topmargin * pagedpi); - width = pagesize.width - (int)((leftmargin + rightmargin) * pagedpi); - height = pagesize.height - (int)((topmargin + bottommargin) * pagedpi); - // Get body font and font size - font = new Font("Monospaced", Font.PLAIN, fontsize); - metrics = frame.getFontMetrics(font); - lineheight = metrics.getHeight( ); - lineascent = metrics.getAscent( ); - charwidth = metrics.charWidth('0'); // Assumes a monospaced font! - // Now compute the number of columns and lines - // that will fit inside the margins - chars_per_line = width / charwidth; - lines_per_page = height / lineheight; - // Get header font information - // And compute baseline of page header: 1/8" above the top margin - headerfont = new Font("SansSerif", Font.ITALIC, fontsize); - headermetrics = frame.getFontMetrics(headerfont); - headery = y0 - (int)(0.125 * pagedpi) - - headermetrics.getHeight( ) + headermetrics.getAscent( ); - // Compute the date/time string to display in the page header - DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, + } + ***********************************/ + + pagedpi = 72; + pagesize = new Dimension((int) (8.5 * pagedpi), 11 * pagedpi); + fontsize = fontsize * pagedpi / 72; + + + // Compute coordinates of the upper-left corner of the page. + // I.e. the coordinates of (leftmargin, topmargin). Also compute + // the width and height inside of the margins. + x0 = (int) (leftmargin * pagedpi); + y0 = (int) (topmargin * pagedpi); + width = pagesize.width - (int) ((leftmargin + rightmargin) * pagedpi); + height = pagesize.height - (int) ((topmargin + bottommargin) * pagedpi); + // Get body font and font size + font = new Font("Monospaced", Font.PLAIN, fontsize); + metrics = frame.getFontMetrics(font); + lineheight = metrics.getHeight(); + lineascent = metrics.getAscent(); + charwidth = metrics.charWidth('0'); // Assumes a monospaced font! + // Now compute the number of columns and lines + // that will fit inside the margins + chars_per_line = width / charwidth; + lines_per_page = height / lineheight; + // Get header font information + // And compute baseline of page header: 1/8" above the top margin + headerfont = new Font("SansSerif", Font.ITALIC, fontsize); + headermetrics = frame.getFontMetrics(headerfont); + headery = y0 - (int) (0.125 * pagedpi) - + headermetrics.getHeight() + headermetrics.getAscent(); + // Compute the date/time string to display in the page header + DateFormat df = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.SHORT); - df.setTimeZone(TimeZone.getDefault( )); - time = df.format(new Date( )); - this.jobname = jobname; // save name - this.fontsize = fontsize; // save font size - } - /** - * This is the write( ) method of the stream. All Writer subclasses - * implement this. All other versions of write( ) are variants of this one - **/ - public void write(char[ ] buffer, int index, int len) { - synchronized(this.lock) { // For thread safety - // Loop through all the characters passed to us - for(int i = index; i < index + len; i++) { - // If we haven't begun a page (or a new page), do that now. - if (page == null) newpage( ); - // If the character is a line terminator, then begin new line, - // unless it is a \n immediately after a \r. - if (buffer[i] == '\n') { - if (!last_char_was_return) newline( ); - continue; - } - if (buffer[i] == '\r') { - newline( ); - last_char_was_return = true; - continue; - } - else last_char_was_return = false; - // If it's some other non-printing character, ignore it. - if (Character.isWhitespace(buffer[i]) && - !Character.isSpaceChar(buffer[i]) && (buffer[i] != '\t')) - continue; - // If no more characters will fit on the line, start new line. - if (charnum >= chars_per_line) { - newline( ); - // Also start a new page, if necessary - if (page == null) newpage( ); - } - // Now print the character: - // If it is a space, skip one space, without output. - // If it is a tab, skip the necessary number of spaces. - // Otherwise, print the character. - // It is inefficient to draw only one character at a time, but - // because our FontMetrics don't match up exactly to what the - // printer uses, we need to position each character individually - if (Character.isSpaceChar(buffer[i])) charnum++; - else if (buffer[i] == '\t') charnum += chars_per_tab - (charnum % chars_per_tab); - else { - page.drawChars(buffer, i, 1, - x0 + charnum * charwidth, - y0 + (linenum*lineheight) + lineascent); - charnum++; - } - } - } - } - /** - * This is the flush( ) method that all Writer subclasses must implement. - * There is no way to flush a PrintJob without prematurely printing the - * page, so we don't do anything. - **/ - public void flush( ) { /* do nothing */ } - /** - * This is the close( ) method that all Writer subclasses must implement. - * Print the pending page (if any) and terminate the PrintJob. - */ - public void close( ) { - synchronized(this.lock) { - if (page != null) page.dispose( ); // Send page to the printer - job.end( ); // Terminate the job - } - } - /** - * Set the font style. The argument should be one of the font style - * constants defined by the java.awt.Font class. All subsequent output - * will be in that style. This method relies on all styles of the - * Monospaced font having the same metrics. - **/ - public void setFontStyle(int style) { - synchronized (this.lock) { - // Try to set a new font, but restore current one if it fails - Font current = font; - try { font = new Font("Monospaced", style, fontsize); } - catch (Exception e) { font = current; } - // If a page is pending, set the new font. Otherwise newpage( ) will - if (page != null) page.setFont(font); - } - } - /** End the current page. Subsequent output will be on a new page. */ - public void pageBreak( ) { - synchronized(this.lock) { newpage( ); } } - /** Return the number of columns of characters that fit on the page */ - public int getCharactersPerLine( ) { - return this.chars_per_line; } - /** Return the number of lines that fit on a page */ - public int getLinesPerPage( ) { - return this.lines_per_page; } - /** This internal method begins a new line */ - protected void newline( ) { - charnum = 0; // Reset character number to 0 - linenum++; // Increment line number - if (linenum >= lines_per_page) { // If we've reached the end of page - page.dispose( ); // send page to printer - page = null; // but don't start a new page yet. - } - } - /** This internal method begins a new page and prints the header. */ - protected void newpage( ) { - page = job.getGraphics( ); // Begin the new page - linenum = 0; charnum = 0; // Reset line and char number - pagenum++; // Increment page number - page.setFont(headerfont); // Set the header font. - page.drawString(jobname, x0, headery); // Print job name left justified - String s = "- " + pagenum + " -"; // Print the page # centered. - int w = headermetrics.stringWidth(s); - page.drawString(s, x0 + (this.width - w)/2, headery); - w = headermetrics.stringWidth(time); // Print date right justified - page.drawString(time, x0 + width - w, headery); - // Draw a line beneath the header - int y = headery + headermetrics.getDescent( ) + 1; - page.drawLine(x0, y, x0+width, y); - // Set the basic monospaced font for the rest of the page. - page.setFont(font); - } - /** - * This is the exception class that the HardcopyWriter constructor - * throws when the user clicks "Cancel" in the print dialog box. - **/ - public static class PrintCanceledException extends Exception { - public PrintCanceledException(String msg) { super(msg); } - } - - - /** - * A program that prints the specified file using HardcopyWriter - **/ - - public static void main(String[ ] args) { - try { + df.setTimeZone(TimeZone.getDefault()); + time = df.format(new Date()); + this.jobname = jobname; // save name + this.fontsize = fontsize; // save font size + } + + /** + * A program that prints the specified file using HardcopyWriter + **/ + + public static void main(String[] args) + { + try + { if (args.length != 1) - throw new IllegalArgumentException("Wrong # of arguments"); + { + throw new IllegalArgumentException("Wrong # of arguments"); + } FileReader in = new FileReader(args[0]); HardcopyWriter out = null; Frame f = new Frame("PrintFile: " + args[0]); f.setSize(200, 50); f.setVisible(true); - try { - out = new HardcopyWriter(f, args[0], 10, .5, .5, .5, .5); + try + { + out = new HardcopyWriter(f, args[0], 10, .5, .5, .5, .5); + } + catch (HardcopyWriter.PrintCanceledException e) + { + System.exit(0); } - catch (HardcopyWriter.PrintCanceledException e) { - System.exit(0); - } f.setVisible(false); - char[ ] buffer = new char[4096]; + char[] buffer = new char[4096]; int numchars; - while((numchars = in.read(buffer)) != -1) - out.write(buffer, 0, numchars); - in.close( ); - out.close( ); - } - catch (Exception e) { - System.err.println(e); - System.err.println("Usage: " + - "java HardcopyWriter$PrintFile "); - System.exit(1); + while ((numchars = in.read(buffer)) != -1) + { + out.write(buffer, 0, numchars); } - System.exit(0); - } - } \ No newline at end of file + in.close(); + out.close(); + } + catch (Exception e) + { + System.err.println(e); + System.err.println("Usage: " + + "java HardcopyWriter$PrintFile "); + System.exit(1); + } + System.exit(0); + } + + /** + * This is the write( ) method of the stream. All Writer subclasses implement this. All other versions of write( ) + * are variants of this one + **/ + public void write(char[] buffer, int index, int len) + { + synchronized (this.lock) + { // For thread safety + // Loop through all the characters passed to us + for (int i = index; i < index + len; i++) + { + // If we haven't begun a page (or a new page), do that now. + if (page == null) + { + newpage(); + } + // If the character is a line terminator, then begin new line, + // unless it is a \n immediately after a \r. + if (buffer[i] == '\n') + { + if (!last_char_was_return) + { + newline(); + } + continue; + } + if (buffer[i] == '\r') + { + newline(); + last_char_was_return = true; + continue; + } + else + { + last_char_was_return = false; + } + // If it's some other non-printing character, ignore it. + if (Character.isWhitespace(buffer[i]) && + !Character.isSpaceChar(buffer[i]) && (buffer[i] != '\t')) + { + continue; + } + // If no more characters will fit on the line, start new line. + if (charnum >= chars_per_line) + { + newline(); + // Also start a new page, if necessary + if (page == null) + { + newpage(); + } + } + // Now print the character: + // If it is a space, skip one space, without output. + // If it is a tab, skip the necessary number of spaces. + // Otherwise, print the character. + // It is inefficient to draw only one character at a time, but + // because our FontMetrics don't match up exactly to what the + // printer uses, we need to position each character individually + if (Character.isSpaceChar(buffer[i])) + { + charnum++; + } + else if (buffer[i] == '\t') + { + charnum += chars_per_tab - (charnum % chars_per_tab); + } + else + { + page.drawChars(buffer, i, 1, + x0 + charnum * charwidth, + y0 + (linenum * lineheight) + lineascent); + charnum++; + } + } + } + } + + /** + * This is the flush( ) method that all Writer subclasses must implement. There is no way to flush a PrintJob + * without prematurely printing the page, so we don't do anything. + **/ + public void flush() + { /* do nothing */ } + + /** + * This is the close( ) method that all Writer subclasses must implement. Print the pending page (if any) and + * terminate the PrintJob. + */ + public void close() + { + synchronized (this.lock) + { + if (page != null) + { + page.dispose(); // Send page to the printer + } + job.end(); // Terminate the job + } + } + + /** + * Set the font style. The argument should be one of the font style constants defined by the java.awt.Font class. + * All subsequent output will be in that style. This method relies on all styles of the Monospaced font having the + * same metrics. + **/ + public void setFontStyle(int style) + { + synchronized (this.lock) + { + // Try to set a new font, but restore current one if it fails + Font current = font; + try + { + font = new Font("Monospaced", style, fontsize); + } + catch (Exception e) + { + font = current; + } + // If a page is pending, set the new font. Otherwise newpage( ) will + if (page != null) + { + page.setFont(font); + } + } + } + + /** End the current page. Subsequent output will be on a new page. */ + public void pageBreak() + { + synchronized (this.lock) + { + newpage(); + } + } + + /** Return the number of columns of characters that fit on the page */ + public int getCharactersPerLine() + { + return this.chars_per_line; + } + + /** Return the number of lines that fit on a page */ + public int getLinesPerPage() + { + return this.lines_per_page; + } + + /** This internal method begins a new line */ + protected void newline() + { + charnum = 0; // Reset character number to 0 + linenum++; // Increment line number + if (linenum >= lines_per_page) + { // If we've reached the end of page + page.dispose(); // send page to printer + page = null; // but don't start a new page yet. + } + } + + /** This internal method begins a new page and prints the header. */ + protected void newpage() + { + page = job.getGraphics(); // Begin the new page + linenum = 0; + charnum = 0; // Reset line and char number + pagenum++; // Increment page number + page.setFont(headerfont); // Set the header font. + page.drawString(jobname, x0, headery); // Print job name left justified + String s = "- " + pagenum + " -"; // Print the page # centered. + int w = headermetrics.stringWidth(s); + page.drawString(s, x0 + (this.width - w) / 2, headery); + w = headermetrics.stringWidth(time); // Print date right justified + page.drawString(time, x0 + width - w, headery); + // Draw a line beneath the header + int y = headery + headermetrics.getDescent() + 1; + page.drawLine(x0, y, x0 + width, y); + // Set the basic monospaced font for the rest of the page. + page.setFont(font); + } + + /** + * This is the exception class that the HardcopyWriter constructor throws when the user clicks "Cancel" in the print + * dialog box. + **/ + public static class PrintCanceledException extends Exception + { + public PrintCanceledException(String msg) + { + super(msg); + } + } +} diff --git a/src/main/java/mars/venus/HelpAboutAction.java b/src/main/java/mars/venus/HelpAboutAction.java index fea54a0..4d178ab 100644 --- a/src/main/java/mars/venus/HelpAboutAction.java +++ b/src/main/java/mars/venus/HelpAboutAction.java @@ -1,8 +1,9 @@ - package mars.venus; - import mars.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import mars.Globals; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,31 +32,34 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the Help -> About menu item - */ - public class HelpAboutAction extends GuiAction { - public HelpAboutAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e){ - JOptionPane.showMessageDialog(mainUI, - "MARS "+Globals.version+" Copyright "+Globals.copyrightYears+"\n"+ - Globals.copyrightHolders+"\n"+ - "MARS is the Mips Assembler and Runtime Simulator.\n\n"+ - "Mars image courtesy of NASA/JPL.\n"+ - "Toolbar and menu icons are from:\n"+ - " * Tango Desktop Project (tango.freedesktop.org),\n"+ - " * glyFX (www.glyfx.com) Common Toolbar Set,\n"+ - " * KDE-Look (www.kde-look.org) crystalline-blue-0.1,\n"+ - " * Icon-King (www.icon-king.com) Nuvola 1.0.\n"+ - "Print feature adapted from HardcopyWriter class in David Flanagan's\n"+ - "Java Examples in a Nutshell 3rd Edition, O'Reilly, ISBN 0-596-00620-9.", - "About Mars", - JOptionPane.INFORMATION_MESSAGE, - new ImageIcon("images/RedMars50.gif")); - } - } \ No newline at end of file + +/** + * Action for the Help -> About menu item + */ +public class HelpAboutAction extends GuiAction +{ + public HelpAboutAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + JOptionPane.showMessageDialog(mainUI, + "MARS " + Globals.version + " Copyright " + Globals.copyrightYears + "\n" + + Globals.copyrightHolders + "\n" + + "MARS is the Mips Assembler and Runtime Simulator.\n\n" + + "Mars image courtesy of NASA/JPL.\n" + + "Toolbar and menu icons are from:\n" + + " * Tango Desktop Project (tango.freedesktop.org),\n" + + " * glyFX (www.glyfx.com) Common Toolbar Set,\n" + + " * KDE-Look (www.kde-look.org) crystalline-blue-0.1,\n" + + " * Icon-King (www.icon-king.com) Nuvola 1.0.\n" + + "Print feature adapted from HardcopyWriter class in David Flanagan's\n" + + "Java Examples in a Nutshell 3rd Edition, O'Reilly, ISBN 0-596-00620-9.", + "About Mars", + JOptionPane.INFORMATION_MESSAGE, + new ImageIcon("images/RedMars50.gif")); + } +} diff --git a/src/main/java/mars/venus/HelpHelpAction.java b/src/main/java/mars/venus/HelpHelpAction.java index 906566b..ffa226e 100644 --- a/src/main/java/mars/venus/HelpHelpAction.java +++ b/src/main/java/mars/venus/HelpHelpAction.java @@ -1,14 +1,26 @@ - package mars.venus; - import mars.*; - import mars.assembler.*; - import mars.mips.instructions.*; - import java.util.*; - import java.io.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.event.*; - import javax.swing.text.html.*; +package mars.venus; + +import mars.Globals; +import mars.assembler.Directives; +import mars.mips.instructions.Instruction; + +import javax.swing.*; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.html.HTMLDocument; +import javax.swing.text.html.HTMLFrameHyperlinkEvent; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.Vector; /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -37,409 +49,455 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the Help -> Help menu item - */ - public class HelpHelpAction extends GuiAction { - public HelpHelpAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - // ideally read or computed from config file... - private Dimension getSize() { - return new Dimension(800,600); - } - // Light gray background color for alternating lines of the instruction lists - static Color altBackgroundColor = new Color(0xEE,0xEE,0xEE); - - /** - * Separates Instruction name descriptor from detailed (operation) description - * in help string. - */ - public static final String descriptionDetailSeparator = ":"; - - /** - * Displays tabs with categories of information - */ - public void actionPerformed(ActionEvent e) { - JTabbedPane tabbedPane = new JTabbedPane(); - tabbedPane.addTab("MIPS", createMipsHelpInfoPanel()); - tabbedPane.addTab("MARS", createMarsHelpInfoPanel()); - tabbedPane.addTab("License", createCopyrightInfoPanel()); - tabbedPane.addTab("Bugs/Comments", createHTMLHelpPanel("BugReportingHelp.html")); - tabbedPane.addTab("Acknowledgements", createHTMLHelpPanel("Acknowledgements.html")); - tabbedPane.addTab("Instruction Set Song", createHTMLHelpPanel("MIPSInstructionSetSong.html")); - // Create non-modal dialog. Based on java.sun.com "How to Make Dialogs", DialogDemo.java - final JDialog dialog = new JDialog(mainUI, "MARS "+Globals.version+" Help"); - // assure the dialog goes away if user clicks the X - dialog.addWindowListener( - new WindowAdapter() { - public void windowClosing(WindowEvent e) { - dialog.setVisible(false); - dialog.dispose(); - } - }); - //Add a "close" button to the non-modal help dialog. - JButton closeButton = new JButton("Close"); - closeButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - dialog.setVisible(false); - dialog.dispose(); - } - }); - JPanel closePanel = new JPanel(); - closePanel.setLayout(new BoxLayout(closePanel,BoxLayout.LINE_AXIS)); - closePanel.add(Box.createHorizontalGlue()); - closePanel.add(closeButton); - closePanel.add(Box.createHorizontalGlue()); - closePanel.setBorder(BorderFactory.createEmptyBorder(0,0,5,5)); - JPanel contentPane = new JPanel(); - contentPane.setLayout(new BoxLayout(contentPane,BoxLayout.PAGE_AXIS)); - contentPane.add(tabbedPane); - contentPane.add(Box.createRigidArea(new Dimension(0,5))); - contentPane.add(closePanel); - contentPane.setOpaque(true); - dialog.setContentPane(contentPane); - //Show it. - dialog.setSize(this.getSize()); - dialog.setLocationRelativeTo(mainUI); - dialog.setVisible(true); - - ////////////////////////////////////////////////////////////////// - } - - - // Create panel containing Help Info read from html document. - private JPanel createHTMLHelpPanel(String filename) { - JPanel helpPanel = new JPanel(new BorderLayout()); - JScrollPane helpScrollPane; - JEditorPane helpDisplay; - try { - InputStream is = this.getClass().getResourceAsStream(Globals.helpPath+filename); - BufferedReader in = new BufferedReader(new InputStreamReader(is)); +/** + * Action for the Help -> Help menu item + */ +public class HelpHelpAction extends GuiAction +{ + /** + * Separates Instruction name descriptor from detailed (operation) description in help string. + */ + public static final String descriptionDetailSeparator = ":"; + + // Light gray background color for alternating lines of the instruction lists + static Color altBackgroundColor = new Color(0xEE, 0xEE, 0xEE); + + public HelpHelpAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + // ideally read or computed from config file... + private Dimension getSize() + { + return new Dimension(800, 600); + } + + /** + * Displays tabs with categories of information + */ + public void actionPerformed(ActionEvent e) + { + JTabbedPane tabbedPane = new JTabbedPane(); + tabbedPane.addTab("MIPS", createMipsHelpInfoPanel()); + tabbedPane.addTab("MARS", createMarsHelpInfoPanel()); + tabbedPane.addTab("License", createCopyrightInfoPanel()); + tabbedPane.addTab("Bugs/Comments", createHTMLHelpPanel("BugReportingHelp.html")); + tabbedPane.addTab("Acknowledgements", createHTMLHelpPanel("Acknowledgements.html")); + tabbedPane.addTab("Instruction Set Song", createHTMLHelpPanel("MIPSInstructionSetSong.html")); + // Create non-modal dialog. Based on java.sun.com "How to Make Dialogs", DialogDemo.java + final JDialog dialog = new JDialog(mainUI, "MARS " + Globals.version + " Help"); + // assure the dialog goes away if user clicks the X + dialog.addWindowListener( + new WindowAdapter() + { + public void windowClosing(WindowEvent e) + { + dialog.setVisible(false); + dialog.dispose(); + } + }); + //Add a "close" button to the non-modal help dialog. + JButton closeButton = new JButton("Close"); + closeButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + dialog.setVisible(false); + dialog.dispose(); + } + }); + JPanel closePanel = new JPanel(); + closePanel.setLayout(new BoxLayout(closePanel, BoxLayout.LINE_AXIS)); + closePanel.add(Box.createHorizontalGlue()); + closePanel.add(closeButton); + closePanel.add(Box.createHorizontalGlue()); + closePanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 5)); + JPanel contentPane = new JPanel(); + contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.PAGE_AXIS)); + contentPane.add(tabbedPane); + contentPane.add(Box.createRigidArea(new Dimension(0, 5))); + contentPane.add(closePanel); + contentPane.setOpaque(true); + dialog.setContentPane(contentPane); + //Show it. + dialog.setSize(this.getSize()); + dialog.setLocationRelativeTo(mainUI); + dialog.setVisible(true); + + ////////////////////////////////////////////////////////////////// + } + + + // Create panel containing Help Info read from html document. + private JPanel createHTMLHelpPanel(String filename) + { + JPanel helpPanel = new JPanel(new BorderLayout()); + JScrollPane helpScrollPane; + JEditorPane helpDisplay; + try + { + InputStream is = this.getClass().getResourceAsStream(Globals.helpPath + filename); + BufferedReader in = new BufferedReader(new InputStreamReader(is)); String line; StringBuffer text = new StringBuffer(); - while ( (line=in.readLine()) != null ) { - text.append(line+"\n"); + while ((line = in.readLine()) != null) + { + text.append(line + "\n"); } in.close(); - helpDisplay = new JEditorPane("text/html",text.toString()); + helpDisplay = new JEditorPane("text/html", text.toString()); helpDisplay.setEditable(false); helpDisplay.setCaretPosition(0); // assure top of document displayed helpScrollPane = new JScrollPane(helpDisplay, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); helpDisplay.addHyperlinkListener(new HelpHyperlinkListener()); - } - catch (Exception ie) { - helpScrollPane = new JScrollPane( - new JLabel("Error ("+ie+"): "+filename+" contents could not be loaded.")); - } - helpPanel.add(helpScrollPane); - return helpPanel; - } - - - // Set up the copyright notice for display. - private JPanel createCopyrightInfoPanel() { - JPanel marsCopyrightInfo = new JPanel(new BorderLayout()); - JScrollPane marsCopyrightScrollPane; - JEditorPane marsCopyrightDisplay; - try { + } + catch (Exception ie) + { + helpScrollPane = new JScrollPane( + new JLabel("Error (" + ie + "): " + filename + " contents could not be loaded.")); + } + helpPanel.add(helpScrollPane); + return helpPanel; + } + + + // Set up the copyright notice for display. + private JPanel createCopyrightInfoPanel() + { + JPanel marsCopyrightInfo = new JPanel(new BorderLayout()); + JScrollPane marsCopyrightScrollPane; + JEditorPane marsCopyrightDisplay; + try + { InputStream is = this.getClass().getResourceAsStream("/MARSlicense.txt"); BufferedReader in = new BufferedReader(new InputStreamReader(is)); String line; StringBuffer text = new StringBuffer("

");
-            while ( (line=in.readLine()) != null ) {
-               text.append(line+"\n");
+            while ((line = in.readLine()) != null)
+            {
+                text.append(line + "\n");
             }
             in.close();
-            text.append("
"); - marsCopyrightDisplay = new JEditorPane("text/html",text.toString()); + text.append(""); + marsCopyrightDisplay = new JEditorPane("text/html", text.toString()); marsCopyrightDisplay.setEditable(false); marsCopyrightDisplay.setCaretPosition(0); // assure top of document displayed marsCopyrightScrollPane = new JScrollPane(marsCopyrightDisplay, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - } - catch (Exception ioe) { - marsCopyrightScrollPane = new JScrollPane( - new JLabel("Error: license contents could not be loaded.")); - } - marsCopyrightInfo.add(marsCopyrightScrollPane); - return marsCopyrightInfo; - } - - // Set up MARS help tab. Subtabs get their contents from HTML files. - private JPanel createMarsHelpInfoPanel() { - JPanel marsHelpInfo = new JPanel(new BorderLayout()); - JTabbedPane tabbedPane = new JTabbedPane(); - tabbedPane.addTab("Intro", createHTMLHelpPanel("MarsHelpIntro.html")); - tabbedPane.addTab("IDE", createHTMLHelpPanel("MarsHelpIDE.html")); - tabbedPane.addTab("Debugging", createHTMLHelpPanel("MarsHelpDebugging.html")); - tabbedPane.addTab("Settings", createHTMLHelpPanel("MarsHelpSettings.html")); - tabbedPane.addTab("Tools", createHTMLHelpPanel("MarsHelpTools.html")); - tabbedPane.addTab("Command", createHTMLHelpPanel("MarsHelpCommand.html")); - tabbedPane.addTab("Limits", createHTMLHelpPanel("MarsHelpLimits.html")); - tabbedPane.addTab("History", createHTMLHelpPanel("MarsHelpHistory.html")); - marsHelpInfo.add(tabbedPane); - return marsHelpInfo; - } - - - // Set up MIPS help tab. Most contents are generated from instruction set info. - private JPanel createMipsHelpInfoPanel() { - JPanel mipsHelpInfo = new JPanel(new BorderLayout()); - String helpRemarksColor = "CCFF99"; - // Introductory remarks go at the top as a label - String helpRemarks = - "
"+// width="+this.getSize().getWidth()+">"+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "" + - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - ""+ - "" + - ""+ - ""+ - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - ""+ - "" + - "
  Operand Key for Example Instructions  
label, targetany textual label
$t1, $t2, $t3any integer register
$f2, $f4, $f6even-numbered floating point register
$f0, $f1, $f3any floating point register
$8any Coprocessor 0 register
1condition flag (0 to 7)
10unsigned 5-bit integer (0 to 31)
-100signed 16-bit integer (-32768 to 32767)
100unsigned 16-bit integer (0 to 65535)
100000signed 32-bit integer (-2147483648 to 2147483647)
Load & Store addressing mode, basic instructions
-100($t2)sign-extended 16-bit integer added to contents of $t2
Load & Store addressing modes, pseudo instructions
($t2)contents of $t2
-100signed 16-bit integer
100unsigned 16-bit integer
100000signed 32-bit integer
100($t2)zero-extended unsigned 16-bit integer added to contents of $t2
100000($t2)signed 32-bit integer added to contents of $t2
label32-bit address of label
label($t2)32-bit address of label added to contents of $t2
label+10000032-bit integer added to label's address
label+100000($t2)   sum of 32-bit integer, label's address, and contents of $t2
"; - // Original code: mipsHelpInfo.add(new JLabel(helpRemarks, JLabel.CENTER), BorderLayout.NORTH); - JLabel helpRemarksLabel = new JLabel(helpRemarks, JLabel.CENTER); - helpRemarksLabel.setOpaque(true); - helpRemarksLabel.setBackground(Color.decode("0x"+helpRemarksColor)); - JScrollPane operandsScrollPane = new JScrollPane(helpRemarksLabel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); - mipsHelpInfo.add(operandsScrollPane, BorderLayout.NORTH); - // Below the label is a tabbed pane with categories of MIPS help - JTabbedPane tabbedPane = new JTabbedPane(); - tabbedPane.addTab("Basic Instructions", createMipsInstructionHelpPane("mars.mips.instructions.BasicInstruction")); - tabbedPane.addTab("Extended (pseudo) Instructions", createMipsInstructionHelpPane("mars.mips.instructions.ExtendedInstruction")); - tabbedPane.addTab("Directives", createMipsDirectivesHelpPane()); - tabbedPane.addTab("Syscalls", createHTMLHelpPanel("SyscallHelp.html")); - tabbedPane.addTab("Exceptions", createHTMLHelpPanel("ExceptionsHelp.html")); - tabbedPane.addTab("Macros", createHTMLHelpPanel("MacrosHelp.html")); - operandsScrollPane.setPreferredSize(new Dimension((int)this.getSize().getWidth(), (int) (this.getSize().getHeight()*.2))); - operandsScrollPane.getVerticalScrollBar().setUnitIncrement(10); - tabbedPane.setPreferredSize(new Dimension((int)this.getSize().getWidth(), (int) (this.getSize().getHeight()*.6))); - JSplitPane splitsville = new JSplitPane(JSplitPane.VERTICAL_SPLIT, operandsScrollPane, tabbedPane); - splitsville.setOneTouchExpandable(true); - splitsville.resetToPreferredSizes(); - mipsHelpInfo.add(splitsville); - //mipsHelpInfo.add(tabbedPane); - return mipsHelpInfo; - } - - /////////////// Methods to construct MIPS help tabs from internal MARS objects ////////////// - - ///////////////////////////////////////////////////////////////////////////// - private JScrollPane createMipsDirectivesHelpPane() { - Vector exampleList = new Vector(); - String blanks = " "; // 12 blanks - Directives direct; - Iterator it = Directives.getDirectiveList().iterator(); - while (it.hasNext()) { - direct = (Directives)it.next(); - exampleList.add(direct.toString() - + blanks.substring(0,Math.max(0,blanks.length()-direct.toString().length())) - + direct.getDescription()); - } - Collections.sort(exampleList); - JList examples = new JList(exampleList); - JScrollPane mipsScrollPane = new JScrollPane(examples,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - examples.setFont(new Font("Monospaced",Font.PLAIN,12)); - return mipsScrollPane; - } - - //////////////////////////////////////////////////////////////////////////// - private JScrollPane createMipsInstructionHelpPane(String instructionClassName) { - ArrayList instructionList = Globals.instructionSet.getInstructionList(); - Vector exampleList = new Vector(instructionList.size()); - Iterator it = instructionList.iterator(); - Instruction instr; - String blanks = " "; // 24 blanks - Class instructionClass; - while (it.hasNext()) { - instr = (Instruction) it.next(); - try { - if (Class.forName(instructionClassName).isInstance(instr)) { - exampleList.add(instr.getExampleFormat() - + blanks.substring(0,Math.max(0,blanks.length()-instr.getExampleFormat().length())) - + instr.getDescription()); - } - } - catch (ClassNotFoundException cnfe) { - System.out.println(cnfe+" "+instructionClassName); - } - } - Collections.sort(exampleList); - JList examples = new JList(exampleList); - JScrollPane mipsScrollPane = new JScrollPane(examples,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - examples.setFont(new Font("Monospaced",Font.PLAIN,12)); - examples.setCellRenderer(new MyCellRenderer()); - return mipsScrollPane; - } - + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + } + catch (Exception ioe) + { + marsCopyrightScrollPane = new JScrollPane( + new JLabel("Error: license contents could not be loaded.")); + } + marsCopyrightInfo.add(marsCopyrightScrollPane); + return marsCopyrightInfo; + } + + // Set up MARS help tab. Subtabs get their contents from HTML files. + private JPanel createMarsHelpInfoPanel() + { + JPanel marsHelpInfo = new JPanel(new BorderLayout()); + JTabbedPane tabbedPane = new JTabbedPane(); + tabbedPane.addTab("Intro", createHTMLHelpPanel("MarsHelpIntro.html")); + tabbedPane.addTab("IDE", createHTMLHelpPanel("MarsHelpIDE.html")); + tabbedPane.addTab("Debugging", createHTMLHelpPanel("MarsHelpDebugging.html")); + tabbedPane.addTab("Settings", createHTMLHelpPanel("MarsHelpSettings.html")); + tabbedPane.addTab("Tools", createHTMLHelpPanel("MarsHelpTools.html")); + tabbedPane.addTab("Command", createHTMLHelpPanel("MarsHelpCommand.html")); + tabbedPane.addTab("Limits", createHTMLHelpPanel("MarsHelpLimits.html")); + tabbedPane.addTab("History", createHTMLHelpPanel("MarsHelpHistory.html")); + marsHelpInfo.add(tabbedPane); + return marsHelpInfo; + } + + + // Set up MIPS help tab. Most contents are generated from instruction set info. + private JPanel createMipsHelpInfoPanel() + { + JPanel mipsHelpInfo = new JPanel(new BorderLayout()); + String helpRemarksColor = "CCFF99"; + // Introductory remarks go at the top as a label + String helpRemarks = + "
" +// width="+this.getSize().getWidth()+">"+ + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "
  Operand Key for Example Instructions  
label, targetany textual label
$t1, $t2, $t3any integer register
$f2, $f4, $f6even-numbered floating point register
$f0, $f1, $f3any floating point register
$8any Coprocessor 0 register
1condition flag (0 to 7)
10unsigned 5-bit integer (0 to 31)
-100signed 16-bit integer (-32768 to 32767)
100unsigned 16-bit integer (0 to 65535)
100000signed 32-bit integer (-2147483648 to 2147483647)
Load & Store addressing mode, basic instructions
-100($t2)sign-extended 16-bit integer added to contents of $t2
Load & Store addressing modes, pseudo instructions
($t2)contents of $t2
-100signed 16-bit integer
100unsigned 16-bit integer
100000signed 32-bit integer
100($t2)zero-extended unsigned 16-bit integer added to contents of $t2
100000($t2)signed 32-bit integer added to contents of $t2
label32-bit address of label
label($t2)32-bit address of label added to contents of $t2
label+10000032-bit integer added to label's address
label+100000($t2)   sum of 32-bit integer, label's address, and contents of $t2
"; + // Original code: mipsHelpInfo.add(new JLabel(helpRemarks, JLabel.CENTER), BorderLayout.NORTH); + JLabel helpRemarksLabel = new JLabel(helpRemarks, JLabel.CENTER); + helpRemarksLabel.setOpaque(true); + helpRemarksLabel.setBackground(Color.decode("0x" + helpRemarksColor)); + JScrollPane operandsScrollPane = new JScrollPane(helpRemarksLabel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); + mipsHelpInfo.add(operandsScrollPane, BorderLayout.NORTH); + // Below the label is a tabbed pane with categories of MIPS help + JTabbedPane tabbedPane = new JTabbedPane(); + tabbedPane.addTab("Basic Instructions", createMipsInstructionHelpPane("mars.mips.instructions.BasicInstruction")); + tabbedPane.addTab("Extended (pseudo) Instructions", createMipsInstructionHelpPane("mars.mips.instructions.ExtendedInstruction")); + tabbedPane.addTab("Directives", createMipsDirectivesHelpPane()); + tabbedPane.addTab("Syscalls", createHTMLHelpPanel("SyscallHelp.html")); + tabbedPane.addTab("Exceptions", createHTMLHelpPanel("ExceptionsHelp.html")); + tabbedPane.addTab("Macros", createHTMLHelpPanel("MacrosHelp.html")); + operandsScrollPane.setPreferredSize(new Dimension((int) this.getSize().getWidth(), (int) (this.getSize().getHeight() * .2))); + operandsScrollPane.getVerticalScrollBar().setUnitIncrement(10); + tabbedPane.setPreferredSize(new Dimension((int) this.getSize().getWidth(), (int) (this.getSize().getHeight() * .6))); + JSplitPane splitsville = new JSplitPane(JSplitPane.VERTICAL_SPLIT, operandsScrollPane, tabbedPane); + splitsville.setOneTouchExpandable(true); + splitsville.resetToPreferredSizes(); + mipsHelpInfo.add(splitsville); + //mipsHelpInfo.add(tabbedPane); + return mipsHelpInfo; + } + + /////////////// Methods to construct MIPS help tabs from internal MARS objects ////////////// + + ///////////////////////////////////////////////////////////////////////////// + private JScrollPane createMipsDirectivesHelpPane() + { + Vector exampleList = new Vector(); + String blanks = " "; // 12 blanks + Directives direct; + Iterator it = Directives.getDirectiveList().iterator(); + while (it.hasNext()) + { + direct = (Directives) it.next(); + exampleList.add(direct.toString() + + blanks.substring(0, Math.max(0, blanks.length() - direct.toString().length())) + + direct.getDescription()); + } + Collections.sort(exampleList); + JList examples = new JList(exampleList); + JScrollPane mipsScrollPane = new JScrollPane(examples, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + examples.setFont(new Font("Monospaced", Font.PLAIN, 12)); + return mipsScrollPane; + } + + //////////////////////////////////////////////////////////////////////////// + private JScrollPane createMipsInstructionHelpPane(String instructionClassName) + { + ArrayList instructionList = Globals.instructionSet.getInstructionList(); + Vector exampleList = new Vector(instructionList.size()); + Iterator it = instructionList.iterator(); + Instruction instr; + String blanks = " "; // 24 blanks + Class instructionClass; + while (it.hasNext()) + { + instr = (Instruction) it.next(); + try + { + if (Class.forName(instructionClassName).isInstance(instr)) + { + exampleList.add(instr.getExampleFormat() + + blanks.substring(0, Math.max(0, blanks.length() - instr.getExampleFormat().length())) + + instr.getDescription()); + } + } + catch (ClassNotFoundException cnfe) + { + System.out.println(cnfe + " " + instructionClassName); + } + } + Collections.sort(exampleList); + JList examples = new JList(exampleList); + JScrollPane mipsScrollPane = new JScrollPane(examples, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + examples.setFont(new Font("Monospaced", Font.PLAIN, 12)); + examples.setCellRenderer(new MyCellRenderer()); + return mipsScrollPane; + } + + + private class MyCellRenderer extends JLabel implements ListCellRenderer + { + // This is the only method defined by ListCellRenderer. + // We just reconfigure the JLabel each time we're called. + public Component getListCellRendererComponent( + JList list, // the list + Object value, // value to display + int index, // cell index + boolean isSelected, // is the cell selected + boolean cellHasFocus) // does the cell have focus + { + String s = value.toString(); + setText(s); + if (isSelected) + { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } + else + { + setBackground((index % 2 == 0) ? altBackgroundColor : list.getBackground()); + setForeground(list.getForeground()); + } + setEnabled(list.isEnabled()); + setFont(list.getFont()); + setOpaque(true); + return this; + } + } - private class MyCellRenderer extends JLabel implements ListCellRenderer { - // This is the only method defined by ListCellRenderer. - // We just reconfigure the JLabel each time we're called. - public Component getListCellRendererComponent( - JList list, // the list - Object value, // value to display - int index, // cell index - boolean isSelected, // is the cell selected - boolean cellHasFocus) // does the cell have focus - { - String s = value.toString(); - setText(s); - if (isSelected) { - setBackground(list.getSelectionBackground()); - setForeground(list.getSelectionForeground()); - } - else { - setBackground((index % 2 == 0) ? altBackgroundColor : list.getBackground()); - setForeground(list.getForeground()); - } - setEnabled(list.isEnabled()); - setFont(list.getFont()); - setOpaque(true); - return this; - } - } - /* * Determines MARS response when user click on hyperlink in displayed help page. * The response will be to pop up a simple dialog with the page contents. It - * will not display URL, no navigation, nothing. Just display the page and + * will not display URL, no navigation, nothing. Just display the page and * provide a Close button. */ - private class HelpHyperlinkListener implements HyperlinkListener { - JDialog webpageDisplay; - JTextField webpageURL; - private static final String cannotDisplayMessage = + private class HelpHyperlinkListener implements HyperlinkListener + { + private static final String cannotDisplayMessage = "Unable to display requested document."; - public void hyperlinkUpdate(HyperlinkEvent e) { - if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - JEditorPane pane = (JEditorPane) e.getSource(); - if (e instanceof HTMLFrameHyperlinkEvent) { - HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e; - HTMLDocument doc = (HTMLDocument)pane.getDocument(); - doc.processHTMLFrameHyperlinkEvent(evt); - } - else { - webpageDisplay = new JDialog(mainUI, "Primitive HTML Viewer"); - webpageDisplay.setLayout(new BorderLayout()); - webpageDisplay.setLocation(mainUI.getSize().width/6, mainUI.getSize().height/6); - JEditorPane webpagePane; - try { - webpagePane = new JEditorPane(e.getURL()); - } - catch (Throwable t) { - webpagePane = new JEditorPane("text/html",cannotDisplayMessage); - } - webpagePane.addHyperlinkListener( - new HyperlinkListener() { - public void hyperlinkUpdate(HyperlinkEvent e) { - if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { - JEditorPane pane = (JEditorPane) e.getSource(); - if (e instanceof HTMLFrameHyperlinkEvent) { - HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent)e; - HTMLDocument doc = (HTMLDocument)pane.getDocument(); - doc.processHTMLFrameHyperlinkEvent(evt); - } - else { - try { - pane.setPage(e.getURL()); - } - catch (Throwable t) { - pane.setText(cannotDisplayMessage); - } - webpageURL.setText(e.getURL().toString()); - } - } - } + + JDialog webpageDisplay; + + JTextField webpageURL; + + public void hyperlinkUpdate(HyperlinkEvent e) + { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) + { + JEditorPane pane = (JEditorPane) e.getSource(); + if (e instanceof HTMLFrameHyperlinkEvent) + { + HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent) e; + HTMLDocument doc = (HTMLDocument) pane.getDocument(); + doc.processHTMLFrameHyperlinkEvent(evt); + } + else + { + webpageDisplay = new JDialog(mainUI, "Primitive HTML Viewer"); + webpageDisplay.setLayout(new BorderLayout()); + webpageDisplay.setLocation(mainUI.getSize().width / 6, mainUI.getSize().height / 6); + JEditorPane webpagePane; + try + { + webpagePane = new JEditorPane(e.getURL()); + } + catch (Throwable t) + { + webpagePane = new JEditorPane("text/html", cannotDisplayMessage); + } + webpagePane.addHyperlinkListener( + new HyperlinkListener() + { + public void hyperlinkUpdate(HyperlinkEvent e) + { + if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) + { + JEditorPane pane = (JEditorPane) e.getSource(); + if (e instanceof HTMLFrameHyperlinkEvent) + { + HTMLFrameHyperlinkEvent evt = (HTMLFrameHyperlinkEvent) e; + HTMLDocument doc = (HTMLDocument) pane.getDocument(); + doc.processHTMLFrameHyperlinkEvent(evt); + } + else + { + try + { + pane.setPage(e.getURL()); + } + catch (Throwable t) + { + pane.setText(cannotDisplayMessage); + } + webpageURL.setText(e.getURL().toString()); + } + } + } }); - webpagePane.setPreferredSize(new Dimension(mainUI.getSize().width*2/3, mainUI.getSize().height*2/3)); - webpagePane.setEditable(false); - webpagePane.setCaretPosition(0); - JScrollPane webpageScrollPane = new JScrollPane(webpagePane, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - webpageURL = new JTextField(e.getURL().toString(), 50); - webpageURL.setEditable(false); - webpageURL.setBackground(Color.WHITE); - JPanel URLPanel= new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 4)); - URLPanel.add(new JLabel("URL: ")); - URLPanel.add(webpageURL); - webpageDisplay.add(URLPanel, BorderLayout.NORTH); - webpageDisplay.add(webpageScrollPane); - JButton closeButton = new JButton("Close"); - closeButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { - webpageDisplay.setVisible(false); - webpageDisplay.dispose(); - } + webpagePane.setPreferredSize(new Dimension(mainUI.getSize().width * 2 / 3, mainUI.getSize().height * 2 / 3)); + webpagePane.setEditable(false); + webpagePane.setCaretPosition(0); + JScrollPane webpageScrollPane = new JScrollPane(webpagePane, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + webpageURL = new JTextField(e.getURL().toString(), 50); + webpageURL.setEditable(false); + webpageURL.setBackground(Color.WHITE); + JPanel URLPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 4, 4)); + URLPanel.add(new JLabel("URL: ")); + URLPanel.add(webpageURL); + webpageDisplay.add(URLPanel, BorderLayout.NORTH); + webpageDisplay.add(webpageScrollPane); + JButton closeButton = new JButton("Close"); + closeButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + webpageDisplay.setVisible(false); + webpageDisplay.dispose(); + } }); - JPanel closePanel = new JPanel(); - closePanel.setLayout(new BoxLayout(closePanel,BoxLayout.LINE_AXIS)); - closePanel.setBorder(BorderFactory.createEmptyBorder(0,0,5,5)); - closePanel.add(Box.createHorizontalGlue()); - closePanel.add(closeButton); - closePanel.add(Box.createHorizontalGlue()); - webpageDisplay.add(closePanel,BorderLayout.SOUTH); - webpageDisplay.pack(); - webpageDisplay.setVisible(true); - } - } - } - } - } \ No newline at end of file + JPanel closePanel = new JPanel(); + closePanel.setLayout(new BoxLayout(closePanel, BoxLayout.LINE_AXIS)); + closePanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 5)); + closePanel.add(Box.createHorizontalGlue()); + closePanel.add(closeButton); + closePanel.add(Box.createHorizontalGlue()); + webpageDisplay.add(closePanel, BorderLayout.SOUTH); + webpageDisplay.pack(); + webpageDisplay.setVisible(true); + } + } + } + } +} diff --git a/src/main/java/mars/venus/LabelsWindow.java b/src/main/java/mars/venus/LabelsWindow.java index 0d21fbe..f38b840 100644 --- a/src/main/java/mars/venus/LabelsWindow.java +++ b/src/main/java/mars/venus/LabelsWindow.java @@ -1,16 +1,23 @@ - package mars.venus; - - import mars.*; - import mars.util.*; - import mars.assembler.*; - import mars.mips.hardware.*; - import java.io.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.table.*; - import javax.swing.border.*; +package mars.venus; + +import mars.Globals; +import mars.MIPSprogram; +import mars.assembler.Symbol; +import mars.assembler.SymbolTable; +import mars.mips.hardware.Memory; +import mars.util.Binary; + +import javax.swing.*; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumnModel; +import java.awt.*; +import java.awt.event.*; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -39,519 +46,623 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Represents the Labels window, which is a type of JInternalFrame. Venus user - * can view MIPS program labels. - * @author Sanderson and Team JSpim - **/ - - public class LabelsWindow extends JInternalFrame{ - private Container contentPane; - private JPanel labelPanel; // holds J - private JCheckBox dataLabels, textLabels; - private ArrayList listOfLabelsForSymbolTable; - private LabelsWindow labelsWindow; - private static final int MAX_DISPLAYED_CHARS = 24; - private static final int PREFERRED_NAME_COLUMN_WIDTH = 60; - private static final int PREFERRED_ADDRESS_COLUMN_WIDTH = 60; - private static final int LABEL_COLUMN = 0; - private static final int ADDRESS_COLUMN = 1; - private static final String[] columnToolTips = { - /* LABEL_COLUMN */ "Programmer-defined label (identifier).", - /* ADDRESS_COLUMN */ "Text or data segment address at which label is defined." - }; - private static String [] columnNames; - private Comparator tableSortComparator; - - ///////////////////////////////////////////////////////////////////////////////////// - // Use 8-state machine to track sort status for displaying tables - // State Sort Column Name sort order Address sort order Click Name Click Addr - // 0 Addr ascend ascend 4 1 - // 1 Addr ascend descend 5 0 - // 2 Addr descend ascend 6 3 - // 3 Addr descend descend 7 2 - // 4 Name ascend ascend 6 0 - // 5 Name ascend descend 7 1 - // 6 Name descend ascend 4 2 - // 7 Name descend descend 5 3 - // "Click Name" column shows which state to go to when Name column is clicked. - // "Click Addr" column shows which state to go to when Addr column is clicked. - ////////////////////////////////////////////////////////////////////////////////////// - // The array of comparators; index corresponds to state in table above. - private final Comparator[] tableSortingComparators = { - /* 0 */ new LabelAddressAscendingComparator(), - /* 1 */ new DescendingComparator(new LabelAddressAscendingComparator()), - /* 2 */ new LabelAddressAscendingComparator(), - /* 3 */ new DescendingComparator(new LabelAddressAscendingComparator()), - /* 4 */ new LabelNameAscendingComparator(), - /* 5 */ new LabelNameAscendingComparator(), - /* 6 */ new DescendingComparator(new LabelNameAscendingComparator()), - /* 7 */ new DescendingComparator(new LabelNameAscendingComparator()) - }; - // The array of state transitions; primary index corresponds to state in table above, - // secondary index corresponds to table columns (0==label name, 1==address). - private static final int[][] sortStateTransitions = { - /* 0 */ { 4, 1 }, - /* 1 */ { 5, 0 }, - /* 2 */ { 6, 3 }, - /* 3 */ { 7, 2 }, - /* 4 */ { 6, 0 }, - /* 5 */ { 7, 1 }, - /* 6 */ { 4, 2 }, - /* 7 */ { 5, 3 } - }; - // The array of column headings; index corresponds to state in table above. - private static final char ASCENDING_SYMBOL = '\u25b2'; //triangle with base at bottom ("points" up, to indicate ascending sort) - private static final char DESCENDING_SYMBOL = '\u25bc';//triangle with base at top ("points" down, to indicate descending sort) - private static final String[][] sortColumnHeadings = { - /* 0 */ {"Label", "Address "+ASCENDING_SYMBOL}, - /* 1 */ {"Label", "Address "+DESCENDING_SYMBOL}, - /* 2 */ {"Label", "Address "+ASCENDING_SYMBOL}, - /* 3 */ {"Label", "Address "+DESCENDING_SYMBOL}, - /* 4 */ {"Label "+ASCENDING_SYMBOL, "Address"}, - /* 5 */ {"Label "+ASCENDING_SYMBOL, "Address"}, - /* 6 */ {"Label "+DESCENDING_SYMBOL, "Address"}, - /* 7 */ {"Label "+DESCENDING_SYMBOL, "Address"} - }; - - // Current sort state (0-7, see table above). Will be set from saved Settings in construtor. - private int sortState = 0; - + +/** + * Represents the Labels window, which is a type of JInternalFrame. Venus user can view MIPS program labels. + * + * @author Sanderson and Team JSpim + **/ + +public class LabelsWindow extends JInternalFrame +{ + private static final int MAX_DISPLAYED_CHARS = 24; + + private static final int PREFERRED_NAME_COLUMN_WIDTH = 60; + + private static final int PREFERRED_ADDRESS_COLUMN_WIDTH = 60; + + private static final int LABEL_COLUMN = 0; + + private static final int ADDRESS_COLUMN = 1; + + private static final String[] columnToolTips = { + /* LABEL_COLUMN */ "Programmer-defined label (identifier).", + /* ADDRESS_COLUMN */ "Text or data segment address at which label is defined." + }; + + // The array of state transitions; primary index corresponds to state in table above, + // secondary index corresponds to table columns (0==label name, 1==address). + private static final int[][] sortStateTransitions = { + /* 0 */ {4, 1}, + /* 1 */ {5, 0}, + /* 2 */ {6, 3}, + /* 3 */ {7, 2}, + /* 4 */ {6, 0}, + /* 5 */ {7, 1}, + /* 6 */ {4, 2}, + /* 7 */ {5, 3} + }; + + // The array of column headings; index corresponds to state in table above. + private static final char ASCENDING_SYMBOL = '\u25b2'; //triangle with base at bottom ("points" up, to indicate ascending sort) + + private static final char DESCENDING_SYMBOL = '\u25bc';//triangle with base at top ("points" down, to indicate descending sort) + + private static final String[][] sortColumnHeadings = { + /* 0 */ {"Label", "Address " + ASCENDING_SYMBOL}, + /* 1 */ {"Label", "Address " + DESCENDING_SYMBOL}, + /* 2 */ {"Label", "Address " + ASCENDING_SYMBOL}, + /* 3 */ {"Label", "Address " + DESCENDING_SYMBOL}, + /* 4 */ {"Label " + ASCENDING_SYMBOL, "Address"}, + /* 5 */ {"Label " + ASCENDING_SYMBOL, "Address"}, + /* 6 */ {"Label " + DESCENDING_SYMBOL, "Address"}, + /* 7 */ {"Label " + DESCENDING_SYMBOL, "Address"} + }; + + private static String[] columnNames; + + ///////////////////////////////////////////////////////////////////////////////////// + // Use 8-state machine to track sort status for displaying tables + // State Sort Column Name sort order Address sort order Click Name Click Addr + // 0 Addr ascend ascend 4 1 + // 1 Addr ascend descend 5 0 + // 2 Addr descend ascend 6 3 + // 3 Addr descend descend 7 2 + // 4 Name ascend ascend 6 0 + // 5 Name ascend descend 7 1 + // 6 Name descend ascend 4 2 + // 7 Name descend descend 5 3 + // "Click Name" column shows which state to go to when Name column is clicked. + // "Click Addr" column shows which state to go to when Addr column is clicked. + ////////////////////////////////////////////////////////////////////////////////////// + // The array of comparators; index corresponds to state in table above. + private final Comparator[] tableSortingComparators = { + /* 0 */ new LabelAddressAscendingComparator(), + /* 1 */ new DescendingComparator(new LabelAddressAscendingComparator()), + /* 2 */ new LabelAddressAscendingComparator(), + /* 3 */ new DescendingComparator(new LabelAddressAscendingComparator()), + /* 4 */ new LabelNameAscendingComparator(), + /* 5 */ new LabelNameAscendingComparator(), + /* 6 */ new DescendingComparator(new LabelNameAscendingComparator()), + /* 7 */ new DescendingComparator(new LabelNameAscendingComparator()) + }; + + private final Container contentPane; + + private final JPanel labelPanel; // holds J + + private final JCheckBox dataLabels; + + private final JCheckBox textLabels; + + private ArrayList listOfLabelsForSymbolTable; + + private final LabelsWindow labelsWindow; + + private Comparator tableSortComparator; + + // Current sort state (0-7, see table above). Will be set from saved Settings in construtor. + private int sortState = 0; + /** - * Constructor for the Labels (symbol table) window. - **/ - - public LabelsWindow (){ - super("Labels", true, false, true, true); - try { + * Constructor for the Labels (symbol table) window. + **/ + + public LabelsWindow() + { + super("Labels", true, false, true, true); + try + { sortState = Integer.parseInt(Globals.getSettings().getLabelSortState()); - } - catch (NumberFormatException nfe) { - sortState = 0; - } - columnNames = sortColumnHeadings[sortState]; - tableSortComparator = tableSortingComparators[sortState]; - labelsWindow = this; - contentPane = this.getContentPane(); - labelPanel = new JPanel(new GridLayout(1,2,10,0)); - JPanel features = new JPanel(); - dataLabels = new JCheckBox("Data", true); - textLabels = new JCheckBox("Text", true); - dataLabels.addItemListener(new LabelItemListener()); - textLabels.addItemListener(new LabelItemListener()); - dataLabels.setToolTipText("If checked, will display labels defined in data segment"); - textLabels.setToolTipText("If checked, will display labels defined in text segment"); - features.add(dataLabels); - features.add(textLabels); - contentPane.add(features, BorderLayout.SOUTH); - contentPane.add(labelPanel); - } - - /** - * Initialize table of labels (symbol table) - */ - public void setupTable(){ - labelPanel.removeAll(); - labelPanel.add(generateLabelScrollPane()); - } - - /** - * Clear the window - */ - public void clearWindow() { - labelPanel.removeAll(); - } - - // - private JScrollPane generateLabelScrollPane() { - listOfLabelsForSymbolTable = new ArrayList(); - listOfLabelsForSymbolTable.add(new LabelsForSymbolTable(null));// global symtab - ArrayList MIPSprogramsAssembled = RunAssembleAction.getMIPSprogramsToAssemble(); - Box allSymtabTables = Box.createVerticalBox(); - for (int i=0; i MAX_DISPLAYED_CHARS) { - name = name.substring(0,MAX_DISPLAYED_CHARS-3)+"..."; - } - // To get left-justified, put file name into first slot of horizontal Box, then glue. - JLabel nameLab = new JLabel(name,JLabel.LEFT); - Box nameLabel = Box.createHorizontalBox(); - nameLabel.add(nameLab); - nameLabel.add(Box.createHorizontalGlue()); - nameLabel.add(Box.createHorizontalStrut(1)); - tableNames.add(nameLabel); - allSymtabTables.add(nameLabel); - JTable table = symtab.generateLabelTable(); - tableHeader = table.getTableHeader(); - // The following is selfish on my part. Column re-ordering doesn't work correctly when - // displaying multiple symbol tables; the headers re-order but the columns do not. - // Given the low perceived benefit of reordering displayed symbol table information - // versus the perceived effort to make reordering work for multiple symbol tables, - // I am taking the easy way out here. PS 19 July 2007. - tableHeader.setReorderingAllowed(false); - table.setSelectionBackground(table.getBackground()); - // Sense click on label/address and scroll Text/Data segment display to it. - table.addMouseListener(new LabelDisplayMouseListener()); - allSymtabTables.add(table); + (MIPSprogram) MIPSprogramsAssembled.get(i))); + } + ArrayList tableNames = new ArrayList(); + JTableHeader tableHeader = null; + for (int i = 0; i < listOfLabelsForSymbolTable.size(); i++) + { + LabelsForSymbolTable symtab = (LabelsForSymbolTable) listOfLabelsForSymbolTable.get(i); + if (symtab.hasSymbols()) + { + String name = symtab.getSymbolTableName(); + if (name.length() > MAX_DISPLAYED_CHARS) + { + name = name.substring(0, MAX_DISPLAYED_CHARS - 3) + "..."; + } + // To get left-justified, put file name into first slot of horizontal Box, then glue. + JLabel nameLab = new JLabel(name, JLabel.LEFT); + Box nameLabel = Box.createHorizontalBox(); + nameLabel.add(nameLab); + nameLabel.add(Box.createHorizontalGlue()); + nameLabel.add(Box.createHorizontalStrut(1)); + tableNames.add(nameLabel); + allSymtabTables.add(nameLabel); + JTable table = symtab.generateLabelTable(); + tableHeader = table.getTableHeader(); + // The following is selfish on my part. Column re-ordering doesn't work correctly when + // displaying multiple symbol tables; the headers re-order but the columns do not. + // Given the low perceived benefit of reordering displayed symbol table information + // versus the perceived effort to make reordering work for multiple symbol tables, + // I am taking the easy way out here. PS 19 July 2007. + tableHeader.setReorderingAllowed(false); + table.setSelectionBackground(table.getBackground()); + // Sense click on label/address and scroll Text/Data segment display to it. + table.addMouseListener(new LabelDisplayMouseListener()); + allSymtabTables.add(table); } - } - JScrollPane labelScrollPane = new JScrollPane(allSymtabTables, - ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); - // Set file name label's max width to scrollpane's viewport width, max height to small. - // Does it do any good? Addressing problem that occurs when label (filename) is wider than - // the table beneath it -- the table column widths are stretched to attain the same width and - // the address information requires scrolling to see. All because of a long file name. - for (int i=0;i= 0 && addrB >= 0 || addrA < 0 && addrB < 0) ? addrA-addrB : addrB ; - } - } - - - //////////////////////////////////////////////////////////////////////////// - // - // Comparator class used to sort in descending order a List of symbols. It will - // sort either alphabetically by name or numerically by address, depending on the - // Comparator object provided as the argument constructor. This works because it - // is implemented by returning the result of the Ascending comparator when - // arguments are reversed. - private class DescendingComparator implements java.util.Comparator { - private Comparator opposite; - private DescendingComparator(Comparator opposite) { + + + ///////////////////////////////////////////////////////////////////// + // When user clicks on table column header, system will sort the + // table based on that column then redraw it. + private class SymbolTableHeaderMouseListener implements MouseListener + { + public void mouseClicked(MouseEvent e) + { + Point p = e.getPoint(); + int index = columnModel.getColumnIndexAtX(p.x); + int realIndex = columnModel.getColumn(index).getModelIndex(); + sortState = sortStateTransitions[sortState][realIndex]; + tableSortComparator = tableSortingComparators[sortState]; + columnNames = sortColumnHeadings[sortState]; + Globals.getSettings().setLabelSortState(Integer.valueOf(sortState).toString()); + setupTable(); + Globals.getGui().getMainPane().getExecutePane().setLabelWindowVisibility(false); + Globals.getGui().getMainPane().getExecutePane().setLabelWindowVisibility(true); + } + + public void mouseEntered(MouseEvent e) + { + } + + public void mouseExited(MouseEvent e) + { + } + + public void mousePressed(MouseEvent e) + { + } + + public void mouseReleased(MouseEvent e) + { + } + } + } + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Comparator class used to sort in ascending order a List of symbols alphabetically by name + private class LabelNameAscendingComparator implements java.util.Comparator + { + public int compare(Object a, Object b) + { + return ((Symbol) a).getName().toLowerCase().compareTo(((Symbol) b).getName().toLowerCase()); + } + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Comparator class used to sort in ascending order a List of symbols numerically + // by address. The kernel address space is all negative integers, so we need some + // special processing to treat int address as unsigned 32 bit value. + // Note: Integer.signum() is Java 1.5 and MARS is 1.4 so I can't use it. + // Remember, if not equal then any value with correct sign will work. + // If both have same sign, a-b will yield correct result. + // If signs differ, b will yield correct result (think about it). + private class LabelAddressAscendingComparator implements java.util.Comparator + { + public int compare(Object a, Object b) + { + int addrA = ((Symbol) a).getAddress(); + int addrB = ((Symbol) b).getAddress(); + return (addrA >= 0 && addrB >= 0 || addrA < 0 && addrB < 0) ? addrA - addrB : addrB; + } + } + + + //////////////////////////////////////////////////////////////////////////// + // + // Comparator class used to sort in descending order a List of symbols. It will + // sort either alphabetically by name or numerically by address, depending on the + // Comparator object provided as the argument constructor. This works because it + // is implemented by returning the result of the Ascending comparator when + // arguments are reversed. + private class DescendingComparator implements java.util.Comparator + { + private final Comparator opposite; + + private DescendingComparator(Comparator opposite) + { this.opposite = opposite; - } - public int compare(Object a, Object b) { - return opposite.compare(b,a); - } - } - - - } + } + + public int compare(Object a, Object b) + { + return opposite.compare(b, a); + } + } + + +} diff --git a/src/main/java/mars/venus/MainPane.java b/src/main/java/mars/venus/MainPane.java index 04b3265..ad06433 100644 --- a/src/main/java/mars/venus/MainPane.java +++ b/src/main/java/mars/venus/MainPane.java @@ -1,14 +1,12 @@ - package mars.venus; - import mars.*; - import javax.swing.*; - import javax.swing.event.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.undo.*; - import java.text.*; - import java.util.*; - import java.io.*; - import javax.swing.plaf.basic.BasicTabbedPaneUI; +package mars.venus; + +import mars.Globals; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.plaf.basic.BasicTabbedPaneUI; +import java.awt.*; /* @@ -39,108 +37,119 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - /** - * Creates the tabbed areas in the UI and also created the internal windows that - * exist in them. - * @author Sanderson and Bumgarner - **/ +/** + * Creates the tabbed areas in the UI and also created the internal windows that exist in them. + * + * @author Sanderson and Bumgarner + **/ + +public class MainPane extends JTabbedPane +{ + EditPane editTab; + + ExecutePane executeTab; + + EditTabbedPane editTabbedPane; + + private final VenusUI mainUI; - public class MainPane extends JTabbedPane{ - EditPane editTab; - ExecutePane executeTab; - EditTabbedPane editTabbedPane; - - private VenusUI mainUI; - /** - * Constructor for the MainPane class. - **/ - - public MainPane(VenusUI appFrame, Editor editor, RegistersWindow regs, - Coprocessor1Window cop1Regs,Coprocessor0Window cop0Regs){ - super(); - this.mainUI = appFrame; - this.setTabPlacement(JTabbedPane.TOP); //LEFT); - if (this.getUI() instanceof BasicTabbedPaneUI) { + * Constructor for the MainPane class. + **/ + + public MainPane(VenusUI appFrame, Editor editor, RegistersWindow regs, + Coprocessor1Window cop1Regs, Coprocessor0Window cop0Regs) + { + super(); + this.mainUI = appFrame; + this.setTabPlacement(JTabbedPane.TOP); //LEFT); + if (this.getUI() instanceof BasicTabbedPaneUI) + { BasicTabbedPaneUI ui = (BasicTabbedPaneUI) this.getUI(); - } - editTabbedPane = new EditTabbedPane(appFrame, editor, this); - executeTab = new ExecutePane(appFrame, regs, cop1Regs, cop0Regs); - String editTabTitle = "Edit"; //"
 
E
d
i
t
 
"; - String executeTabTitle = "Execute"; //"
 
E
x
e
c
u
t
e
 
"; - Icon editTabIcon = null;//new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(Globals.imagesPath+"Edit_tab.jpg"))); - Icon executeTabIcon = null;//new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(Globals.imagesPath+"Execute_tab.jpg"))); - - this.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); - this.addTab(editTabTitle, editTabIcon, editTabbedPane); - - // this.addTab("
 
P
r
o
j
 
1", null, new JTabbedPane()); - // this.addTab("
 
P
r
o
j
 
2", null, new JTabbedPane()); - // this.addTab("
 
P
r
o
j
 
3", null, new JTabbedPane()); - // this.addTab("
 
P
r
o
j
 
4", null, new JTabbedPane()); - - this.addTab(executeTabTitle, executeTabIcon, executeTab); - - this.setToolTipTextAt(0,"Text editor for composing MIPS programs."); - this.setToolTipTextAt(1,"View and control assembly language program execution. Enabled upon successful assemble."); - - /* Listener has one specific purpose: when Execute tab is selected for the - * first time, set the bounds of its internal frames by invoking the - * setWindowsBounds() method. Once this occurs, listener removes itself! - * We do NOT want to reset bounds each time Execute tab is selected. - * See ExecutePane.setWindowsBounds documentation for more details. - */ - this.addChangeListener( - new ChangeListener() { - public void stateChanged(ChangeEvent ce) { - JTabbedPane tabbedPane = (JTabbedPane) ce.getSource(); - int index = tabbedPane.getSelectedIndex(); - Component c = tabbedPane.getComponentAt(index); - ExecutePane executePane = Globals.getGui().getMainPane().getExecutePane(); - if (c == executePane) { + } + editTabbedPane = new EditTabbedPane(appFrame, editor, this); + executeTab = new ExecutePane(appFrame, regs, cop1Regs, cop0Regs); + String editTabTitle = "Edit"; //"
 
E
d
i
t
 
"; + String executeTabTitle = "Execute"; //"
 
E
x
e
c
u
t
e
 
"; + Icon editTabIcon = null;//new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(Globals.imagesPath+"Edit_tab.jpg"))); + Icon executeTabIcon = null;//new ImageIcon(Toolkit.getDefaultToolkit().getImage(this.getClass().getResource(Globals.imagesPath+"Execute_tab.jpg"))); + + this.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); + this.addTab(editTabTitle, editTabIcon, editTabbedPane); + + // this.addTab("
 
P
r
o
j
 
1", null, new JTabbedPane()); + // this.addTab("
 
P
r
o
j
 
2", null, new JTabbedPane()); + // this.addTab("
 
P
r
o
j
 
3", null, new JTabbedPane()); + // this.addTab("
 
P
r
o
j
 
4", null, new JTabbedPane()); + + this.addTab(executeTabTitle, executeTabIcon, executeTab); + + this.setToolTipTextAt(0, "Text editor for composing MIPS programs."); + this.setToolTipTextAt(1, "View and control assembly language program execution. Enabled upon successful assemble."); + + /* Listener has one specific purpose: when Execute tab is selected for the + * first time, set the bounds of its internal frames by invoking the + * setWindowsBounds() method. Once this occurs, listener removes itself! + * We do NOT want to reset bounds each time Execute tab is selected. + * See ExecutePane.setWindowsBounds documentation for more details. + */ + this.addChangeListener( + new ChangeListener() + { + public void stateChanged(ChangeEvent ce) + { + JTabbedPane tabbedPane = (JTabbedPane) ce.getSource(); + int index = tabbedPane.getSelectedIndex(); + Component c = tabbedPane.getComponentAt(index); + ExecutePane executePane = Globals.getGui().getMainPane().getExecutePane(); + if (c == executePane) + { executePane.setWindowBounds(); Globals.getGui().getMainPane().removeChangeListener(this); - } - } - }); - } - - /** - * Returns current edit pane. Implementation changed for MARS 4.0 support - * for multiple panes, but specification is same. - * - * @return the editor pane - */ - public EditPane getEditPane() { - return editTabbedPane.getCurrentEditTab(); - } - - /** - * Returns component containing editor display - * - * @return the editor tabbed pane - */ - public JComponent getEditTabbedPane() { - return editTabbedPane; - } - - /** - * returns component containing execution-time display - * - * @return the execute pane - */ - public ExecutePane getExecutePane() { - return executeTab; - } - - /** - * returns component containing execution-time display. - * Same as getExecutePane(). - * - * @return the execute pane - */ - public ExecutePane getExecuteTab() { - return executeTab; - } - - } \ No newline at end of file + } + } + }); + } + + /** + * Returns current edit pane. Implementation changed for MARS 4.0 support for multiple panes, but specification is + * same. + * + * @return the editor pane + */ + public EditPane getEditPane() + { + return editTabbedPane.getCurrentEditTab(); + } + + /** + * Returns component containing editor display + * + * @return the editor tabbed pane + */ + public JComponent getEditTabbedPane() + { + return editTabbedPane; + } + + /** + * returns component containing execution-time display + * + * @return the execute pane + */ + public ExecutePane getExecutePane() + { + return executeTab; + } + + /** + * returns component containing execution-time display. Same as getExecutePane(). + * + * @return the execute pane + */ + public ExecutePane getExecuteTab() + { + return executeTab; + } + +} diff --git a/src/main/java/mars/venus/MessagesPane.java b/src/main/java/mars/venus/MessagesPane.java index 373612e..eabe5e4 100644 --- a/src/main/java/mars/venus/MessagesPane.java +++ b/src/main/java/mars/venus/MessagesPane.java @@ -47,419 +47,508 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Creates the message window at the bottom of the UI. - * @author Team JSpim - **/ + * Creates the message window at the bottom of the UI. + * + * @author Team JSpim + **/ - public class MessagesPane extends JTabbedPane{ - JTextArea assemble, run; - JPanel assembleTab, runTab; - // These constants are designed to keep scrolled contents of the - // two message areas from becoming overwhelmingly large (which - // seems to slow things down as new text is appended). Once it - // reaches MAXIMUM_SCROLLED_CHARACTERS in length then cut off - // the first NUMBER_OF_CHARACTERS_TO_CUT characters. The latter - // must obviously be smaller than the former. - public static final int MAXIMUM_SCROLLED_CHARACTERS = Globals.maximumMessageCharacters; - public static final int NUMBER_OF_CHARACTERS_TO_CUT = Globals.maximumMessageCharacters/10 ; // 10% - - /** - * Constructor for the class, sets up two fresh tabbed text areas for program feedback. +public class MessagesPane extends JTabbedPane +{ + // These constants are designed to keep scrolled contents of the + // two message areas from becoming overwhelmingly large (which + // seems to slow things down as new text is appended). Once it + // reaches MAXIMUM_SCROLLED_CHARACTERS in length then cut off + // the first NUMBER_OF_CHARACTERS_TO_CUT characters. The latter + // must obviously be smaller than the former. + public static final int MAXIMUM_SCROLLED_CHARACTERS = Globals.maximumMessageCharacters; + + public static final int NUMBER_OF_CHARACTERS_TO_CUT = Globals.maximumMessageCharacters / 10; // 10% + + JTextArea assemble, run; + + JPanel assembleTab, runTab; + + /** + * Constructor for the class, sets up two fresh tabbed text areas for program feedback. **/ - - public MessagesPane() { - super(); - this.setMinimumSize(new Dimension(0,0)); - assemble= new JTextArea(); - run= new JTextArea(); - assemble.setEditable(false); - run.setEditable(false); - // Set both text areas to mono font. For assemble - // pane, will make messages more readable. For run - // pane, will allow properly aligned "text graphics" - // DPS 15 Dec 2008 - Font monoFont = new Font(Font.MONOSPACED, Font.PLAIN, 12); - assemble.setFont(monoFont); - run.setFont(monoFont); - - JButton assembleTabClearButton = new JButton("Clear"); - assembleTabClearButton.setToolTipText("Clear the Mars Messages area"); - assembleTabClearButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e){ - assemble.setText(""); - } - }); - assembleTab = new JPanel(new BorderLayout()); - assembleTab.add(createBoxForButton(assembleTabClearButton),BorderLayout.WEST); - assembleTab.add(new JScrollPane(assemble, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER); - assemble.addMouseListener( - new MouseAdapter() { - public void mouseClicked(MouseEvent e) { - String text; - int lineStart = 0; - int lineEnd = 0; - try { + + public MessagesPane() + { + super(); + this.setMinimumSize(new Dimension(0, 0)); + assemble = new JTextArea(); + run = new JTextArea(); + assemble.setEditable(false); + run.setEditable(false); + // Set both text areas to mono font. For assemble + // pane, will make messages more readable. For run + // pane, will allow properly aligned "text graphics" + // DPS 15 Dec 2008 + Font monoFont = new Font(Font.MONOSPACED, Font.PLAIN, 12); + assemble.setFont(monoFont); + run.setFont(monoFont); + + JButton assembleTabClearButton = new JButton("Clear"); + assembleTabClearButton.setToolTipText("Clear the Mars Messages area"); + assembleTabClearButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + assemble.setText(""); + } + }); + assembleTab = new JPanel(new BorderLayout()); + assembleTab.add(createBoxForButton(assembleTabClearButton), BorderLayout.WEST); + assembleTab.add(new JScrollPane(assemble, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER); + assemble.addMouseListener( + new MouseAdapter() + { + public void mouseClicked(MouseEvent e) + { + String text; + int lineStart = 0; + int lineEnd = 0; + try + { int line = assemble.getLineOfOffset(assemble.viewToModel(e.getPoint())); lineStart = assemble.getLineStartOffset(line); lineEnd = assemble.getLineEndOffset(line); - text = assemble.getText(lineStart, lineEnd-lineStart); - } - catch (BadLocationException ble) { - text = ""; + text = assemble.getText(lineStart, lineEnd - lineStart); + } + catch (BadLocationException ble) + { + text = ""; + } + if (text.length() > 0) + { + // If error or warning, parse out the line and column number. + if (text.startsWith(ErrorList.ERROR_MESSAGE_PREFIX) || text.startsWith(ErrorList.WARNING_MESSAGE_PREFIX)) + { + assemble.select(lineStart, lineEnd); + assemble.setSelectionColor(Color.YELLOW); + assemble.repaint(); + int separatorPosition = text.indexOf(ErrorList.MESSAGE_SEPARATOR); + if (separatorPosition >= 0) + { + text = text.substring(0, separatorPosition); + } + String[] stringTokens = text.split("\\s"); // tokenize with whitespace delimiter + String lineToken = ErrorList.LINE_PREFIX.trim(); + String columnToken = ErrorList.POSITION_PREFIX.trim(); + String lineString = ""; + String columnString = ""; + for (int i = 0; i < stringTokens.length; i++) + { + if (stringTokens[i].equals(lineToken) && i < stringTokens.length - 1) + { + lineString = stringTokens[i + 1]; + } + if (stringTokens[i].equals(columnToken) && i < stringTokens.length - 1) + { + columnString = stringTokens[i + 1]; + } + } + int line = 0; + int column = 0; + try + { + line = Integer.parseInt(lineString); + } + catch (NumberFormatException nfe) + { + line = 0; + } + try + { + column = Integer.parseInt(columnString); + } + catch (NumberFormatException nfe) + { + column = 0; + } + // everything between FILENAME_PREFIX and LINE_PREFIX is filename. + int fileNameStart = text.indexOf(ErrorList.FILENAME_PREFIX) + ErrorList.FILENAME_PREFIX.length(); + int fileNameEnd = text.indexOf(ErrorList.LINE_PREFIX); + String fileName = ""; + if (fileNameStart < fileNameEnd && fileNameStart >= ErrorList.FILENAME_PREFIX.length()) + { + fileName = text.substring(fileNameStart, fileNameEnd).trim(); + } + if (fileName != null && fileName.length() > 0) + { + selectEditorTextLine(fileName, line, column); + selectErrorMessage(fileName, line, column); + } } - if (text.length() > 0) { - // If error or warning, parse out the line and column number. - if (text.startsWith(ErrorList.ERROR_MESSAGE_PREFIX) || text.startsWith(ErrorList.WARNING_MESSAGE_PREFIX)) { - assemble.select(lineStart,lineEnd); - assemble.setSelectionColor(Color.YELLOW); - assemble.repaint(); - int separatorPosition = text.indexOf(ErrorList.MESSAGE_SEPARATOR); - if (separatorPosition >= 0) { - text = text.substring(0,separatorPosition); - } - String[] stringTokens = text.split("\\s"); // tokenize with whitespace delimiter - String lineToken = ErrorList.LINE_PREFIX.trim(); - String columnToken = ErrorList.POSITION_PREFIX.trim(); - String lineString = ""; - String columnString = ""; - for (int i=0; i= ErrorList.FILENAME_PREFIX.length()) { - fileName = text.substring(fileNameStart, fileNameEnd).trim(); - } - if (fileName != null && fileName.length() > 0) { - selectEditorTextLine(fileName, line, column); - selectErrorMessage(fileName, line, column); - } - } - } - } - }); - - JButton runTabClearButton = new JButton("Clear"); - runTabClearButton.setToolTipText("Clear the Run I/O area"); - runTabClearButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e){ - run.setText(""); - } - }); - runTab = new JPanel(new BorderLayout()); - runTab.add(createBoxForButton(runTabClearButton),BorderLayout.WEST); - runTab.add(new JScrollPane(run, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER); - this.addTab("Mars Messages", assembleTab); - this.addTab("Run I/O", runTab); - this.setToolTipTextAt(0,"Messages produced by Run menu. Click on assemble error message to select erroneous line"); - this.setToolTipTextAt(1,"Simulated MIPS console input and output"); - } - - // Center given button in a box, centered vertically and 6 pixels on left and right - private Box createBoxForButton(JButton button) { - Box buttonRow = Box.createHorizontalBox(); - buttonRow.add(Box.createHorizontalStrut(6)); - buttonRow.add(button); - buttonRow.add(Box.createHorizontalStrut(6)); - Box buttonBox = Box.createVerticalBox(); - buttonBox.add(Box.createVerticalGlue()); - buttonBox.add(buttonRow); - buttonBox.add(Box.createVerticalGlue()); - return buttonBox; - } - - /** - * Will select the Mars Messages tab error message that matches the given - * specifications, if it is found. Matching is done by constructing - * a string using the parameter values and searching the text area for the last - * occurrance of that string. - * @param fileName A String containing the file path name. - * @param line Line number for error message - * @param column Column number for error message - */ - public void selectErrorMessage(String fileName, int line, int column) { - String errorReportSubstring = new java.io.File(fileName).getName() + ErrorList.LINE_PREFIX + line + ErrorList.POSITION_PREFIX + column; - int textPosition = assemble.getText().lastIndexOf(errorReportSubstring); - if (textPosition >= 0) { + } + } + }); + + JButton runTabClearButton = new JButton("Clear"); + runTabClearButton.setToolTipText("Clear the Run I/O area"); + runTabClearButton.addActionListener( + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + run.setText(""); + } + }); + runTab = new JPanel(new BorderLayout()); + runTab.add(createBoxForButton(runTabClearButton), BorderLayout.WEST); + runTab.add(new JScrollPane(run, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED), BorderLayout.CENTER); + this.addTab("Mars Messages", assembleTab); + this.addTab("Run I/O", runTab); + this.setToolTipTextAt(0, "Messages produced by Run menu. Click on assemble error message to select erroneous line"); + this.setToolTipTextAt(1, "Simulated MIPS console input and output"); + } + + // Center given button in a box, centered vertically and 6 pixels on left and right + private Box createBoxForButton(JButton button) + { + Box buttonRow = Box.createHorizontalBox(); + buttonRow.add(Box.createHorizontalStrut(6)); + buttonRow.add(button); + buttonRow.add(Box.createHorizontalStrut(6)); + Box buttonBox = Box.createVerticalBox(); + buttonBox.add(Box.createVerticalGlue()); + buttonBox.add(buttonRow); + buttonBox.add(Box.createVerticalGlue()); + return buttonBox; + } + + /** + * Will select the Mars Messages tab error message that matches the given specifications, if it is found. Matching + * is done by constructing a string using the parameter values and searching the text area for the last occurrance + * of that string. + * + * @param fileName A String containing the file path name. + * @param line Line number for error message + * @param column Column number for error message + */ + public void selectErrorMessage(String fileName, int line, int column) + { + String errorReportSubstring = new java.io.File(fileName).getName() + ErrorList.LINE_PREFIX + line + ErrorList.POSITION_PREFIX + column; + int textPosition = assemble.getText().lastIndexOf(errorReportSubstring); + if (textPosition >= 0) + { int textLine = 0; int lineStart = 0; int lineEnd = 0; - try { - textLine = assemble.getLineOfOffset(textPosition); - lineStart = assemble.getLineStartOffset(textLine); - lineEnd = assemble.getLineEndOffset(textLine); - assemble.setSelectionColor(Color.YELLOW); - assemble.select(lineStart,lineEnd); - assemble.getCaret().setSelectionVisible(true); - assemble.repaint(); - } - catch (BadLocationException ble) { - // If there is a problem, simply skip the selection - } - } - } - - - /** - * Will select the specified line in an editor tab. If the file is open - * but not current, its tab will be made current. If the file is not open, - * it will be opened in a new tab and made current, however the line will - * not be selected (apparent apparent problem with JEditTextArea). - * @param fileName A String containing the file path name. - * @param line Line number for error message - * @param column Column number for error message - */ - public void selectEditorTextLine(String fileName, int line, int column) { - EditTabbedPane editTabbedPane = (EditTabbedPane) Globals.getGui().getMainPane().getEditTabbedPane(); - EditPane editPane, currentPane = null; - editPane = editTabbedPane.getEditPaneForFile(new java.io.File(fileName).getPath()); - if (editPane != null) { - if (editPane != editTabbedPane.getCurrentEditTab()) { - editTabbedPane.setCurrentEditTab(editPane); + try + { + textLine = assemble.getLineOfOffset(textPosition); + lineStart = assemble.getLineStartOffset(textLine); + lineEnd = assemble.getLineEndOffset(textLine); + assemble.setSelectionColor(Color.YELLOW); + assemble.select(lineStart, lineEnd); + assemble.getCaret().setSelectionVisible(true); + assemble.repaint(); } - currentPane = editPane; - } - else { // file is not open. Try to open it. - if (editTabbedPane.openFile(new java.io.File(fileName))) { - currentPane = editTabbedPane.getCurrentEditTab(); + catch (BadLocationException ble) + { + // If there is a problem, simply skip the selection } - } - // If editPane == null, it means the desired file was not open. Line selection - // does not properly with the JEditTextArea editor in this situation (it works - // fine for the original generic editor). So we just won't do it. DPS 9-Aug-2010 - if (editPane != null && currentPane != null) { - currentPane.selectLine(line, column); - } - } - - /** - * Returns component used to display assembler messages - * - * @return assembler message text component - */ - public JTextArea getAssembleTextArea() { - return assemble; - } - /** - * Returns component used to display runtime messages - * - * @return runtime message text component - */ - public JTextArea getRunTextArea() { - return run; - } - - /** - * Post a message to the assembler display - * - * @param message String to append to assembler display text - */ - public void postMarsMessage(String message) { - assemble.append(message); - // can do some crude cutting here. If the document gets "very large", - // let's cut off the oldest text. This will limit scrolling but the limit - // can be set reasonably high. - if (assemble.getDocument().getLength() > MAXIMUM_SCROLLED_CHARACTERS) { - try { - assemble.getDocument().remove(0, NUMBER_OF_CHARACTERS_TO_CUT); - } - catch (BadLocationException ble) { - // only if NUMBER_OF_CHARACTERS_TO_CUT > MAXIMUM_SCROLLED_CHARACTERS - } - } - assemble.setCaretPosition(assemble.getDocument().getLength()); - setSelectedComponent(assembleTab); - } - - /** - * Post a message to the runtime display - * - * @param message String to append to runtime display text - */ - // The work of this method is done by "invokeLater" because - // its JTextArea is maintained by the main event thread - // but also used, via this method, by the execution thread for - // "print" syscalls. "invokeLater" schedules the code to be - // run under the event-processing thread no matter what. - // DPS, 23 Aug 2005. - public void postRunMessage(String message) { - final String mess = message; - SwingUtilities.invokeLater( - new Runnable() { - public void run() { - setSelectedComponent(runTab); - run.append(mess); - // can do some crude cutting here. If the document gets "very large", - // let's cut off the oldest text. This will limit scrolling but the limit - // can be set reasonably high. - if (run.getDocument().getLength() > MAXIMUM_SCROLLED_CHARACTERS) { - try { - run.getDocument().remove(0, NUMBER_OF_CHARACTERS_TO_CUT); - } - catch (BadLocationException ble) { - // only if NUMBER_OF_CHARACTERS_TO_CUT > MAXIMUM_SCROLLED_CHARACTERS - } - } - } - }); - } - - /** - * Make the assembler message tab current (up front) - */ - public void selectMarsMessageTab() { - setSelectedComponent(assembleTab); - } - /** - * Make the runtime message tab current (up front) - */ - public void selectRunMessageTab() { - setSelectedComponent(runTab); - } - - /** - * Method used by the SystemIO class to get interactive user input - * requested by a running MIPS program (e.g. syscall #5 to read an - * integer). SystemIO knows whether simulator is being run at - * command line by the user, or by the GUI. If run at command line, - * it gets input from System.in rather than here. - * - * This is an overloaded method. This version, with the String parameter, - * is used to get input from a popup dialog. - * - * @param prompt Prompt to display to the user. - * @return User input. - */ - public String getInputString(String prompt) { - String input; - JOptionPane pane = new JOptionPane(prompt,JOptionPane.QUESTION_MESSAGE,JOptionPane.DEFAULT_OPTION); - pane.setWantsInput(true); - JDialog dialog = pane.createDialog(Globals.getGui(), "MIPS Keyboard Input"); - dialog.setVisible(true); - input = (String) pane.getInputValue(); - this.postRunMessage(Globals.userInputAlert+input+"\n"); - return input; - } - - /** - * Method used by the SystemIO class to get interactive user input - * requested by a running MIPS program (e.g. syscall #5 to read an - * integer). SystemIO knows whether simulator is being run at - * command line by the user, or by the GUI. If run at command line, - * it gets input from System.in rather than here. - * - * This is an overloaded method. This version, with the int parameter, - * is used to get input from the MARS Run I/O window. - * - * @param maxLen: maximum length of input. This method returns when maxLen characters have been read. Use -1 for no length restrictions. - * @return User input. - */ - public String getInputString(int maxLen) { - Asker asker = new Asker(maxLen); // Asker defined immediately below. - return asker.response(); - } + } + } + + + /** + * Will select the specified line in an editor tab. If the file is open but not current, its tab will be made + * current. If the file is not open, it will be opened in a new tab and made current, however the line will not be + * selected (apparent apparent problem with JEditTextArea). + * + * @param fileName A String containing the file path name. + * @param line Line number for error message + * @param column Column number for error message + */ + public void selectEditorTextLine(String fileName, int line, int column) + { + EditTabbedPane editTabbedPane = (EditTabbedPane) Globals.getGui().getMainPane().getEditTabbedPane(); + EditPane editPane, currentPane = null; + editPane = editTabbedPane.getEditPaneForFile(new java.io.File(fileName).getPath()); + if (editPane != null) + { + if (editPane != editTabbedPane.getCurrentEditTab()) + { + editTabbedPane.setCurrentEditTab(editPane); + } + currentPane = editPane; + } + else + { // file is not open. Try to open it. + if (editTabbedPane.openFile(new java.io.File(fileName))) + { + currentPane = editTabbedPane.getCurrentEditTab(); + } + } + // If editPane == null, it means the desired file was not open. Line selection + // does not properly with the JEditTextArea editor in this situation (it works + // fine for the original generic editor). So we just won't do it. DPS 9-Aug-2010 + if (editPane != null && currentPane != null) + { + currentPane.selectLine(line, column); + } + } + + /** + * Returns component used to display assembler messages + * + * @return assembler message text component + */ + public JTextArea getAssembleTextArea() + { + return assemble; + } + + /** + * Returns component used to display runtime messages + * + * @return runtime message text component + */ + public JTextArea getRunTextArea() + { + return run; + } + + /** + * Post a message to the assembler display + * + * @param message String to append to assembler display text + */ + public void postMarsMessage(String message) + { + assemble.append(message); + // can do some crude cutting here. If the document gets "very large", + // let's cut off the oldest text. This will limit scrolling but the limit + // can be set reasonably high. + if (assemble.getDocument().getLength() > MAXIMUM_SCROLLED_CHARACTERS) + { + try + { + assemble.getDocument().remove(0, NUMBER_OF_CHARACTERS_TO_CUT); + } + catch (BadLocationException ble) + { + // only if NUMBER_OF_CHARACTERS_TO_CUT > MAXIMUM_SCROLLED_CHARACTERS + } + } + assemble.setCaretPosition(assemble.getDocument().getLength()); + setSelectedComponent(assembleTab); + } + + /** + * Post a message to the runtime display + * + * @param message String to append to runtime display text + */ + // The work of this method is done by "invokeLater" because + // its JTextArea is maintained by the main event thread + // but also used, via this method, by the execution thread for + // "print" syscalls. "invokeLater" schedules the code to be + // run under the event-processing thread no matter what. + // DPS, 23 Aug 2005. + public void postRunMessage(String message) + { + final String mess = message; + SwingUtilities.invokeLater( + new Runnable() + { + public void run() + { + setSelectedComponent(runTab); + run.append(mess); + // can do some crude cutting here. If the document gets "very large", + // let's cut off the oldest text. This will limit scrolling but the limit + // can be set reasonably high. + if (run.getDocument().getLength() > MAXIMUM_SCROLLED_CHARACTERS) + { + try + { + run.getDocument().remove(0, NUMBER_OF_CHARACTERS_TO_CUT); + } + catch (BadLocationException ble) + { + // only if NUMBER_OF_CHARACTERS_TO_CUT > MAXIMUM_SCROLLED_CHARACTERS + } + } + } + }); + } + + /** + * Make the assembler message tab current (up front) + */ + public void selectMarsMessageTab() + { + setSelectedComponent(assembleTab); + } + + /** + * Make the runtime message tab current (up front) + */ + public void selectRunMessageTab() + { + setSelectedComponent(runTab); + } + + /** + * Method used by the SystemIO class to get interactive user input requested by a running MIPS program (e.g. syscall + * #5 to read an integer). SystemIO knows whether simulator is being run at command line by the user, or by the + * GUI. If run at command line, it gets input from System.in rather than here. + *

+ * This is an overloaded method. This version, with the String parameter, is used to get input from a popup + * dialog. + * + * @param prompt Prompt to display to the user. + * @return User input. + */ + public String getInputString(String prompt) + { + String input; + JOptionPane pane = new JOptionPane(prompt, JOptionPane.QUESTION_MESSAGE, JOptionPane.DEFAULT_OPTION); + pane.setWantsInput(true); + JDialog dialog = pane.createDialog(Globals.getGui(), "MIPS Keyboard Input"); + dialog.setVisible(true); + input = (String) pane.getInputValue(); + this.postRunMessage(Globals.userInputAlert + input + "\n"); + return input; + } + + /** + * Method used by the SystemIO class to get interactive user input requested by a running MIPS program (e.g. syscall + * #5 to read an integer). SystemIO knows whether simulator is being run at command line by the user, or by the + * GUI. If run at command line, it gets input from System.in rather than here. + *

+ * This is an overloaded method. This version, with the int parameter, is used to get input from the MARS Run I/O + * window. + * + * @param maxLen: maximum length of input. This method returns when maxLen characters have been read. Use -1 for + * no length restrictions. + * @return User input. + */ + public String getInputString(int maxLen) + { + Asker asker = new Asker(maxLen); // Asker defined immediately below. + return asker.response(); + } //////////////////////////////////////////////////////////////////////////// // Thread class for obtaining user input in the Run I/O window (MessagesPane) // Written by Ricardo Fernández Pascual [rfernandez@ditec.um.es] December 2009. - class Asker implements Runnable { + class Asker implements Runnable + { ArrayBlockingQueue resultQueue = new ArrayBlockingQueue(1); + int initialPos; - int maxLen; - final DocumentListener listener = - new DocumentListener() { - public void insertUpdate(final DocumentEvent e) { - EventQueue.invokeLater( - new Runnable() { - public void run() { - try { - String inserted = e.getDocument().getText(e.getOffset(), e.getLength()); - int i = inserted.indexOf('\n'); - if (i >= 0) { - int offset = e.getOffset() + i; - if (offset + 1 == e.getDocument().getLength()) { - returnResponse(); - } - else { - // remove the '\n' and put it at the end - e.getDocument().remove(offset, 1); - e.getDocument().insertString(e.getDocument().getLength(), "\n", null); - // insertUpdate will be called again, since we have inserted the '\n' at the end - } - } - else if (maxLen >= 0 && e.getDocument().getLength() - initialPos >= maxLen) { - returnResponse(); - } - } - catch (BadLocationException ex) { - returnResponse(); - } - } - }); - } - public void removeUpdate(final DocumentEvent e) { - EventQueue.invokeLater( - new Runnable() { - public void run() { - if ((e.getDocument().getLength() < initialPos || e.getOffset() < initialPos) && e instanceof UndoableEdit) { - ((UndoableEdit) e).undo(); - run.setCaretPosition(e.getOffset() + e.getLength()); - } - } - }); - } - public void changedUpdate(DocumentEvent e) { } + + final NavigationFilter navigationFilter = + new NavigationFilter() + { + public void moveDot(FilterBypass fb, int dot, Bias bias) + { + if (dot < initialPos) + { + dot = Math.min(initialPos, run.getDocument().getLength()); + } + fb.moveDot(dot, bias); + } + + public void setDot(FilterBypass fb, int dot, Bias bias) + { + if (dot < initialPos) + { + dot = Math.min(initialPos, run.getDocument().getLength()); + } + fb.setDot(dot, bias); + } }; - Asker(int maxLen) { + int maxLen; + + final DocumentListener listener = + new DocumentListener() + { + public void insertUpdate(final DocumentEvent e) + { + EventQueue.invokeLater( + new Runnable() + { + public void run() + { + try + { + String inserted = e.getDocument().getText(e.getOffset(), e.getLength()); + int i = inserted.indexOf('\n'); + if (i >= 0) + { + int offset = e.getOffset() + i; + if (offset + 1 == e.getDocument().getLength()) + { + returnResponse(); + } + else + { + // remove the '\n' and put it at the end + e.getDocument().remove(offset, 1); + e.getDocument().insertString(e.getDocument().getLength(), "\n", null); + // insertUpdate will be called again, since we have inserted the '\n' at the end + } + } + else if (maxLen >= 0 && e.getDocument().getLength() - initialPos >= maxLen) + { + returnResponse(); + } + } + catch (BadLocationException ex) + { + returnResponse(); + } + } + }); + } + + public void removeUpdate(final DocumentEvent e) + { + EventQueue.invokeLater( + new Runnable() + { + public void run() + { + if ((e.getDocument().getLength() < initialPos || e.getOffset() < initialPos) && e instanceof UndoableEdit) + { + ((UndoableEdit) e).undo(); + run.setCaretPosition(e.getOffset() + e.getLength()); + } + } + }); + } + + public void changedUpdate(DocumentEvent e) + { + } + }; + + final Simulator.StopListener stopListener = + new Simulator.StopListener() + { + public void stopped(Simulator s) + { + returnResponse(); + } + }; + + Asker(int maxLen) + { this.maxLen = maxLen; // initialPos will be set in run() } - final NavigationFilter navigationFilter = - new NavigationFilter() { - public void moveDot(FilterBypass fb, int dot, Bias bias) { - if (dot < initialPos) { - dot = Math.min(initialPos, run.getDocument().getLength()); - } - fb.moveDot(dot, bias); - } - public void setDot(FilterBypass fb, int dot, Bias bias) { - if (dot < initialPos) { - dot = Math.min(initialPos, run.getDocument().getLength()); - } - fb.setDot(dot, bias); - } - }; - final Simulator.StopListener stopListener = - new Simulator.StopListener() { - public void stopped(Simulator s) { - returnResponse(); - } - }; - public void run() { // must be invoked from the GUI thread + + public void run() + { // must be invoked from the GUI thread setSelectedComponent(runTab); run.setEditable(true); run.requestFocusInWindow(); @@ -468,42 +557,55 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. run.setNavigationFilter(navigationFilter); run.getDocument().addDocumentListener(listener); Simulator.getInstance().addStopListener(stopListener); - } - void cleanup() { // not required to be called from the GUI thread + } + + void cleanup() + { // not required to be called from the GUI thread EventQueue.invokeLater( - new Runnable() { - public void run() { + new Runnable() + { + public void run() + { run.getDocument().removeDocumentListener(listener); run.setEditable(false); run.setNavigationFilter(null); run.setCaretPosition(run.getDocument().getLength()); Simulator.getInstance().removeStopListener(stopListener); - } - }); - } - void returnResponse() { - try { - int p = Math.min(initialPos, run.getDocument().getLength()); - int l = Math.min(run.getDocument().getLength() - p, maxLen >= 0 ? maxLen : Integer.MAX_VALUE); - resultQueue.offer(run.getText(p, l)); - } - catch (BadLocationException ex) { - // this cannot happen - resultQueue.offer(""); - } - } - String response() { - EventQueue.invokeLater(this); - try { - return resultQueue.take(); - } - catch (InterruptedException ex) { - return null; - } - finally { - cleanup(); + } + }); + } + + void returnResponse() + { + try + { + int p = Math.min(initialPos, run.getDocument().getLength()); + int l = Math.min(run.getDocument().getLength() - p, maxLen >= 0 ? maxLen : Integer.MAX_VALUE); + resultQueue.offer(run.getText(p, l)); } - } - } // Asker class - //////////////////////////////////////////////////////////////////////////// - } + catch (BadLocationException ex) + { + // this cannot happen + resultQueue.offer(""); + } + } + + String response() + { + EventQueue.invokeLater(this); + try + { + return resultQueue.take(); + } + catch (InterruptedException ex) + { + return null; + } + finally + { + cleanup(); + } + } + } // Asker class + //////////////////////////////////////////////////////////////////////////// +} diff --git a/src/main/java/mars/venus/MonoRightCellRenderer.java b/src/main/java/mars/venus/MonoRightCellRenderer.java index b9c3b1c..c922890 100644 --- a/src/main/java/mars/venus/MonoRightCellRenderer.java +++ b/src/main/java/mars/venus/MonoRightCellRenderer.java @@ -1,8 +1,8 @@ - package mars.venus; - import mars.*; - import javax.swing.table.*; - import javax.swing.*; - import java.awt.*; +package mars.venus; + +import javax.swing.*; +import javax.swing.table.DefaultTableCellRenderer; +import java.awt.*; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -38,14 +38,17 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * Strings containing either the decimal or hexidecimal version * of the integer value. */ - class MonoRightCellRenderer extends DefaultTableCellRenderer { - public static final Font MONOSPACED_PLAIN_12POINT = new Font("Monospaced",Font.PLAIN,12); - public Component getTableCellRendererComponent(JTable table, Object value, - boolean isSelected, boolean hasFocus, int row, int column) { - JLabel cell = (JLabel) super.getTableCellRendererComponent(table, value, - isSelected, hasFocus, row, column); - cell.setFont(MONOSPACED_PLAIN_12POINT); - cell.setHorizontalAlignment(SwingConstants.RIGHT); - return cell; - } - } \ No newline at end of file +class MonoRightCellRenderer extends DefaultTableCellRenderer +{ + public static final Font MONOSPACED_PLAIN_12POINT = new Font("Monospaced", Font.PLAIN, 12); + + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, boolean hasFocus, int row, int column) + { + JLabel cell = (JLabel) super.getTableCellRendererComponent(table, value, + isSelected, hasFocus, row, column); + cell.setFont(MONOSPACED_PLAIN_12POINT); + cell.setHorizontalAlignment(SwingConstants.RIGHT); + return cell; + } +} diff --git a/src/main/java/mars/venus/NumberDisplayBaseChooser.java b/src/main/java/mars/venus/NumberDisplayBaseChooser.java index 90111fe..bc50a1b 100644 --- a/src/main/java/mars/venus/NumberDisplayBaseChooser.java +++ b/src/main/java/mars/venus/NumberDisplayBaseChooser.java @@ -1,9 +1,13 @@ - package mars.venus; - import mars.*; - import mars.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import mars.Globals; +import mars.util.Binary; + +import javax.swing.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,271 +36,287 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + /** - * Use to select base for displaying numbers. Initially the - * choices are only 10 (decimal) and 16 (hex), so I'm using - * a check box where checked means hex. If base 8 (octal) - * is added later, the Component will need to change. + * Use to select base for displaying numbers. Initially the choices are only 10 (decimal) and 16 (hex), so I'm using a + * check box where checked means hex. If base 8 (octal) is added later, the Component will need to change. */ - - public class NumberDisplayBaseChooser extends JCheckBox { - public static final int DECIMAL = 10; - public static final int HEXADECIMAL = 16; - public static final int ASCII = 0; - private int base; - private JCheckBoxMenuItem settingMenuItem; - - /** - * constructor. It assumes the text will be worded - * so that a checked box means hexadecimal! - * @param text Text to accompany the check box. - * @param defaultBase Currently either DECIMAL or HEXADECIMAL - */ - public NumberDisplayBaseChooser(String text, boolean displayInHex) { - super(text, displayInHex); - base = getBase(displayInHex); - addItemListener( - new ItemListener() { - public void itemStateChanged(ItemEvent ie) { - NumberDisplayBaseChooser choose = (NumberDisplayBaseChooser)ie.getItem(); - if (ie.getStateChange() == ItemEvent.SELECTED) { + +public class NumberDisplayBaseChooser extends JCheckBox +{ + public static final int DECIMAL = 10; + + public static final int HEXADECIMAL = 16; + + public static final int ASCII = 0; + + private int base; + + private JCheckBoxMenuItem settingMenuItem; + + /** + * constructor. It assumes the text will be worded so that a checked box means hexadecimal! + * + * @param text Text to accompany the check box. + * @param defaultBase Currently either DECIMAL or HEXADECIMAL + */ + public NumberDisplayBaseChooser(String text, boolean displayInHex) + { + super(text, displayInHex); + base = getBase(displayInHex); + addItemListener( + new ItemListener() + { + public void itemStateChanged(ItemEvent ie) + { + NumberDisplayBaseChooser choose = (NumberDisplayBaseChooser) ie.getItem(); + if (ie.getStateChange() == ItemEvent.SELECTED) + { choose.setBase(NumberDisplayBaseChooser.HEXADECIMAL); - } - else { + } + else + { choose.setBase(NumberDisplayBaseChooser.DECIMAL); - } - // Better to use notify, but I am tired... - if (settingMenuItem!=null) { + } + // Better to use notify, but I am tired... + if (settingMenuItem != null) + { settingMenuItem.setSelected(choose.isSelected()); ActionListener[] listeners = settingMenuItem.getActionListeners(); ActionEvent event = new ActionEvent(settingMenuItem, 0, "chooser"); - for (int i=0; iRepeatButton is a JButton which contains a timer - * for firing events while the button is held down. There is a default - * initial delay of 300ms before the first event is fired and a 60ms delay - * between subsequent events. When the user holds the button down and moves - * the mouse out from over the button, the timer stops, but if the user moves - * the mouse back over the button without having released the mouse button, - * the timer starts up again at the same delay rate. If the enabled state is - * changed while the timer is active, it will be stopped. - * - * NOTE: The normal button behavior is that the action event is fired after - * the button is released. It may be important to konw then that this is - * still the case. So in effect, listeners will get 1 more event then what - * the internal timer fires. It's not a "bug", per se, just something to be - * aware of. There seems to be no way to suppress the final event from - * firing anyway, except to process all ActionListeners internally. But - * realistically, it probably doesn't matter. + * RepeatButton is a JButton which contains a timer + * for firing events while the button is held down. There is a default initial delay of 300ms before the first event is + * fired and a 60ms delay between subsequent events. When the user holds the button down and moves the mouse out from + * over the button, the timer stops, but if the user moves the mouse back over the button without having released the + * mouse button, the timer starts up again at the same delay rate. If the enabled state is changed while the timer is + * active, it will be stopped. + *

+ * NOTE: The normal button behavior is that the action event is fired after the button is released. It may be + * important to konw then that this is still the case. So in effect, listeners will get 1 more event then what the + * internal timer fires. It's not a "bug", per se, just something to be aware of. There seems to be no way to suppress + * the final event from firing anyway, except to process all ActionListeners internally. But realistically, it probably + * doesn't matter. */ -public class RepeatButton extends JButton - implements ActionListener, MouseListener { - /** - * The pressed state for this button. - */ - private boolean pressed = false; - - /** - * Flag to indicate that the button should fire events when held. - * If false, the button is effectively a plain old JButton, but - * there may be times when this feature might wish to be disabled. - */ - private boolean repeatEnabled = true; - - /** - * The hold-down timer for this button. - */ - private Timer timer = null; - +public class RepeatButton extends JButton + implements ActionListener, MouseListener +{ + /** + * Testing flag. Set in main method. + */ + private static boolean testing = false; - /** - * The initial delay for this button. Hold-down time before first - * timer firing. In milliseconds. - */ - private int initialDelay = 300; - - /** - * The delay between timer firings for this button once the delay - * period is past. In milliseconds. - */ - private int delay = 60; - + /** + * The pressed state for this button. + */ + private boolean pressed = false; - - /** - * Holder of the modifiers used when the mouse pressed the button. - * This is used for subsequently fired action events. This may change - * after mouse pressed if the user moves the mouse out, releases a key - * and then moves the mouse back in. - */ - private int modifiers = 0; - - /** - * Creates a button with no set text or icon. - */ - public RepeatButton() { - super(); - init(); - } - - /** - * Creates a button where properties are taken from the Action supplied. - * - * @param a the button action - */ - public RepeatButton(Action a) { - super(a); - init(); - } - - /** - * Creates a button with an icon. - * - * @param icon the button icon - */ - public RepeatButton(Icon icon) { - super(icon); - init(); - } - - /** - * Creates a button with text. - * - * @param text the button text - */ - public RepeatButton(String text) { - super(text); - init(); - } - - /** - * Creates a button with initial text and an icon. - * - * @param text the button text - * @param icon the button icon - */ - public RepeatButton(String text, Icon icon) { - super(text, icon); - init(); - } - - /** - * Initializes the button. - */ - private void init() { - this.addMouseListener(this); - // initialize timers for button holding... - this.timer = new Timer(this.delay, this); - this.timer.setRepeats(true); - } - - /** - * Gets the delay for the timer of this button. - * - * @return the delay - */ - public int getDelay() { - return this.delay; - } - - /** - * Set the delay for the timer of this button. - * - * @param d the delay - */ - public void setDelay(int d) { - this.delay = d; - } - - /** - * Gets the initial delay for the timer of this button. - * - * @return the initial delay - */ - public int getInitialDelay() { - return this.initialDelay; - } - - /** - * Sets the initial delay for the timer of this button. - * - * @param d the initial delay - */ - public void setInitialDelay(int d) { - this.initialDelay = d; - } - - /** - * Checks if the button should fire events when held. If false, the - * button is effectively a plain old JButton, but there may be times - * when this feature might wish to be disabled. - * - * @return if true, the button should fire events when held - */ - public boolean isRepeatEnabled() { - return this.repeatEnabled; - } - - /** - * Sets if the button should fire events when held. If false, the - * button is effectively a plain old JButton, but there may be times - * when this feature might wish to be disabled. If false, it will - * also stop the timer if it's running. - * - * @param en if true, the button should fire events when held - */ - public void setRepeatEnabled(boolean en) { - if(!en) { - this.pressed = false; - if(timer.isRunning()) { - timer.stop(); - } - } - this.repeatEnabled = en; - } - - /** - * Sets the enabled state of this button. Overridden to stop the timer - * if it's running. - * - * @param en if true, enables the button - */ - public void setEnabled(boolean en) { - if(en != super.isEnabled()) { - this.pressed = false; - if(timer.isRunning()) { - timer.stop(); - } - } - super.setEnabled(en); - } - - /** - * Handle action events. OVERRIDE THIS IN SUBCLASS! - * - * @param ae the action event - */ - public void actionPerformed(ActionEvent ae) { - // process events only from this components - if(ae.getSource() == this.timer) { - ActionEvent event = new ActionEvent( - this, ActionEvent.ACTION_PERFORMED, - super.getActionCommand(), this.modifiers); - super.fireActionPerformed(event); - } - // testing code... - else if(testing && ae.getSource() == this) { - System.out.println(ae.getActionCommand()); - } - } - - /** - * Handle mouse clicked events. - * - * @param me the mouse event - */ - public void mouseClicked(MouseEvent me) { - // process events only from this components - if(me.getSource() == this) { - this.pressed = false; - if(this.timer.isRunning()) { - this.timer.stop(); - } - } - } - - /** - * Handle mouse pressed events. - * - * @param me the mouse event - */ - public void mousePressed(MouseEvent me) { - // process events only from this components - if(me.getSource() == this && this.isEnabled() && this.isRepeatEnabled()) { - this.pressed = true; - if(!this.timer.isRunning()) { - this.modifiers = me.getModifiers(); - this.timer.setInitialDelay(this.initialDelay); - this.timer.start(); - } - } - } - - /** - * Handle mouse released events. - * - * @param me the mouse event - */ - public void mouseReleased(MouseEvent me) { - // process events only from this components - if(me.getSource() == this) { - this.pressed = false; - if(this.timer.isRunning()) { - this.timer.stop(); - } - } - } - - /** - * Handle mouse entered events. - * - * @param me the mouse event - */ - public void mouseEntered(MouseEvent me) { - // process events only from this components - if(me.getSource() == this && this.isEnabled() && this.isRepeatEnabled()) { - if(this.pressed && !this.timer.isRunning()) { - this.modifiers = me.getModifiers(); - this.timer.setInitialDelay(this.delay); - this.timer.start(); - } - } - } - - /** - * Handle mouse exited events. - * - * @param me the mouse event - */ - public void mouseExited(MouseEvent me) { - // process events only from this components - if(me.getSource() == this) { - if(this.timer.isRunning()) { - this.timer.stop(); - } - } - } - - /** - * Testing flag. Set in main method. - */ - private static boolean testing = false; - - /** - * Main method, for testing. Creates a frame with both styles of menu. - * - * @param args the command-line arguments - */ - public static void main(String[] args) { - testing = true; - JFrame f = new JFrame("RepeatButton Test"); - f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - - JPanel p = new JPanel(); - RepeatButton b = new RepeatButton("hold me"); - b.setActionCommand("test"); - b.addActionListener(b); - p.add(b); - f.getContentPane().add(p); - f.pack(); - f.setVisible(true); - } + /** + * Flag to indicate that the button should fire events when held. If false, the button is effectively a plain old + * JButton, but there may be times when this feature might wish to be disabled. + */ + private boolean repeatEnabled = true; + + /** + * The hold-down timer for this button. + */ + private Timer timer = null; + + /** + * The initial delay for this button. Hold-down time before first timer firing. In milliseconds. + */ + private int initialDelay = 300; + + /** + * The delay between timer firings for this button once the delay period is past. In milliseconds. + */ + private int delay = 60; + + /** + * Holder of the modifiers used when the mouse pressed the button. This is used for subsequently fired action + * events. This may change after mouse pressed if the user moves the mouse out, releases a key and then moves the + * mouse back in. + */ + private int modifiers = 0; + + /** + * Creates a button with no set text or icon. + */ + public RepeatButton() + { + super(); + init(); + } + + /** + * Creates a button where properties are taken from the Action supplied. + * + * @param a the button action + */ + public RepeatButton(Action a) + { + super(a); + init(); + } + + /** + * Creates a button with an icon. + * + * @param icon the button icon + */ + public RepeatButton(Icon icon) + { + super(icon); + init(); + } + + /** + * Creates a button with text. + * + * @param text the button text + */ + public RepeatButton(String text) + { + super(text); + init(); + } + + /** + * Creates a button with initial text and an icon. + * + * @param text the button text + * @param icon the button icon + */ + public RepeatButton(String text, Icon icon) + { + super(text, icon); + init(); + } + + /** + * Main method, for testing. Creates a frame with both styles of menu. + * + * @param args the command-line arguments + */ + public static void main(String[] args) + { + testing = true; + JFrame f = new JFrame("RepeatButton Test"); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + + JPanel p = new JPanel(); + RepeatButton b = new RepeatButton("hold me"); + b.setActionCommand("test"); + b.addActionListener(b); + p.add(b); + f.getContentPane().add(p); + f.pack(); + f.setVisible(true); + } + + /** + * Initializes the button. + */ + private void init() + { + this.addMouseListener(this); + // initialize timers for button holding... + this.timer = new Timer(this.delay, this); + this.timer.setRepeats(true); + } + + /** + * Gets the delay for the timer of this button. + * + * @return the delay + */ + public int getDelay() + { + return this.delay; + } + + /** + * Set the delay for the timer of this button. + * + * @param d the delay + */ + public void setDelay(int d) + { + this.delay = d; + } + + /** + * Gets the initial delay for the timer of this button. + * + * @return the initial delay + */ + public int getInitialDelay() + { + return this.initialDelay; + } + + /** + * Sets the initial delay for the timer of this button. + * + * @param d the initial delay + */ + public void setInitialDelay(int d) + { + this.initialDelay = d; + } + + /** + * Checks if the button should fire events when held. If false, the button is effectively a plain old JButton, but + * there may be times when this feature might wish to be disabled. + * + * @return if true, the button should fire events when held + */ + public boolean isRepeatEnabled() + { + return this.repeatEnabled; + } + + /** + * Sets if the button should fire events when held. If false, the button is effectively a plain old JButton, but + * there may be times when this feature might wish to be disabled. If false, it will also stop the timer if it's + * running. + * + * @param en if true, the button should fire events when held + */ + public void setRepeatEnabled(boolean en) + { + if (!en) + { + this.pressed = false; + if (timer.isRunning()) + { + timer.stop(); + } + } + this.repeatEnabled = en; + } + + /** + * Sets the enabled state of this button. Overridden to stop the timer if it's running. + * + * @param en if true, enables the button + */ + public void setEnabled(boolean en) + { + if (en != super.isEnabled()) + { + this.pressed = false; + if (timer.isRunning()) + { + timer.stop(); + } + } + super.setEnabled(en); + } + + /** + * Handle action events. OVERRIDE THIS IN SUBCLASS! + * + * @param ae the action event + */ + public void actionPerformed(ActionEvent ae) + { + // process events only from this components + if (ae.getSource() == this.timer) + { + ActionEvent event = new ActionEvent( + this, ActionEvent.ACTION_PERFORMED, + super.getActionCommand(), this.modifiers); + super.fireActionPerformed(event); + } + // testing code... + else if (testing && ae.getSource() == this) + { + System.out.println(ae.getActionCommand()); + } + } + + /** + * Handle mouse clicked events. + * + * @param me the mouse event + */ + public void mouseClicked(MouseEvent me) + { + // process events only from this components + if (me.getSource() == this) + { + this.pressed = false; + if (this.timer.isRunning()) + { + this.timer.stop(); + } + } + } + + /** + * Handle mouse pressed events. + * + * @param me the mouse event + */ + public void mousePressed(MouseEvent me) + { + // process events only from this components + if (me.getSource() == this && this.isEnabled() && this.isRepeatEnabled()) + { + this.pressed = true; + if (!this.timer.isRunning()) + { + this.modifiers = me.getModifiers(); + this.timer.setInitialDelay(this.initialDelay); + this.timer.start(); + } + } + } + + /** + * Handle mouse released events. + * + * @param me the mouse event + */ + public void mouseReleased(MouseEvent me) + { + // process events only from this components + if (me.getSource() == this) + { + this.pressed = false; + if (this.timer.isRunning()) + { + this.timer.stop(); + } + } + } + + /** + * Handle mouse entered events. + * + * @param me the mouse event + */ + public void mouseEntered(MouseEvent me) + { + // process events only from this components + if (me.getSource() == this && this.isEnabled() && this.isRepeatEnabled()) + { + if (this.pressed && !this.timer.isRunning()) + { + this.modifiers = me.getModifiers(); + this.timer.setInitialDelay(this.delay); + this.timer.start(); + } + } + } + + /** + * Handle mouse exited events. + * + * @param me the mouse event + */ + public void mouseExited(MouseEvent me) + { + // process events only from this components + if (me.getSource() == this) + { + if (this.timer.isRunning()) + { + this.timer.stop(); + } + } + } } diff --git a/src/main/java/mars/venus/RunAssembleAction.java b/src/main/java/mars/venus/RunAssembleAction.java index 34f0092..92f5c87 100644 --- a/src/main/java/mars/venus/RunAssembleAction.java +++ b/src/main/java/mars/venus/RunAssembleAction.java @@ -1,12 +1,18 @@ - package mars.venus; - import mars.*; - import mars.util.*; - import mars.mips.hardware.*; - import java.util.*; - import java.io.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import mars.*; +import mars.mips.hardware.Coprocessor0; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.Memory; +import mars.mips.hardware.RegisterFile; +import mars.util.FilenameFinder; +import mars.util.SystemIO; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.io.File; +import java.util.ArrayList; /* Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar @@ -35,142 +41,167 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Run -> Assemble menu item (and toolbar icon) - */ - public class RunAssembleAction extends GuiAction { - - private static ArrayList MIPSprogramsToAssemble; - private static boolean extendedAssemblerEnabled; - private static boolean warningsAreErrors; - // Threshold for adding filename to printed message of files being assembled. - private static final int LINE_LENGTH_LIMIT = 60; - - public RunAssembleAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - // These are both used by RunResetAction to re-assemble under identical conditions. - static ArrayList getMIPSprogramsToAssemble() { - return MIPSprogramsToAssemble; - } - static boolean getExtendedAssemblerEnabled() { - return extendedAssemblerEnabled; - } - - static boolean getWarningsAreErrors() { - return warningsAreErrors; - } - - public void actionPerformed(ActionEvent e) { - String name = this.getValue(Action.NAME).toString(); - Component editPane = mainUI.getMainPane().getEditPane(); - ExecutePane executePane = mainUI.getMainPane().getExecutePane(); - RegistersPane registersPane = mainUI.getRegistersPane(); - extendedAssemblerEnabled = Globals.getSettings().getExtendedAssemblerEnabled(); - warningsAreErrors = Globals.getSettings().getWarningsAreErrors(); - if(FileStatus.getFile()!= null){ - if (FileStatus.get() == FileStatus.EDITED) { - mainUI.editor.save(); + +/** + * Action class for the Run -> Assemble menu item (and toolbar icon) + */ +public class RunAssembleAction extends GuiAction +{ + + // Threshold for adding filename to printed message of files being assembled. + private static final int LINE_LENGTH_LIMIT = 60; + + private static ArrayList MIPSprogramsToAssemble; + + private static boolean extendedAssemblerEnabled; + + private static boolean warningsAreErrors; + + public RunAssembleAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + // These are both used by RunResetAction to re-assemble under identical conditions. + static ArrayList getMIPSprogramsToAssemble() + { + return MIPSprogramsToAssemble; + } + + static boolean getExtendedAssemblerEnabled() + { + return extendedAssemblerEnabled; + } + + static boolean getWarningsAreErrors() + { + return warningsAreErrors; + } + + public void actionPerformed(ActionEvent e) + { + String name = this.getValue(Action.NAME).toString(); + Component editPane = mainUI.getMainPane().getEditPane(); + ExecutePane executePane = mainUI.getMainPane().getExecutePane(); + RegistersPane registersPane = mainUI.getRegistersPane(); + extendedAssemblerEnabled = Globals.getSettings().getExtendedAssemblerEnabled(); + warningsAreErrors = Globals.getSettings().getWarningsAreErrors(); + if (FileStatus.getFile() != null) + { + if (FileStatus.get() == FileStatus.EDITED) + { + mainUI.editor.save(); } - try{ - Globals.program = new MIPSprogram(); - ArrayList filesToAssemble; - if (Globals.getSettings().getAssembleAllEnabled()) {// setting calls for multiple file assembly - filesToAssemble = FilenameFinder.getFilenameList( - new File(FileStatus.getName()).getParent(), Globals.fileExtensions); - } - else { - filesToAssemble = new ArrayList(); - filesToAssemble.add(FileStatus.getName()); - } - String exceptionHandler = null; - if (Globals.getSettings().getExceptionHandlerEnabled() && - Globals.getSettings().getExceptionHandler() != null && - Globals.getSettings().getExceptionHandler().length() > 0) { - exceptionHandler = Globals.getSettings().getExceptionHandler(); - } - MIPSprogramsToAssemble = Globals.program.prepareFilesForAssembly(filesToAssemble, FileStatus.getFile().getPath(), exceptionHandler); - mainUI.messagesPane.postMarsMessage(buildFileNameList(name+": assembling ", MIPSprogramsToAssemble)); - // added logic to receive any warnings and output them.... DPS 11/28/06 - ErrorList warnings = Globals.program.assemble(MIPSprogramsToAssemble, extendedAssemblerEnabled, - warningsAreErrors); - if (warnings.warningsOccurred()) { - mainUI.messagesPane.postMarsMessage(warnings.generateWarningReport()); - } - mainUI.messagesPane.postMarsMessage( - name+": operation completed successfully.\n\n"); - FileStatus.setAssembled(true); - FileStatus.set(FileStatus.RUNNABLE); - RegisterFile.resetRegisters(); - Coprocessor1.resetRegisters(); - Coprocessor0.resetRegisters(); - executePane.getTextSegmentWindow().setupTable(); - executePane.getDataSegmentWindow().setupTable(); - executePane.getDataSegmentWindow().highlightCellForAddress(Memory.dataBaseAddress); - executePane.getDataSegmentWindow().clearHighlighting(); - executePane.getLabelsWindow().setupTable(); - executePane.getTextSegmentWindow().setCodeHighlighting(true); - executePane.getTextSegmentWindow().highlightStepAtPC(); - registersPane.getRegistersWindow().clearWindow(); - registersPane.getCoprocessor1Window().clearWindow(); - registersPane.getCoprocessor0Window().clearWindow(); - mainUI.setReset(true); - mainUI.setStarted(false); - mainUI.getMainPane().setSelectedComponent(executePane); - - // Aug. 24, 2005 Ken Vollmar - SystemIO.resetFiles( ); // Ensure that I/O "file descriptors" are initialized for a new program run - + try + { + Globals.program = new MIPSprogram(); + ArrayList filesToAssemble; + if (Globals.getSettings().getAssembleAllEnabled()) + {// setting calls for multiple file assembly + filesToAssemble = FilenameFinder.getFilenameList( + new File(FileStatus.getName()).getParent(), Globals.fileExtensions); + } + else + { + filesToAssemble = new ArrayList(); + filesToAssemble.add(FileStatus.getName()); + } + String exceptionHandler = null; + if (Globals.getSettings().getExceptionHandlerEnabled() && + Globals.getSettings().getExceptionHandler() != null && + Globals.getSettings().getExceptionHandler().length() > 0) + { + exceptionHandler = Globals.getSettings().getExceptionHandler(); + } + MIPSprogramsToAssemble = Globals.program.prepareFilesForAssembly(filesToAssemble, FileStatus.getFile().getPath(), exceptionHandler); + mainUI.messagesPane.postMarsMessage(buildFileNameList(name + ": assembling ", MIPSprogramsToAssemble)); + // added logic to receive any warnings and output them.... DPS 11/28/06 + ErrorList warnings = Globals.program.assemble(MIPSprogramsToAssemble, extendedAssemblerEnabled, + warningsAreErrors); + if (warnings.warningsOccurred()) + { + mainUI.messagesPane.postMarsMessage(warnings.generateWarningReport()); + } + mainUI.messagesPane.postMarsMessage( + name + ": operation completed successfully.\n\n"); + FileStatus.setAssembled(true); + FileStatus.set(FileStatus.RUNNABLE); + RegisterFile.resetRegisters(); + Coprocessor1.resetRegisters(); + Coprocessor0.resetRegisters(); + executePane.getTextSegmentWindow().setupTable(); + executePane.getDataSegmentWindow().setupTable(); + executePane.getDataSegmentWindow().highlightCellForAddress(Memory.dataBaseAddress); + executePane.getDataSegmentWindow().clearHighlighting(); + executePane.getLabelsWindow().setupTable(); + executePane.getTextSegmentWindow().setCodeHighlighting(true); + executePane.getTextSegmentWindow().highlightStepAtPC(); + registersPane.getRegistersWindow().clearWindow(); + registersPane.getCoprocessor1Window().clearWindow(); + registersPane.getCoprocessor0Window().clearWindow(); + VenusUI.setReset(true); + VenusUI.setStarted(false); + mainUI.getMainPane().setSelectedComponent(executePane); + + // Aug. 24, 2005 Ken Vollmar + SystemIO.resetFiles(); // Ensure that I/O "file descriptors" are initialized for a new program run + } - catch (ProcessingException pe) { - String errorReport = pe.errors().generateErrorAndWarningReport(); - mainUI.messagesPane.postMarsMessage(errorReport); - mainUI.messagesPane.postMarsMessage( - name+": operation completed with errors.\n\n"); - // Select editor line containing first error, and corresponding error message. - ArrayList errorMessages = pe.errors().getErrorMessages(); - for (int i=0; i LINE_LENGTH_LIMIT) { - result += "\n"; - lineLength = 0; + } + } + FileStatus.setAssembled(false); + FileStatus.set(FileStatus.NOT_EDITED); } - } - return result + ((lineLength==0)?"":"\n") + "\n"; - } - } + } + } + + // Handy little utility for building comma-separated list of filenames + // while not letting line length get out of hand. + private String buildFileNameList(String preamble, ArrayList programList) + { + String result = preamble; + int lineLength = result.length(); + for (int i = 0; i < programList.size(); i++) + { + String filename = ((MIPSprogram) programList.get(i)).getFilename(); + result += filename + ((i < programList.size() - 1) ? ", " : ""); + lineLength += filename.length(); + if (lineLength > LINE_LENGTH_LIMIT) + { + result += "\n"; + lineLength = 0; + } + } + return result + ((lineLength == 0) ? "" : "\n") + "\n"; + } +} diff --git a/src/main/java/mars/venus/RunBackstepAction.java b/src/main/java/mars/venus/RunBackstepAction.java index 6a92b7c..02635fc 100644 --- a/src/main/java/mars/venus/RunBackstepAction.java +++ b/src/main/java/mars/venus/RunBackstepAction.java @@ -1,9 +1,13 @@ - package mars.venus; - import mars.*; - import mars.mips.hardware.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import mars.Globals; +import mars.mips.hardware.Coprocessor0; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.Memory; +import mars.mips.hardware.RegisterFile; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -32,53 +36,61 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Action for the Run -> Backstep menu item + */ +public class RunBackstepAction extends GuiAction +{ + + String name; + + ExecutePane executePane; + + public RunBackstepAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + /** - * Action for the Run -> Backstep menu item - */ - public class RunBackstepAction extends GuiAction { - - String name; - ExecutePane executePane; - public RunBackstepAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - /** - * perform next simulated instruction step. - */ - public void actionPerformed(ActionEvent e){ - name = this.getValue(Action.NAME).toString(); - executePane = mainUI.getMainPane().getExecutePane(); - boolean done = false; - if(!FileStatus.isAssembled()){ - // note: this should never occur since backstepping is only enabled after successful assembly. - JOptionPane.showMessageDialog(mainUI,"The program must be assembled before it can be run."); + * perform next simulated instruction step. + */ + public void actionPerformed(ActionEvent e) + { + name = this.getValue(Action.NAME).toString(); + executePane = mainUI.getMainPane().getExecutePane(); + boolean done = false; + if (!FileStatus.isAssembled()) + { + // note: this should never occur since backstepping is only enabled after successful assembly. + JOptionPane.showMessageDialog(mainUI, "The program must be assembled before it can be run."); return; - } - mainUI.setStarted(true); - mainUI.messagesPane.setSelectedComponent(mainUI.messagesPane.runTab); - executePane.getTextSegmentWindow().setCodeHighlighting(true); - - if (Globals.getSettings().getBackSteppingEnabled()) { + } + VenusUI.setStarted(true); + mainUI.messagesPane.setSelectedComponent(mainUI.messagesPane.runTab); + executePane.getTextSegmentWindow().setCodeHighlighting(true); + + if (Globals.getSettings().getBackSteppingEnabled()) + { boolean inDelaySlot = Globals.program.getBackStepper().inDelaySlot(); // Added 25 June 2007 - Memory.getInstance().addObserver(executePane.getDataSegmentWindow()); - RegisterFile.addRegistersObserver(executePane.getRegistersWindow()); - Coprocessor0.addRegistersObserver(executePane.getCoprocessor0Window()); - Coprocessor1.addRegistersObserver(executePane.getCoprocessor1Window()); + Memory.getInstance().addObserver(executePane.getDataSegmentWindow()); + RegisterFile.addRegistersObserver(executePane.getRegistersWindow()); + Coprocessor0.addRegistersObserver(executePane.getCoprocessor0Window()); + Coprocessor1.addRegistersObserver(executePane.getCoprocessor1Window()); Globals.program.getBackStepper().backStep(); - Memory.getInstance().deleteObserver(executePane.getDataSegmentWindow()); - RegisterFile.deleteRegistersObserver(executePane.getRegistersWindow()); + Memory.getInstance().deleteObserver(executePane.getDataSegmentWindow()); + RegisterFile.deleteRegistersObserver(executePane.getRegistersWindow()); executePane.getRegistersWindow().updateRegisters(); executePane.getCoprocessor1Window().updateRegisters(); executePane.getCoprocessor0Window().updateRegisters(); executePane.getDataSegmentWindow().updateValues(); executePane.getTextSegmentWindow().highlightStepAtPC(inDelaySlot); // Argument aded 25 June 2007 FileStatus.set(FileStatus.RUNNABLE); - // if we've backed all the way, disable the button - // if (Globals.program.getBackStepper().empty()) { - // ((AbstractAction)((AbstractButton)e.getSource()).getAction()).setEnabled(false); - //} + // if we've backed all the way, disable the button + // if (Globals.program.getBackStepper().empty()) { + // ((AbstractAction)((AbstractButton)e.getSource()).getAction()).setEnabled(false); + //} /* if (pe !=null) { RunGoAction.resetMaxSteps(); @@ -93,7 +105,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. executePane.getTextSegmentWindow().highlightStepAtAddress(RegisterFile.getProgramCounter()-4); } */ - mainUI.setReset(false); - } - } - } \ No newline at end of file + VenusUI.setReset(false); + } + } +} diff --git a/src/main/java/mars/venus/RunClearBreakpointsAction.java b/src/main/java/mars/venus/RunClearBreakpointsAction.java index 887fa47..6985ef0 100644 --- a/src/main/java/mars/venus/RunClearBreakpointsAction.java +++ b/src/main/java/mars/venus/RunClearBreakpointsAction.java @@ -1,8 +1,11 @@ - package mars.venus; - import mars.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.event.*; +package mars.venus; + +import mars.Globals; + +import javax.swing.*; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,41 +34,44 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Run menu item to clear execution breakpoints that have been set. - * It is a listener and is notified whenever a breakpoint is added or removed, thus will - * set its enabled status true or false depending on whether breakpoints remain after that action. - */ - public class RunClearBreakpointsAction extends GuiAction implements TableModelListener { - - /** - * Create the object and register with text segment window as a listener on its table model. - * The table model has not been created yet, so text segment window will hang onto this - * registration info and transfer it to the table model upon creation (which happens with - * each successful assembly). - */ - public RunClearBreakpointsAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().registerTableModelListener(this); - } - /** - * When this option is selected, tell text segment window to clear breakpoints in its table model. - */ - public void actionPerformed(ActionEvent e) { - Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().clearAllBreakpoints(); - } - - /** - * Required TableModelListener method. This is response upon editing of text segment table - * model. The only editable column is breakpoints so this method is called only when user - * adds or removes a breakpoint. Gets new breakpoint count and sets enabled status - * accordingly. - */ - public void tableChanged(TableModelEvent e) { - setEnabled( - Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().getBreakpointCount()>0); - } - - } \ No newline at end of file + +/** + * Action class for the Run menu item to clear execution breakpoints that have been set. It is a listener and is + * notified whenever a breakpoint is added or removed, thus will set its enabled status true or false depending on + * whether breakpoints remain after that action. + */ +public class RunClearBreakpointsAction extends GuiAction implements TableModelListener +{ + + /** + * Create the object and register with text segment window as a listener on its table model. The table model has not + * been created yet, so text segment window will hang onto this registration info and transfer it to the table model + * upon creation (which happens with each successful assembly). + */ + public RunClearBreakpointsAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().registerTableModelListener(this); + } + + /** + * When this option is selected, tell text segment window to clear breakpoints in its table model. + */ + public void actionPerformed(ActionEvent e) + { + Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().clearAllBreakpoints(); + } + + /** + * Required TableModelListener method. This is response upon editing of text segment table model. The only + * editable column is breakpoints so this method is called only when user adds or removes a breakpoint. Gets new + * breakpoint count and sets enabled status accordingly. + */ + public void tableChanged(TableModelEvent e) + { + setEnabled( + Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().getBreakpointCount() > 0); + } + +} diff --git a/src/main/java/mars/venus/RunGoAction.java b/src/main/java/mars/venus/RunGoAction.java index 11678f1..7c88135 100644 --- a/src/main/java/mars/venus/RunGoAction.java +++ b/src/main/java/mars/venus/RunGoAction.java @@ -1,13 +1,14 @@ - package mars.venus; - import mars.*; - import mars.simulator.*; - import mars.mips.hardware.*; - import mars.util.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import mars.Globals; +import mars.ProcessingException; +import mars.mips.hardware.RegisterFile; +import mars.simulator.ProgramArgumentList; +import mars.simulator.Simulator; +import mars.util.SystemIO; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2007, Pete Sanderson and Kenneth Vollmar @@ -36,174 +37,195 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Run -> Go menu item (and toolbar icon) - */ - public class RunGoAction extends GuiAction { - - public static int defaultMaxSteps = -1; // "forever", formerly 10000000; // 10 million - public static int maxSteps = defaultMaxSteps; - private String name; - private ExecutePane executePane; - - public RunGoAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - /** - * Action to take when GO is selected -- run the MIPS program! - */ - public void actionPerformed(ActionEvent e) { - name = this.getValue(Action.NAME).toString(); - executePane = mainUI.getMainPane().getExecutePane(); - if(FileStatus.isAssembled()){ - if (!mainUI.getStarted()) { - processProgramArgumentsIfAny(); // DPS 17-July-2008 - } - if(mainUI.getReset()|| mainUI.getStarted()){ - - mainUI.setStarted(true); // added 8/27/05 - - mainUI.messagesPane.postMarsMessage( - name+": running "+FileStatus.getFile().getName()+"\n\n"); - mainUI.getMessagesPane().selectRunMessageTab(); - executePane.getTextSegmentWindow().setCodeHighlighting(false); - executePane.getTextSegmentWindow().unhighlightAllSteps(); - //FileStatus.set(FileStatus.RUNNING); - mainUI.setMenuState(FileStatus.RUNNING); - try { - int[] breakPoints = executePane.getTextSegmentWindow().getSortedBreakPointsArray(); - boolean done = Globals.program.simulateFromPC(breakPoints,maxSteps,this); - } - catch (ProcessingException pe) { - } - } - else{ - // This should never occur because at termination the Go and Step buttons are disabled. - JOptionPane.showMessageDialog(mainUI,"reset "+mainUI.getReset()+" started "+mainUI.getStarted());//"You must reset before you can execute the program again."); + +/** + * Action class for the Run -> Go menu item (and toolbar icon) + */ +public class RunGoAction extends GuiAction +{ + + public static int defaultMaxSteps = -1; // "forever", formerly 10000000; // 10 million + + public static int maxSteps = defaultMaxSteps; + + private String name; + + private ExecutePane executePane; + + public RunGoAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + /** + * Reset max steps limit to default value at termination of a simulated execution. + */ + + public static void resetMaxSteps() + { + maxSteps = defaultMaxSteps; + } + + /** + * Action to take when GO is selected -- run the MIPS program! + */ + public void actionPerformed(ActionEvent e) + { + name = this.getValue(Action.NAME).toString(); + executePane = mainUI.getMainPane().getExecutePane(); + if (FileStatus.isAssembled()) + { + if (!VenusUI.getStarted()) + { + processProgramArgumentsIfAny(); // DPS 17-July-2008 } - } - else{ + if (VenusUI.getReset() || VenusUI.getStarted()) + { + + VenusUI.setStarted(true); // added 8/27/05 + + mainUI.messagesPane.postMarsMessage( + name + ": running " + FileStatus.getFile().getName() + "\n\n"); + mainUI.getMessagesPane().selectRunMessageTab(); + executePane.getTextSegmentWindow().setCodeHighlighting(false); + executePane.getTextSegmentWindow().unhighlightAllSteps(); + //FileStatus.set(FileStatus.RUNNING); + mainUI.setMenuState(FileStatus.RUNNING); + try + { + int[] breakPoints = executePane.getTextSegmentWindow().getSortedBreakPointsArray(); + boolean done = Globals.program.simulateFromPC(breakPoints, maxSteps, this); + } + catch (ProcessingException pe) + { + } + } + else + { + // This should never occur because at termination the Go and Step buttons are disabled. + JOptionPane.showMessageDialog(mainUI, "reset " + VenusUI.getReset() + " started " + VenusUI.getStarted());//"You must reset before you can execute the program again."); + } + } + else + { // note: this should never occur since "Go" is only enabled after successful assembly. - JOptionPane.showMessageDialog(mainUI,"The program must be assembled before it can be run."); - } - } - - /** - * Method to be called when Pause is selected through menu/toolbar/shortcut. This should only - * happen when MIPS program is running (FileStatus.RUNNING). See VenusUI.java for enabled - * status of menu items based on FileStatus. Set GUI as if at breakpoint or executing - * step by step. - */ - - public void paused(boolean done, int pauseReason, ProcessingException pe) { + JOptionPane.showMessageDialog(mainUI, "The program must be assembled before it can be run."); + } + } + + /** + * Method to be called when Pause is selected through menu/toolbar/shortcut. This should only happen when MIPS + * program is running (FileStatus.RUNNING). See VenusUI.java for enabled status of menu items based on FileStatus. + * Set GUI as if at breakpoint or executing step by step. + */ + + public void paused(boolean done, int pauseReason, ProcessingException pe) + { // I doubt this can happen (pause when execution finished), but if so treat it as stopped. - if (done) { - stopped(pe,Simulator.NORMAL_TERMINATION); + if (done) + { + stopped(pe, Simulator.NORMAL_TERMINATION); return; - } - if (pauseReason == Simulator.BREAKPOINT) { + } + if (pauseReason == Simulator.BREAKPOINT) + { mainUI.messagesPane.postMarsMessage( - name+": execution paused at breakpoint: "+FileStatus.getFile().getName()+"\n\n"); - } - else { + name + ": execution paused at breakpoint: " + FileStatus.getFile().getName() + "\n\n"); + } + else + { mainUI.messagesPane.postMarsMessage( - name+": execution paused by user: "+FileStatus.getFile().getName()+"\n\n"); - } - mainUI.getMessagesPane().selectMarsMessageTab(); - executePane.getTextSegmentWindow().setCodeHighlighting(true); - executePane.getTextSegmentWindow().highlightStepAtPC(); - executePane.getRegistersWindow().updateRegisters(); - executePane.getCoprocessor1Window().updateRegisters(); - executePane.getCoprocessor0Window().updateRegisters(); - executePane.getDataSegmentWindow().updateValues(); - FileStatus.set(FileStatus.RUNNABLE); - mainUI.setReset(false); - } - - /** - * Method to be called when Stop is selected through menu/toolbar/shortcut. This should only - * happen when MIPS program is running (FileStatus.RUNNING). See VenusUI.java for enabled - * status of menu items based on FileStatus. Display finalized values as if execution - * terminated due to completion or exception. - */ - - public void stopped(ProcessingException pe, int reason) { - // show final register and data segment values. - executePane.getRegistersWindow().updateRegisters(); - executePane.getCoprocessor1Window().updateRegisters(); - executePane.getCoprocessor0Window().updateRegisters(); - executePane.getDataSegmentWindow().updateValues(); - FileStatus.set(FileStatus.TERMINATED); - SystemIO.resetFiles(); // close any files opened in MIPS program - // Bring coprocessor 0 to the front if terminated due to exception. - if (pe != null) { + name + ": execution paused by user: " + FileStatus.getFile().getName() + "\n\n"); + } + mainUI.getMessagesPane().selectMarsMessageTab(); + executePane.getTextSegmentWindow().setCodeHighlighting(true); + executePane.getTextSegmentWindow().highlightStepAtPC(); + executePane.getRegistersWindow().updateRegisters(); + executePane.getCoprocessor1Window().updateRegisters(); + executePane.getCoprocessor0Window().updateRegisters(); + executePane.getDataSegmentWindow().updateValues(); + FileStatus.set(FileStatus.RUNNABLE); + VenusUI.setReset(false); + } + + /** + * Method to be called when Stop is selected through menu/toolbar/shortcut. This should only happen when MIPS + * program is running (FileStatus.RUNNING). See VenusUI.java for enabled status of menu items based on FileStatus. + * Display finalized values as if execution terminated due to completion or exception. + */ + + public void stopped(ProcessingException pe, int reason) + { + // show final register and data segment values. + executePane.getRegistersWindow().updateRegisters(); + executePane.getCoprocessor1Window().updateRegisters(); + executePane.getCoprocessor0Window().updateRegisters(); + executePane.getDataSegmentWindow().updateValues(); + FileStatus.set(FileStatus.TERMINATED); + SystemIO.resetFiles(); // close any files opened in MIPS program + // Bring coprocessor 0 to the front if terminated due to exception. + if (pe != null) + { mainUI.getRegistersPane().setSelectedComponent(executePane.getCoprocessor0Window()); executePane.getTextSegmentWindow().setCodeHighlighting(true); executePane.getTextSegmentWindow().unhighlightAllSteps(); - executePane.getTextSegmentWindow().highlightStepAtAddress(RegisterFile.getProgramCounter()-4); - } - switch (reason) { - case Simulator.NORMAL_TERMINATION : - mainUI.getMessagesPane().postMarsMessage( - "\n"+name+": execution completed successfully.\n\n"); - mainUI.getMessagesPane().postRunMessage( - "\n-- program is finished running --\n\n"); - mainUI.getMessagesPane().selectRunMessageTab(); - break; - case Simulator.CLIFF_TERMINATION : - mainUI.getMessagesPane().postMarsMessage( - "\n"+name+": execution terminated by null instruction.\n\n"); - mainUI.getMessagesPane().postRunMessage( - "\n-- program is finished running (dropped off bottom) --\n\n"); - mainUI.getMessagesPane().selectRunMessageTab(); - break; - case Simulator.EXCEPTION : - mainUI.getMessagesPane().postMarsMessage( - pe.errors().generateErrorReport()); - mainUI.getMessagesPane().postMarsMessage( - "\n"+name+": execution terminated with errors.\n\n"); - break; - case Simulator.PAUSE_OR_STOP : - mainUI.getMessagesPane().postMarsMessage( - "\n"+name+": execution terminated by user.\n\n"); - mainUI.getMessagesPane().selectMarsMessageTab(); - break; - case Simulator.MAX_STEPS : - mainUI.getMessagesPane().postMarsMessage( - "\n"+name+": execution step limit of "+maxSteps+" exceeded.\n\n"); - mainUI.getMessagesPane().selectMarsMessageTab(); - break; - case Simulator.BREAKPOINT : // should never get here - break; - } - RunGoAction.resetMaxSteps(); - mainUI.setReset(false); - } - - /** - * Reset max steps limit to default value at termination of a simulated execution. - */ - - public static void resetMaxSteps() { - maxSteps = defaultMaxSteps; - } - - //////////////////////////////////////////////////////////////////////////////////// - // Method to store any program arguments into MIPS memory and registers before - // execution begins. Arguments go into the gap between $sp and kernel memory. - // Argument pointers and count go into runtime stack and $sp is adjusted accordingly. - // $a0 gets argument count (argc), $a1 gets stack address of first arg pointer (argv). - private void processProgramArgumentsIfAny() { - String programArguments = executePane.getTextSegmentWindow().getProgramArguments(); - if (programArguments == null || programArguments.length() == 0 || - !Globals.getSettings().getProgramArguments()) { + executePane.getTextSegmentWindow().highlightStepAtAddress(RegisterFile.getProgramCounter() - 4); + } + switch (reason) + { + case Simulator.NORMAL_TERMINATION: + mainUI.getMessagesPane().postMarsMessage( + "\n" + name + ": execution completed successfully.\n\n"); + mainUI.getMessagesPane().postRunMessage( + "\n-- program is finished running --\n\n"); + mainUI.getMessagesPane().selectRunMessageTab(); + break; + case Simulator.CLIFF_TERMINATION: + mainUI.getMessagesPane().postMarsMessage( + "\n" + name + ": execution terminated by null instruction.\n\n"); + mainUI.getMessagesPane().postRunMessage( + "\n-- program is finished running (dropped off bottom) --\n\n"); + mainUI.getMessagesPane().selectRunMessageTab(); + break; + case Simulator.EXCEPTION: + mainUI.getMessagesPane().postMarsMessage( + pe.errors().generateErrorReport()); + mainUI.getMessagesPane().postMarsMessage( + "\n" + name + ": execution terminated with errors.\n\n"); + break; + case Simulator.PAUSE_OR_STOP: + mainUI.getMessagesPane().postMarsMessage( + "\n" + name + ": execution terminated by user.\n\n"); + mainUI.getMessagesPane().selectMarsMessageTab(); + break; + case Simulator.MAX_STEPS: + mainUI.getMessagesPane().postMarsMessage( + "\n" + name + ": execution step limit of " + maxSteps + " exceeded.\n\n"); + mainUI.getMessagesPane().selectMarsMessageTab(); + break; + case Simulator.BREAKPOINT: // should never get here + break; + } + RunGoAction.resetMaxSteps(); + VenusUI.setReset(false); + } + + //////////////////////////////////////////////////////////////////////////////////// + // Method to store any program arguments into MIPS memory and registers before + // execution begins. Arguments go into the gap between $sp and kernel memory. + // Argument pointers and count go into runtime stack and $sp is adjusted accordingly. + // $a0 gets argument count (argc), $a1 gets stack address of first arg pointer (argv). + private void processProgramArgumentsIfAny() + { + String programArguments = executePane.getTextSegmentWindow().getProgramArguments(); + if (programArguments == null || programArguments.length() == 0 || + !Globals.getSettings().getProgramArguments()) + { return; - } - new ProgramArgumentList(programArguments).storeProgramArguments(); - } - - - } \ No newline at end of file + } + new ProgramArgumentList(programArguments).storeProgramArguments(); + } + + +} diff --git a/src/main/java/mars/venus/RunPauseAction.java b/src/main/java/mars/venus/RunPauseAction.java index 78409a7..1009c20 100644 --- a/src/main/java/mars/venus/RunPauseAction.java +++ b/src/main/java/mars/venus/RunPauseAction.java @@ -1,10 +1,9 @@ - package mars.venus; - import mars.simulator.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import mars.simulator.Simulator; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,20 +32,23 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Run -> Pause menu item (and toolbar icon) - */ - public class RunPauseAction extends GuiAction { - - public RunPauseAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e) { - Simulator.getInstance().stopExecution(this); - // RunGoAction's "paused" method will do the cleanup. - } - - } \ No newline at end of file + +/** + * Action class for the Run -> Pause menu item (and toolbar icon) + */ +public class RunPauseAction extends GuiAction +{ + + public RunPauseAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + Simulator.getInstance().stopExecution(this); + // RunGoAction's "paused" method will do the cleanup. + } + +} diff --git a/src/main/java/mars/venus/RunResetAction.java b/src/main/java/mars/venus/RunResetAction.java index 37da058..30abeba 100644 --- a/src/main/java/mars/venus/RunResetAction.java +++ b/src/main/java/mars/venus/RunResetAction.java @@ -1,10 +1,15 @@ - package mars.venus; - import mars.*; - import mars.util.*; - import mars.mips.hardware.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import mars.Globals; +import mars.ProcessingException; +import mars.mips.hardware.Coprocessor0; +import mars.mips.hardware.Coprocessor1; +import mars.mips.hardware.Memory; +import mars.mips.hardware.RegisterFile; +import mars.util.SystemIO; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2009, Pete Sanderson and Kenneth Vollmar @@ -33,67 +38,72 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action for the Run -> Reset menu item - */ - public class RunResetAction extends GuiAction { - - public RunResetAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - /** - * reset GUI components and MIPS resources - */ - public void actionPerformed(ActionEvent e){ - RunGoAction.resetMaxSteps(); - String name = this.getValue(Action.NAME).toString(); - ExecutePane executePane = mainUI.getMainPane().getExecutePane(); - // The difficult part here is resetting the data segment. Two approaches are: - // 1. After each assembly, get a deep copy of the Globals.memory array - // containing data segment. Then replace it upon reset. - // 2. Simply re-assemble the program upon reset, and the assembler will - // build a new data segment. Reset can only be done after a successful - // assembly, so there is "no" chance of assembler error. - // I am choosing the second approach although it will slow down the reset - // operation. The first approach requires additional Memory class methods. - try { - Globals.program.assemble(RunAssembleAction.getMIPSprogramsToAssemble(), - RunAssembleAction.getExtendedAssemblerEnabled(), - RunAssembleAction.getWarningsAreErrors()); - } - catch (ProcessingException pe) { - mainUI.getMessagesPane().postMarsMessage( - //pe.errors().generateErrorReport()); - "Unable to reset. Please close file then re-open and re-assemble.\n"); - return; - } - RegisterFile.resetRegisters(); - Coprocessor1.resetRegisters(); - Coprocessor0.resetRegisters(); - executePane.getRegistersWindow().clearHighlighting(); - executePane.getRegistersWindow().updateRegisters(); - executePane.getCoprocessor1Window().clearHighlighting(); - executePane.getCoprocessor1Window().updateRegisters(); - executePane.getCoprocessor0Window().clearHighlighting(); - executePane.getCoprocessor0Window().updateRegisters(); - executePane.getDataSegmentWindow().highlightCellForAddress(Memory.dataBaseAddress); - executePane.getDataSegmentWindow().clearHighlighting(); - executePane.getTextSegmentWindow().resetModifiedSourceCode(); - executePane.getTextSegmentWindow().setCodeHighlighting(true); - executePane.getTextSegmentWindow().highlightStepAtPC(); - mainUI.getRegistersPane().setSelectedComponent(executePane.getRegistersWindow()); - FileStatus.set(FileStatus.RUNNABLE); - mainUI.setReset(true); - mainUI.setStarted(false); - - // Aug. 24, 2005 Ken Vollmar - SystemIO.resetFiles( ); // Ensure that I/O "file descriptors" are initialized for a new program run - - mainUI.getMessagesPane().postRunMessage( - "\n"+name+": reset completed.\n\n"); - } - } \ No newline at end of file +/** + * Action for the Run -> Reset menu item + */ +public class RunResetAction extends GuiAction +{ + + public RunResetAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + /** + * reset GUI components and MIPS resources + */ + public void actionPerformed(ActionEvent e) + { + RunGoAction.resetMaxSteps(); + String name = this.getValue(Action.NAME).toString(); + ExecutePane executePane = mainUI.getMainPane().getExecutePane(); + // The difficult part here is resetting the data segment. Two approaches are: + // 1. After each assembly, get a deep copy of the Globals.memory array + // containing data segment. Then replace it upon reset. + // 2. Simply re-assemble the program upon reset, and the assembler will + // build a new data segment. Reset can only be done after a successful + // assembly, so there is "no" chance of assembler error. + // I am choosing the second approach although it will slow down the reset + // operation. The first approach requires additional Memory class methods. + try + { + Globals.program.assemble(RunAssembleAction.getMIPSprogramsToAssemble(), + RunAssembleAction.getExtendedAssemblerEnabled(), + RunAssembleAction.getWarningsAreErrors()); + } + catch (ProcessingException pe) + { + mainUI.getMessagesPane().postMarsMessage( + //pe.errors().generateErrorReport()); + "Unable to reset. Please close file then re-open and re-assemble.\n"); + return; + } + RegisterFile.resetRegisters(); + Coprocessor1.resetRegisters(); + Coprocessor0.resetRegisters(); + + executePane.getRegistersWindow().clearHighlighting(); + executePane.getRegistersWindow().updateRegisters(); + executePane.getCoprocessor1Window().clearHighlighting(); + executePane.getCoprocessor1Window().updateRegisters(); + executePane.getCoprocessor0Window().clearHighlighting(); + executePane.getCoprocessor0Window().updateRegisters(); + executePane.getDataSegmentWindow().highlightCellForAddress(Memory.dataBaseAddress); + executePane.getDataSegmentWindow().clearHighlighting(); + executePane.getTextSegmentWindow().resetModifiedSourceCode(); + executePane.getTextSegmentWindow().setCodeHighlighting(true); + executePane.getTextSegmentWindow().highlightStepAtPC(); + mainUI.getRegistersPane().setSelectedComponent(executePane.getRegistersWindow()); + FileStatus.set(FileStatus.RUNNABLE); + VenusUI.setReset(true); + VenusUI.setStarted(false); + + // Aug. 24, 2005 Ken Vollmar + SystemIO.resetFiles(); // Ensure that I/O "file descriptors" are initialized for a new program run + + mainUI.getMessagesPane().postRunMessage( + "\n" + name + ": reset completed.\n\n"); + } +} diff --git a/src/main/java/mars/venus/RunSpeedPanel.java b/src/main/java/mars/venus/RunSpeedPanel.java index d4e1bcd..bf448ce 100644 --- a/src/main/java/mars/venus/RunSpeedPanel.java +++ b/src/main/java/mars/venus/RunSpeedPanel.java @@ -1,9 +1,11 @@ - package mars.venus; - import mars.*; - import javax.swing.*; - import javax.swing.event.*; - import java.awt.*; - import java.awt.event.*; +package mars.venus; + +import mars.Globals; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import java.awt.*; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -32,118 +34,140 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Class for the Run speed slider control. One is created and can be obtained using - * getInstance(). - * - * @author Pete Sanderson - * @version August 2005 - */ - - public class RunSpeedPanel extends JPanel { - /** Constant that represents unlimited run speed. Compare with return value of - * getRunSpeed() to determine if set to unlimited. At the unlimited setting, the GUI - * will not attempt to update register and memory contents as each instruction - * is executed. This is the only possible value for command-line use of Mars. */ - public final static double UNLIMITED_SPEED = 40; - - private final static int SPEED_INDEX_MIN = 0; - private final static int SPEED_INDEX_MAX = 40; - private final static int SPEED_INDEX_INIT = 40; - private static final int SPEED_INDEX_INTERACTION_LIMIT = 35; - private double[] speedTable = { - .05, .1, .2, .3, .4, .5, 1, 2, 3, 4, 5, // 0-10 - 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 11-20 - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 21-30 - 26, 27, 28, 29, 30, UNLIMITED_SPEED,UNLIMITED_SPEED, // 31-37 - UNLIMITED_SPEED,UNLIMITED_SPEED,UNLIMITED_SPEED // 38-40 - }; - private JLabel sliderLabel=null; - private JSlider runSpeedSlider=null; - private static RunSpeedPanel runSpeedPanel=null; - private volatile int runSpeedIndex = SPEED_INDEX_MAX; - - /** - * Retrieve the run speed panel object - * - * @return the run speed panel - */ - - public static RunSpeedPanel getInstance() { - if (runSpeedPanel==null) { + +/** + * Class for the Run speed slider control. One is created and can be obtained using getInstance(). + * + * @author Pete Sanderson + * @version August 2005 + */ + +public class RunSpeedPanel extends JPanel +{ + /** + * Constant that represents unlimited run speed. Compare with return value of getRunSpeed() to determine if set to + * unlimited. At the unlimited setting, the GUI will not attempt to update register and memory contents as each + * instruction is executed. This is the only possible value for command-line use of Mars. + */ + public final static double UNLIMITED_SPEED = 40; + + private final static int SPEED_INDEX_MIN = 0; + + private final static int SPEED_INDEX_MAX = 40; + + private final static int SPEED_INDEX_INIT = 40; + + private static final int SPEED_INDEX_INTERACTION_LIMIT = 35; + + private static RunSpeedPanel runSpeedPanel = null; + + private final double[] speedTable = { + .05, .1, .2, .3, .4, .5, 1, 2, 3, 4, 5, // 0-10 + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, // 11-20 + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // 21-30 + 26, 27, 28, 29, 30, UNLIMITED_SPEED, UNLIMITED_SPEED, // 31-37 + UNLIMITED_SPEED, UNLIMITED_SPEED, UNLIMITED_SPEED // 38-40 + }; + + private JLabel sliderLabel = null; + + private JSlider runSpeedSlider = null; + + private volatile int runSpeedIndex = SPEED_INDEX_MAX; + + /* + * private constructor (this is a singleton class) + */ + private RunSpeedPanel() + { + super(new BorderLayout()); + runSpeedSlider = new JSlider(JSlider.HORIZONTAL, SPEED_INDEX_MIN, SPEED_INDEX_MAX, SPEED_INDEX_INIT); + runSpeedSlider.setSize(new Dimension(100, (int) runSpeedSlider.getSize().getHeight())); + runSpeedSlider.setMaximumSize(runSpeedSlider.getSize()); + runSpeedSlider.setMajorTickSpacing(5); + runSpeedSlider.setPaintTicks(true); //Create the label table + runSpeedSlider.addChangeListener(new RunSpeedListener()); + sliderLabel = new JLabel(setLabel(runSpeedIndex)); + sliderLabel.setHorizontalAlignment(JLabel.CENTER); + sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT); + this.add(sliderLabel, BorderLayout.NORTH); + this.add(runSpeedSlider, BorderLayout.CENTER); + this.setToolTipText("Simulation speed for \"Go\". At " + + ((int) speedTable[SPEED_INDEX_INTERACTION_LIMIT]) + " inst/sec or less, tables updated " + + "after each instruction."); + } + + /** + * Retrieve the run speed panel object + * + * @return the run speed panel + */ + + public static RunSpeedPanel getInstance() + { + if (runSpeedPanel == null) + { runSpeedPanel = new RunSpeedPanel(); - Globals.runSpeedPanelExists = true; // DPS 24 July 2008 (needed for standalone tools) - } - return runSpeedPanel; - } - - /* - * private constructor (this is a singleton class) - */ - private RunSpeedPanel() { - super(new BorderLayout()); - runSpeedSlider = new JSlider(JSlider.HORIZONTAL, SPEED_INDEX_MIN,SPEED_INDEX_MAX,SPEED_INDEX_INIT); - runSpeedSlider.setSize(new Dimension(100,(int)runSpeedSlider.getSize().getHeight())); - runSpeedSlider.setMaximumSize(runSpeedSlider.getSize()); - runSpeedSlider.setMajorTickSpacing(5); - runSpeedSlider.setPaintTicks(true); //Create the label table - runSpeedSlider.addChangeListener(new RunSpeedListener()); - sliderLabel = new JLabel(setLabel(runSpeedIndex)); - sliderLabel.setHorizontalAlignment(JLabel.CENTER); - sliderLabel.setAlignmentX(Component.CENTER_ALIGNMENT); - this.add(sliderLabel, BorderLayout.NORTH); - this.add(runSpeedSlider, BorderLayout.CENTER); - this.setToolTipText("Simulation speed for \"Go\". At "+ - ((int)speedTable[SPEED_INDEX_INTERACTION_LIMIT])+" inst/sec or less, tables updated "+ - "after each instruction."); - } - - /** - * returns current run speed setting, in instructions/second. Unlimited speed - * setting is equal to RunSpeedPanel.UNLIMITED_SPEED - * - * @return run speed setting in instructions/second. - */ - - public double getRunSpeed() { - return speedTable[runSpeedIndex]; - } - - /* - * set label wording depending on current speed setting - */ - private String setLabel(int index) { - String result = "Run speed ";; - if (index <= SPEED_INDEX_INTERACTION_LIMIT) { - if (speedTable[index] < 1) { - result += speedTable[index]; - } - else { - result += ((int)speedTable[index]); + Globals.runSpeedPanelExists = true; // DPS 24 July 2008 (needed for standalone tools) + } + return runSpeedPanel; + } + + /** + * returns current run speed setting, in instructions/second. Unlimited speed setting is equal to + * RunSpeedPanel.UNLIMITED_SPEED + * + * @return run speed setting in instructions/second. + */ + + public double getRunSpeed() + { + return speedTable[runSpeedIndex]; + } + + /* + * set label wording depending on current speed setting + */ + private String setLabel(int index) + { + String result = "Run speed "; + if (index <= SPEED_INDEX_INTERACTION_LIMIT) + { + if (speedTable[index] < 1) + { + result += speedTable[index]; + } + else + { + result += ((int) speedTable[index]); } result += " inst/sec"; - } - else { - result += ("at max (no interaction)"); - } - return result; - } - - - /* - * Both revises label as user slides and updates current index when sliding stops. - */ - - private class RunSpeedListener implements ChangeListener { - public void stateChanged(ChangeEvent e) { - JSlider source = (JSlider)e.getSource(); - if (!source.getValueIsAdjusting()) { - runSpeedIndex = (int)source.getValue(); - } - else { - sliderLabel.setText(setLabel(source.getValue())); + } + else + { + result += ("at max (no interaction)"); + } + return result; + } + + + /* + * Both revises label as user slides and updates current index when sliding stops. + */ + + private class RunSpeedListener implements ChangeListener + { + public void stateChanged(ChangeEvent e) + { + JSlider source = (JSlider) e.getSource(); + if (!source.getValueIsAdjusting()) + { + runSpeedIndex = source.getValue(); } - } - } - } \ No newline at end of file + else + { + sliderLabel.setText(setLabel(source.getValue())); + } + } + } +} diff --git a/src/main/java/mars/venus/RunStepAction.java b/src/main/java/mars/venus/RunStepAction.java index 0277500..136f336 100644 --- a/src/main/java/mars/venus/RunStepAction.java +++ b/src/main/java/mars/venus/RunStepAction.java @@ -1,10 +1,13 @@ - package mars.venus; - import mars.*; - import mars.simulator.*; - import mars.mips.hardware.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; +package mars.venus; + +import mars.Globals; +import mars.ProcessingException; +import mars.mips.hardware.RegisterFile; +import mars.simulator.ProgramArgumentList; +import mars.simulator.Simulator; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,95 +36,114 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + +/** + * Action for the Run -> Step menu item + */ +public class RunStepAction extends GuiAction +{ + + String name; + + ExecutePane executePane; + + public RunStepAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + /** - * Action for the Run -> Step menu item - */ - public class RunStepAction extends GuiAction { - - String name; - ExecutePane executePane; - public RunStepAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - /** - * perform next simulated instruction step. - */ - public void actionPerformed(ActionEvent e){ - name = this.getValue(Action.NAME).toString(); - executePane = mainUI.getMainPane().getExecutePane(); - boolean done = false; - if(FileStatus.isAssembled()){ - if (!mainUI.getStarted()) { // DPS 17-July-2008 - processProgramArgumentsIfAny(); - } - mainUI.setStarted(true); + * perform next simulated instruction step. + */ + public void actionPerformed(ActionEvent e) + { + name = this.getValue(Action.NAME).toString(); + executePane = mainUI.getMainPane().getExecutePane(); + boolean done = false; + if (FileStatus.isAssembled()) + { + if (!VenusUI.getStarted()) + { // DPS 17-July-2008 + processProgramArgumentsIfAny(); + } + VenusUI.setStarted(true); mainUI.messagesPane.setSelectedComponent(mainUI.messagesPane.runTab); executePane.getTextSegmentWindow().setCodeHighlighting(true); - try { - done = Globals.program.simulateStepAtPC(this); - } - catch (ProcessingException ev) {} - } - else{ + try + { + done = Globals.program.simulateStepAtPC(this); + } + catch (ProcessingException ev) + { + } + } + else + { // note: this should never occur since "Step" is only enabled after successful assembly. - JOptionPane.showMessageDialog(mainUI,"The program must be assembled before it can be run."); - } - } - - // When step is completed, control returns here (from execution thread, indirectly) - // to update the GUI. - public void stepped(boolean done, int reason, ProcessingException pe) { - executePane.getRegistersWindow().updateRegisters(); - executePane.getCoprocessor1Window().updateRegisters(); - executePane.getCoprocessor0Window().updateRegisters(); - executePane.getDataSegmentWindow().updateValues(); - if (!done) { + JOptionPane.showMessageDialog(mainUI, "The program must be assembled before it can be run."); + } + } + + // When step is completed, control returns here (from execution thread, indirectly) + // to update the GUI. + public void stepped(boolean done, int reason, ProcessingException pe) + { + executePane.getRegistersWindow().updateRegisters(); + executePane.getCoprocessor1Window().updateRegisters(); + executePane.getCoprocessor0Window().updateRegisters(); + executePane.getDataSegmentWindow().updateValues(); + if (!done) + { executePane.getTextSegmentWindow().highlightStepAtPC(); FileStatus.set(FileStatus.RUNNABLE); - } - if (done) { + } + if (done) + { RunGoAction.resetMaxSteps(); executePane.getTextSegmentWindow().unhighlightAllSteps(); FileStatus.set(FileStatus.TERMINATED); - } - if (done && pe == null) { + } + if (done && pe == null) + { mainUI.getMessagesPane().postMarsMessage( - "\n"+name+": execution "+ - ((reason==Simulator.CLIFF_TERMINATION) ? "terminated due to null instruction." - : "completed successfully.")+"\n\n"); + "\n" + name + ": execution " + + ((reason == Simulator.CLIFF_TERMINATION) ? "terminated due to null instruction." + : "completed successfully.") + "\n\n"); mainUI.getMessagesPane().postRunMessage( - "\n-- program is finished running "+ - ((reason==Simulator.CLIFF_TERMINATION)? "(dropped off bottom)" : "") +" --\n\n"); + "\n-- program is finished running " + + ((reason == Simulator.CLIFF_TERMINATION) ? "(dropped off bottom)" : "") + " --\n\n"); mainUI.getMessagesPane().selectRunMessageTab(); - } - if (pe !=null) { + } + if (pe != null) + { RunGoAction.resetMaxSteps(); mainUI.getMessagesPane().postMarsMessage( - pe.errors().generateErrorReport()); + pe.errors().generateErrorReport()); mainUI.getMessagesPane().postMarsMessage( - "\n"+name+": execution terminated with errors.\n\n"); + "\n" + name + ": execution terminated with errors.\n\n"); mainUI.getRegistersPane().setSelectedComponent(executePane.getCoprocessor0Window()); FileStatus.set(FileStatus.TERMINATED); // should be redundant. - executePane.getTextSegmentWindow().setCodeHighlighting(true); - executePane.getTextSegmentWindow().unhighlightAllSteps(); - executePane.getTextSegmentWindow().highlightStepAtAddress(RegisterFile.getProgramCounter()-4); - } - mainUI.setReset(false); - } - - //////////////////////////////////////////////////////////////////////////////////// - // Method to store any program arguments into MIPS memory and registers before - // execution begins. Arguments go into the gap between $sp and kernel memory. - // Argument pointers and count go into runtime stack and $sp is adjusted accordingly. - // $a0 gets argument count (argc), $a1 gets stack address of first arg pointer (argv). - private void processProgramArgumentsIfAny() { - String programArguments = executePane.getTextSegmentWindow().getProgramArguments(); - if (programArguments == null || programArguments.length() == 0 || - !Globals.getSettings().getProgramArguments()) { + executePane.getTextSegmentWindow().setCodeHighlighting(true); + executePane.getTextSegmentWindow().unhighlightAllSteps(); + executePane.getTextSegmentWindow().highlightStepAtAddress(RegisterFile.getProgramCounter() - 4); + } + VenusUI.setReset(false); + } + + //////////////////////////////////////////////////////////////////////////////////// + // Method to store any program arguments into MIPS memory and registers before + // execution begins. Arguments go into the gap between $sp and kernel memory. + // Argument pointers and count go into runtime stack and $sp is adjusted accordingly. + // $a0 gets argument count (argc), $a1 gets stack address of first arg pointer (argv). + private void processProgramArgumentsIfAny() + { + String programArguments = executePane.getTextSegmentWindow().getProgramArguments(); + if (programArguments == null || programArguments.length() == 0 || + !Globals.getSettings().getProgramArguments()) + { return; - } - new ProgramArgumentList(programArguments).storeProgramArguments(); - } - } \ No newline at end of file + } + new ProgramArgumentList(programArguments).storeProgramArguments(); + } +} diff --git a/src/main/java/mars/venus/RunStopAction.java b/src/main/java/mars/venus/RunStopAction.java index c5e3f33..a4d4593 100644 --- a/src/main/java/mars/venus/RunStopAction.java +++ b/src/main/java/mars/venus/RunStopAction.java @@ -1,10 +1,9 @@ - package mars.venus; - import mars.simulator.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import mars.simulator.Simulator; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -33,21 +32,24 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Run -> Stop menu item (and toolbar icon) - */ - public class RunStopAction extends GuiAction { - - - public RunStopAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e) { - Simulator.getInstance().stopExecution(this); - // RunGoAction's "stopped" method will take care of the cleanup. - } - - } \ No newline at end of file + +/** + * Action class for the Run -> Stop menu item (and toolbar icon) + */ +public class RunStopAction extends GuiAction +{ + + + public RunStopAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + Simulator.getInstance().stopExecution(this); + // RunGoAction's "stopped" method will take care of the cleanup. + } + +} diff --git a/src/main/java/mars/venus/RunToggleBreakpointsAction.java b/src/main/java/mars/venus/RunToggleBreakpointsAction.java index 4116fab..ab71be6 100644 --- a/src/main/java/mars/venus/RunToggleBreakpointsAction.java +++ b/src/main/java/mars/venus/RunToggleBreakpointsAction.java @@ -1,8 +1,9 @@ - package mars.venus; - import mars.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.event.*; +package mars.venus; + +import mars.Globals; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -31,29 +32,32 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Run menu item to clear execution breakpoints that have been set. - * It is a listener and is notified whenever a breakpoint is added or removed, thus will - * set its enabled status true or false depending on whether breakpoints remain after that action. - */ - public class RunToggleBreakpointsAction extends GuiAction { - - /** - * Create the object and register with text segment window as a listener on its table model. - * The table model has not been created yet, so text segment window will hang onto this - * registration info and transfer it to the table model upon creation (which happens with - * each successful assembly). - */ - public RunToggleBreakpointsAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - /** - * When this option is selected, tell text segment window to clear breakpoints in its table model. - */ - public void actionPerformed(ActionEvent e) { - Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().toggleBreakpoints(); - } - - } \ No newline at end of file + +/** + * Action class for the Run menu item to clear execution breakpoints that have been set. It is a listener and is + * notified whenever a breakpoint is added or removed, thus will set its enabled status true or false depending on + * whether breakpoints remain after that action. + */ +public class RunToggleBreakpointsAction extends GuiAction +{ + + /** + * Create the object and register with text segment window as a listener on its table model. The table model has not + * been created yet, so text segment window will hang onto this registration info and transfer it to the table model + * upon creation (which happens with each successful assembly). + */ + public RunToggleBreakpointsAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + /** + * When this option is selected, tell text segment window to clear breakpoints in its table model. + */ + public void actionPerformed(ActionEvent e) + { + Globals.getGui().getMainPane().getExecutePane().getTextSegmentWindow().toggleBreakpoints(); + } + +} diff --git a/src/main/java/mars/venus/SettingsAddressDisplayBaseAction.java b/src/main/java/mars/venus/SettingsAddressDisplayBaseAction.java index 0bf3f04..71e75b9 100644 --- a/src/main/java/mars/venus/SettingsAddressDisplayBaseAction.java +++ b/src/main/java/mars/venus/SettingsAddressDisplayBaseAction.java @@ -1,11 +1,9 @@ - package mars.venus; - import mars.simulator.*; - import mars.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import mars.Globals; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -34,22 +32,25 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Settings menu item to control number base (10 or 16) of memory addresses. - */ - public class SettingsAddressDisplayBaseAction extends GuiAction { - - - public SettingsAddressDisplayBaseAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e) { - boolean isHex = ((JCheckBoxMenuItem) e.getSource()).isSelected(); - Globals.getGui().getMainPane().getExecutePane().getAddressDisplayBaseChooser().setSelected(isHex); - Globals.getSettings().setDisplayAddressesInHex(isHex); - } - - } \ No newline at end of file + +/** + * Action class for the Settings menu item to control number base (10 or 16) of memory addresses. + */ +public class SettingsAddressDisplayBaseAction extends GuiAction +{ + + + public SettingsAddressDisplayBaseAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + boolean isHex = ((JCheckBoxMenuItem) e.getSource()).isSelected(); + Globals.getGui().getMainPane().getExecutePane().getAddressDisplayBaseChooser().setSelected(isHex); + Globals.getSettings().setDisplayAddressesInHex(isHex); + } + +} diff --git a/src/main/java/mars/venus/SettingsAssembleAllAction.java b/src/main/java/mars/venus/SettingsAssembleAllAction.java index 8f52bad..f081945 100644 --- a/src/main/java/mars/venus/SettingsAssembleAllAction.java +++ b/src/main/java/mars/venus/SettingsAssembleAllAction.java @@ -1,11 +1,9 @@ - package mars.venus; - import mars.simulator.*; - import mars.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import mars.Globals; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -34,22 +32,25 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Settings menu item to determine whether assemble operation applies - * only to current file or to all files in its directory. - */ - public class SettingsAssembleAllAction extends GuiAction { - - - public SettingsAssembleAllAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e) { - Globals.getSettings().setAssembleAllEnabled( - ((JCheckBoxMenuItem) e.getSource()).isSelected()); - } - - } \ No newline at end of file + +/** + * Action class for the Settings menu item to determine whether assemble operation applies only to current file or to + * all files in its directory. + */ +public class SettingsAssembleAllAction extends GuiAction +{ + + + public SettingsAssembleAllAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + Globals.getSettings().setAssembleAllEnabled( + ((JCheckBoxMenuItem) e.getSource()).isSelected()); + } + +} diff --git a/src/main/java/mars/venus/SettingsAssembleOnOpenAction.java b/src/main/java/mars/venus/SettingsAssembleOnOpenAction.java index f34d075..bc1a404 100644 --- a/src/main/java/mars/venus/SettingsAssembleOnOpenAction.java +++ b/src/main/java/mars/venus/SettingsAssembleOnOpenAction.java @@ -1,11 +1,9 @@ - package mars.venus; - import mars.simulator.*; - import mars.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import mars.Globals; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2006, Pete Sanderson and Kenneth Vollmar @@ -34,21 +32,24 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Settings menu item to control automatic assemble of file upon opening. - */ - public class SettingsAssembleOnOpenAction extends GuiAction { - - - public SettingsAssembleOnOpenAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e) { - Globals.getSettings().setAssembleOnOpenEnabled( - ((JCheckBoxMenuItem) e.getSource()).isSelected()); - } - - } \ No newline at end of file + +/** + * Action class for the Settings menu item to control automatic assemble of file upon opening. + */ +public class SettingsAssembleOnOpenAction extends GuiAction +{ + + + public SettingsAssembleOnOpenAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + Globals.getSettings().setAssembleOnOpenEnabled( + ((JCheckBoxMenuItem) e.getSource()).isSelected()); + } + +} diff --git a/src/main/java/mars/venus/SettingsDelayedBranchingAction.java b/src/main/java/mars/venus/SettingsDelayedBranchingAction.java index fae7f70..d1d3f34 100644 --- a/src/main/java/mars/venus/SettingsDelayedBranchingAction.java +++ b/src/main/java/mars/venus/SettingsDelayedBranchingAction.java @@ -1,11 +1,10 @@ - package mars.venus; - import mars.simulator.*; - import mars.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import java.io.*; +package mars.venus; + +import mars.Globals; +import mars.simulator.Simulator; + +import javax.swing.*; +import java.awt.event.ActionEvent; /* Copyright (c) 2003-2007, Pete Sanderson and Kenneth Vollmar @@ -34,42 +33,43 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Settings menu item to control delayed branching. - * Note: Changing this setting while the current program is runnable - * (assembled, or stepped execution) or terminated triggers a re-assembly. - * This is necessary to maintain consistency because the machine - * code assembled for branch instructions differs depending on - * this setting -- would branch to incorrect address if setting - * were changed between assembly and execution. - * Note: This action is disabled while the MIPS program is running. - * The user need only pause or stop execution to re-enable it. - */ - public class SettingsDelayedBranchingAction extends GuiAction { - - - public SettingsDelayedBranchingAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - public void actionPerformed(ActionEvent e) { - Globals.getSettings().setDelayedBranchingEnabled( - ((JCheckBoxMenuItem) e.getSource()).isSelected()); - // 25 June 2007 Re-assemble if the situation demands it to maintain consistency. - if (Globals.getGui() != null && - (FileStatus.get() == FileStatus.RUNNABLE || - FileStatus.get() == FileStatus.RUNNING || - FileStatus.get() == FileStatus.TERMINATED) - ) { - // Stop execution if executing -- should NEVER happen because this - // Action's widget is disabled during MIPS execution. - if (FileStatus.get() == FileStatus.RUNNING) { - Simulator.getInstance().stopExecution(this); + +/** + * Action class for the Settings menu item to control delayed branching. Note: Changing this setting while the current + * program is runnable (assembled, or stepped execution) or terminated triggers a re-assembly. This is necessary to + * maintain consistency because the machine code assembled for branch instructions differs depending on this setting -- + * would branch to incorrect address if setting were changed between assembly and execution. Note: This action is + * disabled while the MIPS program is running. The user need only pause or stop execution to re-enable it. + */ +public class SettingsDelayedBranchingAction extends GuiAction +{ + + + public SettingsDelayedBranchingAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + public void actionPerformed(ActionEvent e) + { + Globals.getSettings().setDelayedBranchingEnabled( + ((JCheckBoxMenuItem) e.getSource()).isSelected()); + // 25 June 2007 Re-assemble if the situation demands it to maintain consistency. + if (Globals.getGui() != null && + (FileStatus.get() == FileStatus.RUNNABLE || + FileStatus.get() == FileStatus.RUNNING || + FileStatus.get() == FileStatus.TERMINATED) + ) + { + // Stop execution if executing -- should NEVER happen because this + // Action's widget is disabled during MIPS execution. + if (FileStatus.get() == FileStatus.RUNNING) + { + Simulator.getInstance().stopExecution(this); } Globals.getGui().getRunAssembleAction().actionPerformed(null); - } - } - - } \ No newline at end of file + } + } + +} diff --git a/src/main/java/mars/venus/SettingsEditorAction.java b/src/main/java/mars/venus/SettingsEditorAction.java index f32681e..131807e 100644 --- a/src/main/java/mars/venus/SettingsEditorAction.java +++ b/src/main/java/mars/venus/SettingsEditorAction.java @@ -1,17 +1,20 @@ - package mars.venus; - import mars.simulator.*; - import mars.*; - import mars.util.*; - import mars.venus.editors.jeditsyntax.*; - import mars.venus.editors.jeditsyntax.tokenmarker.*; - import java.util.*; - import java.awt.*; - import java.awt.event.*; - import javax.swing.*; - import javax.swing.text.*; - import javax.swing.border.*; - import javax.swing.event.*; - import java.io.*; +package mars.venus; + +import mars.Globals; +import mars.Settings; +import mars.venus.editors.jeditsyntax.SyntaxStyle; +import mars.venus.editors.jeditsyntax.SyntaxUtilities; +import mars.venus.editors.jeditsyntax.tokenmarker.MIPSTokenMarker; + +import javax.swing.*; +import javax.swing.border.BevelBorder; +import javax.swing.border.Border; +import javax.swing.border.LineBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.text.Caret; +import java.awt.*; +import java.awt.event.*; /* Copyright (c) 2003-2011, Pete Sanderson and Kenneth Vollmar @@ -40,101 +43,137 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - - /** - * Action class for the Settings menu item for text editor settings. - */ - public class SettingsEditorAction extends GuiAction { - - JDialog editorDialog; - JComboBox fontFamilySelector, fontStyleSelector; - JSlider tabSizeSelector; - JTextField fontSizeDisplay; - - // Used to determine upon OK, whether or not anything has changed. - String initialFontFamily, initialFontStyle, initialFontSize; - - /** - * Create a new SettingsEditorAction. Has all the GuiAction parameters. - */ - public SettingsEditorAction(String name, Icon icon, String descrip, - Integer mnemonic, KeyStroke accel, VenusUI gui) { - super(name, icon, descrip, mnemonic, accel, gui); - } - - /** - * When this action is triggered, launch a dialog to view and modify - * editor settings. - */ - public void actionPerformed(ActionEvent e) { - editorDialog = new EditorFontDialog(Globals.getGui(), "Text Editor Settings", true, Globals.getSettings().getEditorFont() ); - editorDialog.setVisible(true); - - } - - private static final int gridVGap = 2; - private static final int gridHGap = 2; - private static final Border ColorSelectButtonEnabledBorder = new BevelBorder(BevelBorder.RAISED, Color.WHITE, Color.GRAY); - private static final Border ColorSelectButtonDisabledBorder = new LineBorder(Color.GRAY, 2); - - private static final String GENERIC_TOOL_TIP_TEXT = "Use generic editor (original MARS editor, similar to Notepad) instead of language-aware styled editor"; - - private static final String SAMPLE_TOOL_TIP_TEXT = "Current setting; modify using buttons to the right"; - private static final String FOREGROUND_TOOL_TIP_TEXT = "Click, to select text color"; - private static final String BOLD_TOOL_TIP_TEXT = "Toggle text bold style"; - private static final String ITALIC_TOOL_TIP_TEXT = "Toggle text italic style"; - private static final String DEFAULT_TOOL_TIP_TEXT = "Check, to select defaults (disables buttons)"; - private static final String BOLD_BUTTON_TOOL_TIP_TEXT = "B"; - private static final String ITALIC_BUTTON_TOOL_TIP_TEXT = "I"; - - private static final String TAB_SIZE_TOOL_TIP_TEXT = "Current tab size in characters"; - private static final String BLINK_SPINNER_TOOL_TIP_TEXT = "Current blinking rate in milliseconds"; - private static final String BLINK_SAMPLE_TOOL_TIP_TEXT = "Displays current blinking rate"; - private static final String CURRENT_LINE_HIGHLIGHT_TOOL_TIP_TEXT = "Check, to highlight line currently being edited"; - private static final String AUTO_INDENT_TOOL_TIP_TEXT = "Check, to enable auto-indent to previous line when Enter key is pressed"; - private static final String[] POPUP_GUIDANCE_TOOL_TIP_TEXT = { "Turns off instruction and directive guide popup while typing", - "Generates instruction guide popup after first letter of potential instruction is typed", - "Generates instruction guide popup after second letter of potential instruction is typed" - }; - - // Concrete font chooser class. - private class EditorFontDialog extends AbstractFontSettingDialog { - - private JButton[] foregroundButtons; - private JLabel[] samples; - private JToggleButton[] bold, italic; - private JCheckBox[] useDefault; - - private int[] syntaxStyleIndex; - private SyntaxStyle[] defaultStyles,initialStyles, currentStyles; - private Font previewFont; - - private JPanel dialogPanel,syntaxStylePanel,otherSettingsPanel; /////4 Aug 2010 - - private JSlider tabSizeSelector; - private JSpinner tabSizeSpinSelector, blinkRateSpinSelector, popupPrefixLengthSpinSelector; - private JCheckBox lineHighlightCheck, genericEditorCheck, autoIndentCheck; - private Caret blinkCaret; - private JTextField blinkSample; - private ButtonGroup popupGuidanceButtons; - private JRadioButton[] popupGuidanceOptions; - // Flag to indicate whether any syntax style buttons have been clicked - // since dialog created or most recent "apply". - private boolean syntaxStylesAction = false; - - private int initialEditorTabSize, initialCaretBlinkRate, initialPopupGuidance; - private boolean initialLineHighlighting, initialGenericTextEditor, initialAutoIndent; - - public EditorFontDialog(Frame owner, String title, boolean modality, Font font) { + +/** + * Action class for the Settings menu item for text editor settings. + */ +public class SettingsEditorAction extends GuiAction +{ + + private static final int gridVGap = 2; + + private static final int gridHGap = 2; + + private static final Border ColorSelectButtonEnabledBorder = new BevelBorder(BevelBorder.RAISED, Color.WHITE, Color.GRAY); + + private static final Border ColorSelectButtonDisabledBorder = new LineBorder(Color.GRAY, 2); + + private static final String GENERIC_TOOL_TIP_TEXT = "Use generic editor (original MARS editor, similar to Notepad) instead of language-aware styled editor"; + + private static final String SAMPLE_TOOL_TIP_TEXT = "Current setting; modify using buttons to the right"; + + private static final String FOREGROUND_TOOL_TIP_TEXT = "Click, to select text color"; + + private static final String BOLD_TOOL_TIP_TEXT = "Toggle text bold style"; + + private static final String ITALIC_TOOL_TIP_TEXT = "Toggle text italic style"; + + private static final String DEFAULT_TOOL_TIP_TEXT = "Check, to select defaults (disables buttons)"; + + private static final String BOLD_BUTTON_TOOL_TIP_TEXT = "B"; + + private static final String ITALIC_BUTTON_TOOL_TIP_TEXT = "I"; + + private static final String TAB_SIZE_TOOL_TIP_TEXT = "Current tab size in characters"; + + private static final String BLINK_SPINNER_TOOL_TIP_TEXT = "Current blinking rate in milliseconds"; + + private static final String BLINK_SAMPLE_TOOL_TIP_TEXT = "Displays current blinking rate"; + + private static final String CURRENT_LINE_HIGHLIGHT_TOOL_TIP_TEXT = "Check, to highlight line currently being edited"; + + private static final String AUTO_INDENT_TOOL_TIP_TEXT = "Check, to enable auto-indent to previous line when Enter key is pressed"; + + private static final String[] POPUP_GUIDANCE_TOOL_TIP_TEXT = {"Turns off instruction and directive guide popup while typing", + "Generates instruction guide popup after first letter of potential instruction is typed", + "Generates instruction guide popup after second letter of potential instruction is typed" + }; + + JDialog editorDialog; + + JComboBox fontFamilySelector, fontStyleSelector; + + JSlider tabSizeSelector; + + JTextField fontSizeDisplay; + + // Used to determine upon OK, whether or not anything has changed. + String initialFontFamily, initialFontStyle, initialFontSize; + + /** + * Create a new SettingsEditorAction. Has all the GuiAction parameters. + */ + public SettingsEditorAction(String name, Icon icon, String descrip, + Integer mnemonic, KeyStroke accel, VenusUI gui) + { + super(name, icon, descrip, mnemonic, accel, gui); + } + + /** + * When this action is triggered, launch a dialog to view and modify editor settings. + */ + public void actionPerformed(ActionEvent e) + { + editorDialog = new EditorFontDialog(Globals.getGui(), "Text Editor Settings", true, Globals.getSettings().getEditorFont()); + editorDialog.setVisible(true); + + } + + // Concrete font chooser class. + private class EditorFontDialog extends AbstractFontSettingDialog + { + + private JButton[] foregroundButtons; + + private JLabel[] samples; + + private JToggleButton[] bold, italic; + + private JCheckBox[] useDefault; + + private int[] syntaxStyleIndex; + + private SyntaxStyle[] defaultStyles, initialStyles, currentStyles; + + private Font previewFont; + + private JPanel dialogPanel, syntaxStylePanel, otherSettingsPanel; /////4 Aug 2010 + + private JSlider tabSizeSelector; + + private JSpinner tabSizeSpinSelector, blinkRateSpinSelector, popupPrefixLengthSpinSelector; + + private JCheckBox lineHighlightCheck, genericEditorCheck, autoIndentCheck; + + private Caret blinkCaret; + + private JTextField blinkSample; + + private ButtonGroup popupGuidanceButtons; + + private JRadioButton[] popupGuidanceOptions; + + // Flag to indicate whether any syntax style buttons have been clicked + // since dialog created or most recent "apply". + private boolean syntaxStylesAction = false; + + private int initialEditorTabSize, initialCaretBlinkRate, initialPopupGuidance; + + private boolean initialLineHighlighting, initialGenericTextEditor, initialAutoIndent; + + public EditorFontDialog(Frame owner, String title, boolean modality, Font font) + { super(owner, title, modality, font); - if (Globals.getSettings().getBooleanSetting(Settings.GENERIC_TEXT_EDITOR)) { - syntaxStylePanel.setVisible(false); - otherSettingsPanel.setVisible(false); + if (Globals.getSettings().getBooleanSetting(Settings.GENERIC_TEXT_EDITOR)) + { + syntaxStylePanel.setVisible(false); + otherSettingsPanel.setVisible(false); } - } - - // build the dialog here - protected JPanel buildDialogPanel() { + } + + // build the dialog here + protected JPanel buildDialogPanel() + { JPanel dialog = new JPanel(new BorderLayout()); JPanel fontDialogPanel = super.buildDialogPanel(); JPanel syntaxStylePanel = buildSyntaxStylePanel(); @@ -149,165 +188,192 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. this.syntaxStylePanel = syntaxStylePanel; /////4 Aug 2010 this.otherSettingsPanel = otherSettingsPanel; /////4 Aug 2010 return dialog; - } - - // Row of control buttons to be placed along the button of the dialog - protected Component buildControlPanel() { + } + + // Row of control buttons to be placed along the button of the dialog + protected Component buildControlPanel() + { Box controlPanel = Box.createHorizontalBox(); JButton okButton = new JButton("Apply and Close"); okButton.setToolTipText(SettingsHighlightingAction.CLOSE_TOOL_TIP_TEXT); okButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performApply(); closeDialog(); - } - }); + } + }); JButton applyButton = new JButton("Apply"); applyButton.setToolTipText(SettingsHighlightingAction.APPLY_TOOL_TIP_TEXT); applyButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performApply(); - } - }); + } + }); JButton cancelButton = new JButton("Cancel"); cancelButton.setToolTipText(SettingsHighlightingAction.CANCEL_TOOL_TIP_TEXT); cancelButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { closeDialog(); - } - }); + } + }); JButton resetButton = new JButton("Reset"); resetButton.setToolTipText(SettingsHighlightingAction.RESET_TOOL_TIP_TEXT); resetButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { reset(); - } - }); + } + }); initialGenericTextEditor = Globals.getSettings().getBooleanSetting(Settings.GENERIC_TEXT_EDITOR); genericEditorCheck = new JCheckBox("Use Generic Editor", initialGenericTextEditor); genericEditorCheck.setToolTipText(GENERIC_TOOL_TIP_TEXT); genericEditorCheck.addItemListener( - new ItemListener() { - public void itemStateChanged(ItemEvent e) { - if (e.getStateChange()==ItemEvent.SELECTED) { - syntaxStylePanel.setVisible(false); - otherSettingsPanel.setVisible(false); - } - else { - syntaxStylePanel.setVisible(true); - otherSettingsPanel.setVisible(true); + new ItemListener() + { + public void itemStateChanged(ItemEvent e) + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + syntaxStylePanel.setVisible(false); + otherSettingsPanel.setVisible(false); } - } - }); - + else + { + syntaxStylePanel.setVisible(true); + otherSettingsPanel.setVisible(true); + } + } + }); + controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(okButton); controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(applyButton); controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(cancelButton); - controlPanel.add(Box.createHorizontalGlue()); + controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(resetButton); controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(genericEditorCheck); controlPanel.add(Box.createHorizontalGlue()); return controlPanel; - } - - // User has clicked "Apply" or "Apply and Close" button. Required method, is - // abstract in superclass. - protected void apply(Font font) { + } + + // User has clicked "Apply" or "Apply and Close" button. Required method, is + // abstract in superclass. + protected void apply(Font font) + { Globals.getSettings().setBooleanSetting(Settings.GENERIC_TEXT_EDITOR, genericEditorCheck.isSelected()); Globals.getSettings().setBooleanSetting(Settings.EDITOR_CURRENT_LINE_HIGHLIGHTING, lineHighlightCheck.isSelected()); Globals.getSettings().setBooleanSetting(Settings.AUTO_INDENT, autoIndentCheck.isSelected()); - Globals.getSettings().setCaretBlinkRate(((Integer)blinkRateSpinSelector.getValue()).intValue()); + Globals.getSettings().setCaretBlinkRate(((Integer) blinkRateSpinSelector.getValue()).intValue()); Globals.getSettings().setEditorTabSize(tabSizeSelector.getValue()); - if (syntaxStylesAction) { - for (int i=0; i 0) { + } + settings.setDataSegmentHighlighting(currentDataHighlightSetting); + settings.setRegistersHighlighting(currentRegisterHighlightSetting); + ExecutePane executePane = Globals.getGui().getMainPane().getExecutePane(); + executePane.getRegistersWindow().refresh(); + executePane.getCoprocessor0Window().refresh(); + executePane.getCoprocessor1Window().refresh(); + // If a successful assembly has occured, the various panes will be populated with tables + // and we want to apply the new settings. If it has NOT occurred, there are no tables + // in the Data and Text segment windows so we don't want to disturb them. + // In the latter case, the component count for the Text segment window is 0 (but is 1 + // for Data segment window). + if (executePane.getTextSegmentWindow().getContentPane().getComponentCount() > 0) + { executePane.getDataSegmentWindow().updateValues(); executePane.getTextSegmentWindow().highlightStepAtPC(); - } - } - - // Called when Reset selected. - private void resetButtonColors() { - Settings settings = Globals.getSettings(); - dataHighlightButton.setText(getHighlightControlText(initialDataHighlightSetting)); - registerHighlightButton.setText(getHighlightControlText(initialRegisterHighlightSetting)); - Color backgroundSetting, foregroundSetting; - Font fontSetting; - for (int i=0; i= 0; i--) { - // namesPanel.add(new JLabel(configurationItemNames[i])); - // } + // for (int i=numItems-1; i >= 0; i--) { + // namesPanel.add(new JLabel(configurationItemNames[i])); + // } addressDisplay = new JTextField[numItems]; - for (int i=0; i= 0; i--) { - namesPanel.add(nameDisplay[i]); - valuesPanel.add(addressDisplay[i]); + for (int i = 0; i < numItems; i++) + { + nameDisplay[i] = new JLabel(); + addressDisplay[i] = new JTextField(); + addressDisplay[i].setEditable(false); + addressDisplay[i].setFont(monospaced); + } + // Display vertically from high to low memory addresses so + // add the components in reverse order. + for (int i = addressDisplay.length - 1; i >= 0; i--) + { + namesPanel.add(nameDisplay[i]); + valuesPanel.add(addressDisplay[i]); } setConfigDisplay(config); Box columns = Box.createHorizontalBox(); @@ -162,137 +185,159 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. columns.add(Box.createHorizontalStrut(6)); columns.add(namesPanel); displayPanel.add(columns); - return displayPanel; - } - - - // Carry out action for the radio buttons. - public void actionPerformed(ActionEvent e) { + return displayPanel; + } + + + // Carry out action for the radio buttons. + public void actionPerformed(ActionEvent e) + { MemoryConfiguration config = ((ConfigurationButton) e.getSource()).getConfiguration(); setConfigDisplay(config); this.selectedConfigurationButton = (ConfigurationButton) e.getSource(); - } - - - // Row of control buttons to be placed along the button of the dialog - private Component buildControlPanel() { + } + + + // Row of control buttons to be placed along the button of the dialog + private Component buildControlPanel() + { Box controlPanel = Box.createHorizontalBox(); JButton okButton = new JButton("Apply and Close"); okButton.setToolTipText(SettingsHighlightingAction.CLOSE_TOOL_TIP_TEXT); okButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performApply(); performClose(); - } - }); + } + }); JButton applyButton = new JButton("Apply"); applyButton.setToolTipText(SettingsHighlightingAction.APPLY_TOOL_TIP_TEXT); applyButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performApply(); - } - }); + } + }); JButton cancelButton = new JButton("Cancel"); cancelButton.setToolTipText(SettingsHighlightingAction.CANCEL_TOOL_TIP_TEXT); cancelButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performClose(); - } - }); + } + }); JButton resetButton = new JButton("Reset"); resetButton.setToolTipText(SettingsHighlightingAction.RESET_TOOL_TIP_TEXT); resetButton.addActionListener( - new ActionListener() { - public void actionPerformed(ActionEvent e) { + new ActionListener() + { + public void actionPerformed(ActionEvent e) + { performReset(); - } - }); + } + }); controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(okButton); controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(applyButton); controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(cancelButton); - controlPanel.add(Box.createHorizontalGlue()); + controlPanel.add(Box.createHorizontalGlue()); controlPanel.add(resetButton); controlPanel.add(Box.createHorizontalGlue()); return controlPanel; - } - - private void performApply() { - if (MemoryConfigurations.setCurrentConfiguration(this.selectedConfigurationButton.getConfiguration())) { - Globals.getSettings().setMemoryConfiguration(this.selectedConfigurationButton.getConfiguration().getConfigurationIdentifier()); - Globals.getGui().getRegistersPane().getRegistersWindow().clearHighlighting(); - Globals.getGui().getRegistersPane().getRegistersWindow().updateRegisters(); - Globals.getGui().getMainPane().getExecutePane().getDataSegmentWindow().updateBaseAddressComboBox(); - // 21 July 2009 Re-assemble if the situation demands it to maintain consistency. - if (FileStatus.get() == FileStatus.RUNNABLE || - FileStatus.get() == FileStatus.RUNNING || - FileStatus.get() == FileStatus.TERMINATED) { - // Stop execution if executing -- should NEVER happen because this - // Action's widget is disabled during MIPS execution. - if (FileStatus.get() == FileStatus.RUNNING) { - Simulator.getInstance().stopExecution(thisAction); - } - Globals.getGui().getRunAssembleAction().actionPerformed(null); - } + } + + private void performApply() + { + if (MemoryConfigurations.setCurrentConfiguration(this.selectedConfigurationButton.getConfiguration())) + { + Globals.getSettings().setMemoryConfiguration(this.selectedConfigurationButton.getConfiguration().getConfigurationIdentifier()); + Globals.getGui().getRegistersPane().getRegistersWindow().clearHighlighting(); + Globals.getGui().getRegistersPane().getRegistersWindow().updateRegisters(); + Globals.getGui().getMainPane().getExecutePane().getDataSegmentWindow().updateBaseAddressComboBox(); + // 21 July 2009 Re-assemble if the situation demands it to maintain consistency. + if (FileStatus.get() == FileStatus.RUNNABLE || + FileStatus.get() == FileStatus.RUNNING || + FileStatus.get() == FileStatus.TERMINATED) + { + // Stop execution if executing -- should NEVER happen because this + // Action's widget is disabled during MIPS execution. + if (FileStatus.get() == FileStatus.RUNNING) + { + Simulator.getInstance().stopExecution(thisAction); + } + Globals.getGui().getRunAssembleAction().actionPerformed(null); + } } - } - - private void performClose() { + } + + private void performClose() + { this.setVisible(false); this.dispose(); - } - - private void performReset() { + } + + private void performReset() + { this.selectedConfigurationButton = this.initialConfigurationButton; this.selectedConfigurationButton.setSelected(true); setConfigDisplay(this.selectedConfigurationButton.getConfiguration()); - } - - - // Set name values in JLabels and address values in the JTextFields - private void setConfigDisplay(MemoryConfiguration config) { + } + + + // Set name values in JLabels and address values in the JTextFields + private void setConfigDisplay(MemoryConfiguration config) + { String[] configurationItemNames = config.getConfigurationItemNames(); int[] configurationItemValues = config.getConfigurationItemValues(); - // Will use TreeMap to extract list of address-name pairs sorted by - // hex-stringified address. This will correctly handle kernel addresses, - // whose int values are negative and thus normal sorting yields incorrect - // results. There can be duplicate addresses, so I concatenate the name - // onto the address to make each key unique. Then slice off the name upon - // extraction. + // Will use TreeMap to extract list of address-name pairs sorted by + // hex-stringified address. This will correctly handle kernel addresses, + // whose int values are negative and thus normal sorting yields incorrect + // results. There can be duplicate addresses, so I concatenate the name + // onto the address to make each key unique. Then slice off the name upon + // extraction. TreeMap treeSortedByAddress = new TreeMap(); - for (int i=0; i text-address mapping. Maintain a Hashtable of - * (text-address, model-row) pairs to speed text-address -> model-row mapping. - * The former is used for breakpoints and changing display base (e.g. base 10 - * to 16); the latter is used for highlighting. Both structures will remain - * consistent once set up, since address column is not editable. - */ - private int[] intAddresses; // index is table model row, value is text address - private Hashtable addressRows; // key is text address, value is table model row - private Hashtable executeMods; // key is table model row, value is original code, basic, source. - private Container contentPane; - private TextTableModel tableModel; - private Font tableCellFont = new Font("Monospaced",Font.PLAIN,12); - private boolean codeHighlighting; - private boolean breakpointsEnabled; // Added 31 Dec 2009 - private int highlightAddress; - private TableModelListener tableModelListener; - private boolean inDelaySlot; // Added 25 June 2007 - - private static String[] columnNames = {"Bkpt", "Address", "Code", "Basic", "Source"}; - private static final int BREAK_COLUMN = 0; - private static final int ADDRESS_COLUMN = 1; - private static final int CODE_COLUMN = 2; - private static final int BASIC_COLUMN = 3; - private static final int SOURCE_COLUMN = 4; - - private static final Font monospacedPlain12Point = new Font("Monospaced",Font.PLAIN,12); - // The following is displayed in the Basic and Source columns if existing code is overwritten using self-modifying code feature - private static final String modifiedCodeMarker = " ------ "; - - /** - * Constructor, sets up a new JInternalFrame. - **/ - - public TextSegmentWindow(){ - super("Text Segment", true, false, true, true); - Simulator.getInstance().addObserver(this); - Globals.getSettings().addObserver(this); - contentPane = this.getContentPane(); - codeHighlighting = true; - breakpointsEnabled = true; - programArgumentsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); - programArgumentsPanel.add(new JLabel("Program Arguments: ")); - programArgumentsTextField = new JTextField(PROGRAM_ARGUMENT_TEXTFIELD_COLUMNS); - programArgumentsTextField.setToolTipText("Arguments provided to program at runtime via $a0 (argc) and $a1 (argv)"); - programArgumentsPanel.add(programArgumentsTextField); - } - - - /** - * Method to be called once the user compiles the program. - * Should convert the lines of code over to the table rows and columns. - **/ - public void setupTable(){ - int addressBase = Globals.getGui().getMainPane().getExecutePane().getAddressDisplayBase(); - codeHighlighting = true; - breakpointsEnabled = true; - ArrayList sourceStatementList = Globals.program.getMachineList(); - data = new Object[sourceStatementList.size()][columnNames.length]; - intAddresses = new int[data.length]; - addressRows = new Hashtable(data.length); - executeMods = new Hashtable(data.length); - // Get highest source line number to determine #leading spaces so line numbers will vertically align - // In multi-file situation, this will not necessarily be the last line b/c sourceStatementList contains - // source lines from all files. DPS 3-Oct-10 - int maxSourceLineNumber = 0; - for (int i=sourceStatementList.size()-1; i>=0; i--) { +/** + * Creates the Text Segment window in the Execute tab of the UI + * + * @author Team JSpim + **/ + +public class TextSegmentWindow extends JInternalFrame implements Observer +{ + private static final int PROGRAM_ARGUMENT_TEXTFIELD_COLUMNS = 40; + + private static final int BREAK_COLUMN = 0; + + private static final int ADDRESS_COLUMN = 1; + + private static final int CODE_COLUMN = 2; + + private static final int BASIC_COLUMN = 3; + + private static final int SOURCE_COLUMN = 4; + + private static final Font monospacedPlain12Point = new Font("Monospaced", Font.PLAIN, 12); + + // The following is displayed in the Basic and Source columns if existing code is overwritten using self-modifying code feature + private static final String modifiedCodeMarker = " ------ "; + + private static final String[] columnNames = {"Bkpt", "Address", "Code", "Basic", "Source"}; + + private final JPanel programArgumentsPanel; // DPS 17-July-2008 + + private final JTextField programArgumentsTextField; // DPS 17-July-2008 + + private JTable table; + + private JScrollPane tableScroller; + + private Object[][] data; + + /* Maintain an int array of code addresses in parallel with ADDRESS_COLUMN, + * to speed model-row -> text-address mapping. Maintain a Hashtable of + * (text-address, model-row) pairs to speed text-address -> model-row mapping. + * The former is used for breakpoints and changing display base (e.g. base 10 + * to 16); the latter is used for highlighting. Both structures will remain + * consistent once set up, since address column is not editable. + */ + private int[] intAddresses; // index is table model row, value is text address + + private Hashtable addressRows; // key is text address, value is table model row + + private Hashtable executeMods; // key is table model row, value is original code, basic, source. + + private final Container contentPane; + + private TextTableModel tableModel; + + private final Font tableCellFont = new Font("Monospaced", Font.PLAIN, 12); + + private boolean codeHighlighting; + + private boolean breakpointsEnabled; // Added 31 Dec 2009 + + private int highlightAddress; + + private TableModelListener tableModelListener; + + private boolean inDelaySlot; // Added 25 June 2007 + + /** + * Constructor, sets up a new JInternalFrame. + **/ + + public TextSegmentWindow() + { + super("Text Segment", true, false, true, true); + Simulator.getInstance().addObserver(this); + Globals.getSettings().addObserver(this); + contentPane = this.getContentPane(); + codeHighlighting = true; + breakpointsEnabled = true; + programArgumentsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT)); + programArgumentsPanel.add(new JLabel("Program Arguments: ")); + programArgumentsTextField = new JTextField(PROGRAM_ARGUMENT_TEXTFIELD_COLUMNS); + programArgumentsTextField.setToolTipText("Arguments provided to program at runtime via $a0 (argc) and $a1 (argv)"); + programArgumentsPanel.add(programArgumentsTextField); + } + + + /** + * Method to be called once the user compiles the program. Should convert the lines of code over to the table rows + * and columns. + **/ + public void setupTable() + { + int addressBase = Globals.getGui().getMainPane().getExecutePane().getAddressDisplayBase(); + codeHighlighting = true; + breakpointsEnabled = true; + ArrayList sourceStatementList = Globals.program.getMachineList(); + data = new Object[sourceStatementList.size()][columnNames.length]; + intAddresses = new int[data.length]; + addressRows = new Hashtable(data.length); + executeMods = new Hashtable(data.length); + // Get highest source line number to determine #leading spaces so line numbers will vertically align + // In multi-file situation, this will not necessarily be the last line b/c sourceStatementList contains + // source lines from all files. DPS 3-Oct-10 + int maxSourceLineNumber = 0; + for (int i = sourceStatementList.size() - 1; i >= 0; i--) + { ProgramStatement statement = (ProgramStatement) sourceStatementList.get(i); - if (statement.getSourceLine() > maxSourceLineNumber) { - maxSourceLineNumber = statement.getSourceLine(); + if (statement.getSourceLine() > maxSourceLineNumber) + { + maxSourceLineNumber = statement.getSourceLine(); } - } - int sourceLineDigits = (""+maxSourceLineNumber).length(); - int leadingSpaces = 0; - int lastLine = -1; - for (int i = 0; i < sourceStatementList.size(); i++) { + } + int sourceLineDigits = ("" + maxSourceLineNumber).length(); + int leadingSpaces = 0; + int lastLine = -1; + for (int i = 0; i < sourceStatementList.size(); i++) + { ProgramStatement statement = (ProgramStatement) sourceStatementList.get(i); intAddresses[i] = statement.getAddress(); - addressRows.put(new Integer(intAddresses[i]), new Integer(i)); + addressRows.put(Integer.valueOf(intAddresses[i]), Integer.valueOf(i)); data[i][BREAK_COLUMN] = Boolean.FALSE; data[i][ADDRESS_COLUMN] = NumberDisplayBaseChooser.formatUnsignedInteger(statement.getAddress(), addressBase); data[i][CODE_COLUMN] = NumberDisplayBaseChooser.formatNumber(statement.getBinaryStatement(), 16); data[i][BASIC_COLUMN] = statement.getPrintableBasicAssemblyStatement(); String sourceString = ""; - if (!statement.getSource().equals("")) { - leadingSpaces = sourceLineDigits - ("" + statement.getSourceLine()).length(); - String lineNumber = " ".substring(0, leadingSpaces) - + statement.getSourceLine()+ ": "; - if (statement.getSourceLine()==lastLine) - lineNumber=" ".substring(0, sourceLineDigits)+" "; - sourceString = lineNumber - + mars.util.EditorFont.substituteSpacesForTabs(statement.getSource()); + if (!statement.getSource().equals("")) + { + leadingSpaces = sourceLineDigits - ("" + statement.getSourceLine()).length(); + String lineNumber = " ".substring(0, leadingSpaces) + + statement.getSourceLine() + ": "; + if (statement.getSourceLine() == lastLine) + { + lineNumber = " ".substring(0, sourceLineDigits) + " "; + } + sourceString = lineNumber + + mars.util.EditorFont.substituteSpacesForTabs(statement.getSource()); } data[i][SOURCE_COLUMN] = sourceString; - lastLine=statement.getSourceLine(); - } - contentPane.removeAll(); - tableModel = new TextTableModel(data); - if (tableModelListener!=null) { + lastLine = statement.getSourceLine(); + } + contentPane.removeAll(); + tableModel = new TextTableModel(data); + if (tableModelListener != null) + { tableModel.addTableModelListener(tableModelListener); tableModel.fireTableDataChanged();// initialize listener - } - table= new MyTippedJTable(tableModel); - - // prevents cells in row from being highlighted when user clicks on breakpoint checkbox - table.setRowSelectionAllowed(false); - - table.getColumnModel().getColumn(BREAK_COLUMN).setMinWidth(40); - table.getColumnModel().getColumn(ADDRESS_COLUMN).setMinWidth(80); - table.getColumnModel().getColumn(CODE_COLUMN).setMinWidth(80); - - table.getColumnModel().getColumn(BREAK_COLUMN).setMaxWidth(50); - table.getColumnModel().getColumn(ADDRESS_COLUMN).setMaxWidth(90); - table.getColumnModel().getColumn(CODE_COLUMN).setMaxWidth(90); - table.getColumnModel().getColumn(BASIC_COLUMN).setMaxWidth(200); - - table.getColumnModel().getColumn(BREAK_COLUMN).setPreferredWidth(40); - table.getColumnModel().getColumn(ADDRESS_COLUMN).setPreferredWidth(80); - table.getColumnModel().getColumn(CODE_COLUMN).setPreferredWidth(80); - table.getColumnModel().getColumn(BASIC_COLUMN).setPreferredWidth(160); - table.getColumnModel().getColumn(SOURCE_COLUMN).setPreferredWidth(280); - - CodeCellRenderer codeStepHighlighter = new CodeCellRenderer(); - table.getColumnModel().getColumn(BASIC_COLUMN).setCellRenderer(codeStepHighlighter); - table.getColumnModel().getColumn(SOURCE_COLUMN).setCellRenderer(codeStepHighlighter); - // to render String right-justified in mono font - table.getColumnModel().getColumn(ADDRESS_COLUMN).setCellRenderer(new MonoRightCellRenderer()); - table.getColumnModel().getColumn(CODE_COLUMN).setCellRenderer(new MachineCodeCellRenderer()); - table.getColumnModel().getColumn(BREAK_COLUMN).setCellRenderer(new CheckBoxTableCellRenderer()); - reorderColumns(); // Re-order columns according to current preference... - // Add listener to catch column re-ordering for updating settings. - table.getColumnModel().addColumnModelListener(new MyTableColumnMovingListener()); - - tableScroller = new JScrollPane(table, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); - contentPane.add(tableScroller); - if (Globals.getSettings().getProgramArguments()) { - addProgramArgumentsPanel(); - } - - deleteAsTextSegmentObserver(); - if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) { + } + table = new MyTippedJTable(tableModel); + + // prevents cells in row from being highlighted when user clicks on breakpoint checkbox + table.setRowSelectionAllowed(false); + + table.getColumnModel().getColumn(BREAK_COLUMN).setMinWidth(40); + table.getColumnModel().getColumn(ADDRESS_COLUMN).setMinWidth(80); + table.getColumnModel().getColumn(CODE_COLUMN).setMinWidth(80); + + table.getColumnModel().getColumn(BREAK_COLUMN).setMaxWidth(50); + table.getColumnModel().getColumn(ADDRESS_COLUMN).setMaxWidth(90); + table.getColumnModel().getColumn(CODE_COLUMN).setMaxWidth(90); + table.getColumnModel().getColumn(BASIC_COLUMN).setMaxWidth(200); + + table.getColumnModel().getColumn(BREAK_COLUMN).setPreferredWidth(40); + table.getColumnModel().getColumn(ADDRESS_COLUMN).setPreferredWidth(80); + table.getColumnModel().getColumn(CODE_COLUMN).setPreferredWidth(80); + table.getColumnModel().getColumn(BASIC_COLUMN).setPreferredWidth(160); + table.getColumnModel().getColumn(SOURCE_COLUMN).setPreferredWidth(280); + + CodeCellRenderer codeStepHighlighter = new CodeCellRenderer(); + table.getColumnModel().getColumn(BASIC_COLUMN).setCellRenderer(codeStepHighlighter); + table.getColumnModel().getColumn(SOURCE_COLUMN).setCellRenderer(codeStepHighlighter); + // to render String right-justified in mono font + table.getColumnModel().getColumn(ADDRESS_COLUMN).setCellRenderer(new MonoRightCellRenderer()); + table.getColumnModel().getColumn(CODE_COLUMN).setCellRenderer(new MachineCodeCellRenderer()); + table.getColumnModel().getColumn(BREAK_COLUMN).setCellRenderer(new CheckBoxTableCellRenderer()); + reorderColumns(); // Re-order columns according to current preference... + // Add listener to catch column re-ordering for updating settings. + table.getColumnModel().addColumnModelListener(new MyTableColumnMovingListener()); + + tableScroller = new JScrollPane(table, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + contentPane.add(tableScroller); + if (Globals.getSettings().getProgramArguments()) + { + addProgramArgumentsPanel(); + } + + deleteAsTextSegmentObserver(); + if (Globals.getSettings().getBooleanSetting(Settings.SELF_MODIFYING_CODE_ENABLED)) + { addAsTextSegmentObserver(); - } - } - - //////////// Support for program arguments added DPS 17-July-2008 ////////////// - /** - * Get program arguments from text field in south border of text segment window. - * - * @return String containing program arguments - */ - public String getProgramArguments() { - return programArgumentsTextField.getText(); - } - - public void addProgramArgumentsPanel() { - // Don't add it if text segment window blank (file closed or no assemble yet) - if (contentPane != null && contentPane.getComponentCount() > 0) { - contentPane.add(programArgumentsPanel, BorderLayout.NORTH); - contentPane.validate(); - } - } - - public void removeProgramArgumentsPanel() { - if (contentPane != null) { + } + } + + //////////// Support for program arguments added DPS 17-July-2008 ////////////// + + /** + * Get program arguments from text field in south border of text segment window. + * + * @return String containing program arguments + */ + public String getProgramArguments() + { + return programArgumentsTextField.getText(); + } + + public void addProgramArgumentsPanel() + { + // Don't add it if text segment window blank (file closed or no assemble yet) + if (contentPane != null && contentPane.getComponentCount() > 0) + { + contentPane.add(programArgumentsPanel, BorderLayout.NORTH); + contentPane.validate(); + } + } + + public void removeProgramArgumentsPanel() + { + if (contentPane != null) + { contentPane.remove(programArgumentsPanel); contentPane.validate(); - } - } - // - ///////////////////////// end program arguments section //////////////////////// - - /** - * remove all components - */ - public void clearWindow() { - contentPane.removeAll(); - } - - - /** - * Assign listener to Table model. Used for breakpoints, since that is the only editable - * column in the table. Since table model objects are transient (get a new one with each - * successful assemble), this method will simply keep the identity of the listener then - * add it as a listener each time a new table model object is created. Limit 1 listener. - */ - public void registerTableModelListener(TableModelListener tml) { - tableModelListener = tml; - } - - /** - * Redisplay the addresses. This should only be done when address display base is - * modified (e.g. between base 16 hex and base 10 dec). - */ - public void updateCodeAddresses() { - if (contentPane.getComponentCount() == 0) + } + } + // + ///////////////////////// end program arguments section //////////////////////// + + /** + * remove all components + */ + public void clearWindow() + { + contentPane.removeAll(); + } + + + /** + * Assign listener to Table model. Used for breakpoints, since that is the only editable column in the table. + * Since table model objects are transient (get a new one with each successful assemble), this method will simply + * keep the identity of the listener then add it as a listener each time a new table model object is created. Limit + * 1 listener. + */ + public void registerTableModelListener(TableModelListener tml) + { + tableModelListener = tml; + } + + /** + * Redisplay the addresses. This should only be done when address display base is modified (e.g. between base 16 + * hex and base 10 dec). + */ + public void updateCodeAddresses() + { + if (contentPane.getComponentCount() == 0) + { return; // ignore if no content to change - int addressBase = Globals.getGui().getMainPane().getExecutePane().getAddressDisplayBase(); - int address; - String formattedAddress; - for (int i=0; i do nothing. - if (tableModel.getValueAt(row, CODE_COLUMN).equals(strValue)) { - return; - } - mc = new ModifiedCode( - row, - tableModel.getValueAt(row, CODE_COLUMN), - tableModel.getValueAt(row, BASIC_COLUMN), - tableModel.getValueAt(row, SOURCE_COLUMN) - ); - executeMods.put(row, mc); - // make a ProgramStatement and get basic code to display in BASIC_COLUMN - strBasic = new ProgramStatement(value,address).getPrintableBasicAssemblyStatement(); - } - else { - // If restored to original value, restore the basic and source - // This will be the case upon backstepping. - if (mc.getCode().equals(strValue)) { - strBasic = (String) mc.getBasic(); - strSource = (String) mc.getSource(); - // remove from executeMods since we are back to original - executeMods.remove(row); - } - else { - // make a ProgramStatement and get basic code to display in BASIC_COLUMN - strBasic = new ProgramStatement(value,address).getPrintableBasicAssemblyStatement(); - } - } - // For the code column, we don't want to do the following: - // tableModel.setValueAt(strValue, row, CODE_COLUMN) - // because that method will write to memory using Memory.setRawWord() which will - // trigger notification to observers, which brings us back to here!!! Infinite - // indirect recursion results. Neither fun nor productive. So what happens is - // this: (1) change to memory cell causes setValueAt() to be automatically be - // called. (2) it updates the memory cell which in turn notifies us which invokes - // the update() method - the method we're in right now. All we need to do here is - // update the table model then notify the controller/view to update its display. - data[row][CODE_COLUMN] = strValue; - tableModel.fireTableCellUpdated(row, CODE_COLUMN); - // The other columns do not present a problem since they are not editable by user. - tableModel.setValueAt(strBasic, row, BASIC_COLUMN); - tableModel.setValueAt(strSource, row, SOURCE_COLUMN); - // Let's update the value displayed in the DataSegmentWindow too. But it only observes memory while - // the MIPS program is running, and even then only in timed or step mode. There are good reasons - // for that. So we'll pretend to be Memory observable and send it a fake memory write update. - try { - Globals.getGui().getMainPane().getExecutePane().getDataSegmentWindow() - .update(Memory.getInstance(),new MemoryAccessNotice(AccessNotice.WRITE, address, value)); - } - catch (Exception e) { - // Not sure if anything bad can happen in this sequence, but if anything does we can let it go. - } + if (access.getAccessType() == AccessNotice.WRITE) + { + int address = access.getAddress(); + int value = access.getValue(); + String strValue = mars.util.Binary.intToHexString(access.getValue()); + String strBasic = modifiedCodeMarker; + String strSource = modifiedCodeMarker; + // Translate the address into table model row and modify the values in that row accordingly. + int row = 0; + try + { + row = findRowForAddress(address); + } + catch (IllegalArgumentException e) + { + return; // do nothing if address modified is outside the range of original program. + } + ModifiedCode mc = executeMods.get(row); + if (mc == null) + { // if not already modified + // Not already modified and new code is same as original --> do nothing. + if (tableModel.getValueAt(row, CODE_COLUMN).equals(strValue)) + { + return; + } + mc = new ModifiedCode( + row, + tableModel.getValueAt(row, CODE_COLUMN), + tableModel.getValueAt(row, BASIC_COLUMN), + tableModel.getValueAt(row, SOURCE_COLUMN) + ); + executeMods.put(row, mc); + // make a ProgramStatement and get basic code to display in BASIC_COLUMN + strBasic = new ProgramStatement(value, address).getPrintableBasicAssemblyStatement(); + } + else + { + // If restored to original value, restore the basic and source + // This will be the case upon backstepping. + if (mc.getCode().equals(strValue)) + { + strBasic = (String) mc.getBasic(); + strSource = (String) mc.getSource(); + // remove from executeMods since we are back to original + executeMods.remove(row); + } + else + { + // make a ProgramStatement and get basic code to display in BASIC_COLUMN + strBasic = new ProgramStatement(value, address).getPrintableBasicAssemblyStatement(); + } + } + // For the code column, we don't want to do the following: + // tableModel.setValueAt(strValue, row, CODE_COLUMN) + // because that method will write to memory using Memory.setRawWord() which will + // trigger notification to observers, which brings us back to here!!! Infinite + // indirect recursion results. Neither fun nor productive. So what happens is + // this: (1) change to memory cell causes setValueAt() to be automatically be + // called. (2) it updates the memory cell which in turn notifies us which invokes + // the update() method - the method we're in right now. All we need to do here is + // update the table model then notify the controller/view to update its display. + data[row][CODE_COLUMN] = strValue; + tableModel.fireTableCellUpdated(row, CODE_COLUMN); + // The other columns do not present a problem since they are not editable by user. + tableModel.setValueAt(strBasic, row, BASIC_COLUMN); + tableModel.setValueAt(strSource, row, SOURCE_COLUMN); + // Let's update the value displayed in the DataSegmentWindow too. But it only observes memory while + // the MIPS program is running, and even then only in timed or step mode. There are good reasons + // for that. So we'll pretend to be Memory observable and send it a fake memory write update. + try + { + Globals.getGui().getMainPane().getExecutePane().getDataSegmentWindow() + .update(Memory.getInstance(), new MemoryAccessNotice(AccessNotice.WRITE, address, value)); + } + catch (Exception e) + { + // Not sure if anything bad can happen in this sequence, but if anything does we can let it go. + } } - } - } - - /** - * Called by RunResetAction to restore display of any table rows that were - * overwritten due to self-modifying code feature. - */ - void resetModifiedSourceCode() { - if (executeMods != null && !executeMods.isEmpty()) { - for (Enumeration elements = executeMods.elements(); elements.hasMoreElements();) { - ModifiedCode mc = elements.nextElement(); - tableModel.setValueAt(mc.getCode(), mc.getRow(), CODE_COLUMN); - tableModel.setValueAt(mc.getBasic(), mc.getRow(), BASIC_COLUMN); - tableModel.setValueAt(mc.getSource(), mc.getRow(), SOURCE_COLUMN); + } + } + + /** + * Called by RunResetAction to restore display of any table rows that were overwritten due to self-modifying code + * feature. + */ + void resetModifiedSourceCode() + { + if (executeMods != null && !executeMods.isEmpty()) + { + for (Enumeration elements = executeMods.elements(); elements.hasMoreElements(); ) + { + ModifiedCode mc = elements.nextElement(); + tableModel.setValueAt(mc.getCode(), mc.getRow(), CODE_COLUMN); + tableModel.setValueAt(mc.getBasic(), mc.getRow(), BASIC_COLUMN); + tableModel.setValueAt(mc.getSource(), mc.getRow(), SOURCE_COLUMN); } executeMods.clear(); - } - } - - /** - * Return code address as an int, for the specified row of the table. This should only - * be used by the code renderer so I will not verify row. - */ - int getIntCodeAddressAtRow(int row) { - return intAddresses[row]; - } - - /** - * Returns number of breakpoints currently set. - * @return number of current breakpoints - */ - - public int getBreakpointCount() { - int breakpointCount = 0; - for(int i=0; i < data.length; i++){ - if (((Boolean)data[i][BREAK_COLUMN]).booleanValue()) { - breakpointCount++; + } + } + + /** + * Return code address as an int, for the specified row of the table. This should only be used by the code renderer + * so I will not verify row. + */ + int getIntCodeAddressAtRow(int row) + { + return intAddresses[row]; + } + + /** + * Returns number of breakpoints currently set. + * + * @return number of current breakpoints + */ + + public int getBreakpointCount() + { + int breakpointCount = 0; + for (int i = 0; i < data.length; i++) + { + if (((Boolean) data[i][BREAK_COLUMN]).booleanValue()) + { + breakpointCount++; } - } - return breakpointCount; - } - - /** - * Returns array of current breakpoints, each represented by a MIPS program counter address. - * These are stored in the BREAK_COLUMN of the table model. - * @return int array of breakpoints, sorted by PC address, or null if there are none. - */ - public int[] getSortedBreakPointsArray() { - int breakpointCount = getBreakpointCount(); - if (breakpointCount == 0 || !breakpointsEnabled) { // added second condition 31-dec-09 DPS + } + return breakpointCount; + } + + /** + * Returns array of current breakpoints, each represented by a MIPS program counter address. These are stored in the + * BREAK_COLUMN of the table model. + * + * @return int array of breakpoints, sorted by PC address, or null if there are none. + */ + public int[] getSortedBreakPointsArray() + { + int breakpointCount = getBreakpointCount(); + if (breakpointCount == 0 || !breakpointsEnabled) + { // added second condition 31-dec-09 DPS return null; - } - int[] breakpoints = new int[breakpointCount]; - breakpointCount = 0; - for(int i=0; i < data.length; i++){ - if (((Boolean)data[i][BREAK_COLUMN]).booleanValue()) { - breakpoints[breakpointCount++] = intAddresses[i]; + } + int[] breakpoints = new int[breakpointCount]; + breakpointCount = 0; + for (int i = 0; i < data.length; i++) + { + if (((Boolean) data[i][BREAK_COLUMN]).booleanValue()) + { + breakpoints[breakpointCount++] = intAddresses[i]; } - } - Arrays.sort(breakpoints); - return breakpoints; - } - - /** - * Clears all breakpoints that have been set since last assemble, and - * updates the display of the breakpoint column. - */ - public void clearAllBreakpoints() { - for(int i=0; i < tableModel.getRowCount(); i++){ - if (((Boolean)data[i][BREAK_COLUMN]).booleanValue()) { - // must use this method to assure display updated and listener notified - tableModel.setValueAt(Boolean.FALSE, i, BREAK_COLUMN); - } - } - // Handles an obscure situation: if you click to set some breakpoints then "immediately" clear them - // all using the shortcut (CTRL-K), the last checkmark set is not removed even though the breakpoint - // is removed (tableModel.setValueAt(Boolean.FALSE, i, BREAK_COLUMN)) and all the other checkmarks - // are removed. The checkmark remains although if you subsequently run the program it will blow - // through because the data model cell really has been cleared (contains false). Occurs only when - // the last checked breakpoint check box still has the "focus". There is but one renderer and editor - // per column. Getting the renderer and setting it "setSelected(false)" will not work. You have - // to get the editor instead. (PS, 7 Aug 2006) - ((JCheckBox)((DefaultCellEditor)table.getCellEditor(0,BREAK_COLUMN)).getComponent()).setSelected(false); - } - - - /** - * Highlights the source code line whose address matches the current - * program counter value. This is used for stepping through code - * execution and when reaching breakpoints. - */ - public void highlightStepAtPC() { - highlightStepAtAddress(RegisterFile.getProgramCounter(), false); - } - - /** - * Highlights the source code line whose address matches the current - * program counter value. This is used for stepping through code - * execution and when reaching breakpoints. - * @param inDelaySlot Set true if delayed branching is enabled and the - * instruction at this address is executing in the delay slot, false - * otherwise. - */ - public void highlightStepAtPC(boolean inDelaySlot) { - highlightStepAtAddress(RegisterFile.getProgramCounter(), inDelaySlot); - } - - /** - * Highlights the source code line whose address matches the given - * text segment address. - * - * @param address text segment address of instruction to be highlighted. - */ - - public void highlightStepAtAddress(int address) { - highlightStepAtAddress(address, false); - } - - /** - * Highlights the source code line whose address matches the given - * text segment address. - * - * @param address Text segment address of instruction to be highlighted. - * @param inDelaySlot Set true if delayed branching is enabled and the - * instruction at this address is executing in the delay slot, false - * otherwise. - */ - - public void highlightStepAtAddress(int address, boolean inDelaySlot) { - highlightAddress = address; - // Scroll if necessary to assure highlighted row is visible. - int row = 0; - try { + } + Arrays.sort(breakpoints); + return breakpoints; + } + + /** + * Clears all breakpoints that have been set since last assemble, and updates the display of the breakpoint column. + */ + public void clearAllBreakpoints() + { + for (int i = 0; i < tableModel.getRowCount(); i++) + { + if (((Boolean) data[i][BREAK_COLUMN]).booleanValue()) + { + // must use this method to assure display updated and listener notified + tableModel.setValueAt(Boolean.FALSE, i, BREAK_COLUMN); + } + } + // Handles an obscure situation: if you click to set some breakpoints then "immediately" clear them + // all using the shortcut (CTRL-K), the last checkmark set is not removed even though the breakpoint + // is removed (tableModel.setValueAt(Boolean.FALSE, i, BREAK_COLUMN)) and all the other checkmarks + // are removed. The checkmark remains although if you subsequently run the program it will blow + // through because the data model cell really has been cleared (contains false). Occurs only when + // the last checked breakpoint check box still has the "focus". There is but one renderer and editor + // per column. Getting the renderer and setting it "setSelected(false)" will not work. You have + // to get the editor instead. (PS, 7 Aug 2006) + ((JCheckBox) ((DefaultCellEditor) table.getCellEditor(0, BREAK_COLUMN)).getComponent()).setSelected(false); + } + + + /** + * Highlights the source code line whose address matches the current program counter value. This is used for + * stepping through code execution and when reaching breakpoints. + */ + public void highlightStepAtPC() + { + highlightStepAtAddress(RegisterFile.getProgramCounter(), false); + } + + /** + * Highlights the source code line whose address matches the current program counter value. This is used for + * stepping through code execution and when reaching breakpoints. + * + * @param inDelaySlot Set true if delayed branching is enabled and the instruction at this address is executing + * in the delay slot, false otherwise. + */ + public void highlightStepAtPC(boolean inDelaySlot) + { + highlightStepAtAddress(RegisterFile.getProgramCounter(), inDelaySlot); + } + + /** + * Highlights the source code line whose address matches the given text segment address. + * + * @param address text segment address of instruction to be highlighted. + */ + + public void highlightStepAtAddress(int address) + { + highlightStepAtAddress(address, false); + } + + /** + * Highlights the source code line whose address matches the given text segment address. + * + * @param address Text segment address of instruction to be highlighted. + * @param inDelaySlot Set true if delayed branching is enabled and the instruction at this address is executing + * in the delay slot, false otherwise. + */ + + public void highlightStepAtAddress(int address, boolean inDelaySlot) + { + highlightAddress = address; + // Scroll if necessary to assure highlighted row is visible. + int row = 0; + try + { row = findRowForAddress(address); - } - catch (IllegalArgumentException e) { - return; - } - table.scrollRectToVisible(table.getCellRect(row, 0, true)); - this.inDelaySlot = inDelaySlot;// Added 25 June 2007 - // Trigger highlighting, which is done by the column's cell renderer. - // IMPLEMENTATION NOTE: Pretty crude implementation; mark all rows - // as changed so assure that the previously highlighted row is - // unhighlighted. Would be better to keep track of previous row - // then fire two events: one for it and one for the new row. - table.tableChanged(new TableModelEvent(tableModel)); - //this.inDelaySlot = false;// Added 25 June 2007 - } - - /** Used to enable or disable source code highlighting. If true (normally while - * stepping through execution) then MIPS statement at current program counter - * is highlighted. The code column's cell renderer tests this variable. - * @param highlightSetting true to enable highlighting, false to disable. - */ - public void setCodeHighlighting(boolean highlightSetting) { - codeHighlighting = highlightSetting; - } - - /** - * Get code highlighting status. - * @return true if code highlighting currently enabled, false otherwise. - */ - public boolean getCodeHighlighting() { - return codeHighlighting; - } - - /** - * If any steps are highlighted, this erases the highlighting. - */ - public void unhighlightAllSteps() { - boolean saved = this.getCodeHighlighting(); - this.setCodeHighlighting(false); - table.tableChanged(new TableModelEvent(tableModel,0,data.length-1, BASIC_COLUMN)); - table.tableChanged(new TableModelEvent(tableModel,0,data.length-1, SOURCE_COLUMN)); - this.setCodeHighlighting(saved); - } - - /** - * Scroll the viewport so the step (table row) at the given text segment address - * is visible, vertically centered if possible, and selected. - * Developed July 2007 for new feature that shows source code step where - * label is defined when that label is clicked on in the Label Window. - * @param address text segment address of source code step. - */ - - void selectStepAtAddress(int address) { - int addressRow = 0; - try { + } + catch (IllegalArgumentException e) + { + return; + } + table.scrollRectToVisible(table.getCellRect(row, 0, true)); + this.inDelaySlot = inDelaySlot;// Added 25 June 2007 + // Trigger highlighting, which is done by the column's cell renderer. + // IMPLEMENTATION NOTE: Pretty crude implementation; mark all rows + // as changed so assure that the previously highlighted row is + // unhighlighted. Would be better to keep track of previous row + // then fire two events: one for it and one for the new row. + table.tableChanged(new TableModelEvent(tableModel)); + //this.inDelaySlot = false;// Added 25 June 2007 + } + + /** + * Get code highlighting status. + * + * @return true if code highlighting currently enabled, false otherwise. + */ + public boolean getCodeHighlighting() + { + return codeHighlighting; + } + + /** + * Used to enable or disable source code highlighting. If true (normally while stepping through execution) then + * MIPS statement at current program counter is highlighted. The code column's cell renderer tests this variable. + * + * @param highlightSetting true to enable highlighting, false to disable. + */ + public void setCodeHighlighting(boolean highlightSetting) + { + codeHighlighting = highlightSetting; + } + + /** + * If any steps are highlighted, this erases the highlighting. + */ + public void unhighlightAllSteps() + { + boolean saved = this.getCodeHighlighting(); + this.setCodeHighlighting(false); + table.tableChanged(new TableModelEvent(tableModel, 0, data.length - 1, BASIC_COLUMN)); + table.tableChanged(new TableModelEvent(tableModel, 0, data.length - 1, SOURCE_COLUMN)); + this.setCodeHighlighting(saved); + } + + /** + * Scroll the viewport so the step (table row) at the given text segment address is visible, vertically centered if + * possible, and selected. Developed July 2007 for new feature that shows source code step where label is defined + * when that label is clicked on in the Label Window. + * + * @param address text segment address of source code step. + */ + + void selectStepAtAddress(int address) + { + int addressRow = 0; + try + { addressRow = findRowForAddress(address); - } - catch (IllegalArgumentException e) { - return; - } - // Scroll to assure desired row is centered in view port. - int addressSourceColumn = table.convertColumnIndexToView(SOURCE_COLUMN); - Rectangle sourceCell = table.getCellRect(addressRow, addressSourceColumn, true); - double cellHeight = sourceCell.getHeight(); - double viewHeight = tableScroller.getViewport().getExtentSize().getHeight(); - int numberOfVisibleRows = (int) (viewHeight / cellHeight); - int newViewPositionY = Math.max((int)((addressRow-(numberOfVisibleRows/2))*cellHeight), 0); - tableScroller.getViewport().setViewPosition(new Point(0, newViewPositionY)); - // Select the source code cell for this row by generating a fake Mouse Pressed event - // and explicitly invoking the table's mouse listener. - MouseEvent fakeMouseEvent = new MouseEvent(table, MouseEvent.MOUSE_PRESSED, - new Date().getTime(), MouseEvent.BUTTON1_MASK, - (int)sourceCell.getX()+1, - (int)sourceCell.getY()+1, 1, false); - MouseListener[] mouseListeners = table.getMouseListeners(); - for (int i=0; iGo * setMenuStateTerminated: set upon completion of simulated execution */ - void setMenuState(int status) { + void setMenuState(int status) + { menuState = status; - switch (status) { + switch (status) + { case FileStatus.NO_FILE: setMenuStateInitial(); break; @@ -289,7 +333,8 @@ public class VenusUI extends JFrame { * @return Boolean true if the register values have been reset. **/ - public static boolean getReset() { + public static boolean getReset() + { return reset; } @@ -299,7 +344,8 @@ public class VenusUI extends JFrame { * @param b Boolean true if the register values have been reset. **/ - public static void setReset(boolean b) { + public static void setReset(boolean b) + { reset = b; } @@ -308,7 +354,8 @@ public class VenusUI extends JFrame { * * @return true if MIPS program is currently executing. **/ - public static boolean getStarted() { + public static boolean getStarted() + { return started; } @@ -318,7 +365,8 @@ public class VenusUI extends JFrame { * @param b true if the MIPS program execution has started. **/ - public static void setStarted(boolean b) { + public static void setStarted(boolean b) + { started = b; } @@ -327,10 +375,12 @@ public class VenusUI extends JFrame { * a menu item and a toolbar button. Does nice things like disable both if the action is * disabled, etc. */ - private void createActionObjects() { + private void createActionObjects() + { Toolkit tk = Toolkit.getDefaultToolkit(); Class cs = this.getClass(); - try { + try + { fileNewAction = new FileNewAction("New", new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "New22.png"))), "Create a new file for editing", KeyEvent.VK_N, KeyStroke.getKeyStroke(KeyEvent.VK_N, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), mainUI); fileOpenAction = new FileOpenAction("Open ...", new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "Open22.png"))), "Open a file for editing", KeyEvent.VK_O, KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), mainUI); fileCloseAction = new FileCloseAction("Close", null, "Close the current file", KeyEvent.VK_C, KeyStroke.getKeyStroke(KeyEvent.VK_W, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), mainUI); @@ -348,7 +398,7 @@ public class VenusUI extends JFrame { editPasteAction = new EditPasteAction("Paste", new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "Paste22.png"))), "Paste", KeyEvent.VK_P, KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), mainUI); editFindReplaceAction = new EditFindReplaceAction("Find/Replace", new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "Find22.png"))), "Find/Replace", KeyEvent.VK_F, KeyStroke.getKeyStroke(KeyEvent.VK_F, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), mainUI); editSelectAllAction = new EditSelectAllAction("Select All", null, //new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath+"Find22.png"))), - "Select All", KeyEvent.VK_A, KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), mainUI); + "Select All", KeyEvent.VK_A, KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()), mainUI); runAssembleAction = new RunAssembleAction("Assemble", new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "Assemble22.png"))), "Assemble the current file and clear breakpoints", KeyEvent.VK_A, KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0), mainUI); runGoAction = new RunGoAction("Go", new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "Play22.png"))), "Run the current program", KeyEvent.VK_G, KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0), mainUI); runStepAction = new RunStepAction("Step", new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "StepForward22.png"))), "Run one step at a time", KeyEvent.VK_T, KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0), mainUI); @@ -377,14 +427,17 @@ public class VenusUI extends JFrame { settingsMemoryConfigurationAction = new SettingsMemoryConfigurationAction("Memory Configuration...", null, "View and modify memory segment base addresses for simulated MIPS.", null, null, mainUI); helpHelpAction = new HelpHelpAction("Help", new ImageIcon(tk.getImage(cs.getResource(Globals.imagesPath + "Help22.png"))), "Help", KeyEvent.VK_H, KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0), mainUI); helpAboutAction = new HelpAboutAction("About ...", null, "Information about Mars", null, null, mainUI); - } catch (NullPointerException e) { + } + catch (NullPointerException e) + { System.out.println("Internal Error: images folder not found, or other null pointer exception while creating Action objects"); e.printStackTrace(); System.exit(0); } } - private JMenuBar setUpMenuBar() { + private JMenuBar setUpMenuBar() + { Toolkit tk = Toolkit.getDefaultToolkit(); Class cs = this.getClass(); @@ -431,7 +484,8 @@ public class VenusUI extends JFrame { file.add(fileSave); file.add(fileSaveAs); file.add(fileSaveAll); - if (new mars.mips.dump.DumpFormatLoader().loadDumpFormats().size() > 0) { + if (new mars.mips.dump.DumpFormatLoader().loadDumpFormats().size() > 0) + { file.add(fileDumpMemory); } file.addSeparator(); @@ -559,7 +613,10 @@ public class VenusUI extends JFrame { menuBar.add(run); menuBar.add(settings); JMenu toolMenu = new ToolLoader().buildToolsMenu(); - if (toolMenu != null) menuBar.add(toolMenu); + if (toolMenu != null) + { + menuBar.add(toolMenu); + } menuBar.add(help); // experiment with popup menu for settings. 3 Aug 2006 PS @@ -568,7 +625,8 @@ public class VenusUI extends JFrame { return menuBar; } - JToolBar setUpToolBar() { + JToolBar setUpToolBar() + { JToolBar toolBar = new JToolBar(); New = new JButton(fileNewAction); @@ -620,7 +678,8 @@ public class VenusUI extends JFrame { toolBar.add(Open); toolBar.add(Save); toolBar.add(SaveAs); - if (new mars.mips.dump.DumpFormatLoader().loadDumpFormats().size() > 0) { + if (new mars.mips.dump.DumpFormatLoader().loadDumpFormats().size() > 0) + { toolBar.add(DumpMemory); } toolBar.add(Print); @@ -646,7 +705,8 @@ public class VenusUI extends JFrame { return toolBar; } - void setMenuStateInitial() { + void setMenuStateInitial() + { fileNewAction.setEnabled(true); fileOpenAction.setEnabled(true); fileCloseAction.setEnabled(false); @@ -684,7 +744,8 @@ public class VenusUI extends JFrame { /* Added DPS 9-Aug-2011, for newly-opened files. Retain existing Run menu state (except Assemble, which is always true). Thus if there was a valid assembly it is retained. */ - void setMenuStateNotEdited() { + void setMenuStateNotEdited() + { /* Note: undo and redo are handled separately by the undo manager*/ fileNewAction.setEnabled(true); fileOpenAction.setEnabled(true); @@ -706,7 +767,8 @@ public class VenusUI extends JFrame { runAssembleAction.setEnabled(true); // If assemble-all, allow previous Run menu settings to remain. // Otherwise, clear them out. DPS 9-Aug-2011 - if (!Globals.getSettings().getBooleanSetting(mars.Settings.ASSEMBLE_ALL_ENABLED)) { + if (!Globals.getSettings().getBooleanSetting(mars.Settings.ASSEMBLE_ALL_ENABLED)) + { runGoAction.setEnabled(false); runStepAction.setEnabled(false); runBackstepAction.setEnabled(false); @@ -722,7 +784,8 @@ public class VenusUI extends JFrame { editRedoAction.updateRedoState(); } - void setMenuStateEditing() { + void setMenuStateEditing() + { /* Note: undo and redo are handled separately by the undo manager*/ fileNewAction.setEnabled(true); fileOpenAction.setEnabled(true); @@ -758,7 +821,8 @@ public class VenusUI extends JFrame { /* Use this when "File -> New" is used */ - void setMenuStateEditingNew() { + void setMenuStateEditingNew() + { /* Note: undo and redo are handled separately by the undo manager*/ fileNewAction.setEnabled(true); fileOpenAction.setEnabled(true); @@ -794,7 +858,8 @@ public class VenusUI extends JFrame { /* Use this upon successful assemble or reset */ - void setMenuStateRunnable() { + void setMenuStateRunnable() + { /* Note: undo and redo are handled separately by the undo manager */ fileNewAction.setEnabled(true); fileOpenAction.setEnabled(true); @@ -829,7 +894,8 @@ public class VenusUI extends JFrame { /* Use this while program is running */ - void setMenuStateRunning() { + void setMenuStateRunning() + { /* Note: undo and redo are handled separately by the undo manager */ fileNewAction.setEnabled(false); fileOpenAction.setEnabled(false); @@ -864,7 +930,8 @@ public class VenusUI extends JFrame { /* Use this upon completion of execution */ - void setMenuStateTerminated() { + void setMenuStateTerminated() + { /* Note: undo and redo are handled separately by the undo manager */ fileNewAction.setEnabled(true); fileOpenAction.setEnabled(true); @@ -903,7 +970,8 @@ public class VenusUI extends JFrame { * @return Editor for the GUI. **/ - public Editor getEditor() { + public Editor getEditor() + { return editor; } @@ -913,7 +981,8 @@ public class VenusUI extends JFrame { * @return MessagesPane object associated with the GUI. **/ - public MainPane getMainPane() { + public MainPane getMainPane() + { return mainPane; } @@ -923,7 +992,8 @@ public class VenusUI extends JFrame { * @return MessagesPane object associated with the GUI. **/ - public MessagesPane getMessagesPane() { + public MessagesPane getMessagesPane() + { return messagesPane; } @@ -933,7 +1003,8 @@ public class VenusUI extends JFrame { * @return RegistersPane object associated with the GUI. **/ - public RegistersPane getRegistersPane() { + public RegistersPane getRegistersPane() + { return registersPane; } @@ -943,7 +1014,8 @@ public class VenusUI extends JFrame { * @return the menu item **/ - public JCheckBoxMenuItem getValueDisplayBaseMenuItem() { + public JCheckBoxMenuItem getValueDisplayBaseMenuItem() + { return settingsValueDisplayBase; } @@ -953,24 +1025,27 @@ public class VenusUI extends JFrame { * @return the menu item **/ - public JCheckBoxMenuItem getAddressDisplayBaseMenuItem() { + public JCheckBoxMenuItem getAddressDisplayBaseMenuItem() + { return settingsAddressDisplayBase; } /** - * Return reference tothe Run->Assemble item's action. Needed by File->Open in case - * assemble-upon-open flag is set. + * Return reference tothe Run->Assemble item's action. Needed by File->Open in case assemble-upon-open flag is + * set. * * @return the Action object for the Run->Assemble operation. */ - public Action getRunAssembleAction() { + public Action getRunAssembleAction() + { return runAssembleAction; } /** * Have the menu request keyboard focus. DPS 5-4-10 */ - public void haveMenuRequestFocus() { + public void haveMenuRequestFocus() + { this.menu.requestFocus(); } @@ -979,12 +1054,14 @@ public class VenusUI extends JFrame { * * @param evt KeyEvent for menu component to consider for processing. */ - public void dispatchEventToMenu(KeyEvent evt) { + public void dispatchEventToMenu(KeyEvent evt) + { this.menu.dispatchEvent(evt); } // pop up menu experiment 3 Aug 2006. Keep for possible later revival. - private void setupPopupMenu() { + private void setupPopupMenu() + { JPopupMenu popup; popup = new JPopupMenu(); // cannot put the same menu item object on two different menus. diff --git a/src/main/java/mars/venus/editors/MARSTextEditingArea.java b/src/main/java/mars/venus/editors/MARSTextEditingArea.java index 323b4d5..e341640 100644 --- a/src/main/java/mars/venus/editors/MARSTextEditingArea.java +++ b/src/main/java/mars/venus/editors/MARSTextEditingArea.java @@ -1,8 +1,8 @@ - package mars.venus.editors; - import javax.swing.text.*; - import javax.swing.undo.*; - import javax.swing.*; - import java.awt.*; +package mars.venus.editors; + +import javax.swing.text.Document; +import javax.swing.undo.UndoManager; +import java.awt.*; /* Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar @@ -33,57 +33,99 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /** - * Specifies capabilities that any test editor used in MARS must have. - * + * Specifies capabilities that any test editor used in MARS must have. */ - - public interface MARSTextEditingArea { - - // Used by Find/Replace - public static final int TEXT_NOT_FOUND = 0; - public static final int TEXT_FOUND = 1; - public static final int TEXT_REPLACED_FOUND_NEXT = 2; - public static final int TEXT_REPLACED_NOT_FOUND_NEXT = 3; - - - public void copy(); - public void cut(); - public int doFindText(String find, boolean caseSensitive); - public int doReplace(String find, String replace, boolean caseSensitive); - public int doReplaceAll(String find, String replace, boolean caseSensitive); - public int getCaretPosition(); - public Document getDocument(); - public String getSelectedText(); - public int getSelectionEnd(); - public int getSelectionStart(); - public void select(int selectionStart, int selectionEnd); - public void selectAll(); - public String getText(); - public UndoManager getUndoManager(); - public void paste(); - public void replaceSelection(String str); - public void setCaretPosition(int position); - public void setEditable(boolean editable); - public void setSelectionEnd(int pos); - public void setSelectionStart(int pos); - public void setText(String text); - public void setFont(Font f); - public Font getFont(); - public boolean requestFocusInWindow(); - public FontMetrics getFontMetrics(Font f); - public void setBackground(Color c); - public void setEnabled(boolean enabled); - public void grabFocus(); - public void redo(); - public void revalidate(); - public void setSourceCode(String code, boolean editable); - public void setCaretVisible(boolean vis); - public void setSelectionVisible(boolean vis); - public void undo(); - public void discardAllUndoableEdits(); - public void setLineHighlightEnabled(boolean highlight); - public void setCaretBlinkRate(int rate); - public void setTabSize(int chars); - public void updateSyntaxStyles(); - public Component getOuterComponent(); - } \ No newline at end of file + +public interface MARSTextEditingArea +{ + + // Used by Find/Replace + int TEXT_NOT_FOUND = 0; + + int TEXT_FOUND = 1; + + int TEXT_REPLACED_FOUND_NEXT = 2; + + int TEXT_REPLACED_NOT_FOUND_NEXT = 3; + + + void copy(); + + void cut(); + + int doFindText(String find, boolean caseSensitive); + + int doReplace(String find, String replace, boolean caseSensitive); + + int doReplaceAll(String find, String replace, boolean caseSensitive); + + int getCaretPosition(); + + void setCaretPosition(int position); + + Document getDocument(); + + String getSelectedText(); + + int getSelectionEnd(); + + void setSelectionEnd(int pos); + + int getSelectionStart(); + + void setSelectionStart(int pos); + + void select(int selectionStart, int selectionEnd); + + void selectAll(); + + String getText(); + + void setText(String text); + + UndoManager getUndoManager(); + + void paste(); + + void replaceSelection(String str); + + void setEditable(boolean editable); + + Font getFont(); + + void setFont(Font f); + + boolean requestFocusInWindow(); + + FontMetrics getFontMetrics(Font f); + + void setBackground(Color c); + + void setEnabled(boolean enabled); + + void grabFocus(); + + void redo(); + + void revalidate(); + + void setSourceCode(String code, boolean editable); + + void setCaretVisible(boolean vis); + + void setSelectionVisible(boolean vis); + + void undo(); + + void discardAllUndoableEdits(); + + void setLineHighlightEnabled(boolean highlight); + + void setCaretBlinkRate(int rate); + + void setTabSize(int chars); + + void updateSyntaxStyles(); + + Component getOuterComponent(); +} diff --git a/src/main/java/mars/venus/editors/generic/GenericTextArea.java b/src/main/java/mars/venus/editors/generic/GenericTextArea.java index aa8a482..4356e47 100644 --- a/src/main/java/mars/venus/editors/generic/GenericTextArea.java +++ b/src/main/java/mars/venus/editors/generic/GenericTextArea.java @@ -1,13 +1,19 @@ - package mars.venus.editors.generic; +package mars.venus.editors.generic; - import mars.Globals; - import mars.venus.EditPane; - import mars.venus.editors.MARSTextEditingArea; - import java.awt.*; - import javax.swing.*; - import javax.swing.event.*; - import javax.swing.undo.*; - import java.util.*; +import mars.Globals; +import mars.venus.EditPane; +import mars.venus.editors.MARSTextEditingArea; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoManager; +import java.awt.*; /* Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar @@ -37,344 +43,393 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - public class GenericTextArea extends JTextArea implements MARSTextEditingArea { - - - private EditPane editPane; - private UndoManager undoManager; - private UndoableEditListener undoableEditListener; - private JTextArea sourceCode; - private JScrollPane editAreaScrollPane; - - private boolean isCompoundEdit = false; - private CompoundEdit compoundEdit; - - public GenericTextArea(EditPane editPain, JComponent lineNumbers) { - this.editPane = editPain; - this.sourceCode = this; - this.setFont(Globals.getSettings().getEditorFont()); - this.setTabSize(Globals.getSettings().getEditorTabSize()); - this.setMargin(new Insets(0,3,3,3)); - this.setCaretBlinkRate(Globals.getSettings().getCaretBlinkRate()); - - JPanel source = new JPanel(new BorderLayout()); - source.add(lineNumbers, BorderLayout.WEST); - source.add(this, BorderLayout.CENTER); - editAreaScrollPane = new JScrollPane(source, - ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); - editAreaScrollPane.getVerticalScrollBar().setUnitIncrement( - sourceCode.getFontMetrics(this.sourceCode.getFont()).getHeight()); - - this.undoManager = new UndoManager(); - - this.getCaret().addChangeListener( - new ChangeListener() { - public void stateChanged(ChangeEvent e) { - editPane.displayCaretPosition(getCaretPosition()); - } - }); - - // Needed to support unlimited undo/redo capability - undoableEditListener = - new UndoableEditListener() { - public void undoableEditHappened(UndoableEditEvent e) { - //Remember the edit and update the menus. - if (isCompoundEdit) { - compoundEdit.addEdit(e.getEdit()); - } - else { - undoManager.addEdit(e.getEdit()); - editPane.updateUndoState(); - editPane.updateRedoState(); - } - } +public class GenericTextArea extends JTextArea implements MARSTextEditingArea +{ + + + private final EditPane editPane; + + private final UndoManager undoManager; + + private final UndoableEditListener undoableEditListener; + + private final JTextArea sourceCode; + + private final JScrollPane editAreaScrollPane; + + private boolean isCompoundEdit = false; + + private CompoundEdit compoundEdit; + + public GenericTextArea(EditPane editPain, JComponent lineNumbers) + { + this.editPane = editPain; + this.sourceCode = this; + this.setFont(Globals.getSettings().getEditorFont()); + this.setTabSize(Globals.getSettings().getEditorTabSize()); + this.setMargin(new Insets(0, 3, 3, 3)); + this.setCaretBlinkRate(Globals.getSettings().getCaretBlinkRate()); + + JPanel source = new JPanel(new BorderLayout()); + source.add(lineNumbers, BorderLayout.WEST); + source.add(this, BorderLayout.CENTER); + editAreaScrollPane = new JScrollPane(source, + ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + editAreaScrollPane.getVerticalScrollBar().setUnitIncrement( + sourceCode.getFontMetrics(this.sourceCode.getFont()).getHeight()); + + this.undoManager = new UndoManager(); + + this.getCaret().addChangeListener( + new ChangeListener() + { + public void stateChanged(ChangeEvent e) + { + editPane.displayCaretPosition(getCaretPosition()); + } + }); + + // Needed to support unlimited undo/redo capability + undoableEditListener = + new UndoableEditListener() + { + public void undoableEditHappened(UndoableEditEvent e) + { + //Remember the edit and update the menus. + if (isCompoundEdit) + { + compoundEdit.addEdit(e.getEdit()); + } + else + { + undoManager.addEdit(e.getEdit()); + editPane.updateUndoState(); + editPane.updateRedoState(); + } + } }; - this.getDocument().addUndoableEditListener(undoableEditListener); - } - - /** - * Does nothing, but required by the interface. This editor does not support - * highlighting of the line currently being edited. - */ - public void setLineHighlightEnabled(boolean highlight) { - } - - /** - * Does nothing, but required by the interface. This editor does not support - * syntax styling (colors, bold/italic). - */ - public void updateSyntaxStyles() { - } - - /** - * Set the caret blinking rate in milliseconds. If rate is 0 - * it will not blink. If negative, do nothing. - * @param rate blinking rate in milliseconds - */ - public void setCaretBlinkRate(int rate) { - if (rate >= 0) { - getCaret().setBlinkRate(rate); - } - } - - - public Component getOuterComponent() { - return editAreaScrollPane; - } - - /** - * For initalizing the source code when opening an ASM file - * @param s String containing text - * @param editable set true if code is editable else false - **/ - - public void setSourceCode(String s, boolean editable){ - this.setText(s); - this.setBackground( (editable)? Color.WHITE : Color.GRAY); - this.setEditable(editable); - this.setEnabled(editable); - this.getCaret().setVisible(editable); - this.setCaretPosition(0); - if (editable) this.requestFocusInWindow(); - } - - /** - * Tell UndoManager to discard all its collected undoable edits. - */ - public void discardAllUndoableEdits() { - undoManager.discardAllEdits(); - } - - /** - * Override inherited setText to temporarily remove UndoableEditListener because this - * operation is not undoable. - * - * @param s String with new contents for the editing area. Replaces current content. - */ - public void setText(String s) { - this.getDocument().removeUndoableEditListener(undoableEditListener); - super.setText(s); - this.getDocument().addUndoableEditListener(undoableEditListener); - } - - /** - * Control caret visibility - * - * @param vis true to display caret, false to hide it - */ - public void setCaretVisible(boolean vis) { - this.getCaret().setVisible(vis); - } - - /** - * Control selection visibility - * - * @param vis true to display selection, false to hide it - */ - public void setSelectionVisible(boolean vis) { - this.getCaret().setSelectionVisible(vis); - } - - + this.getDocument().addUndoableEditListener(undoableEditListener); + } + /** - * Returns the undo manager for this editing area - * @return the undo manager + * Does nothing, but required by the interface. This editor does not support highlighting of the line currently + * being edited. */ - public UndoManager getUndoManager() { - return undoManager; - } - - /** - * Undo previous edit - */ - public void undo() { - try { + public void setLineHighlightEnabled(boolean highlight) + { + } + + /** + * Does nothing, but required by the interface. This editor does not support syntax styling (colors, bold/italic). + */ + public void updateSyntaxStyles() + { + } + + /** + * Set the caret blinking rate in milliseconds. If rate is 0 it will not blink. If negative, do nothing. + * + * @param rate blinking rate in milliseconds + */ + public void setCaretBlinkRate(int rate) + { + if (rate >= 0) + { + getCaret().setBlinkRate(rate); + } + } + + + public Component getOuterComponent() + { + return editAreaScrollPane; + } + + /** + * For initalizing the source code when opening an ASM file + * + * @param s String containing text + * @param editable set true if code is editable else false + **/ + + public void setSourceCode(String s, boolean editable) + { + this.setText(s); + this.setBackground((editable) ? Color.WHITE : Color.GRAY); + this.setEditable(editable); + this.setEnabled(editable); + this.getCaret().setVisible(editable); + this.setCaretPosition(0); + if (editable) + { + this.requestFocusInWindow(); + } + } + + /** + * Tell UndoManager to discard all its collected undoable edits. + */ + public void discardAllUndoableEdits() + { + undoManager.discardAllEdits(); + } + + /** + * Override inherited setText to temporarily remove UndoableEditListener because this operation is not undoable. + * + * @param s String with new contents for the editing area. Replaces current content. + */ + public void setText(String s) + { + this.getDocument().removeUndoableEditListener(undoableEditListener); + super.setText(s); + this.getDocument().addUndoableEditListener(undoableEditListener); + } + + /** + * Control caret visibility + * + * @param vis true to display caret, false to hide it + */ + public void setCaretVisible(boolean vis) + { + this.getCaret().setVisible(vis); + } + + /** + * Control selection visibility + * + * @param vis true to display selection, false to hide it + */ + public void setSelectionVisible(boolean vis) + { + this.getCaret().setSelectionVisible(vis); + } + + + /** + * Returns the undo manager for this editing area + * + * @return the undo manager + */ + public UndoManager getUndoManager() + { + return undoManager; + } + + /** + * Undo previous edit + */ + public void undo() + { + try + { this.undoManager.undo(); - } - catch (CannotUndoException ex) { - System.out.println("Unable to undo: " + ex); - ex.printStackTrace(); - } - this.setCaretVisible(true); - } - - /** - * Redo previous edit - */ - public void redo() { - try { + } + catch (CannotUndoException ex) + { + System.out.println("Unable to undo: " + ex); + ex.printStackTrace(); + } + this.setCaretVisible(true); + } + + /** + * Redo previous edit + */ + public void redo() + { + try + { this.undoManager.redo(); - } - catch (CannotRedoException ex) { - System.out.println("Unable to redo: " + ex); - ex.printStackTrace(); - } - this.setCaretVisible(true); - } - - - - - - ////////////////////////////////////////////////////////////////////////// - // Methods to support Find/Replace feature - // - // Basis for this Find/Replace solution is: - // http://java.ittoolbox.com/groups/technical-functional/java-l/search-and-replace-using-jtextpane-630964 - // as written by Chris Dickenson in 2005 - // - // sourceCode is implemented as JTextArea rather than JTextPane but the necessary methods are inherited - // by both from JTextComponent. - - /** Finds next occurrence of text in a forward search of a string. Search begins - * at the current cursor location, and wraps around when the end of the string - * is reached. - * @param find the text to locate in the string + } + catch (CannotRedoException ex) + { + System.out.println("Unable to redo: " + ex); + ex.printStackTrace(); + } + this.setCaretVisible(true); + } + + + ////////////////////////////////////////////////////////////////////////// + // Methods to support Find/Replace feature + // + // Basis for this Find/Replace solution is: + // http://java.ittoolbox.com/groups/technical-functional/java-l/search-and-replace-using-jtextpane-630964 + // as written by Chris Dickenson in 2005 + // + // sourceCode is implemented as JTextArea rather than JTextPane but the necessary methods are inherited + // by both from JTextComponent. + + /** + * Finds next occurrence of text in a forward search of a string. Search begins at the current cursor location, and + * wraps around when the end of the string is reached. + * + * @param find the text to locate in the string * @param caseSensitive true if search is to be case-sensitive, false otherwise * @return TEXT_FOUND or TEXT_NOT_FOUND, depending on the result. */ - public int doFindText(String find, boolean caseSensitive) { - int findPosn = sourceCode.getCaretPosition(); - int nextPosn = 0; - nextPosn = nextIndex( sourceCode.getText(), find, findPosn, caseSensitive ); - if ( nextPosn >= 0 ) { + public int doFindText(String find, boolean caseSensitive) + { + int findPosn = sourceCode.getCaretPosition(); + int nextPosn = 0; + nextPosn = nextIndex(sourceCode.getText(), find, findPosn, caseSensitive); + if (nextPosn >= 0) + { sourceCode.requestFocus(); // guarantees visibility of the blue highlight - sourceCode.setSelectionStart( nextPosn ); // position cursor at word start - sourceCode.setSelectionEnd( nextPosn + find.length() ); - sourceCode.setSelectionStart( nextPosn ); // position cursor at word start + sourceCode.setSelectionStart(nextPosn); // position cursor at word start + sourceCode.setSelectionEnd(nextPosn + find.length()); + sourceCode.setSelectionStart(nextPosn); // position cursor at word start return TEXT_FOUND; - } - else { + } + else + { return TEXT_NOT_FOUND; - } - } - - /** Returns next posn of word in text - forward search. If end of string is - * reached during the search, will wrap around to the beginning one time. - * @return next indexed position of found text or -1 if not found - * @param input the string to search - * @param find the string to find - * @param start the character position to start the search - * @param caseSensitive true for case sensitive. false to ignore case - */ - public int nextIndex(String input, String find, int start, boolean caseSensitive ) { - int textPosn = -1; - if ( input != null && find != null && start < input.length() ) { - if ( caseSensitive ) { // indexOf() returns -1 if not found - textPosn = input.indexOf( find, start ); - // If not found from non-starting cursor position, wrap around - if (start > 0 && textPosn < 0) { - textPosn = input.indexOf( find ); - } - } - else { - String lowerCaseText = input.toLowerCase(); - textPosn = lowerCaseText.indexOf( find.toLowerCase(), start ); + } + } + + /** + * Returns next posn of word in text - forward search. If end of string is reached during the search, will wrap + * around to the beginning one time. + * + * @param input the string to search + * @param find the string to find + * @param start the character position to start the search + * @param caseSensitive true for case sensitive. false to ignore case + * @return next indexed position of found text or -1 if not found + */ + public int nextIndex(String input, String find, int start, boolean caseSensitive) + { + int textPosn = -1; + if (input != null && find != null && start < input.length()) + { + if (caseSensitive) + { // indexOf() returns -1 if not found + textPosn = input.indexOf(find, start); // If not found from non-starting cursor position, wrap around - if (start > 0 && textPosn < 0) { - textPosn = lowerCaseText.indexOf( find.toLowerCase() ); - } + if (start > 0 && textPosn < 0) + { + textPosn = input.indexOf(find); + } } - } - return textPosn; - } - - - /** Finds and replaces next occurrence of text in a string in a forward search. - * If cursor is initially at end - * of matching selection, will immediately replace then find and select the - * next occurrence if any. Otherwise it performs a find operation. The replace - * can be undone with one undo operation. - * - * @param find the text to locate in the string - * @param replace the text to replace the find text with - if the find text exists - * @param caseSensitive true for case sensitive. false to ignore case - * @return Returns TEXT_FOUND if not initially at end of selected match and matching - * occurrence is found. Returns TEXT_NOT_FOUND if the text is not matched. - * Returns TEXT_REPLACED_NOT_FOUND_NEXT if replacement is successful but there are - * no additional matches. Returns TEXT_REPLACED_FOUND_NEXT if reaplacement is - * successful and there is at least one additional match. - */ - public int doReplace(String find, String replace, boolean caseSensitive) { - int nextPosn = 0; - int posn; - // Will perform a "find" and return, unless positioned at the end of - // a selected "find" result. - if (find==null || !find.equals(sourceCode.getSelectedText()) || - sourceCode.getSelectionEnd()!=sourceCode.getCaretPosition()) { + else + { + String lowerCaseText = input.toLowerCase(); + textPosn = lowerCaseText.indexOf(find.toLowerCase(), start); + // If not found from non-starting cursor position, wrap around + if (start > 0 && textPosn < 0) + { + textPosn = lowerCaseText.indexOf(find.toLowerCase()); + } + } + } + return textPosn; + } + + + /** + * Finds and replaces next occurrence of text in a string in a forward search. If cursor is initially at end of + * matching selection, will immediately replace then find and select the next occurrence if any. Otherwise it + * performs a find operation. The replace can be undone with one undo operation. + * + * @param find the text to locate in the string + * @param replace the text to replace the find text with - if the find text exists + * @param caseSensitive true for case sensitive. false to ignore case + * @return Returns TEXT_FOUND if not initially at end of selected match and matching occurrence is found. Returns + * TEXT_NOT_FOUND if the text is not matched. Returns TEXT_REPLACED_NOT_FOUND_NEXT if replacement is successful + * but there are no additional matches. Returns TEXT_REPLACED_FOUND_NEXT if reaplacement is successful and + * there is at least one additional match. + */ + public int doReplace(String find, String replace, boolean caseSensitive) + { + int nextPosn = 0; + int posn; + // Will perform a "find" and return, unless positioned at the end of + // a selected "find" result. + if (find == null || !find.equals(sourceCode.getSelectedText()) || + sourceCode.getSelectionEnd() != sourceCode.getCaretPosition()) + { return doFindText(find, caseSensitive); - } + } // We are positioned at end of selected "find". Rreplace and find next. - nextPosn = sourceCode.getSelectionStart(); - sourceCode.grabFocus(); - sourceCode.setSelectionStart( nextPosn ); // posn cursor at word start - sourceCode.setSelectionEnd( nextPosn + find.length() ); //select found text - isCompoundEdit = true; - compoundEdit = new CompoundEdit(); - sourceCode.replaceSelection(replace); - compoundEdit.end(); - undoManager.addEdit( compoundEdit ); - editPane.updateUndoState(); - editPane.updateRedoState(); - isCompoundEdit = false; - sourceCode.setCaretPosition(nextPosn + replace.length()); - if (doFindText(find, caseSensitive) == TEXT_NOT_FOUND) { + nextPosn = sourceCode.getSelectionStart(); + sourceCode.grabFocus(); + sourceCode.setSelectionStart(nextPosn); // posn cursor at word start + sourceCode.setSelectionEnd(nextPosn + find.length()); //select found text + isCompoundEdit = true; + compoundEdit = new CompoundEdit(); + sourceCode.replaceSelection(replace); + compoundEdit.end(); + undoManager.addEdit(compoundEdit); + editPane.updateUndoState(); + editPane.updateRedoState(); + isCompoundEdit = false; + sourceCode.setCaretPosition(nextPosn + replace.length()); + if (doFindText(find, caseSensitive) == TEXT_NOT_FOUND) + { return TEXT_REPLACED_NOT_FOUND_NEXT; - } - else { + } + else + { return TEXT_REPLACED_FOUND_NEXT; - } - } - - /** Finds and replaces ALL occurrences of text in a string in a forward search. - * All replacements are bundled into one CompoundEdit, so one Undo operation will - * undo all of them. - * @param find the text to locate in the string - * @param replace the text to replace the find text with - if the find text exists - * @param caseSensitive true for case sensitive. false to ignore case - * @return the number of occurrences that were matched and replaced. - */ - public int doReplaceAll(String find, String replace, boolean caseSensitive) { - int nextPosn = 0; - int findPosn = 0; // *** begin at start of text - int replaceCount = 0; - compoundEdit = null; // new one will be created upon first replacement - isCompoundEdit = true; // undo manager's action listener needs this - while (nextPosn >= 0) { - nextPosn = nextIndex( sourceCode.getText(), find, findPosn, caseSensitive ); - if ( nextPosn >= 0 ) { - // nextIndex() will wrap around, which causes infinite loop if - // find string is a substring of replacement string. This - // statement will prevent that. - if (nextPosn < findPosn) { - break; - } - sourceCode.grabFocus(); - sourceCode.setSelectionStart( nextPosn ); // posn cursor at word start - sourceCode.setSelectionEnd( nextPosn + find.length() ); //select found text - if (compoundEdit == null) { - compoundEdit = new CompoundEdit(); - } - sourceCode.replaceSelection(replace); - findPosn = nextPosn + replace.length(); // set for next search - replaceCount++; + } + } + + /** + * Finds and replaces ALL occurrences of text in a string in a forward search. All replacements are bundled + * into one CompoundEdit, so one Undo operation will undo all of them. + * + * @param find the text to locate in the string + * @param replace the text to replace the find text with - if the find text exists + * @param caseSensitive true for case sensitive. false to ignore case + * @return the number of occurrences that were matched and replaced. + */ + public int doReplaceAll(String find, String replace, boolean caseSensitive) + { + int nextPosn = 0; + int findPosn = 0; // *** begin at start of text + int replaceCount = 0; + compoundEdit = null; // new one will be created upon first replacement + isCompoundEdit = true; // undo manager's action listener needs this + while (nextPosn >= 0) + { + nextPosn = nextIndex(sourceCode.getText(), find, findPosn, caseSensitive); + if (nextPosn >= 0) + { + // nextIndex() will wrap around, which causes infinite loop if + // find string is a substring of replacement string. This + // statement will prevent that. + if (nextPosn < findPosn) + { + break; + } + sourceCode.grabFocus(); + sourceCode.setSelectionStart(nextPosn); // posn cursor at word start + sourceCode.setSelectionEnd(nextPosn + find.length()); //select found text + if (compoundEdit == null) + { + compoundEdit = new CompoundEdit(); + } + sourceCode.replaceSelection(replace); + findPosn = nextPosn + replace.length(); // set for next search + replaceCount++; } - } - isCompoundEdit = false; - // Will be true if any replacements were performed - if (compoundEdit != null) { + } + isCompoundEdit = false; + // Will be true if any replacements were performed + if (compoundEdit != null) + { compoundEdit.end(); - undoManager.addEdit( compoundEdit ); + undoManager.addEdit(compoundEdit); editPane.updateUndoState(); editPane.updateRedoState(); - } - return replaceCount; - } - // - ///////////////////////////// End Find/Replace methods ////////////////////////// - - - - - } \ No newline at end of file + } + return replaceCount; + } + // + ///////////////////////////// End Find/Replace methods ////////////////////////// + + +} diff --git a/src/main/java/mars/venus/editors/jeditsyntax/DefaultInputHandler.java b/src/main/java/mars/venus/editors/jeditsyntax/DefaultInputHandler.java index acbd1b6..49303e9 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/DefaultInputHandler.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/DefaultInputHandler.java @@ -7,392 +7,406 @@ * remains intact in all source distributions of this package. */ - package mars.venus.editors.jeditsyntax; +package mars.venus.editors.jeditsyntax; - import javax.swing.KeyStroke; - import java.awt.event.*; - import java.awt.Toolkit; - import java.util.Hashtable; - import java.util.StringTokenizer; - import java.util.Properties; +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionListener; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.util.Hashtable; +import java.util.StringTokenizer; /** - * The default input handler. It maps sequences of keystrokes into actions - * and inserts key typed events into the text area. + * The default input handler. It maps sequences of keystrokes into actions and inserts key typed events into the text + * area. + * * @author Slava Pestov * @version $Id: DefaultInputHandler.java,v 1.18 1999/12/13 03:40:30 sp Exp $ */ - public class DefaultInputHandler extends InputHandler - { - /** - * Creates a new input handler with no key bindings defined. - */ - public DefaultInputHandler() - { - bindings = currentBindings = new Hashtable(); - } - - /** - * Sets up the default key bindings. - */ - public void addDefaultKeyBindings() - { - addKeyBinding("BACK_SPACE",BACKSPACE); - addKeyBinding("C+BACK_SPACE",BACKSPACE_WORD); - addKeyBinding("DELETE",DELETE); - addKeyBinding("C+DELETE",DELETE_WORD); - - addKeyBinding("ENTER",INSERT_BREAK); - addKeyBinding("TAB",INSERT_TAB); - - addKeyBinding("INSERT",OVERWRITE); - addKeyBinding("C+\\",TOGGLE_RECT); - - addKeyBinding("HOME",HOME); - addKeyBinding("END",END); - addKeyBinding("C+A",SELECT_ALL); - addKeyBinding("S+HOME",SELECT_HOME); - addKeyBinding("S+END",SELECT_END); - addKeyBinding("C+HOME",DOCUMENT_HOME); - addKeyBinding("C+END",DOCUMENT_END); - addKeyBinding("CS+HOME",SELECT_DOC_HOME); - addKeyBinding("CS+END",SELECT_DOC_END); - - addKeyBinding("PAGE_UP",PREV_PAGE); - addKeyBinding("PAGE_DOWN",NEXT_PAGE); - addKeyBinding("S+PAGE_UP",SELECT_PREV_PAGE); - addKeyBinding("S+PAGE_DOWN",SELECT_NEXT_PAGE); - - addKeyBinding("LEFT",PREV_CHAR); - addKeyBinding("S+LEFT",SELECT_PREV_CHAR); - addKeyBinding("C+LEFT",PREV_WORD); - addKeyBinding("CS+LEFT",SELECT_PREV_WORD); - addKeyBinding("RIGHT",NEXT_CHAR); - addKeyBinding("S+RIGHT",SELECT_NEXT_CHAR); - addKeyBinding("C+RIGHT",NEXT_WORD); - addKeyBinding("CS+RIGHT",SELECT_NEXT_WORD); - addKeyBinding("UP",PREV_LINE); - addKeyBinding("S+UP",SELECT_PREV_LINE); - addKeyBinding("DOWN",NEXT_LINE); - addKeyBinding("S+DOWN",SELECT_NEXT_LINE); - - addKeyBinding("C+ENTER",REPEAT); - - // Clipboard - addKeyBinding("C+C", CLIP_COPY); - addKeyBinding("C+V", CLIP_PASTE); - addKeyBinding("C+X", CLIP_CUT); - } - - /** - * Adds a key binding to this input handler. The key binding is - * a list of white space separated key strokes of the form - * [modifiers+]key where modifier is C for Control, A for Alt, - * or S for Shift, and key is either a character (a-z) or a field - * name in the KeyEvent class prefixed with VK_ (e.g., BACK_SPACE) - * @param keyBinding The key binding - * @param action The action - */ - public void addKeyBinding(String keyBinding, ActionListener action) - { - Hashtable current = bindings; - - StringTokenizer st = new StringTokenizer(keyBinding); - while(st.hasMoreTokens()) - { - KeyStroke keyStroke = parseKeyStroke(st.nextToken()); - if(keyStroke == null) - return; - - if(st.hasMoreTokens()) - { - Object o = current.get(keyStroke); - if(o instanceof Hashtable) - current = (Hashtable)o; - else - { - o = new Hashtable(); - current.put(keyStroke,o); - current = (Hashtable)o; - } - } - else - current.put(keyStroke,action); - } - } - - /** - * Removes a key binding from this input handler. This is not yet - * implemented. - * @param keyBinding The key binding - */ - public void removeKeyBinding(String keyBinding) - { - throw new InternalError("Not yet implemented"); - } - - /** - * Removes all key bindings from this input handler. - */ - public void removeAllKeyBindings() - { - bindings.clear(); - } - - /** - * Returns a copy of this input handler that shares the same - * key bindings. Setting key bindings in the copy will also - * set them in the original. - */ - public InputHandler copy() - { - return new DefaultInputHandler(this); - } - - /** - * Handle a key pressed event. This will look up the binding for - * the key stroke and execute it. - */ - public void keyPressed(KeyEvent evt) - { - int keyCode = evt.getKeyCode(); - int modifiers = evt.getModifiers(); - if(keyCode == KeyEvent.VK_CONTROL || - keyCode == KeyEvent.VK_SHIFT || - keyCode == KeyEvent.VK_ALT || - keyCode == KeyEvent.VK_META) - return; - - if((modifiers & ~KeyEvent.SHIFT_MASK) != 0 - || evt.isActionKey() - || keyCode == KeyEvent.VK_BACK_SPACE - || keyCode == KeyEvent.VK_DELETE - || keyCode == KeyEvent.VK_ENTER - || keyCode == KeyEvent.VK_TAB - || keyCode == KeyEvent.VK_ESCAPE) - { - if(grabAction != null) - { - handleGrabAction(evt); - return; - } - - KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers); - Object o = currentBindings.get(keyStroke); - - if(o == null) - { - // Don't beep if the user presses some - // key we don't know about unless a - // prefix is active. Otherwise it will - // beep when caps lock is pressed, etc. - if(currentBindings != bindings) - { - Toolkit.getDefaultToolkit().beep(); - // F10 should be passed on, but C+e F10 - // shouldn't - repeatCount = 0; - repeat = false; - evt.consume(); - } - currentBindings = bindings; - // No binding for this keyStroke, pass it to menu - // (mnemonic, accelerator). DPS 4-may-2010 - mars.Globals.getGui().dispatchEventToMenu(evt); - evt.consume(); - return; - } - else if(o instanceof ActionListener) - { - currentBindings = bindings; - executeAction(((ActionListener)o), - evt.getSource(),null); - - evt.consume(); - return; - } - else if(o instanceof Hashtable) - { - currentBindings = (Hashtable)o; - evt.consume(); - return; - } - } - } - - /** - * Handle a key typed event. This inserts the key into the text area. - */ - public void keyTyped(KeyEvent evt) - { - int modifiers = evt.getModifiers(); - char c = evt.getKeyChar(); - // This IF statement needed to prevent Macintosh shortcut keyChar from - // being echoed to the text area. E.g. Command-s, for Save, will echo - // the 's' character unless filtered out here. Command modifier - // matches KeyEvent.META_MASK. DPS 30-Nov-2010 - if ((modifiers & KeyEvent.META_MASK) != 0) - return; - // DPS 9-Jan-2013. Umberto Villano from Italy describes Alt combinations - // not working on Italian Mac keyboards, where # requires Alt (Option). - // This is preventing him from writing comments. Similar complaint from - // Joachim Parrow in Sweden, only for the $ character. Villano pointed - // me to this method. Plus a Google search on "jeditsyntax alt key" - // (without quotes) took me to - // http://compgroups.net/comp.lang.java.programmer/option-key-in-jedit-syntax-package/1068227 - // which says to comment out the second condition in this IF statement: - // if(c != KeyEvent.CHAR_UNDEFINED && (modifiers & KeyEvent.ALT_MASK) == 0) - // So let's give it a try! - // (...later) Bummer, it results in keystroke echoed into editing area when I use Alt - // combination for shortcut menu access (e.g. Alt+f to open the File menu). - // - // Torsten Maehne: This is a shortcoming of the menu - // shortcuts handling in the jedit component: It assumes that - // modifier keys are the same across all platforms. However, - // the menu shortcut keymask varies between OS X and - // Windows/Linux, it is Cmd + instead of Alt + - // . The "Java Development Guide for Mac" explicitly - // discusses the issue in: - // - // - // As jedit always considers Alt + as a keyboard - // shortcut, they block their output in the editor, which - // prevents the entry of special characters on OS X that uses - // Alt + for this purpose instead of AltGr + , as - // on Windows or Linux. - // - // For the latest jedit version (5.0.0), the menu - // accelerators don't work on OS X, at least the special - // characters can be entered using Alt + . The issue is - // still open, but there seems to be progress: - // - // http://sourceforge.net/tracker/index.php?func=detail&aid=3558572&group_id=588&atid=300588 - // http://sourceforge.net/tracker/?func=detail&atid=300588&aid=3604532&group_id=588 - // - // Until this is resolved upstream, don't ignore characters - // on OS X, which have been entered with the ALT modifier: - if(c != KeyEvent.CHAR_UNDEFINED && (((modifiers & KeyEvent.ALT_MASK) == 0) || System.getProperty("os.name").contains("OS X"))) - { - if(c >= 0x20 && c != 0x7f) - { - KeyStroke keyStroke = KeyStroke.getKeyStroke( - Character.toUpperCase(c)); - Object o = currentBindings.get(keyStroke); - - if(o instanceof Hashtable) - { - currentBindings = (Hashtable)o; - return; - } - else if(o instanceof ActionListener) - { - currentBindings = bindings; - executeAction((ActionListener)o, - evt.getSource(), - String.valueOf(c)); - return; - } - - currentBindings = bindings; - - if(grabAction != null) - { - handleGrabAction(evt); - return; - } - - // 0-9 adds another 'digit' to the repeat number - if(repeat && Character.isDigit(c)) - { - repeatCount *= 10; - repeatCount += (c - '0'); - return; - } - executeAction(INSERT_CHAR,evt.getSource(), - String.valueOf(evt.getKeyChar())); - repeatCount = 0; - repeat = false; - } - } - } - - /** - * Converts a string to a keystroke. The string should be of the - * form modifiers+shortcut where modifiers - * is any combination of A for Alt, C for Control, S for Shift - * or M for Meta, and shortcut is either a single character, - * or a keycode name from the KeyEvent class, without - * the VK_ prefix. - * @param keyStroke A string description of the key stroke - */ - public static KeyStroke parseKeyStroke(String keyStroke) - { - if(keyStroke == null) +public class DefaultInputHandler extends InputHandler +{ + // private members + private final Hashtable bindings; + + private Hashtable currentBindings; + + /** + * Creates a new input handler with no key bindings defined. + */ + public DefaultInputHandler() + { + bindings = currentBindings = new Hashtable(); + } + + private DefaultInputHandler(DefaultInputHandler copy) + { + bindings = currentBindings = copy.bindings; + } + + /** + * Converts a string to a keystroke. The string should be of the form modifiers+shortcut where + * modifiers is any combination of A for Alt, C for Control, S for Shift or M for Meta, and shortcut + * is either a single character, or a keycode name from the KeyEvent class, without the + * VK_ prefix. + * + * @param keyStroke A string description of the key stroke + */ + public static KeyStroke parseKeyStroke(String keyStroke) + { + if (keyStroke == null) + { return null; - int modifiers = 0; - int index = keyStroke.indexOf('+'); - if(index != -1) - { - for(int i = 0; i < index; i++) + } + int modifiers = 0; + int index = keyStroke.indexOf('+'); + if (index != -1) + { + for (int i = 0; i < index; i++) { - switch(Character.toUpperCase(keyStroke - .charAt(i))) - { - case 'A': - modifiers |= InputEvent.ALT_MASK; - break; - case 'C': - modifiers |= InputEvent.CTRL_MASK; - break; - case 'M': - modifiers |= InputEvent.META_MASK; - break; - case 'S': - modifiers |= InputEvent.SHIFT_MASK; - break; - } + switch (Character.toUpperCase(keyStroke + .charAt(i))) + { + case 'A': + modifiers |= InputEvent.ALT_MASK; + break; + case 'C': + modifiers |= InputEvent.CTRL_MASK; + break; + case 'M': + modifiers |= InputEvent.META_MASK; + break; + case 'S': + modifiers |= InputEvent.SHIFT_MASK; + break; + } } - } - String key = keyStroke.substring(index + 1); - if(key.length() == 1) - { + } + String key = keyStroke.substring(index + 1); + if (key.length() == 1) + { char ch = Character.toUpperCase(key.charAt(0)); - if(modifiers == 0) - return KeyStroke.getKeyStroke(ch); + if (modifiers == 0) + { + return KeyStroke.getKeyStroke(ch); + } else - return KeyStroke.getKeyStroke(ch,modifiers); - } - else if(key.length() == 0) - { + { + return KeyStroke.getKeyStroke(ch, modifiers); + } + } + else if (key.length() == 0) + { System.err.println("Invalid key stroke: " + keyStroke); return null; - } - else - { + } + else + { int ch; - + try { - ch = KeyEvent.class.getField("VK_".concat(key)) - .getInt(null); + ch = KeyEvent.class.getField("VK_".concat(key)) + .getInt(null); } - catch(Exception e) - { - System.err.println("Invalid key stroke: " - + keyStroke); - return null; - } - - return KeyStroke.getKeyStroke(ch,modifiers); - } - } - - // private members - private Hashtable bindings; - private Hashtable currentBindings; - - private DefaultInputHandler(DefaultInputHandler copy) - { - bindings = currentBindings = copy.bindings; - } - } + catch (Exception e) + { + System.err.println("Invalid key stroke: " + + keyStroke); + return null; + } + + return KeyStroke.getKeyStroke(ch, modifiers); + } + } + + /** + * Sets up the default key bindings. + */ + public void addDefaultKeyBindings() + { + addKeyBinding("BACK_SPACE", BACKSPACE); + addKeyBinding("C+BACK_SPACE", BACKSPACE_WORD); + addKeyBinding("DELETE", DELETE); + addKeyBinding("C+DELETE", DELETE_WORD); + + addKeyBinding("ENTER", INSERT_BREAK); + addKeyBinding("TAB", INSERT_TAB); + + addKeyBinding("INSERT", OVERWRITE); + addKeyBinding("C+\\", TOGGLE_RECT); + + addKeyBinding("HOME", HOME); + addKeyBinding("END", END); + addKeyBinding("C+A", SELECT_ALL); + addKeyBinding("S+HOME", SELECT_HOME); + addKeyBinding("S+END", SELECT_END); + addKeyBinding("C+HOME", DOCUMENT_HOME); + addKeyBinding("C+END", DOCUMENT_END); + addKeyBinding("CS+HOME", SELECT_DOC_HOME); + addKeyBinding("CS+END", SELECT_DOC_END); + + addKeyBinding("PAGE_UP", PREV_PAGE); + addKeyBinding("PAGE_DOWN", NEXT_PAGE); + addKeyBinding("S+PAGE_UP", SELECT_PREV_PAGE); + addKeyBinding("S+PAGE_DOWN", SELECT_NEXT_PAGE); + + addKeyBinding("LEFT", PREV_CHAR); + addKeyBinding("S+LEFT", SELECT_PREV_CHAR); + addKeyBinding("C+LEFT", PREV_WORD); + addKeyBinding("CS+LEFT", SELECT_PREV_WORD); + addKeyBinding("RIGHT", NEXT_CHAR); + addKeyBinding("S+RIGHT", SELECT_NEXT_CHAR); + addKeyBinding("C+RIGHT", NEXT_WORD); + addKeyBinding("CS+RIGHT", SELECT_NEXT_WORD); + addKeyBinding("UP", PREV_LINE); + addKeyBinding("S+UP", SELECT_PREV_LINE); + addKeyBinding("DOWN", NEXT_LINE); + addKeyBinding("S+DOWN", SELECT_NEXT_LINE); + + addKeyBinding("C+ENTER", REPEAT); + + // Clipboard + addKeyBinding("C+C", CLIP_COPY); + addKeyBinding("C+V", CLIP_PASTE); + addKeyBinding("C+X", CLIP_CUT); + } + + /** + * Adds a key binding to this input handler. The key binding is a list of white space separated key strokes of the + * form + * [modifiers+]key where modifier is C for Control, A for Alt, + * or S for Shift, and key is either a character (a-z) or a field name in the KeyEvent class prefixed with VK_ + * (e.g., BACK_SPACE) + * + * @param keyBinding The key binding + * @param action The action + */ + public void addKeyBinding(String keyBinding, ActionListener action) + { + Hashtable current = bindings; + + StringTokenizer st = new StringTokenizer(keyBinding); + while (st.hasMoreTokens()) + { + KeyStroke keyStroke = parseKeyStroke(st.nextToken()); + if (keyStroke == null) + { + return; + } + + if (st.hasMoreTokens()) + { + Object o = current.get(keyStroke); + if (o instanceof Hashtable) + { + current = (Hashtable) o; + } + else + { + o = new Hashtable(); + current.put(keyStroke, o); + current = (Hashtable) o; + } + } + else + { + current.put(keyStroke, action); + } + } + } + + /** + * Removes a key binding from this input handler. This is not yet implemented. + * + * @param keyBinding The key binding + */ + public void removeKeyBinding(String keyBinding) + { + throw new InternalError("Not yet implemented"); + } + + /** + * Removes all key bindings from this input handler. + */ + public void removeAllKeyBindings() + { + bindings.clear(); + } + + /** + * Returns a copy of this input handler that shares the same key bindings. Setting key bindings in the copy will + * also set them in the original. + */ + public InputHandler copy() + { + return new DefaultInputHandler(this); + } + + /** + * Handle a key pressed event. This will look up the binding for the key stroke and execute it. + */ + public void keyPressed(KeyEvent evt) + { + int keyCode = evt.getKeyCode(); + int modifiers = evt.getModifiers(); + if (keyCode == KeyEvent.VK_CONTROL || + keyCode == KeyEvent.VK_SHIFT || + keyCode == KeyEvent.VK_ALT || + keyCode == KeyEvent.VK_META) + { + return; + } + + if ((modifiers & ~KeyEvent.SHIFT_MASK) != 0 + || evt.isActionKey() + || keyCode == KeyEvent.VK_BACK_SPACE + || keyCode == KeyEvent.VK_DELETE + || keyCode == KeyEvent.VK_ENTER + || keyCode == KeyEvent.VK_TAB + || keyCode == KeyEvent.VK_ESCAPE) + { + if (grabAction != null) + { + handleGrabAction(evt); + return; + } + + KeyStroke keyStroke = KeyStroke.getKeyStroke(keyCode, modifiers); + Object o = currentBindings.get(keyStroke); + + if (o == null) + { + // Don't beep if the user presses some + // key we don't know about unless a + // prefix is active. Otherwise it will + // beep when caps lock is pressed, etc. + if (currentBindings != bindings) + { + Toolkit.getDefaultToolkit().beep(); + // F10 should be passed on, but C+e F10 + // shouldn't + repeatCount = 0; + repeat = false; + evt.consume(); + } + currentBindings = bindings; + // No binding for this keyStroke, pass it to menu + // (mnemonic, accelerator). DPS 4-may-2010 + mars.Globals.getGui().dispatchEventToMenu(evt); + evt.consume(); + } + else if (o instanceof ActionListener) + { + currentBindings = bindings; + executeAction(((ActionListener) o), + evt.getSource(), null); + + evt.consume(); + } + else if (o instanceof Hashtable) + { + currentBindings = (Hashtable) o; + evt.consume(); + } + } + } + + /** + * Handle a key typed event. This inserts the key into the text area. + */ + public void keyTyped(KeyEvent evt) + { + int modifiers = evt.getModifiers(); + char c = evt.getKeyChar(); + // This IF statement needed to prevent Macintosh shortcut keyChar from + // being echoed to the text area. E.g. Command-s, for Save, will echo + // the 's' character unless filtered out here. Command modifier + // matches KeyEvent.META_MASK. DPS 30-Nov-2010 + if ((modifiers & KeyEvent.META_MASK) != 0) + { + return; + } + // DPS 9-Jan-2013. Umberto Villano from Italy describes Alt combinations + // not working on Italian Mac keyboards, where # requires Alt (Option). + // This is preventing him from writing comments. Similar complaint from + // Joachim Parrow in Sweden, only for the $ character. Villano pointed + // me to this method. Plus a Google search on "jeditsyntax alt key" + // (without quotes) took me to + // http://compgroups.net/comp.lang.java.programmer/option-key-in-jedit-syntax-package/1068227 + // which says to comment out the second condition in this IF statement: + // if(c != KeyEvent.CHAR_UNDEFINED && (modifiers & KeyEvent.ALT_MASK) == 0) + // So let's give it a try! + // (...later) Bummer, it results in keystroke echoed into editing area when I use Alt + // combination for shortcut menu access (e.g. Alt+f to open the File menu). + // + // Torsten Maehne: This is a shortcoming of the menu + // shortcuts handling in the jedit component: It assumes that + // modifier keys are the same across all platforms. However, + // the menu shortcut keymask varies between OS X and + // Windows/Linux, it is Cmd + instead of Alt + + // . The "Java Development Guide for Mac" explicitly + // discusses the issue in: + // + // + // As jedit always considers Alt + as a keyboard + // shortcut, they block their output in the editor, which + // prevents the entry of special characters on OS X that uses + // Alt + for this purpose instead of AltGr + , as + // on Windows or Linux. + // + // For the latest jedit version (5.0.0), the menu + // accelerators don't work on OS X, at least the special + // characters can be entered using Alt + . The issue is + // still open, but there seems to be progress: + // + // http://sourceforge.net/tracker/index.php?func=detail&aid=3558572&group_id=588&atid=300588 + // http://sourceforge.net/tracker/?func=detail&atid=300588&aid=3604532&group_id=588 + // + // Until this is resolved upstream, don't ignore characters + // on OS X, which have been entered with the ALT modifier: + if (c != KeyEvent.CHAR_UNDEFINED && (((modifiers & KeyEvent.ALT_MASK) == 0) || System.getProperty("os.name").contains("OS X"))) + { + if (c >= 0x20 && c != 0x7f) + { + KeyStroke keyStroke = KeyStroke.getKeyStroke( + Character.toUpperCase(c)); + Object o = currentBindings.get(keyStroke); + + if (o instanceof Hashtable) + { + currentBindings = (Hashtable) o; + return; + } + else if (o instanceof ActionListener) + { + currentBindings = bindings; + executeAction((ActionListener) o, + evt.getSource(), + String.valueOf(c)); + return; + } + + currentBindings = bindings; + + if (grabAction != null) + { + handleGrabAction(evt); + return; + } + + // 0-9 adds another 'digit' to the repeat number + if (repeat && Character.isDigit(c)) + { + repeatCount *= 10; + repeatCount += (c - '0'); + return; + } + executeAction(INSERT_CHAR, evt.getSource(), + String.valueOf(evt.getKeyChar())); + repeatCount = 0; + repeat = false; + } + } + } +} diff --git a/src/main/java/mars/venus/editors/jeditsyntax/InputHandler.java b/src/main/java/mars/venus/editors/jeditsyntax/InputHandler.java index 4f7f832..31a2e47 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/InputHandler.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/InputHandler.java @@ -7,1111 +7,1241 @@ * remains intact in all source distributions of this package. */ - package mars.venus.editors.jeditsyntax; +package mars.venus.editors.jeditsyntax; - import javax.swing.text.*; - import javax.swing.JPopupMenu; - import java.awt.event.*; - import java.awt.Component; - import java.util.*; +import javax.swing.*; +import javax.swing.text.BadLocationException; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.Enumeration; +import java.util.EventObject; +import java.util.Hashtable; /** - * An input handler converts the user's key strokes into concrete actions. - * It also takes care of macro recording and action repetition.

+ * An input handler converts the user's key strokes into concrete actions. It also takes care of macro recording and + * action repetition.

+ *

+ * This class provides all the necessary support code for an input handler, but doesn't actually do any key binding + * logic. It is up to the implementations of this class to do so. * - * This class provides all the necessary support code for an input - * handler, but doesn't actually do any key binding logic. It is up - * to the implementations of this class to do so. - * * @author Slava Pestov * @version $Id: InputHandler.java,v 1.14 1999/12/13 03:40:30 sp Exp $ * @see org.syntax.jedit.DefaultInputHandler - * - * 08/12/2002 Clipboard actions (Oliver Henning) + *

+ * 08/12/2002 Clipboard actions (Oliver Henning) */ - public abstract class InputHandler extends KeyAdapter - { - /** - * If this client property is set to Boolean.TRUE on the text area, - * the home/end keys will support 'smart' BRIEF-like behaviour - * (one press = start/end of line, two presses = start/end of - * viewscreen, three presses = start/end of document). By default, - * this property is not set. - */ - public static final String SMART_HOME_END_PROPERTY = "InputHandler.homeEnd"; - - public static final ActionListener BACKSPACE = new backspace(); - public static final ActionListener BACKSPACE_WORD = new backspace_word(); - public static final ActionListener DELETE = new delete(); - public static final ActionListener DELETE_WORD = new delete_word(); - public static final ActionListener END = new end(false); - public static final ActionListener DOCUMENT_END = new document_end(false); - public static final ActionListener SELECT_ALL = new select_all(); - public static final ActionListener SELECT_END = new end(true); - public static final ActionListener SELECT_DOC_END = new document_end(true); - public static final ActionListener INSERT_BREAK = new insert_break(); - public static final ActionListener INSERT_TAB = new insert_tab(); - public static final ActionListener HOME = new home(false); - public static final ActionListener DOCUMENT_HOME = new document_home(false); - public static final ActionListener SELECT_HOME = new home(true); - public static final ActionListener SELECT_DOC_HOME = new document_home(true); - public static final ActionListener NEXT_CHAR = new next_char(false); - public static final ActionListener NEXT_LINE = new next_line(false); - public static final ActionListener NEXT_PAGE = new next_page(false); - public static final ActionListener NEXT_WORD = new next_word(false); - public static final ActionListener SELECT_NEXT_CHAR = new next_char(true); - public static final ActionListener SELECT_NEXT_LINE = new next_line(true); - public static final ActionListener SELECT_NEXT_PAGE = new next_page(true); - public static final ActionListener SELECT_NEXT_WORD = new next_word(true); - public static final ActionListener OVERWRITE = new overwrite(); - public static final ActionListener PREV_CHAR = new prev_char(false); - public static final ActionListener PREV_LINE = new prev_line(false); - public static final ActionListener PREV_PAGE = new prev_page(false); - public static final ActionListener PREV_WORD = new prev_word(false); - public static final ActionListener SELECT_PREV_CHAR = new prev_char(true); - public static final ActionListener SELECT_PREV_LINE = new prev_line(true); - public static final ActionListener SELECT_PREV_PAGE = new prev_page(true); - public static final ActionListener SELECT_PREV_WORD = new prev_word(true); - public static final ActionListener REPEAT = new repeat(); - public static final ActionListener TOGGLE_RECT = new toggle_rect(); - // Clipboard - public static final ActionListener CLIP_COPY = new clip_copy(); - public static final ActionListener CLIP_PASTE = new clip_paste(); - public static final ActionListener CLIP_CUT = new clip_cut(); - - // Default action - public static final ActionListener INSERT_CHAR = new insert_char(); - - private static Hashtable actions; - - static - { - actions = new Hashtable(); - actions.put("backspace",BACKSPACE); - actions.put("backspace-word",BACKSPACE_WORD); - actions.put("delete",DELETE); - actions.put("delete-word",DELETE_WORD); - actions.put("end",END); - actions.put("select-all",SELECT_ALL); - actions.put("select-end",SELECT_END); - actions.put("document-end",DOCUMENT_END); - actions.put("select-doc-end",SELECT_DOC_END); - actions.put("insert-break",INSERT_BREAK); - actions.put("insert-tab",INSERT_TAB); - actions.put("home",HOME); - actions.put("select-home",SELECT_HOME); - actions.put("document-home",DOCUMENT_HOME); - actions.put("select-doc-home",SELECT_DOC_HOME); - actions.put("next-char",NEXT_CHAR); - actions.put("next-line",NEXT_LINE); - actions.put("next-page",NEXT_PAGE); - actions.put("next-word",NEXT_WORD); - actions.put("select-next-char",SELECT_NEXT_CHAR); - actions.put("select-next-line",SELECT_NEXT_LINE); - actions.put("select-next-page",SELECT_NEXT_PAGE); - actions.put("select-next-word",SELECT_NEXT_WORD); - actions.put("overwrite",OVERWRITE); - actions.put("prev-char",PREV_CHAR); - actions.put("prev-line",PREV_LINE); - actions.put("prev-page",PREV_PAGE); - actions.put("prev-word",PREV_WORD); - actions.put("select-prev-char",SELECT_PREV_CHAR); - actions.put("select-prev-line",SELECT_PREV_LINE); - actions.put("select-prev-page",SELECT_PREV_PAGE); - actions.put("select-prev-word",SELECT_PREV_WORD); - actions.put("repeat",REPEAT); - actions.put("toggle-rect",TOGGLE_RECT); - actions.put("insert-char",INSERT_CHAR); - actions.put("clipboard-copy",CLIP_COPY); - actions.put("clipboard-paste",CLIP_PASTE); - actions.put("clipboard-cut",CLIP_CUT); - } - - /** - * Returns a named text area action. - * @param name The action name - */ - public static ActionListener getAction(String name) - { - return (ActionListener)actions.get(name); - } - - /** - * Returns the name of the specified text area action. - * @param listener The action - */ - public static String getActionName(ActionListener listener) - { - Enumeration enumeration = getActions(); - while(enumeration.hasMoreElements()) - { - String name = (String)enumeration.nextElement(); +public abstract class InputHandler extends KeyAdapter +{ + /** + * If this client property is set to Boolean.TRUE on the text area, the home/end keys will support 'smart' + * BRIEF-like behaviour (one press = start/end of line, two presses = start/end of viewscreen, three presses = + * start/end of document). By default, this property is not set. + */ + public static final String SMART_HOME_END_PROPERTY = "InputHandler.homeEnd"; + + public static final ActionListener BACKSPACE = new backspace(); + + public static final ActionListener BACKSPACE_WORD = new backspace_word(); + + public static final ActionListener DELETE = new delete(); + + public static final ActionListener DELETE_WORD = new delete_word(); + + public static final ActionListener END = new end(false); + + public static final ActionListener DOCUMENT_END = new document_end(false); + + public static final ActionListener SELECT_ALL = new select_all(); + + public static final ActionListener SELECT_END = new end(true); + + public static final ActionListener SELECT_DOC_END = new document_end(true); + + public static final ActionListener INSERT_BREAK = new insert_break(); + + public static final ActionListener INSERT_TAB = new insert_tab(); + + public static final ActionListener HOME = new home(false); + + public static final ActionListener DOCUMENT_HOME = new document_home(false); + + public static final ActionListener SELECT_HOME = new home(true); + + public static final ActionListener SELECT_DOC_HOME = new document_home(true); + + public static final ActionListener NEXT_CHAR = new next_char(false); + + public static final ActionListener NEXT_LINE = new next_line(false); + + public static final ActionListener NEXT_PAGE = new next_page(false); + + public static final ActionListener NEXT_WORD = new next_word(false); + + public static final ActionListener SELECT_NEXT_CHAR = new next_char(true); + + public static final ActionListener SELECT_NEXT_LINE = new next_line(true); + + public static final ActionListener SELECT_NEXT_PAGE = new next_page(true); + + public static final ActionListener SELECT_NEXT_WORD = new next_word(true); + + public static final ActionListener OVERWRITE = new overwrite(); + + public static final ActionListener PREV_CHAR = new prev_char(false); + + public static final ActionListener PREV_LINE = new prev_line(false); + + public static final ActionListener PREV_PAGE = new prev_page(false); + + public static final ActionListener PREV_WORD = new prev_word(false); + + public static final ActionListener SELECT_PREV_CHAR = new prev_char(true); + + public static final ActionListener SELECT_PREV_LINE = new prev_line(true); + + public static final ActionListener SELECT_PREV_PAGE = new prev_page(true); + + public static final ActionListener SELECT_PREV_WORD = new prev_word(true); + + public static final ActionListener REPEAT = new repeat(); + + public static final ActionListener TOGGLE_RECT = new toggle_rect(); + + // Clipboard + public static final ActionListener CLIP_COPY = new clip_copy(); + + public static final ActionListener CLIP_PASTE = new clip_paste(); + + public static final ActionListener CLIP_CUT = new clip_cut(); + + // Default action + public static final ActionListener INSERT_CHAR = new insert_char(); + + private static final Hashtable actions; + + static + { + actions = new Hashtable(); + actions.put("backspace", BACKSPACE); + actions.put("backspace-word", BACKSPACE_WORD); + actions.put("delete", DELETE); + actions.put("delete-word", DELETE_WORD); + actions.put("end", END); + actions.put("select-all", SELECT_ALL); + actions.put("select-end", SELECT_END); + actions.put("document-end", DOCUMENT_END); + actions.put("select-doc-end", SELECT_DOC_END); + actions.put("insert-break", INSERT_BREAK); + actions.put("insert-tab", INSERT_TAB); + actions.put("home", HOME); + actions.put("select-home", SELECT_HOME); + actions.put("document-home", DOCUMENT_HOME); + actions.put("select-doc-home", SELECT_DOC_HOME); + actions.put("next-char", NEXT_CHAR); + actions.put("next-line", NEXT_LINE); + actions.put("next-page", NEXT_PAGE); + actions.put("next-word", NEXT_WORD); + actions.put("select-next-char", SELECT_NEXT_CHAR); + actions.put("select-next-line", SELECT_NEXT_LINE); + actions.put("select-next-page", SELECT_NEXT_PAGE); + actions.put("select-next-word", SELECT_NEXT_WORD); + actions.put("overwrite", OVERWRITE); + actions.put("prev-char", PREV_CHAR); + actions.put("prev-line", PREV_LINE); + actions.put("prev-page", PREV_PAGE); + actions.put("prev-word", PREV_WORD); + actions.put("select-prev-char", SELECT_PREV_CHAR); + actions.put("select-prev-line", SELECT_PREV_LINE); + actions.put("select-prev-page", SELECT_PREV_PAGE); + actions.put("select-prev-word", SELECT_PREV_WORD); + actions.put("repeat", REPEAT); + actions.put("toggle-rect", TOGGLE_RECT); + actions.put("insert-char", INSERT_CHAR); + actions.put("clipboard-copy", CLIP_COPY); + actions.put("clipboard-paste", CLIP_PASTE); + actions.put("clipboard-cut", CLIP_CUT); + } + + // protected members + protected ActionListener grabAction; + + protected boolean repeat; + + protected int repeatCount; + + protected InputHandler.MacroRecorder recorder; + + /** + * Returns a named text area action. + * + * @param name The action name + */ + public static ActionListener getAction(String name) + { + return (ActionListener) actions.get(name); + } + + /** + * Returns the name of the specified text area action. + * + * @param listener The action + */ + public static String getActionName(ActionListener listener) + { + Enumeration enumeration = getActions(); + while (enumeration.hasMoreElements()) + { + String name = (String) enumeration.nextElement(); ActionListener _listener = getAction(name); - if(_listener == listener) - return name; - } - return null; - } - - /** - * Returns an enumeration of all available actions. - */ - public static Enumeration getActions() - { - return actions.keys(); - } - - /** - * Adds the default key bindings to this input handler. - * This should not be called in the constructor of this - * input handler, because applications might load the - * key bindings from a file, etc. - */ - public abstract void addDefaultKeyBindings(); - - /** - * Adds a key binding to this input handler. - * @param keyBinding The key binding (the format of this is - * input-handler specific) - * @param action The action - */ - public abstract void addKeyBinding(String keyBinding, ActionListener action); - - /** - * Removes a key binding from this input handler. - * @param keyBinding The key binding - */ - public abstract void removeKeyBinding(String keyBinding); - - /** - * Removes all key bindings from this input handler. - */ - public abstract void removeAllKeyBindings(); - - /** - * Grabs the next key typed event and invokes the specified - * action with the key as a the action command. - * @param action The action - */ - public void grabNextKeyStroke(ActionListener listener) - { - grabAction = listener; - } - - /** - * Returns if repeating is enabled. When repeating is enabled, - * actions will be executed multiple times. This is usually - * invoked with a special key stroke in the input handler. - */ - public boolean isRepeatEnabled() - { - return repeat; - } - - /** - * Enables repeating. When repeating is enabled, actions will be - * executed multiple times. Once repeating is enabled, the input - * handler should read a number from the keyboard. - */ - public void setRepeatEnabled(boolean repeat) - { - this.repeat = repeat; - } - - /** - * Returns the number of times the next action will be repeated. - */ - public int getRepeatCount() - { - return (repeat ? Math.max(1,repeatCount) : 1); - } - - /** - * Sets the number of times the next action will be repeated. - * @param repeatCount The repeat count - */ - public void setRepeatCount(int repeatCount) - { - this.repeatCount = repeatCount; - } - - /** - * Returns the macro recorder. If this is non-null, all executed - * actions should be forwarded to the recorder. - */ - public InputHandler.MacroRecorder getMacroRecorder() - { - return recorder; - } - - /** - * Sets the macro recorder. If this is non-null, all executed - * actions should be forwarded to the recorder. - * @param recorder The macro recorder - */ - public void setMacroRecorder(InputHandler.MacroRecorder recorder) - { - this.recorder = recorder; - } - - /** - * Returns a copy of this input handler that shares the same - * key bindings. Setting key bindings in the copy will also - * set them in the original. - */ - public abstract InputHandler copy(); - - /** - * Executes the specified action, repeating and recording it as - * necessary. - * @param listener The action listener - * @param source The event source - * @param actionCommand The action command - */ - public void executeAction(ActionListener listener, Object source, - String actionCommand) - { - // create event - ActionEvent evt = new ActionEvent(source, + if (_listener == listener) + { + return name; + } + } + return null; + } + + /** + * Returns an enumeration of all available actions. + */ + public static Enumeration getActions() + { + return actions.keys(); + } + + /** + * Returns the text area that fired the specified event. + * + * @param evt The event + */ + public static JEditTextArea getTextArea(EventObject evt) + { + if (evt != null) + { + Object o = evt.getSource(); + if (o instanceof Component) + { + // find the parent text area + Component c = (Component) o; + for (; ; ) + { + if (c instanceof JEditTextArea) + { + return (JEditTextArea) c; + } + else if (c == null) + { + break; + } + if (c instanceof JPopupMenu) + { + c = ((JPopupMenu) c) + .getInvoker(); + } + else + { + c = c.getParent(); + } + } + } + } + + // this shouldn't happen + System.err.println("BUG: getTextArea() returning null"); + System.err.println("Report this to Slava Pestov "); + return null; + } + + /** + * Adds the default key bindings to this input handler. This should not be called in the constructor of this input + * handler, because applications might load the key bindings from a file, etc. + */ + public abstract void addDefaultKeyBindings(); + + /** + * Adds a key binding to this input handler. + * + * @param keyBinding The key binding (the format of this is input-handler specific) + * @param action The action + */ + public abstract void addKeyBinding(String keyBinding, ActionListener action); + + /** + * Removes a key binding from this input handler. + * + * @param keyBinding The key binding + */ + public abstract void removeKeyBinding(String keyBinding); + + /** + * Removes all key bindings from this input handler. + */ + public abstract void removeAllKeyBindings(); + + /** + * Grabs the next key typed event and invokes the specified action with the key as a the action command. + * + * @param action The action + */ + public void grabNextKeyStroke(ActionListener listener) + { + grabAction = listener; + } + + /** + * Returns if repeating is enabled. When repeating is enabled, actions will be executed multiple times. This is + * usually invoked with a special key stroke in the input handler. + */ + public boolean isRepeatEnabled() + { + return repeat; + } + + /** + * Enables repeating. When repeating is enabled, actions will be executed multiple times. Once repeating is enabled, + * the input handler should read a number from the keyboard. + */ + public void setRepeatEnabled(boolean repeat) + { + this.repeat = repeat; + } + + /** + * Returns the number of times the next action will be repeated. + */ + public int getRepeatCount() + { + return (repeat ? Math.max(1, repeatCount) : 1); + } + + /** + * Sets the number of times the next action will be repeated. + * + * @param repeatCount The repeat count + */ + public void setRepeatCount(int repeatCount) + { + this.repeatCount = repeatCount; + } + + + // protected members + + /** + * Returns the macro recorder. If this is non-null, all executed actions should be forwarded to the recorder. + */ + public InputHandler.MacroRecorder getMacroRecorder() + { + return recorder; + } + + /** + * Sets the macro recorder. If this is non-null, all executed actions should be forwarded to the recorder. + * + * @param recorder The macro recorder + */ + public void setMacroRecorder(InputHandler.MacroRecorder recorder) + { + this.recorder = recorder; + } + + /** + * Returns a copy of this input handler that shares the same key bindings. Setting key bindings in the copy will + * also set them in the original. + */ + public abstract InputHandler copy(); + + /** + * Executes the specified action, repeating and recording it as necessary. + * + * @param listener The action listener + * @param source The event source + * @param actionCommand The action command + */ + public void executeAction(ActionListener listener, Object source, + String actionCommand) + { + // create event + ActionEvent evt = new ActionEvent(source, ActionEvent.ACTION_PERFORMED, actionCommand); - - // don't do anything if the action is a wrapper - // (like EditAction.Wrapper) - if(listener instanceof Wrapper) - { + + // don't do anything if the action is a wrapper + // (like EditAction.Wrapper) + if (listener instanceof Wrapper) + { listener.actionPerformed(evt); return; - } - - // remember old values, in case action changes them - boolean _repeat = repeat; - int _repeatCount = getRepeatCount(); - - // execute the action - if(listener instanceof InputHandler.NonRepeatable) - { + } + + // remember old values, in case action changes them + boolean _repeat = repeat; + int _repeatCount = getRepeatCount(); + + // execute the action + if (listener instanceof InputHandler.NonRepeatable) + { listener.actionPerformed(evt); - } - else - { - for(int i = 0; i < Math.max(1,repeatCount); i++) - listener.actionPerformed(evt); - } - - // do recording. Notice that we do no recording whatsoever - // for actions that grab keys - if(grabAction == null) - { - if(recorder != null) + } + else + { + for (int i = 0; i < Math.max(1, repeatCount); i++) { - if(!(listener instanceof InputHandler.NonRecordable)) - { - if(_repeatCount != 1) - recorder.actionPerformed(REPEAT,String.valueOf(_repeatCount)); - - recorder.actionPerformed(listener,actionCommand); - } + listener.actionPerformed(evt); } - - // If repeat was true originally, clear it - // Otherwise it might have been set by the action, etc - if(_repeat) + } + + // do recording. Notice that we do no recording whatsoever + // for actions that grab keys + if (grabAction == null) + { + if (recorder != null) { - repeat = false; - repeatCount = 0; + if (!(listener instanceof InputHandler.NonRecordable)) + { + if (_repeatCount != 1) + { + recorder.actionPerformed(REPEAT, String.valueOf(_repeatCount)); + } + + recorder.actionPerformed(listener, actionCommand); + } } - } - } - - /** - * Returns the text area that fired the specified event. - * @param evt The event - */ - public static JEditTextArea getTextArea(EventObject evt) - { - if(evt != null) - { - Object o = evt.getSource(); - if(o instanceof Component) + + // If repeat was true originally, clear it + // Otherwise it might have been set by the action, etc + if (_repeat) { - // find the parent text area - Component c = (Component)o; - for(;;) - { - if(c instanceof JEditTextArea) - return (JEditTextArea)c; - else if(c == null) - break; - if(c instanceof JPopupMenu) - c = ((JPopupMenu)c) - .getInvoker(); - else - c = c.getParent(); - } + repeat = false; + repeatCount = 0; } - } - - // this shouldn't happen - System.err.println("BUG: getTextArea() returning null"); - System.err.println("Report this to Slava Pestov "); - return null; - } - - - // protected members - - /** - * If a key is being grabbed, this method should be called with - * the appropriate key event. It executes the grab action with - * the typed character as the parameter. - */ - protected void handleGrabAction(KeyEvent evt) - { - // Clear it *before* it is executed so that executeAction() - // resets the repeat count - ActionListener _grabAction = grabAction; - grabAction = null; - executeAction(_grabAction,evt.getSource(), + } + } + + /** + * If a key is being grabbed, this method should be called with the appropriate key event. It executes the grab + * action with the typed character as the parameter. + */ + protected void handleGrabAction(KeyEvent evt) + { + // Clear it *before* it is executed so that executeAction() + // resets the repeat count + ActionListener _grabAction = grabAction; + grabAction = null; + executeAction(_grabAction, evt.getSource(), String.valueOf(evt.getKeyChar())); - } - - // protected members - protected ActionListener grabAction; - protected boolean repeat; - protected int repeatCount; - protected InputHandler.MacroRecorder recorder; - - /** - * If an action implements this interface, it should not be repeated. - * Instead, it will handle the repetition itself. - */ - public interface NonRepeatable {} - - /** - * If an action implements this interface, it should not be recorded - * by the macro recorder. Instead, it will do its own recording. - */ - public interface NonRecordable {} - - /** - * For use by EditAction.Wrapper only. - * @since jEdit 2.2final - */ - public interface Wrapper {} - - /** - * Macro recorder. - */ - public interface MacroRecorder - { - void actionPerformed(ActionListener listener, - String actionCommand); - } - - public static class backspace implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { + } + + /** + * If an action implements this interface, it should not be repeated. Instead, it will handle the repetition + * itself. + */ + public interface NonRepeatable {} + + /** + * If an action implements this interface, it should not be recorded by the macro recorder. Instead, it will do its + * own recording. + */ + public interface NonRecordable {} + + /** + * For use by EditAction.Wrapper only. + * + * @since jEdit 2.2final + */ + public interface Wrapper {} + + /** + * Macro recorder. + */ + public interface MacroRecorder + { + void actionPerformed(ActionListener listener, + String actionCommand); + } + + public static class backspace implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); - - if(!textArea.isEditable()) + + if (!textArea.isEditable()) { - textArea.getToolkit().beep(); - return; + textArea.getToolkit().beep(); + return; } - - if(textArea.getSelectionStart() - != textArea.getSelectionEnd()) + + if (textArea.getSelectionStart() + != textArea.getSelectionEnd()) { - textArea.setSelectedText(""); + textArea.setSelectedText(""); } else { - int caret = textArea.getCaretPosition(); - if(caret == 0) - { - textArea.getToolkit().beep(); - return; - } - try - { - textArea.getDocument().remove(caret - 1,1); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } + int caret = textArea.getCaretPosition(); + if (caret == 0) + { + textArea.getToolkit().beep(); + return; + } + try + { + textArea.getDocument().remove(caret - 1, 1); + } + catch (BadLocationException bl) + { + bl.printStackTrace(); + } } - } - } - - public static class backspace_word implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { + } + } + + public static class backspace_word implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int start = textArea.getSelectionStart(); - if(start != textArea.getSelectionEnd()) + if (start != textArea.getSelectionEnd()) { - textArea.setSelectedText(""); + textArea.setSelectedText(""); } - + int line = textArea.getCaretLine(); int lineStart = textArea.getLineStartOffset(line); int caret = start - lineStart; - + String lineText = textArea.getLineText(textArea - .getCaretLine()); - - if(caret == 0) + .getCaretLine()); + + if (caret == 0) { - if(lineStart == 0) - { - textArea.getToolkit().beep(); - return; - } - caret--; + if (lineStart == 0) + { + textArea.getToolkit().beep(); + return; + } + caret--; } else { - String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); - caret = TextUtilities.findWordStart(lineText,caret,noWordSep); + String noWordSep = (String) textArea.getDocument().getProperty("noWordSep"); + caret = TextUtilities.findWordStart(lineText, caret, noWordSep); } - + try { - textArea.getDocument().remove( - caret + lineStart, - start - (caret + lineStart)); + textArea.getDocument().remove( + caret + lineStart, + start - (caret + lineStart)); } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } - } - } - - public static class delete implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { + catch (BadLocationException bl) + { + bl.printStackTrace(); + } + } + } + + public static class delete implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); - - if(!textArea.isEditable()) + + if (!textArea.isEditable()) { - textArea.getToolkit().beep(); - return; + textArea.getToolkit().beep(); + return; } - - if(textArea.getSelectionStart() - != textArea.getSelectionEnd()) + + if (textArea.getSelectionStart() + != textArea.getSelectionEnd()) { - textArea.setSelectedText(""); + textArea.setSelectedText(""); } else { - int caret = textArea.getCaretPosition(); - if(caret == textArea.getDocumentLength()) - { - textArea.getToolkit().beep(); - return; - } - try - { - textArea.getDocument().remove(caret,1); - } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } + int caret = textArea.getCaretPosition(); + if (caret == textArea.getDocumentLength()) + { + textArea.getToolkit().beep(); + return; + } + try + { + textArea.getDocument().remove(caret, 1); + } + catch (BadLocationException bl) + { + bl.printStackTrace(); + } } - } - } - - public static class delete_word implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { + } + } + + public static class delete_word implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int start = textArea.getSelectionStart(); - if(start != textArea.getSelectionEnd()) + if (start != textArea.getSelectionEnd()) { - textArea.setSelectedText(""); + textArea.setSelectedText(""); } - + int line = textArea.getCaretLine(); int lineStart = textArea.getLineStartOffset(line); int caret = start - lineStart; - + String lineText = textArea.getLineText(textArea - .getCaretLine()); - - if(caret == lineText.length()) + .getCaretLine()); + + if (caret == lineText.length()) { - if(lineStart + caret == textArea.getDocumentLength()) - { - textArea.getToolkit().beep(); - return; - } - caret++; + if (lineStart + caret == textArea.getDocumentLength()) + { + textArea.getToolkit().beep(); + return; + } + caret++; } else { - String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); - caret = TextUtilities.findWordEnd(lineText,caret,noWordSep); + String noWordSep = (String) textArea.getDocument().getProperty("noWordSep"); + caret = TextUtilities.findWordEnd(lineText, caret, noWordSep); } - + try { - textArea.getDocument().remove(start, - (caret + lineStart) - start); + textArea.getDocument().remove(start, + (caret + lineStart) - start); } - catch(BadLocationException bl) - { - bl.printStackTrace(); - } - } - } - - public static class end implements ActionListener - { - private boolean select; - - public end(boolean select) - { + catch (BadLocationException bl) + { + bl.printStackTrace(); + } + } + } + + public static class end implements ActionListener + { + private final boolean select; + + public end(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); - + int caret = textArea.getCaretPosition(); - + int lastOfLine = textArea.getLineEndOffset( - textArea.getCaretLine()) - 1; + textArea.getCaretLine()) - 1; int lastVisibleLine = textArea.getFirstLine() - + textArea.getVisibleLines(); - if(lastVisibleLine >= textArea.getLineCount()) + + textArea.getVisibleLines(); + if (lastVisibleLine >= textArea.getLineCount()) { - lastVisibleLine = Math.min(textArea.getLineCount() - 1, - lastVisibleLine); + lastVisibleLine = Math.min(textArea.getLineCount() - 1, + lastVisibleLine); } else - lastVisibleLine -= (textArea.getElectricScroll() + 1); - + { + lastVisibleLine -= (textArea.getElectricScroll() + 1); + } + int lastVisible = textArea.getLineEndOffset(lastVisibleLine) - 1; int lastDocument = textArea.getDocumentLength(); - - if(caret == lastDocument) + + if (caret == lastDocument) { - textArea.getToolkit().beep(); - return; + textArea.getToolkit().beep(); + return; + } + else if (!Boolean.TRUE.equals(textArea.getClientProperty( + SMART_HOME_END_PROPERTY))) + { + caret = lastOfLine; + } + else if (caret == lastVisible) + { + caret = lastDocument; + } + else if (caret == lastOfLine) + { + caret = lastVisible; } - else if(!Boolean.TRUE.equals(textArea.getClientProperty( - SMART_HOME_END_PROPERTY))) - caret = lastOfLine; - else if(caret == lastVisible) - caret = lastDocument; - else if(caret == lastOfLine) - caret = lastVisible; else - caret = lastOfLine; - - if(select) - textArea.select(textArea.getMarkPosition(),caret); + { + caret = lastOfLine; + } + + if (select) + { + textArea.select(textArea.getMarkPosition(), caret); + } else - textArea.setCaretPosition(caret); - } - } - - public static class select_all implements ActionListener { - public void actionPerformed(ActionEvent evt) - { + { + textArea.setCaretPosition(caret); + } + } + } + + public static class select_all implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); textArea.selectAll(); - } - } - - public static class document_end implements ActionListener - { - private boolean select; - - public document_end(boolean select) - { + } + } + + public static class document_end implements ActionListener + { + private final boolean select; + + public document_end(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); - if(select) - textArea.select(textArea.getMarkPosition(), - textArea.getDocumentLength()); - else - textArea.setCaretPosition(textArea - .getDocumentLength()); - } - } - - public static class home implements ActionListener - { - private boolean select; - - public home(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - - int caret = textArea.getCaretPosition(); - - int firstLine = textArea.getFirstLine(); - - int firstOfLine = textArea.getLineStartOffset( - textArea.getCaretLine()); - int firstVisibleLine = (firstLine == 0 ? 0 : - firstLine + textArea.getElectricScroll()); - int firstVisible = textArea.getLineStartOffset( - firstVisibleLine); - - if(caret == 0) + if (select) { - textArea.getToolkit().beep(); - return; + textArea.select(textArea.getMarkPosition(), + textArea.getDocumentLength()); } - else if(!Boolean.TRUE.equals(textArea.getClientProperty( - SMART_HOME_END_PROPERTY))) - caret = firstOfLine; - else if(caret == firstVisible) - caret = 0; - else if(caret == firstOfLine) - caret = firstVisible; else - caret = firstOfLine; - - if(select) - textArea.select(textArea.getMarkPosition(),caret); - else - textArea.setCaretPosition(caret); - } - } - - public static class document_home implements ActionListener - { - private boolean select; - - public document_home(boolean select) - { - this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - if(select) - textArea.select(textArea.getMarkPosition(),0); - else - textArea.setCaretPosition(0); - } - } - - public static class insert_break implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { - JEditTextArea textArea = getTextArea(evt); - - if(!textArea.isEditable()) { - textArea.getToolkit().beep(); - return; + textArea.setCaretPosition(textArea + .getDocumentLength()); + } + } + } + + public static class home implements ActionListener + { + private final boolean select; + + public home(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + + int caret = textArea.getCaretPosition(); + + int firstLine = textArea.getFirstLine(); + + int firstOfLine = textArea.getLineStartOffset( + textArea.getCaretLine()); + int firstVisibleLine = (firstLine == 0 ? 0 : + firstLine + textArea.getElectricScroll()); + int firstVisible = textArea.getLineStartOffset( + firstVisibleLine); + + if (caret == 0) + { + textArea.getToolkit().beep(); + return; + } + else if (!Boolean.TRUE.equals(textArea.getClientProperty( + SMART_HOME_END_PROPERTY))) + { + caret = firstOfLine; + } + else if (caret == firstVisible) + { + caret = 0; + } + else if (caret == firstOfLine) + { + caret = firstVisible; + } + else + { + caret = firstOfLine; + } + + if (select) + { + textArea.select(textArea.getMarkPosition(), caret); + } + else + { + textArea.setCaretPosition(caret); + } + } + } + + public static class document_home implements ActionListener + { + private final boolean select; + + public document_home(boolean select) + { + this.select = select; + } + + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + if (select) + { + textArea.select(textArea.getMarkPosition(), 0); + } + else + { + textArea.setCaretPosition(0); + } + } + } + + public static class insert_break implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + JEditTextArea textArea = getTextArea(evt); + + if (!textArea.isEditable()) + { + textArea.getToolkit().beep(); + return; } // AutoIndent feature added DPS 31-Dec-2010 - textArea.setSelectedText("\n"+textArea.getAutoIndent()); - } - } - - public static class insert_tab implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { + textArea.setSelectedText("\n" + textArea.getAutoIndent()); + } + } + + public static class insert_tab implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); - - if(!textArea.isEditable()) + + if (!textArea.isEditable()) { - textArea.getToolkit().beep(); - return; + textArea.getToolkit().beep(); + return; } - + textArea.overwriteSetSelectedText("\t"); - } - } - - public static class next_char implements ActionListener - { - private boolean select; - - public next_char(boolean select) - { + } + } + + public static class next_char implements ActionListener + { + private final boolean select; + + public next_char(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int caret = textArea.getCaretPosition(); - if(caret == textArea.getDocumentLength()) + if (caret == textArea.getDocumentLength()) { - textArea.getToolkit().beep(); - return; + textArea.getToolkit().beep(); + return; + } + + if (select) + { + textArea.select(textArea.getMarkPosition(), + caret + 1); } - - if(select) - textArea.select(textArea.getMarkPosition(), - caret + 1); else - textArea.setCaretPosition(caret + 1); - } - } - - public static class next_line implements ActionListener - { - private boolean select; - - public next_line(boolean select) - { + { + textArea.setCaretPosition(caret + 1); + } + } + } + + public static class next_line implements ActionListener + { + private final boolean select; + + public next_line(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int caret = textArea.getCaretPosition(); int line = textArea.getCaretLine(); - - if(line == textArea.getLineCount() - 1) + + if (line == textArea.getLineCount() - 1) { - textArea.getToolkit().beep(); - return; + textArea.getToolkit().beep(); + return; } - + int magic = textArea.getMagicCaretPosition(); - if(magic == -1) + if (magic == -1) { - magic = textArea.offsetToX(line, - caret - textArea.getLineStartOffset(line)); + magic = textArea.offsetToX(line, + caret - textArea.getLineStartOffset(line)); } - + caret = textArea.getLineStartOffset(line + 1) - + textArea.xToOffset(line + 1,magic); - if(select) - textArea.select(textArea.getMarkPosition(),caret); + + textArea.xToOffset(line + 1, magic); + if (select) + { + textArea.select(textArea.getMarkPosition(), caret); + } else - textArea.setCaretPosition(caret); + { + textArea.setCaretPosition(caret); + } textArea.setMagicCaretPosition(magic); - } - } - - public static class next_page implements ActionListener - { - private boolean select; - - public next_page(boolean select) - { + } + } + + public static class next_page implements ActionListener + { + private final boolean select; + + public next_page(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int lineCount = textArea.getLineCount(); int firstLine = textArea.getFirstLine(); int visibleLines = textArea.getVisibleLines(); int line = textArea.getCaretLine(); - + firstLine += visibleLines; - - if(firstLine + visibleLines >= lineCount - 1) - firstLine = lineCount - visibleLines; - + + if (firstLine + visibleLines >= lineCount - 1) + { + firstLine = lineCount - visibleLines; + } + textArea.setFirstLine(firstLine); - + int caret = textArea.getLineStartOffset( - Math.min(textArea.getLineCount() - 1, - line + visibleLines)); - if(select) - textArea.select(textArea.getMarkPosition(),caret); + Math.min(textArea.getLineCount() - 1, + line + visibleLines)); + if (select) + { + textArea.select(textArea.getMarkPosition(), caret); + } else - textArea.setCaretPosition(caret); - } - } - - public static class next_word implements ActionListener - { - private boolean select; - - public next_word(boolean select) - { + { + textArea.setCaretPosition(caret); + } + } + } + + public static class next_word implements ActionListener + { + private final boolean select; + + public next_word(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int caret = textArea.getCaretPosition(); int line = textArea.getCaretLine(); int lineStart = textArea.getLineStartOffset(line); caret -= lineStart; - + String lineText = textArea.getLineText(textArea - .getCaretLine()); - - if(caret == lineText.length()) + .getCaretLine()); + + if (caret == lineText.length()) { - if(lineStart + caret == textArea.getDocumentLength()) - { - textArea.getToolkit().beep(); - return; - } - caret++; + if (lineStart + caret == textArea.getDocumentLength()) + { + textArea.getToolkit().beep(); + return; + } + caret++; } else { - String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); - caret = TextUtilities.findWordEnd(lineText,caret,noWordSep); + String noWordSep = (String) textArea.getDocument().getProperty("noWordSep"); + caret = TextUtilities.findWordEnd(lineText, caret, noWordSep); + } + + if (select) + { + textArea.select(textArea.getMarkPosition(), + lineStart + caret); } - - if(select) - textArea.select(textArea.getMarkPosition(), - lineStart + caret); else - textArea.setCaretPosition(lineStart + caret); - } - } - - public static class overwrite implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { + { + textArea.setCaretPosition(lineStart + caret); + } + } + } + + public static class overwrite implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); textArea.setOverwriteEnabled( - !textArea.isOverwriteEnabled()); - } - } - - public static class prev_char implements ActionListener - { - private boolean select; - - public prev_char(boolean select) - { + !textArea.isOverwriteEnabled()); + } + } + + public static class prev_char implements ActionListener + { + private final boolean select; + + public prev_char(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int caret = textArea.getCaretPosition(); - if(caret == 0) + if (caret == 0) { - textArea.getToolkit().beep(); - return; + textArea.getToolkit().beep(); + return; + } + + if (select) + { + textArea.select(textArea.getMarkPosition(), + caret - 1); } - - if(select) - textArea.select(textArea.getMarkPosition(), - caret - 1); else - textArea.setCaretPosition(caret - 1); - } - } - - public static class prev_line implements ActionListener - { - private boolean select; - - public prev_line(boolean select) - { + { + textArea.setCaretPosition(caret - 1); + } + } + } + + public static class prev_line implements ActionListener + { + private final boolean select; + + public prev_line(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int caret = textArea.getCaretPosition(); int line = textArea.getCaretLine(); - - if(line == 0) + + if (line == 0) { - textArea.getToolkit().beep(); - return; + textArea.getToolkit().beep(); + return; } - + int magic = textArea.getMagicCaretPosition(); - if(magic == -1) + if (magic == -1) { - magic = textArea.offsetToX(line, - caret - textArea.getLineStartOffset(line)); + magic = textArea.offsetToX(line, + caret - textArea.getLineStartOffset(line)); } - + caret = textArea.getLineStartOffset(line - 1) - + textArea.xToOffset(line - 1,magic); - if(select) - textArea.select(textArea.getMarkPosition(),caret); + + textArea.xToOffset(line - 1, magic); + if (select) + { + textArea.select(textArea.getMarkPosition(), caret); + } else - textArea.setCaretPosition(caret); + { + textArea.setCaretPosition(caret); + } textArea.setMagicCaretPosition(magic); - } - } - - public static class prev_page implements ActionListener - { - private boolean select; - - public prev_page(boolean select) - { + } + } + + public static class prev_page implements ActionListener + { + private final boolean select; + + public prev_page(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int firstLine = textArea.getFirstLine(); int visibleLines = textArea.getVisibleLines(); int line = textArea.getCaretLine(); - - if(firstLine < visibleLines) - firstLine = visibleLines; - + + if (firstLine < visibleLines) + { + firstLine = visibleLines; + } + textArea.setFirstLine(firstLine - visibleLines); - + int caret = textArea.getLineStartOffset( - Math.max(0,line - visibleLines)); - if(select) - textArea.select(textArea.getMarkPosition(),caret); + Math.max(0, line - visibleLines)); + if (select) + { + textArea.select(textArea.getMarkPosition(), caret); + } else - textArea.setCaretPosition(caret); - } - } - - public static class prev_word implements ActionListener - { - private boolean select; - - public prev_word(boolean select) - { + { + textArea.setCaretPosition(caret); + } + } + } + + public static class prev_word implements ActionListener + { + private final boolean select; + + public prev_word(boolean select) + { this.select = select; - } - - public void actionPerformed(ActionEvent evt) - { + } + + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); int caret = textArea.getCaretPosition(); int line = textArea.getCaretLine(); int lineStart = textArea.getLineStartOffset(line); caret -= lineStart; - + String lineText = textArea.getLineText(textArea - .getCaretLine()); - - if(caret == 0) + .getCaretLine()); + + if (caret == 0) { - if(lineStart == 0) - { - textArea.getToolkit().beep(); - return; - } - caret--; + if (lineStart == 0) + { + textArea.getToolkit().beep(); + return; + } + caret--; } else { - String noWordSep = (String)textArea.getDocument().getProperty("noWordSep"); - caret = TextUtilities.findWordStart(lineText,caret,noWordSep); + String noWordSep = (String) textArea.getDocument().getProperty("noWordSep"); + caret = TextUtilities.findWordStart(lineText, caret, noWordSep); + } + + if (select) + { + textArea.select(textArea.getMarkPosition(), + lineStart + caret); } - - if(select) - textArea.select(textArea.getMarkPosition(), - lineStart + caret); else - textArea.setCaretPosition(lineStart + caret); - } - } - - public static class repeat implements ActionListener, - InputHandler.NonRecordable - { - public void actionPerformed(ActionEvent evt) - { + { + textArea.setCaretPosition(lineStart + caret); + } + } + } + + public static class repeat implements ActionListener, + InputHandler.NonRecordable + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); textArea.getInputHandler().setRepeatEnabled(true); String actionCommand = evt.getActionCommand(); - if(actionCommand != null) + if (actionCommand != null) { - textArea.getInputHandler().setRepeatCount( - Integer.parseInt(actionCommand)); + textArea.getInputHandler().setRepeatCount( + Integer.parseInt(actionCommand)); } - } - } - - public static class toggle_rect implements ActionListener - { - public void actionPerformed(ActionEvent evt) - { + } + } + + public static class toggle_rect implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); textArea.setSelectionRectangular( - !textArea.isSelectionRectangular()); - } - } - - public static class insert_char implements ActionListener, - InputHandler.NonRepeatable - { - public void actionPerformed(ActionEvent evt) - { + !textArea.isSelectionRectangular()); + } + } + + public static class insert_char implements ActionListener, + InputHandler.NonRepeatable + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); String str = evt.getActionCommand(); int repeatCount = textArea.getInputHandler().getRepeatCount(); - - if(textArea.isEditable()) + + if (textArea.isEditable()) { - StringBuffer buf = new StringBuffer(); - for(int i = 0; i < repeatCount; i++) - buf.append(str); - textArea.overwriteSetSelectedText(buf.toString()); + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < repeatCount; i++) + { + buf.append(str); + } + textArea.overwriteSetSelectedText(buf.toString()); } else { - textArea.getToolkit().beep(); + textArea.getToolkit().beep(); } - } - } - - public static class clip_copy implements ActionListener { - public void actionPerformed(ActionEvent evt) - { + } + } + + public static class clip_copy implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); textArea.copy(); - } - } - - public static class clip_paste implements ActionListener { - public void actionPerformed(ActionEvent evt) - { + } + } + + public static class clip_paste implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); textArea.paste(); - } - } - - public static class clip_cut implements ActionListener { - public void actionPerformed(ActionEvent evt) - { + } + } + + public static class clip_cut implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { JEditTextArea textArea = getTextArea(evt); textArea.cut(); - } - } - } + } + } +} diff --git a/src/main/java/mars/venus/editors/jeditsyntax/JEditBasedTextArea.java b/src/main/java/mars/venus/editors/jeditsyntax/JEditBasedTextArea.java index c116579..3d80792 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/JEditBasedTextArea.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/JEditBasedTextArea.java @@ -1,390 +1,447 @@ - package mars.venus.editors.jeditsyntax; +package mars.venus.editors.jeditsyntax; - import mars.venus.editors.jeditsyntax.tokenmarker.*; - import mars.venus.editors.MARSTextEditingArea; - import mars.venus.EditPane; - import mars.*; - import java.awt.*; - import javax.swing.event.*; - import javax.swing.text.*; - import javax.swing.undo.*; - import javax.swing.*; +import mars.Globals; +import mars.venus.EditPane; +import mars.venus.editors.MARSTextEditingArea; +import mars.venus.editors.jeditsyntax.tokenmarker.MIPSTokenMarker; + +import javax.swing.*; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.event.UndoableEditEvent; +import javax.swing.event.UndoableEditListener; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoManager; +import java.awt.*; - /** - * Adaptor subclass for JEditTextArea - * - * Provides those methods required by the MARSTextEditingArea interface - * that are not defined by JEditTextArea. This permits JEditTextArea - * to be used within MARS largely without modification. DPS 4-20-2010 - * - * @since 4.0 - * @author Pete Sanderson - */ - - public class JEditBasedTextArea extends JEditTextArea implements MARSTextEditingArea, CaretListener { - - private EditPane editPane; - private UndoManager undoManager; - private UndoableEditListener undoableEditListener; - private boolean isCompoundEdit = false; - private CompoundEdit compoundEdit; - private JEditBasedTextArea sourceCode; - - - - - public JEditBasedTextArea(EditPane editPain, JComponent lineNumbers) { - super(lineNumbers); - this.editPane = editPain; - this.undoManager = new UndoManager(); - this.compoundEdit = new CompoundEdit(); - this.sourceCode = this; - - // Needed to support unlimited undo/redo capability - undoableEditListener = - new UndoableEditListener() { - public void undoableEditHappened(UndoableEditEvent e) { - //Remember the edit and update the menus. - if (isCompoundEdit) { - compoundEdit.addEdit(e.getEdit()); - } - else { - undoManager.addEdit(e.getEdit()); - editPane.updateUndoState(); - editPane.updateRedoState(); - } - } +/** + * Adaptor subclass for JEditTextArea + *

+ * Provides those methods required by the MARSTextEditingArea interface that are not defined by JEditTextArea. This + * permits JEditTextArea to be used within MARS largely without modification. DPS 4-20-2010 + * + * @author Pete Sanderson + * @since 4.0 + */ + +public class JEditBasedTextArea extends JEditTextArea implements MARSTextEditingArea, CaretListener +{ + + private final EditPane editPane; + + private final UndoManager undoManager; + + private final UndoableEditListener undoableEditListener; + + private boolean isCompoundEdit = false; + + private CompoundEdit compoundEdit; + + private final JEditBasedTextArea sourceCode; + + + public JEditBasedTextArea(EditPane editPain, JComponent lineNumbers) + { + super(lineNumbers); + this.editPane = editPain; + this.undoManager = new UndoManager(); + this.compoundEdit = new CompoundEdit(); + this.sourceCode = this; + + // Needed to support unlimited undo/redo capability + undoableEditListener = + new UndoableEditListener() + { + public void undoableEditHappened(UndoableEditEvent e) + { + //Remember the edit and update the menus. + if (isCompoundEdit) + { + compoundEdit.addEdit(e.getEdit()); + } + else + { + undoManager.addEdit(e.getEdit()); + editPane.updateUndoState(); + editPane.updateRedoState(); + } + } }; - this.getDocument().addUndoableEditListener(undoableEditListener); - this.setFont(Globals.getSettings().getEditorFont()); - this.setTokenMarker(new MIPSTokenMarker()); - - addCaretListener(this); - } - - - public void setFont(Font f) { - getPainter().setFont(f); - } - - - public Font getFont() { - return getPainter().getFont(); - } - - -// public void repaint() { getPainter().repaint(); } -// public Dimension getSize() { return painter.getSize(); } -// public void setSize(Dimension d) { painter.setSize(d);} - - - /** - * Use for highlighting the line currently being edited. - * @param highlight true to enable line highlighting, false to disable. - */ - public void setLineHighlightEnabled(boolean highlight) { - getPainter().setLineHighlightEnabled(highlight); - } - - /** - * Set the caret blinking rate in milliseconds. If rate is 0 - * will disable blinking. If negative, do nothing. - * @param rate blinking rate in milliseconds - */ - public void setCaretBlinkRate(int rate) { - if (rate == 0) { + this.getDocument().addUndoableEditListener(undoableEditListener); + this.setFont(Globals.getSettings().getEditorFont()); + this.setTokenMarker(new MIPSTokenMarker()); + + addCaretListener(this); + } + + public Font getFont() + { + return getPainter().getFont(); + } + + public void setFont(Font f) + { + getPainter().setFont(f); + } + + + // public void repaint() { getPainter().repaint(); } + // public Dimension getSize() { return painter.getSize(); } + // public void setSize(Dimension d) { painter.setSize(d);} + + /** + * Use for highlighting the line currently being edited. + * + * @param highlight true to enable line highlighting, false to disable. + */ + public void setLineHighlightEnabled(boolean highlight) + { + getPainter().setLineHighlightEnabled(highlight); + } + + /** + * Set the caret blinking rate in milliseconds. If rate is 0 will disable blinking. If negative, do nothing. + * + * @param rate blinking rate in milliseconds + */ + public void setCaretBlinkRate(int rate) + { + if (rate == 0) + { caretBlinks = false; - } - if (rate > 0) { + } + if (rate > 0) + { caretBlinks = true; caretBlinkRate = rate; caretTimer.setDelay(rate); caretTimer.setInitialDelay(rate); caretTimer.restart(); - } - } - - - /** - * Set the number of characters a tab will expand to. - * @param chars number of characters - */ - public void setTabSize(int chars) { - painter.setTabSize(chars); - } - - /** - * Update the syntax style table, which is obtained from - * SyntaxUtilities. - */ - public void updateSyntaxStyles() { - painter.setStyles(SyntaxUtilities.getCurrentSyntaxStyles()); - } - - - public Component getOuterComponent() { - return this; - } - - /** - * Get rid of any accumulated undoable edits. It is useful to call - * this method after opening a file into the text area. The - * act of setting its text content upon reading the file will generate - * an undoable edit. Normally you don't want a freshly-opened file - * to appear with its Undo action enabled. But it will unless you - * call this after setting the text. - */ - public void discardAllUndoableEdits() { - this.undoManager.discardAllEdits(); - } - - /** - * Display caret position on the edit pane. - * @param e A CaretEvent - */ - - public void caretUpdate(CaretEvent e) { - editPane.displayCaretPosition(((MutableCaretEvent)e).getDot()); - } - - - /** - * Same as setSelectedText but named for compatibility with - * JTextComponent method replaceSelection. - * DPS, 14 Apr 2010 - * @param replacementText The replacement text for the selection - */ - public void replaceSelection(String replacementText) { - setSelectedText(replacementText); - } - // - // - public void setSelectionVisible(boolean vis) { - - } - - // - // - public void setSourceCode(String s, boolean editable) { - this.setText(s); - this.setBackground( (editable)? Color.WHITE : Color.GRAY); - this.setEditable(editable); - this.setEnabled(editable); - //this.getCaret().setVisible(editable); - this.setCaretPosition(0); - if (editable) this.requestFocusInWindow(); - } - + } + } + + /** - * Returns the undo manager for this editing area - * @return the undo manager + * Set the number of characters a tab will expand to. + * + * @param chars number of characters */ - public UndoManager getUndoManager() { - return undoManager; - } - - /** - * Undo previous edit - */ - public void undo() { - // "unredoing" is mode used by DocumentHandler's insertUpdate() and removeUpdate() - // to pleasingly mark the text and location of the undo. - unredoing = true; - try { + public void setTabSize(int chars) + { + painter.setTabSize(chars); + } + + /** + * Update the syntax style table, which is obtained from SyntaxUtilities. + */ + public void updateSyntaxStyles() + { + painter.setStyles(SyntaxUtilities.getCurrentSyntaxStyles()); + } + + + public Component getOuterComponent() + { + return this; + } + + /** + * Get rid of any accumulated undoable edits. It is useful to call this method after opening a file into the text + * area. The act of setting its text content upon reading the file will generate an undoable edit. Normally you + * don't want a freshly-opened file to appear with its Undo action enabled. But it will unless you call this after + * setting the text. + */ + public void discardAllUndoableEdits() + { + this.undoManager.discardAllEdits(); + } + + /** + * Display caret position on the edit pane. + * + * @param e A CaretEvent + */ + + public void caretUpdate(CaretEvent e) + { + editPane.displayCaretPosition(e.getDot()); + } + + + /** + * Same as setSelectedText but named for compatibility with JTextComponent method replaceSelection. DPS, 14 Apr + * 2010 + * + * @param replacementText The replacement text for the selection + */ + public void replaceSelection(String replacementText) + { + setSelectedText(replacementText); + } + + // + // + public void setSelectionVisible(boolean vis) + { + + } + + // + // + public void setSourceCode(String s, boolean editable) + { + this.setText(s); + this.setBackground((editable) ? Color.WHITE : Color.GRAY); + this.setEditable(editable); + this.setEnabled(editable); + //this.getCaret().setVisible(editable); + this.setCaretPosition(0); + if (editable) + { + this.requestFocusInWindow(); + } + } + + /** + * Returns the undo manager for this editing area + * + * @return the undo manager + */ + public UndoManager getUndoManager() + { + return undoManager; + } + + /** + * Undo previous edit + */ + public void undo() + { + // "unredoing" is mode used by DocumentHandler's insertUpdate() and removeUpdate() + // to pleasingly mark the text and location of the undo. + unredoing = true; + try + { this.undoManager.undo(); - } - catch (CannotUndoException ex) { - System.out.println("Unable to undo: " + ex); - ex.printStackTrace(); - } - unredoing = false; - this.setCaretVisible(true); - } - - /** - * Redo previous edit - */ - public void redo() { - // "unredoing" is mode used by DocumentHandler's insertUpdate() and removeUpdate() - // to pleasingly mark the text and location of the redo. - unredoing = true; - try { + } + catch (CannotUndoException ex) + { + System.out.println("Unable to undo: " + ex); + ex.printStackTrace(); + } + unredoing = false; + this.setCaretVisible(true); + } + + /** + * Redo previous edit + */ + public void redo() + { + // "unredoing" is mode used by DocumentHandler's insertUpdate() and removeUpdate() + // to pleasingly mark the text and location of the redo. + unredoing = true; + try + { this.undoManager.redo(); - } - catch (CannotRedoException ex) { - System.out.println("Unable to redo: " + ex); - ex.printStackTrace(); - } - unredoing = false; - this.setCaretVisible(true); - } - - - ////////////////////////////////////////////////////////////////////////// - // Methods to support Find/Replace feature - // - // Basis for this Find/Replace solution is: - // http://java.ittoolbox.com/groups/technical-functional/java-l/search-and-replace-using-jtextpane-630964 - // as written by Chris Dickenson in 2005 - // - - - /** Finds next occurrence of text in a forward search of a string. Search begins - * at the current cursor location, and wraps around when the end of the string - * is reached. - * @param find the text to locate in the string + } + catch (CannotRedoException ex) + { + System.out.println("Unable to redo: " + ex); + ex.printStackTrace(); + } + unredoing = false; + this.setCaretVisible(true); + } + + + ////////////////////////////////////////////////////////////////////////// + // Methods to support Find/Replace feature + // + // Basis for this Find/Replace solution is: + // http://java.ittoolbox.com/groups/technical-functional/java-l/search-and-replace-using-jtextpane-630964 + // as written by Chris Dickenson in 2005 + // + + + /** + * Finds next occurrence of text in a forward search of a string. Search begins at the current cursor location, and + * wraps around when the end of the string is reached. + * + * @param find the text to locate in the string * @param caseSensitive true if search is to be case-sensitive, false otherwise * @return TEXT_FOUND or TEXT_NOT_FOUND, depending on the result. */ - public int doFindText(String find, boolean caseSensitive) { - int findPosn = sourceCode.getCaretPosition(); - int nextPosn = 0; - nextPosn = nextIndex( sourceCode.getText(), find, findPosn, caseSensitive ); - if ( nextPosn >= 0 ) { + public int doFindText(String find, boolean caseSensitive) + { + int findPosn = sourceCode.getCaretPosition(); + int nextPosn = 0; + nextPosn = nextIndex(sourceCode.getText(), find, findPosn, caseSensitive); + if (nextPosn >= 0) + { sourceCode.requestFocus(); // guarantees visibility of the blue highlight - sourceCode.setSelectionStart( nextPosn ); // position cursor at word start - sourceCode.setSelectionEnd( nextPosn + find.length() ); - // Need to repeat start due to quirk in JEditTextArea implementation of setSelectionStart. - sourceCode.setSelectionStart( nextPosn ); + sourceCode.setSelectionStart(nextPosn); // position cursor at word start + sourceCode.setSelectionEnd(nextPosn + find.length()); + // Need to repeat start due to quirk in JEditTextArea implementation of setSelectionStart. + sourceCode.setSelectionStart(nextPosn); return TEXT_FOUND; - } - else { + } + else + { return TEXT_NOT_FOUND; - } - } - - /** Returns next posn of word in text - forward search. If end of string is - * reached during the search, will wrap around to the beginning one time. - * @return next indexed position of found text or -1 if not found - * @param input the string to search - * @param find the string to find - * @param start the character position to start the search - * @param caseSensitive true for case sensitive. false to ignore case - */ - public int nextIndex(String input, String find, int start, boolean caseSensitive ) { - int textPosn = -1; - if ( input != null && find != null && start < input.length() ) { - if ( caseSensitive ) { // indexOf() returns -1 if not found - textPosn = input.indexOf( find, start ); - // If not found from non-starting cursor position, wrap around - if (start > 0 && textPosn < 0) { - textPosn = input.indexOf( find ); - } - } - else { - String lowerCaseText = input.toLowerCase(); - textPosn = lowerCaseText.indexOf( find.toLowerCase(), start ); + } + } + + /** + * Returns next posn of word in text - forward search. If end of string is reached during the search, will wrap + * around to the beginning one time. + * + * @param input the string to search + * @param find the string to find + * @param start the character position to start the search + * @param caseSensitive true for case sensitive. false to ignore case + * @return next indexed position of found text or -1 if not found + */ + public int nextIndex(String input, String find, int start, boolean caseSensitive) + { + int textPosn = -1; + if (input != null && find != null && start < input.length()) + { + if (caseSensitive) + { // indexOf() returns -1 if not found + textPosn = input.indexOf(find, start); // If not found from non-starting cursor position, wrap around - if (start > 0 && textPosn < 0) { - textPosn = lowerCaseText.indexOf( find.toLowerCase() ); - } + if (start > 0 && textPosn < 0) + { + textPosn = input.indexOf(find); + } } - } - return textPosn; - } - - - /** Finds and replaces next occurrence of text in a string in a forward search. - * If cursor is initially at end - * of matching selection, will immediately replace then find and select the - * next occurrence if any. Otherwise it performs a find operation. The replace - * can be undone with one undo operation. - * - * @param find the text to locate in the string - * @param replace the text to replace the find text with - if the find text exists - * @param caseSensitive true for case sensitive. false to ignore case - * @return Returns TEXT_FOUND if not initially at end of selected match and matching - * occurrence is found. Returns TEXT_NOT_FOUND if the text is not matched. - * Returns TEXT_REPLACED_NOT_FOUND_NEXT if replacement is successful but there are - * no additional matches. Returns TEXT_REPLACED_FOUND_NEXT if reaplacement is - * successful and there is at least one additional match. - */ - public int doReplace(String find, String replace, boolean caseSensitive) { - int nextPosn = 0; - int posn; - // Will perform a "find" and return, unless positioned at the end of - // a selected "find" result. - if (find==null || !find.equals(sourceCode.getSelectedText()) || - sourceCode.getSelectionEnd()!=sourceCode.getCaretPosition()) { + else + { + String lowerCaseText = input.toLowerCase(); + textPosn = lowerCaseText.indexOf(find.toLowerCase(), start); + // If not found from non-starting cursor position, wrap around + if (start > 0 && textPosn < 0) + { + textPosn = lowerCaseText.indexOf(find.toLowerCase()); + } + } + } + return textPosn; + } + + + /** + * Finds and replaces next occurrence of text in a string in a forward search. If cursor is initially at end of + * matching selection, will immediately replace then find and select the next occurrence if any. Otherwise it + * performs a find operation. The replace can be undone with one undo operation. + * + * @param find the text to locate in the string + * @param replace the text to replace the find text with - if the find text exists + * @param caseSensitive true for case sensitive. false to ignore case + * @return Returns TEXT_FOUND if not initially at end of selected match and matching occurrence is found. Returns + * TEXT_NOT_FOUND if the text is not matched. Returns TEXT_REPLACED_NOT_FOUND_NEXT if replacement is successful + * but there are no additional matches. Returns TEXT_REPLACED_FOUND_NEXT if reaplacement is successful and + * there is at least one additional match. + */ + public int doReplace(String find, String replace, boolean caseSensitive) + { + int nextPosn = 0; + int posn; + // Will perform a "find" and return, unless positioned at the end of + // a selected "find" result. + if (find == null || !find.equals(sourceCode.getSelectedText()) || + sourceCode.getSelectionEnd() != sourceCode.getCaretPosition()) + { return doFindText(find, caseSensitive); - } + } // We are positioned at end of selected "find". Rreplace and find next. - nextPosn = sourceCode.getSelectionStart(); - sourceCode.grabFocus(); - sourceCode.setSelectionStart( nextPosn ); // posn cursor at word start - sourceCode.setSelectionEnd( nextPosn + find.length() ); //select found text - // Need to repeat start due to quirk in JEditTextArea implementation of setSelectionStart. - sourceCode.setSelectionStart( nextPosn ); - isCompoundEdit = true; - compoundEdit = new CompoundEdit(); - sourceCode.replaceSelection(replace); - compoundEdit.end(); - undoManager.addEdit( compoundEdit ); - editPane.updateUndoState(); - editPane.updateRedoState(); - isCompoundEdit = false; - sourceCode.setCaretPosition(nextPosn + replace.length()); - if (doFindText(find, caseSensitive) == TEXT_NOT_FOUND) { + nextPosn = sourceCode.getSelectionStart(); + sourceCode.grabFocus(); + sourceCode.setSelectionStart(nextPosn); // posn cursor at word start + sourceCode.setSelectionEnd(nextPosn + find.length()); //select found text + // Need to repeat start due to quirk in JEditTextArea implementation of setSelectionStart. + sourceCode.setSelectionStart(nextPosn); + isCompoundEdit = true; + compoundEdit = new CompoundEdit(); + sourceCode.replaceSelection(replace); + compoundEdit.end(); + undoManager.addEdit(compoundEdit); + editPane.updateUndoState(); + editPane.updateRedoState(); + isCompoundEdit = false; + sourceCode.setCaretPosition(nextPosn + replace.length()); + if (doFindText(find, caseSensitive) == TEXT_NOT_FOUND) + { return TEXT_REPLACED_NOT_FOUND_NEXT; - } - else { + } + else + { return TEXT_REPLACED_FOUND_NEXT; - } - } - - /** Finds and replaces ALL occurrences of text in a string in a forward search. - * All replacements are bundled into one CompoundEdit, so one Undo operation will - * undo all of them. - * @param find the text to locate in the string - * @param replace the text to replace the find text with - if the find text exists - * @param caseSensitive true for case sensitive. false to ignore case - * @return the number of occurrences that were matched and replaced. - */ - public int doReplaceAll(String find, String replace, boolean caseSensitive) { - int nextPosn = 0; - int findPosn = 0; // *** begin at start of text - int replaceCount = 0; - compoundEdit = null; // new one will be created upon first replacement - isCompoundEdit = true; // undo manager's action listener needs this - while (nextPosn >= 0) { - nextPosn = nextIndex( sourceCode.getText(), find, findPosn, caseSensitive ); - if ( nextPosn >= 0 ) { - // nextIndex() will wrap around, which causes infinite loop if - // find string is a substring of replacement string. This - // statement will prevent that. - if (nextPosn < findPosn) { - break; - } - sourceCode.grabFocus(); - sourceCode.setSelectionStart( nextPosn ); // posn cursor at word start - sourceCode.setSelectionEnd( nextPosn + find.length() ); //select found text - // Need to repeat start due to quirk in JEditTextArea implementation of setSelectionStart. - sourceCode.setSelectionStart( nextPosn ); - if (compoundEdit == null) { - compoundEdit = new CompoundEdit(); - } - sourceCode.replaceSelection(replace); - findPosn = nextPosn + replace.length(); // set for next search - replaceCount++; + } + } + + /** + * Finds and replaces ALL occurrences of text in a string in a forward search. All replacements are bundled + * into one CompoundEdit, so one Undo operation will undo all of them. + * + * @param find the text to locate in the string + * @param replace the text to replace the find text with - if the find text exists + * @param caseSensitive true for case sensitive. false to ignore case + * @return the number of occurrences that were matched and replaced. + */ + public int doReplaceAll(String find, String replace, boolean caseSensitive) + { + int nextPosn = 0; + int findPosn = 0; // *** begin at start of text + int replaceCount = 0; + compoundEdit = null; // new one will be created upon first replacement + isCompoundEdit = true; // undo manager's action listener needs this + while (nextPosn >= 0) + { + nextPosn = nextIndex(sourceCode.getText(), find, findPosn, caseSensitive); + if (nextPosn >= 0) + { + // nextIndex() will wrap around, which causes infinite loop if + // find string is a substring of replacement string. This + // statement will prevent that. + if (nextPosn < findPosn) + { + break; + } + sourceCode.grabFocus(); + sourceCode.setSelectionStart(nextPosn); // posn cursor at word start + sourceCode.setSelectionEnd(nextPosn + find.length()); //select found text + // Need to repeat start due to quirk in JEditTextArea implementation of setSelectionStart. + sourceCode.setSelectionStart(nextPosn); + if (compoundEdit == null) + { + compoundEdit = new CompoundEdit(); + } + sourceCode.replaceSelection(replace); + findPosn = nextPosn + replace.length(); // set for next search + replaceCount++; } - } - isCompoundEdit = false; - // Will be true if any replacements were performed - if (compoundEdit != null) { + } + isCompoundEdit = false; + // Will be true if any replacements were performed + if (compoundEdit != null) + { compoundEdit.end(); - undoManager.addEdit( compoundEdit ); + undoManager.addEdit(compoundEdit); editPane.updateUndoState(); editPane.updateRedoState(); - } - return replaceCount; - } - // - ///////////////////////////// End Find/Replace methods ////////////////////////// - - // - ////////////////////////////////////////////////////////////////// - - - - } \ No newline at end of file + } + return replaceCount; + } + // + ///////////////////////////// End Find/Replace methods ////////////////////////// + + // + ////////////////////////////////////////////////////////////////// + + +} diff --git a/src/main/java/mars/venus/editors/jeditsyntax/JEditTextArea.java b/src/main/java/mars/venus/editors/jeditsyntax/JEditTextArea.java index b31b870..7537fe3 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/JEditTextArea.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/JEditTextArea.java @@ -11,24 +11,28 @@ package mars.venus.editors.jeditsyntax; import mars.Globals; import mars.Settings; -import mars.venus.editors.jeditsyntax.tokenmarker.*; +import mars.venus.editors.jeditsyntax.tokenmarker.Token; +import mars.venus.editors.jeditsyntax.tokenmarker.TokenMarker; + +import javax.swing.*; import javax.swing.event.*; import javax.swing.text.*; -import javax.swing.undo.*; -import javax.swing.*; -import javax.swing.plaf.basic.BasicMenuItemUI; -import java.awt.datatransfer.*; -import java.awt.event.*; +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.UndoableEdit; import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.event.*; +import java.util.ArrayList; import java.util.Enumeration; import java.util.Vector; -import java.util.ArrayList; /** - * jEdit's text area component. It is more suited for editing program - * source code than JEditorPane, because it drops the unnecessary features - * (images, variable-width lines, and so on) and adds a whole bunch of - * useful goodies such as: + * jEdit's text area component. It is more suited for editing program source code than JEditorPane, because it drops the + * unnecessary features (images, variable-width lines, and so on) and adds a whole bunch of useful goodies such as: *

    *
  • More flexible key binding scheme *
  • Supports macro recorders @@ -41,7 +45,7 @@ import java.util.ArrayList; * It is also faster and doesn't have as many problems. It can be used * in other applications; the only other part of jEdit it depends on is * the syntax package.

    - * + *

    * To use it in your app, treat it like any other component, for example: *

    JEditTextArea ta = new JEditTextArea();
      * ta.setTokenMarker(new JavaTokenMarker());
    @@ -54,2482 +58,2725 @@ import java.util.ArrayList;
      * @author Slava Pestov
      * @version $Id: JEditTextArea.java,v 1.36 1999/12/13 03:40:30 sp Exp $
      */
    -public class JEditTextArea extends JComponent 
    +public class JEditTextArea extends JComponent
     {
    -/**
    - * Adding components with this name to the text area will place
    - * them left of the horizontal scroll bar. In jEdit, the status
    - * bar is added this way.
    - */
    -   public static String LEFT_OF_SCROLLBAR = "los";
    -   public static Color POPUP_HELP_TEXT_COLOR = Color.BLACK;  // DPS 11-July-2014
    -	
    - // Number of text lines moved for each click of the vertical scrollbar buttons.
    -   private static final int VERTICAL_SCROLLBAR_UNIT_INCREMENT_IN_LINES = 1;
    - // Number of text lines moved for each "notch" of the mouse wheel scroller.
    -   private static final int LINES_PER_MOUSE_WHEEL_NOTCH = 3;
    +    // Number of text lines moved for each click of the vertical scrollbar buttons.
    +    private static final int VERTICAL_SCROLLBAR_UNIT_INCREMENT_IN_LINES = 1;
     
    -/**
    - * Creates a new JEditTextArea with the default settings.
    - */
    -   public JEditTextArea(JComponent lineNumbers)
    -   {
    -      this(TextAreaDefaults.getDefaults(), lineNumbers);
    -   }
    +    // Number of text lines moved for each "notch" of the mouse wheel scroller.
    +    private static final int LINES_PER_MOUSE_WHEEL_NOTCH = 3;
     
    -/**
    - * Creates a new JEditTextArea with the specified settings.
    - * @param defaults The default settings
    - */
    - 
    -   private JScrollBar lineNumbersVertical;//************************************
    +    /**
    +     * Adding components with this name to the text area will place them left of the horizontal scroll bar. In jEdit,
    +     * the status bar is added this way.
    +     */
    +    public static String LEFT_OF_SCROLLBAR = "los";
    +
    +    public static Color POPUP_HELP_TEXT_COLOR = Color.BLACK;  // DPS 11-July-2014
    +
    +    // protected members
    +    protected static String CENTER = "center";
    +
    +    protected static String RIGHT = "right";
    +
    +    protected static String BOTTOM = "bottom";
     
     
    -   public JEditTextArea(TextAreaDefaults defaults, JComponent lineNumbers)
    -   {
    -   // Enable the necessary events
    -      enableEvents(AWTEvent.KEY_EVENT_MASK);
    -   
    -   // Initialize some misc. stuff
    -      painter = new TextAreaPainter(this,defaults);
    -      documentHandler = new DocumentHandler();
    -      listenerList = new EventListenerList();
    -      caretEvent = new MutableCaretEvent();
    -      lineSegment = new Segment();
    -      bracketLine = bracketPosition = -1;
    -      blink = true;
    -      unredoing = false;
    -   
    -      JScrollPane lineNumberScroller = new JScrollPane(lineNumbers,
    -                    ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER, 
    -                    ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    -      lineNumberScroller.setBorder(new javax.swing.border.EmptyBorder(1,1,1,1));
    -      lineNumbersVertical = lineNumberScroller.getVerticalScrollBar();
    -   				
    -   // Initialize the GUI
    -      JPanel lineNumbersPlusPainter = new JPanel(new BorderLayout());
    -      lineNumbersPlusPainter.add(painter, BorderLayout.CENTER);
    -      lineNumbersPlusPainter.add(lineNumberScroller, BorderLayout.WEST);
    -      setLayout(new ScrollLayout());
    -      add(CENTER, lineNumbersPlusPainter); //was: painter
    -      add(RIGHT,vertical = new JScrollBar(JScrollBar.VERTICAL));
    -      add(BOTTOM,horizontal = new JScrollBar(JScrollBar.HORIZONTAL));
    -   
    -   
    -   // Add some event listeners
    -      vertical.addAdjustmentListener(new AdjustHandler());
    -      horizontal.addAdjustmentListener(new AdjustHandler());
    -      painter.addComponentListener(new ComponentHandler());
    -      painter.addMouseListener(new MouseHandler());
    -      painter.addMouseMotionListener(new DragHandler());
    -      painter.addMouseWheelListener(new MouseWheelHandler()); // DPS 5-5-10
    -      addFocusListener(new FocusHandler());
    -   
    -   // Load the defaults
    -      setInputHandler(defaults.inputHandler);
    -      setDocument(defaults.document);
    -      editable = defaults.editable;
    -      caretVisible = defaults.caretVisible;
    -      caretBlinks = defaults.caretBlinks;
    -      caretBlinkRate = defaults.caretBlinkRate;
    -      electricScroll = defaults.electricScroll;
    -   
    -      popup = defaults.popup; 
    -   
    -      caretTimer.setDelay(caretBlinkRate);
    -   	
    -   	// Intercept keystrokes before focus manager gets them.  If in editing window,
    -   	// pass TAB keystrokes on to the key processor instead of letting focus
    -   	// manager use them for focus traversal.  
    -   	// One can also accomplish this using: setFocusTraversalKeysEnabled(false);
    -   	// but that seems heavy-handed.
    -   	// DPS 12May2010
    -      KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher( 
    -            new KeyEventDispatcher() {
    -               public boolean dispatchKeyEvent(KeyEvent e) {
    -                  if (JEditTextArea.this.isFocusOwner() && e.getKeyCode()==KeyEvent.VK_TAB &&  e.getModifiers()==0) {
    -                     processKeyEvent(e);
    -                     return true;
    -                  } 
    -                  else {
    -                     return false;
    -                  }
    -               }
    +    /**
    +     * Returns if this component can be traversed by pressing
    +     * the Tab key. This returns false.
    +     */
    +    //        public final boolean isManagingFocus()
    +    //       {
    +    //          return true;
    +    //       }
    +
    +    protected static JEditTextArea focusedComponent;
    +
    +    protected static Timer caretTimer;
    +
    +    static
    +    {
    +        caretTimer = new Timer(500, new CaretBlinker());
    +        caretTimer.setInitialDelay(500);
    +        caretTimer.start();
    +    }
    +
    +    protected TextAreaPainter painter;
    +
    +    protected JPopupMenu popup;
    +
    +    protected EventListenerList listenerList;
    +
    +    protected MutableCaretEvent caretEvent;
    +
    +    protected boolean caretBlinks;
    +
    +    protected boolean caretVisible;
    +
    +    protected boolean blink;
    +
    +    protected boolean editable;
    +
    +    protected int caretBlinkRate;
    +
    +    protected int firstLine;
    +
    +    protected int visibleLines;
    +
    +    protected int electricScroll;
    +
    +    protected int horizontalOffset;
    +
    +    protected JScrollBar vertical;
    +
    +    protected JScrollBar horizontal;
    +
    +    protected boolean scrollBarsInitialized;
    +
    +    protected InputHandler inputHandler;
    +
    +    protected SyntaxDocument document;
    +
    +    protected DocumentHandler documentHandler;
    +
    +    protected Segment lineSegment;
    +
    +    protected int selectionStart;
    +
    +    protected int selectionStartLine;
    +
    +    protected int selectionEnd;
    +
    +    protected int selectionEndLine;
    +
    +    protected boolean biasLeft;
    +
    +    protected int bracketPosition;
    +
    +    protected int bracketLine;
    +
    +    protected int magicCaret;
    +
    +    protected boolean overwrite;
    +
    +    protected boolean rectSelect;
    +
    +    // "unredoing" is mode used by DocumentHandler's insertUpdate() and removeUpdate()
    +    // to pleasingly select the text and location of the undo.   DPS 3-May-2010
    +    protected boolean unredoing = false;
    +
    +    JPopupMenu popupMenu;
    +
    +    /**
    +     * Creates a new JEditTextArea with the specified settings.
    +     *
    +     * @param defaults The default settings
    +     */
    +
    +    private final JScrollBar lineNumbersVertical;//************************************
    +
    +    /**
    +     * Creates a new JEditTextArea with the default settings.
    +     */
    +    public JEditTextArea(JComponent lineNumbers)
    +    {
    +        this(TextAreaDefaults.getDefaults(), lineNumbers);
    +    }
    +
    +    public JEditTextArea(TextAreaDefaults defaults, JComponent lineNumbers)
    +    {
    +        // Enable the necessary events
    +        enableEvents(AWTEvent.KEY_EVENT_MASK);
    +
    +        // Initialize some misc. stuff
    +        painter = new TextAreaPainter(this, defaults);
    +        documentHandler = new DocumentHandler();
    +        listenerList = new EventListenerList();
    +        caretEvent = new MutableCaretEvent();
    +        lineSegment = new Segment();
    +        bracketLine = bracketPosition = -1;
    +        blink = true;
    +        unredoing = false;
    +
    +        JScrollPane lineNumberScroller = new JScrollPane(lineNumbers,
    +            ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER,
    +            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
    +        lineNumberScroller.setBorder(new javax.swing.border.EmptyBorder(1, 1, 1, 1));
    +        lineNumbersVertical = lineNumberScroller.getVerticalScrollBar();
    +
    +        // Initialize the GUI
    +        JPanel lineNumbersPlusPainter = new JPanel(new BorderLayout());
    +        lineNumbersPlusPainter.add(painter, BorderLayout.CENTER);
    +        lineNumbersPlusPainter.add(lineNumberScroller, BorderLayout.WEST);
    +        setLayout(new ScrollLayout());
    +        add(CENTER, lineNumbersPlusPainter); //was: painter
    +        add(RIGHT, vertical = new JScrollBar(JScrollBar.VERTICAL));
    +        add(BOTTOM, horizontal = new JScrollBar(JScrollBar.HORIZONTAL));
    +
    +
    +        // Add some event listeners
    +        vertical.addAdjustmentListener(new AdjustHandler());
    +        horizontal.addAdjustmentListener(new AdjustHandler());
    +        painter.addComponentListener(new ComponentHandler());
    +        painter.addMouseListener(new MouseHandler());
    +        painter.addMouseMotionListener(new DragHandler());
    +        painter.addMouseWheelListener(new MouseWheelHandler()); // DPS 5-5-10
    +        addFocusListener(new FocusHandler());
    +
    +        // Load the defaults
    +        setInputHandler(defaults.inputHandler);
    +        setDocument(defaults.document);
    +        editable = defaults.editable;
    +        caretVisible = defaults.caretVisible;
    +        caretBlinks = defaults.caretBlinks;
    +        caretBlinkRate = defaults.caretBlinkRate;
    +        electricScroll = defaults.electricScroll;
    +
    +        popup = defaults.popup;
    +
    +        caretTimer.setDelay(caretBlinkRate);
    +
    +        // Intercept keystrokes before focus manager gets them.  If in editing window,
    +        // pass TAB keystrokes on to the key processor instead of letting focus
    +        // manager use them for focus traversal.
    +        // One can also accomplish this using: setFocusTraversalKeysEnabled(false);
    +        // but that seems heavy-handed.
    +        // DPS 12May2010
    +        KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(
    +            new KeyEventDispatcher()
    +            {
    +                public boolean dispatchKeyEvent(KeyEvent e)
    +                {
    +                    if (JEditTextArea.this.isFocusOwner() && e.getKeyCode() == KeyEvent.VK_TAB && e.getModifiers() == 0)
    +                    {
    +                        processKeyEvent(e);
    +                        return true;
    +                    }
    +                    else
    +                    {
    +                        return false;
    +                    }
    +                }
                 });
    -   		
    -   // We don't seem to get the initial focus event?
    -      focusedComponent = this;
    -   }
     
    +        // We don't seem to get the initial focus event?
    +        focusedComponent = this;
    +    }
     
    -	
    -/**
    - * Returns if this component can be traversed by pressing
    - * the Tab key. This returns false.
    - */
    -//        public final boolean isManagingFocus()
    -//       {
    -//          return true;
    -//       }
    +    /**
    +     * Returns the object responsible for painting this text area.
    +     */
    +    public final TextAreaPainter getPainter()
    +    {
    +        return painter;
    +    }
     
    -/**
    - * Returns the object responsible for painting this text area.
    - */
    -   public final TextAreaPainter getPainter()
    -   {
    -      return painter;
    -   }
    +    /**
    +     * Returns the input handler.
    +     */
    +    public final InputHandler getInputHandler()
    +    {
    +        return inputHandler;
    +    }
     
    -/**
    - * Returns the input handler.
    - */
    -   public final InputHandler getInputHandler()
    -   {
    -      return inputHandler;
    -   }
    +    /**
    +     * Sets the input handler.
    +     *
    +     * @param inputHandler The new input handler
    +     */
    +    public void setInputHandler(InputHandler inputHandler)
    +    {
    +        this.inputHandler = inputHandler;
    +    }
     
    -/**
    - * Sets the input handler.
    - * @param inputHandler The new input handler
    - */
    -   public void setInputHandler(InputHandler inputHandler)
    -   {
    -      this.inputHandler = inputHandler;
    -   }
    +    /**
    +     * Returns true if the caret is blinking, false otherwise.
    +     */
    +    public final boolean isCaretBlinkEnabled()
    +    {
    +        return caretBlinks;
    +    }
     
    -/**
    - * Returns true if the caret is blinking, false otherwise.
    - */
    -   public final boolean isCaretBlinkEnabled()
    -   {
    -      return caretBlinks;
    -   }
    +    /**
    +     * Toggles caret blinking.
    +     *
    +     * @param caretBlinks True if the caret should blink, false otherwise
    +     */
    +    public void setCaretBlinkEnabled(boolean caretBlinks)
    +    {
    +        this.caretBlinks = caretBlinks;
    +        if (!caretBlinks)
    +        {
    +            blink = false;
    +        }
     
    -/**
    - * Toggles caret blinking.
    - * @param caretBlinks True if the caret should blink, false otherwise
    - */
    -   public void setCaretBlinkEnabled(boolean caretBlinks)
    -   {
    -      this.caretBlinks = caretBlinks;
    -      if(!caretBlinks)
    -         blink = false;
    -   
    -      painter.invalidateSelectedLines();
    -   }
    +        painter.invalidateSelectedLines();
    +    }
     
    -/**
    - * Returns true if the caret is visible, false otherwise.
    - */
    -   public final boolean isCaretVisible()
    -   {
    -      return (!caretBlinks || blink) && caretVisible;
    -   }
    +    /**
    +     * Returns true if the caret is visible, false otherwise.
    +     */
    +    public final boolean isCaretVisible()
    +    {
    +        return (!caretBlinks || blink) && caretVisible;
    +    }
     
    -/**
    - * Sets if the caret should be visible.
    - * @param caretVisible True if the caret should be visible, false
    - * otherwise
    - */
    -   public void setCaretVisible(boolean caretVisible)
    -   {
    -      this.caretVisible = caretVisible;
    -      blink = true;
    -   
    -      painter.invalidateSelectedLines();
    -   }
    +    /**
    +     * Sets if the caret should be visible.
    +     *
    +     * @param caretVisible True if the caret should be visible, false otherwise
    +     */
    +    public void setCaretVisible(boolean caretVisible)
    +    {
    +        this.caretVisible = caretVisible;
    +        blink = true;
     
    -/**
    - * Blinks the caret.
    - */
    -   public final void blinkCaret()
    -   {
    -      if(caretBlinks)
    -      {
    -         blink = !blink;
    -         painter.invalidateSelectedLines();
    -      }
    -      else
    -         blink = true;
    -   }
    +        painter.invalidateSelectedLines();
    +    }
     
    -/**
    - * Returns the number of lines from the top and button of the
    - * text area that are always visible.
    - */
    -   public final int getElectricScroll()
    -   {
    -      return electricScroll;
    -   }
    +    /**
    +     * Blinks the caret.
    +     */
    +    public final void blinkCaret()
    +    {
    +        if (caretBlinks)
    +        {
    +            blink = !blink;
    +            painter.invalidateSelectedLines();
    +        }
    +        else
    +        {
    +            blink = true;
    +        }
    +    }
     
    -/**
    - * Sets the number of lines from the top and bottom of the text
    - * area that are always visible
    - * @param electricScroll The number of lines always visible from
    - * the top or bottom
    - */
    -   public final void setElectricScroll(int electricScroll)
    -   {
    -      this.electricScroll = electricScroll;
    -   }
    +    /**
    +     * Returns the number of lines from the top and button of the text area that are always visible.
    +     */
    +    public final int getElectricScroll()
    +    {
    +        return electricScroll;
    +    }
     
    -/**
    - * Updates the state of the scroll bars. This should be called
    - * if the number of lines in the document changes, or when the
    - * size of the text are changes.
    - */
    -   public void updateScrollBars()
    -   {
    -      if(vertical != null && visibleLines != 0)
    -      {
    -         vertical.setValues(firstLine,visibleLines,0,getLineCount());
    -         vertical.setUnitIncrement(VERTICAL_SCROLLBAR_UNIT_INCREMENT_IN_LINES);
    -         vertical.setBlockIncrement(visibleLines);
    -      	
    -      	// Editing area scrollbar has custom model that increments by number of text lines instead of
    -      	// number of pixels. The line number display uses a standard (but invisible) scrollbar based
    -      	// on pixels, so I need to adjust accordingly to keep it in synch with the editing area scrollbar.
    -      	// DPS 4-May-2010
    -         int height = painter.getFontMetrics(painter.getFont()).getHeight();
    -         lineNumbersVertical.setValues(firstLine*height, visibleLines*height,0,getLineCount()*height);
    -         lineNumbersVertical.setUnitIncrement(VERTICAL_SCROLLBAR_UNIT_INCREMENT_IN_LINES*height);
    -         lineNumbersVertical.setBlockIncrement(visibleLines*height);
    -      }
    -   
    -      int width = painter.getWidth();
    -      if(horizontal != null && width != 0)
    -      {
    -         horizontal.setValues(-horizontalOffset,width,0,width * 5);
    -         horizontal.setUnitIncrement(painter.getFontMetrics()
    -            .charWidth('w'));
    -         horizontal.setBlockIncrement(width / 2);
    -      }
    -   }
    +    /**
    +     * Sets the number of lines from the top and bottom of the text area that are always visible
    +     *
    +     * @param electricScroll The number of lines always visible from the top or bottom
    +     */
    +    public final void setElectricScroll(int electricScroll)
    +    {
    +        this.electricScroll = electricScroll;
    +    }
     
    -/**
    - * Returns the line displayed at the text area's origin.
    - */
    -   public final int getFirstLine()
    -   {
    -      return firstLine;
    -   }
    +    /**
    +     * Updates the state of the scroll bars. This should be called if the number of lines in the document changes, or
    +     * when the size of the text are changes.
    +     */
    +    public void updateScrollBars()
    +    {
    +        if (vertical != null && visibleLines != 0)
    +        {
    +            vertical.setValues(firstLine, visibleLines, 0, getLineCount());
    +            vertical.setUnitIncrement(VERTICAL_SCROLLBAR_UNIT_INCREMENT_IN_LINES);
    +            vertical.setBlockIncrement(visibleLines);
     
    -/**
    - * Sets the line displayed at the text area's origin and
    - * updates the scroll bars.
    - */
    -   public void setFirstLine(int firstLine)
    -   {
    -      if(firstLine == this.firstLine)
    -         return;
    -      int oldFirstLine = this.firstLine;
    -      this.firstLine = firstLine;
    -      updateScrollBars();
    -      painter.repaint();
    -   }
    +            // Editing area scrollbar has custom model that increments by number of text lines instead of
    +            // number of pixels. The line number display uses a standard (but invisible) scrollbar based
    +            // on pixels, so I need to adjust accordingly to keep it in synch with the editing area scrollbar.
    +            // DPS 4-May-2010
    +            int height = painter.getFontMetrics(painter.getFont()).getHeight();
    +            lineNumbersVertical.setValues(firstLine * height, visibleLines * height, 0, getLineCount() * height);
    +            lineNumbersVertical.setUnitIncrement(VERTICAL_SCROLLBAR_UNIT_INCREMENT_IN_LINES * height);
    +            lineNumbersVertical.setBlockIncrement(visibleLines * height);
    +        }
     
    -/**
    - * Returns the number of lines visible in this text area.
    - */
    -   public final int getVisibleLines()
    -   {
    -      return visibleLines;
    -   }
    +        int width = painter.getWidth();
    +        if (horizontal != null && width != 0)
    +        {
    +            horizontal.setValues(-horizontalOffset, width, 0, width * 5);
    +            horizontal.setUnitIncrement(painter.getFontMetrics()
    +                .charWidth('w'));
    +            horizontal.setBlockIncrement(width / 2);
    +        }
    +    }
     
    -/**
    - * Recalculates the number of visible lines. This should not
    - * be called directly.
    - */
    -   public final void recalculateVisibleLines()
    -   {
    -      if(painter == null)
    -         return;
    -      int height = painter.getHeight();
    -      int lineHeight = painter.getFontMetrics().getHeight();
    -      int oldVisibleLines = visibleLines;
    -      visibleLines = height / lineHeight;
    -      updateScrollBars();
    -   }
    +    /**
    +     * Returns the line displayed at the text area's origin.
    +     */
    +    public final int getFirstLine()
    +    {
    +        return firstLine;
    +    }
     
    -/**
    - * Returns the horizontal offset of drawn lines.
    - */
    -   public final int getHorizontalOffset()
    -   {
    -      return horizontalOffset;
    -   }
    +    /**
    +     * Sets the line displayed at the text area's origin and updates the scroll bars.
    +     */
    +    public void setFirstLine(int firstLine)
    +    {
    +        if (firstLine == this.firstLine)
    +        {
    +            return;
    +        }
    +        int oldFirstLine = this.firstLine;
    +        this.firstLine = firstLine;
    +        updateScrollBars();
    +        painter.repaint();
    +    }
     
    -/**
    - * Sets the horizontal offset of drawn lines. This can be used to
    - * implement horizontal scrolling.
    - * @param horizontalOffset offset The new horizontal offset
    - */
    -   public void setHorizontalOffset(int horizontalOffset)
    -   {
    -      if(horizontalOffset == this.horizontalOffset)
    -         return;
    -      this.horizontalOffset = horizontalOffset;
    -      if(horizontalOffset != horizontal.getValue())
    -         updateScrollBars();
    -      painter.repaint();
    -   }
    +    /**
    +     * Returns the number of lines visible in this text area.
    +     */
    +    public final int getVisibleLines()
    +    {
    +        return visibleLines;
    +    }
     
    -/**
    - * A fast way of changing both the first line and horizontal
    - * offset.
    - * @param firstLine The new first line
    - * @param horizontalOffset The new horizontal offset
    - * @return True if any of the values were changed, false otherwise
    - */
    -   public boolean setOrigin(int firstLine, int horizontalOffset)
    -   {
    -      boolean changed = false;
    -      int oldFirstLine = this.firstLine;
    -   
    -      if(horizontalOffset != this.horizontalOffset)
    -      {
    -         this.horizontalOffset = horizontalOffset;
    -         changed = true;
    -      }
    -   
    -      if(firstLine != this.firstLine)
    -      {
    -         this.firstLine = firstLine;
    -         changed = true;
    -      }
    -   
    -      if(changed)
    -      {
    -         updateScrollBars();
    -         painter.repaint();
    -      }
    -   
    -      return changed;
    -   }
    +    /**
    +     * Recalculates the number of visible lines. This should not be called directly.
    +     */
    +    public final void recalculateVisibleLines()
    +    {
    +        if (painter == null)
    +        {
    +            return;
    +        }
    +        int height = painter.getHeight();
    +        int lineHeight = painter.getFontMetrics().getHeight();
    +        int oldVisibleLines = visibleLines;
    +        visibleLines = height / lineHeight;
    +        updateScrollBars();
    +    }
     
    -/**
    - * Ensures that the caret is visible by scrolling the text area if
    - * necessary.
    - * @return True if scrolling was actually performed, false if the
    - * caret was already visible
    - */
    -   public boolean scrollToCaret()
    -   {
    -      int line = getCaretLine();
    -      int lineStart = getLineStartOffset(line);
    -      int offset = Math.max(0,Math.min(getLineLength(line) - 1,
    -         getCaretPosition() - lineStart));
    -   
    -      return scrollTo(line,offset);
    -   }
    +    /**
    +     * Returns the horizontal offset of drawn lines.
    +     */
    +    public final int getHorizontalOffset()
    +    {
    +        return horizontalOffset;
    +    }
     
    -/**
    - * Ensures that the specified line and offset is visible by scrolling
    - * the text area if necessary.
    - * @param line The line to scroll to
    - * @param offset The offset in the line to scroll to
    - * @return True if scrolling was actually performed, false if the
    - * line and offset was already visible
    - */
    -   public boolean scrollTo(int line, int offset)
    -   {
    -   // visibleLines == 0 before the component is realized
    -   // we can't do any proper scrolling then, so we have
    -   // this hack...
    -      if(visibleLines == 0)
    -      { 
    -         setFirstLine(Math.max(0,line - electricScroll));
    -         return true;
    -      }
    -   
    -      int newFirstLine = firstLine;
    -      int newHorizontalOffset = horizontalOffset;
    -   
    -      if(line < firstLine + electricScroll)
    -      {
    -         newFirstLine = Math.max(0,line - electricScroll);
    -      }
    -      else if(line + electricScroll >= firstLine + visibleLines)
    -      {
    -         newFirstLine = (line - visibleLines) + electricScroll + 1;
    -         if(newFirstLine + visibleLines >= getLineCount())
    -            newFirstLine = getLineCount() - visibleLines;
    -         if(newFirstLine < 0)
    -            newFirstLine = 0;
    -      }
    -   
    -      int x = _offsetToX(line,offset);
    -      int width = painter.getFontMetrics().charWidth('w');
    -   
    -      if(x < 0)
    -      {
    -         newHorizontalOffset = Math.min(0,horizontalOffset
    -            - x + width + 5);
    -      }
    -      else if(x + width >= painter.getWidth())
    -      {
    -         newHorizontalOffset = horizontalOffset +
    -            (painter.getWidth() - x) - width - 5;
    -      }
    -   
    -      return setOrigin(newFirstLine,newHorizontalOffset);
    -   }
    +    /**
    +     * Sets the horizontal offset of drawn lines. This can be used to implement horizontal scrolling.
    +     *
    +     * @param horizontalOffset offset The new horizontal offset
    +     */
    +    public void setHorizontalOffset(int horizontalOffset)
    +    {
    +        if (horizontalOffset == this.horizontalOffset)
    +        {
    +            return;
    +        }
    +        this.horizontalOffset = horizontalOffset;
    +        if (horizontalOffset != horizontal.getValue())
    +        {
    +            updateScrollBars();
    +        }
    +        painter.repaint();
    +    }
     
    -/**
    - * Converts a line index to a y co-ordinate.
    - * @param line The line
    - */
    -   public int lineToY(int line)
    -   {
    -      FontMetrics fm = painter.getFontMetrics();
    -      return (line - firstLine) * fm.getHeight()
    -         - (fm.getLeading() + fm.getMaxDescent());
    -   }
    +    /**
    +     * A fast way of changing both the first line and horizontal offset.
    +     *
    +     * @param firstLine The new first line
    +     * @param horizontalOffset The new horizontal offset
    +     * @return True if any of the values were changed, false otherwise
    +     */
    +    public boolean setOrigin(int firstLine, int horizontalOffset)
    +    {
    +        boolean changed = false;
    +        int oldFirstLine = this.firstLine;
     
    -/**
    - * Converts a y co-ordinate to a line index.
    - * @param y The y co-ordinate
    - */
    -   public int yToLine(int y)
    -   {
    -      FontMetrics fm = painter.getFontMetrics();
    -      int height = fm.getHeight();
    -      return Math.max(0,Math.min(getLineCount() - 1,
    -         y / height + firstLine));
    -   }
    +        if (horizontalOffset != this.horizontalOffset)
    +        {
    +            this.horizontalOffset = horizontalOffset;
    +            changed = true;
    +        }
     
    -/**
    - * Converts an offset in a line into an x co-ordinate. This is a
    - * slow version that can be used any time.
    - * @param line The line
    - * @param offset The offset, from the start of the line
    - */
    -   public final int offsetToX(int line, int offset)
    -   {
    -   // don't use cached tokens
    -      painter.currentLineTokens = null;
    -      return _offsetToX(line,offset);
    -   }
    +        if (firstLine != this.firstLine)
    +        {
    +            this.firstLine = firstLine;
    +            changed = true;
    +        }
     
    -/**
    - * Converts an offset in a line into an x co-ordinate. This is a
    - * fast version that should only be used if no changes were made
    - * to the text since the last repaint.
    - * @param line The line
    - * @param offset The offset, from the start of the line
    - */
    -   public int _offsetToX(int line, int offset)
    -   {
    -      TokenMarker tokenMarker = getTokenMarker();
    -   
    -   /* Use painter's cached info for speed */
    -      FontMetrics fm = painter.getFontMetrics();
    -   
    -      getLineText(line,lineSegment);
    -   
    -      int segmentOffset = lineSegment.offset;
    -      int x = horizontalOffset;
    -   
    -   /* If syntax coloring is disabled, do simple translation */
    -      if(tokenMarker == null)
    -      {
    -         lineSegment.count = offset;
    -         return x + Utilities.getTabbedTextWidth(lineSegment,
    -            fm,x,painter,0);
    -      }
    -      /* If syntax coloring is enabled, we have to do this because
    -      * tokens can vary in width */
    -      else
    -      {
    -         Token tokens;
    -         if(painter.currentLineIndex == line
    -         && painter.currentLineTokens != null)
    -            tokens = painter.currentLineTokens;
    -         else
    -         {
    -            painter.currentLineIndex = line;
    -            tokens = painter.currentLineTokens
    -               = tokenMarker.markTokens(lineSegment,line);
    -         }
    -      
    -         Toolkit toolkit = painter.getToolkit();
    -         Font defaultFont = painter.getFont();
    -         SyntaxStyle[] styles = painter.getStyles();
    -      
    -         for(;;)
    -         {
    -            byte id = tokens.id;
    -            if(id == Token.END)
    +        if (changed)
    +        {
    +            updateScrollBars();
    +            painter.repaint();
    +        }
    +
    +        return changed;
    +    }
    +
    +    /**
    +     * Ensures that the caret is visible by scrolling the text area if necessary.
    +     *
    +     * @return True if scrolling was actually performed, false if the caret was already visible
    +     */
    +    public boolean scrollToCaret()
    +    {
    +        int line = getCaretLine();
    +        int lineStart = getLineStartOffset(line);
    +        int offset = Math.max(0, Math.min(getLineLength(line) - 1,
    +            getCaretPosition() - lineStart));
    +
    +        return scrollTo(line, offset);
    +    }
    +
    +    /**
    +     * Ensures that the specified line and offset is visible by scrolling the text area if necessary.
    +     *
    +     * @param line The line to scroll to
    +     * @param offset The offset in the line to scroll to
    +     * @return True if scrolling was actually performed, false if the line and offset was already visible
    +     */
    +    public boolean scrollTo(int line, int offset)
    +    {
    +        // visibleLines == 0 before the component is realized
    +        // we can't do any proper scrolling then, so we have
    +        // this hack...
    +        if (visibleLines == 0)
    +        {
    +            setFirstLine(Math.max(0, line - electricScroll));
    +            return true;
    +        }
    +
    +        int newFirstLine = firstLine;
    +        int newHorizontalOffset = horizontalOffset;
    +
    +        if (line < firstLine + electricScroll)
    +        {
    +            newFirstLine = Math.max(0, line - electricScroll);
    +        }
    +        else if (line + electricScroll >= firstLine + visibleLines)
    +        {
    +            newFirstLine = (line - visibleLines) + electricScroll + 1;
    +            if (newFirstLine + visibleLines >= getLineCount())
                 {
    -               return x;
    +                newFirstLine = getLineCount() - visibleLines;
                 }
    -         
    -            if(id == Token.NULL)
    -               fm = painter.getFontMetrics();
    -            else
    -               fm = styles[id].getFontMetrics(defaultFont);
    -         
    -            int length = tokens.length;
    -         
    -            if(offset + segmentOffset < lineSegment.offset + length)
    +            if (newFirstLine < 0)
                 {
    -               lineSegment.count = offset - (lineSegment.offset - segmentOffset);
    -               return x + Utilities.getTabbedTextWidth(
    -                  lineSegment,fm,x,painter,0);
    +                newFirstLine = 0;
    +            }
    +        }
    +
    +        int x = _offsetToX(line, offset);
    +        int width = painter.getFontMetrics().charWidth('w');
    +
    +        if (x < 0)
    +        {
    +            newHorizontalOffset = Math.min(0, horizontalOffset
    +                - x + width + 5);
    +        }
    +        else if (x + width >= painter.getWidth())
    +        {
    +            newHorizontalOffset = horizontalOffset +
    +                (painter.getWidth() - x) - width - 5;
    +        }
    +
    +        return setOrigin(newFirstLine, newHorizontalOffset);
    +    }
    +
    +    /**
    +     * Converts a line index to a y co-ordinate.
    +     *
    +     * @param line The line
    +     */
    +    public int lineToY(int line)
    +    {
    +        FontMetrics fm = painter.getFontMetrics();
    +        return (line - firstLine) * fm.getHeight()
    +            - (fm.getLeading() + fm.getMaxDescent());
    +    }
    +
    +    /**
    +     * Converts a y co-ordinate to a line index.
    +     *
    +     * @param y The y co-ordinate
    +     */
    +    public int yToLine(int y)
    +    {
    +        FontMetrics fm = painter.getFontMetrics();
    +        int height = fm.getHeight();
    +        return Math.max(0, Math.min(getLineCount() - 1,
    +            y / height + firstLine));
    +    }
    +
    +    /**
    +     * Converts an offset in a line into an x co-ordinate. This is a slow version that can be used any time.
    +     *
    +     * @param line The line
    +     * @param offset The offset, from the start of the line
    +     */
    +    public final int offsetToX(int line, int offset)
    +    {
    +        // don't use cached tokens
    +        painter.currentLineTokens = null;
    +        return _offsetToX(line, offset);
    +    }
    +
    +    /**
    +     * Converts an offset in a line into an x co-ordinate. This is a fast version that should only be used if no changes
    +     * were made to the text since the last repaint.
    +     *
    +     * @param line The line
    +     * @param offset The offset, from the start of the line
    +     */
    +    public int _offsetToX(int line, int offset)
    +    {
    +        TokenMarker tokenMarker = getTokenMarker();
    +
    +        /* Use painter's cached info for speed */
    +        FontMetrics fm = painter.getFontMetrics();
    +
    +        getLineText(line, lineSegment);
    +
    +        int segmentOffset = lineSegment.offset;
    +        int x = horizontalOffset;
    +
    +        /* If syntax coloring is disabled, do simple translation */
    +        if (tokenMarker == null)
    +        {
    +            lineSegment.count = offset;
    +            return x + Utilities.getTabbedTextWidth(lineSegment,
    +                fm, x, painter, 0);
    +        }
    +        /* If syntax coloring is enabled, we have to do this because
    +         * tokens can vary in width */
    +        else
    +        {
    +            Token tokens;
    +            if (painter.currentLineIndex == line
    +                && painter.currentLineTokens != null)
    +            {
    +                tokens = painter.currentLineTokens;
                 }
                 else
                 {
    -               lineSegment.count = length;
    -               x += Utilities.getTabbedTextWidth(
    -                  lineSegment,fm,x,painter,0);
    -               lineSegment.offset += length;
    +                painter.currentLineIndex = line;
    +                tokens = painter.currentLineTokens
    +                    = tokenMarker.markTokens(lineSegment, line);
                 }
    -            tokens = tokens.next;
    -         }
    -      }
    -   }
     
    -/**
    - * Converts an x co-ordinate to an offset within a line.
    - * @param line The line
    - * @param x The x co-ordinate
    - */
    -   public int xToOffset(int line, int x)
    -   {
    -      TokenMarker tokenMarker = getTokenMarker();
    -   
    -   /* Use painter's cached info for speed */
    -      FontMetrics fm = painter.getFontMetrics();
    -   
    -      getLineText(line,lineSegment);
    -   
    -      char[] segmentArray = lineSegment.array;
    -      int segmentOffset = lineSegment.offset;
    -      int segmentCount = lineSegment.count;
    -   
    -      int width = horizontalOffset;
    -   
    -      if(tokenMarker == null)
    -      {
    -         for(int i = 0; i < segmentCount; i++)
    -         {
    -            char c = segmentArray[i + segmentOffset];
    -            int charWidth;
    -            if(c == '\t')
    -               charWidth = (int)painter.nextTabStop(width,i)
    -                  - width;
    -            else
    -               charWidth = fm.charWidth(c);
    -         
    -            if(painter.isBlockCaretEnabled())
    +            Toolkit toolkit = painter.getToolkit();
    +            Font defaultFont = painter.getFont();
    +            SyntaxStyle[] styles = painter.getStyles();
    +
    +            for (; ; )
                 {
    -               if(x - charWidth <= width)
    -                  return i;
    +                byte id = tokens.id;
    +                if (id == Token.END)
    +                {
    +                    return x;
    +                }
    +
    +                if (id == Token.NULL)
    +                {
    +                    fm = painter.getFontMetrics();
    +                }
    +                else
    +                {
    +                    fm = styles[id].getFontMetrics(defaultFont);
    +                }
    +
    +                int length = tokens.length;
    +
    +                if (offset + segmentOffset < lineSegment.offset + length)
    +                {
    +                    lineSegment.count = offset - (lineSegment.offset - segmentOffset);
    +                    return x + Utilities.getTabbedTextWidth(
    +                        lineSegment, fm, x, painter, 0);
    +                }
    +                else
    +                {
    +                    lineSegment.count = length;
    +                    x += Utilities.getTabbedTextWidth(
    +                        lineSegment, fm, x, painter, 0);
    +                    lineSegment.offset += length;
    +                }
    +                tokens = tokens.next;
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Converts an x co-ordinate to an offset within a line.
    +     *
    +     * @param line The line
    +     * @param x The x co-ordinate
    +     */
    +    public int xToOffset(int line, int x)
    +    {
    +        TokenMarker tokenMarker = getTokenMarker();
    +
    +        /* Use painter's cached info for speed */
    +        FontMetrics fm = painter.getFontMetrics();
    +
    +        getLineText(line, lineSegment);
    +
    +        char[] segmentArray = lineSegment.array;
    +        int segmentOffset = lineSegment.offset;
    +        int segmentCount = lineSegment.count;
    +
    +        int width = horizontalOffset;
    +
    +        if (tokenMarker == null)
    +        {
    +            for (int i = 0; i < segmentCount; i++)
    +            {
    +                char c = segmentArray[i + segmentOffset];
    +                int charWidth;
    +                if (c == '\t')
    +                {
    +                    charWidth = (int) painter.nextTabStop(width, i)
    +                        - width;
    +                }
    +                else
    +                {
    +                    charWidth = fm.charWidth(c);
    +                }
    +
    +                if (painter.isBlockCaretEnabled())
    +                {
    +                    if (x - charWidth <= width)
    +                    {
    +                        return i;
    +                    }
    +                }
    +                else
    +                {
    +                    if (x - charWidth / 2 <= width)
    +                    {
    +                        return i;
    +                    }
    +                }
    +
    +                width += charWidth;
    +            }
    +
    +            return segmentCount;
    +        }
    +        else
    +        {
    +            Token tokens;
    +            if (painter.currentLineIndex == line && painter
    +                .currentLineTokens != null)
    +            {
    +                tokens = painter.currentLineTokens;
                 }
                 else
                 {
    -               if(x - charWidth / 2 <= width)
    -                  return i;
    +                painter.currentLineIndex = line;
    +                tokens = painter.currentLineTokens
    +                    = tokenMarker.markTokens(lineSegment, line);
                 }
    -         
    -            width += charWidth;
    -         }
    -      
    -         return segmentCount;
    -      }
    -      else
    -      {
    -         Token tokens;
    -         if(painter.currentLineIndex == line && painter
    -         .currentLineTokens != null)
    -            tokens = painter.currentLineTokens;
    -         else
    -         {
    -            painter.currentLineIndex = line;
    -            tokens = painter.currentLineTokens
    -               = tokenMarker.markTokens(lineSegment,line);
    -         }
    -      
    -         int offset = 0;
    -         Toolkit toolkit = painter.getToolkit();
    -         Font defaultFont = painter.getFont();
    -         SyntaxStyle[] styles = painter.getStyles();
    -      
    -         for(;;)
    -         {
    -            byte id = tokens.id;
    -            if(id == Token.END)
    -               return offset;
    -         
    -            if(id == Token.NULL)
    -               fm = painter.getFontMetrics();
    -            else
    -               fm = styles[id].getFontMetrics(defaultFont);
    -         
    -            int length = tokens.length;
    -         
    -            for(int i = 0; i < length; i++)
    +
    +            int offset = 0;
    +            Toolkit toolkit = painter.getToolkit();
    +            Font defaultFont = painter.getFont();
    +            SyntaxStyle[] styles = painter.getStyles();
    +
    +            for (; ; )
                 {
    -               char c = segmentArray[segmentOffset + offset + i];
    -               int charWidth;
    -               if(c == '\t')
    -                  charWidth = (int)painter.nextTabStop(width,offset + i)
    -                     - width;
    -               else
    -                  charWidth = fm.charWidth(c);
    -            
    -               if(painter.isBlockCaretEnabled())
    -               {
    -                  if(x - charWidth <= width)
    -                     return offset + i;
    -               }
    -               else
    -               {
    -                  if(x - charWidth / 2 <= width)
    -                     return offset + i;
    -               }
    -            
    -               width += charWidth;
    +                byte id = tokens.id;
    +                if (id == Token.END)
    +                {
    +                    return offset;
    +                }
    +
    +                if (id == Token.NULL)
    +                {
    +                    fm = painter.getFontMetrics();
    +                }
    +                else
    +                {
    +                    fm = styles[id].getFontMetrics(defaultFont);
    +                }
    +
    +                int length = tokens.length;
    +
    +                for (int i = 0; i < length; i++)
    +                {
    +                    char c = segmentArray[segmentOffset + offset + i];
    +                    int charWidth;
    +                    if (c == '\t')
    +                    {
    +                        charWidth = (int) painter.nextTabStop(width, offset + i)
    +                            - width;
    +                    }
    +                    else
    +                    {
    +                        charWidth = fm.charWidth(c);
    +                    }
    +
    +                    if (painter.isBlockCaretEnabled())
    +                    {
    +                        if (x - charWidth <= width)
    +                        {
    +                            return offset + i;
    +                        }
    +                    }
    +                    else
    +                    {
    +                        if (x - charWidth / 2 <= width)
    +                        {
    +                            return offset + i;
    +                        }
    +                    }
    +
    +                    width += charWidth;
    +                }
    +
    +                offset += length;
    +                tokens = tokens.next;
                 }
    -         
    -            offset += length;
    -            tokens = tokens.next;
    -         }
    -      }
    -   }
    +        }
    +    }
     
    -/**
    - * Converts a point to an offset, from the start of the text.
    - * @param x The x co-ordinate of the point
    - * @param y The y co-ordinate of the point
    - */
    -   public int xyToOffset(int x, int y)
    -   {
    -      int line = yToLine(y);
    -      int start = getLineStartOffset(line);
    -      return start + xToOffset(line,x);
    -   }
    +    /**
    +     * Converts a point to an offset, from the start of the text.
    +     *
    +     * @param x The x co-ordinate of the point
    +     * @param y The y co-ordinate of the point
    +     */
    +    public int xyToOffset(int x, int y)
    +    {
    +        int line = yToLine(y);
    +        int start = getLineStartOffset(line);
    +        return start + xToOffset(line, x);
    +    }
     
    -/**
    - * Returns the document this text area is editing.
    - */
    -   public final Document getDocument()
    -   {
    -      return document;
    -   }
    +    /**
    +     * Returns the document this text area is editing.
    +     */
    +    public final Document getDocument()
    +    {
    +        return document;
    +    }
     
    -/**
    - * Sets the document this text area is editing.
    - * @param document The document
    - */
    -   public void setDocument(SyntaxDocument document)
    -   {
    -      if(this.document == document)
    -         return;
    -      if(this.document != null)
    -         this.document.removeDocumentListener(documentHandler);
    -      this.document = document;
    -   
    -      document.addDocumentListener(documentHandler);
    -   
    -      select(0,0);
    -      updateScrollBars();
    -      painter.repaint();
    -   }
    +    /**
    +     * Sets the document this text area is editing.
    +     *
    +     * @param document The document
    +     */
    +    public void setDocument(SyntaxDocument document)
    +    {
    +        if (this.document == document)
    +        {
    +            return;
    +        }
    +        if (this.document != null)
    +        {
    +            this.document.removeDocumentListener(documentHandler);
    +        }
    +        this.document = document;
     
    -/**
    - * Returns the document's token marker. Equivalent to calling
    - * getDocument().getTokenMarker().
    - */
    -   public final TokenMarker getTokenMarker()
    -   {
    -      return document.getTokenMarker();
    -   }
    +        document.addDocumentListener(documentHandler);
     
    -/**
    - * Sets the document's token marker. Equivalent to caling
    - * getDocument().setTokenMarker().
    - * @param tokenMarker The token marker
    - */
    -   public final void setTokenMarker(TokenMarker tokenMarker)
    -   {
    -      document.setTokenMarker(tokenMarker);
    -   }
    +        select(0, 0);
    +        updateScrollBars();
    +        painter.repaint();
    +    }
     
    -/**
    - * Returns the length of the document. Equivalent to calling
    - * getDocument().getLength().
    - */
    -   public final int getDocumentLength()
    -   {
    -      return document.getLength();
    -   }
    +    /**
    +     * Returns the document's token marker. Equivalent to calling
    +     * getDocument().getTokenMarker().
    +     */
    +    public final TokenMarker getTokenMarker()
    +    {
    +        return document.getTokenMarker();
    +    }
     
    -/**
    - * Returns the number of lines in the document.
    - */
    -   public final int getLineCount()
    -   {
    -      return document.getDefaultRootElement().getElementCount();
    -   }
    +    /**
    +     * Sets the document's token marker. Equivalent to caling
    +     * getDocument().setTokenMarker().
    +     *
    +     * @param tokenMarker The token marker
    +     */
    +    public final void setTokenMarker(TokenMarker tokenMarker)
    +    {
    +        document.setTokenMarker(tokenMarker);
    +    }
     
    -/**
    - * Returns the line containing the specified offset.
    - * @param offset The offset
    - */
    -   public final int getLineOfOffset(int offset)
    -   {
    -      return document.getDefaultRootElement().getElementIndex(offset);
    -   }
    +    /**
    +     * Returns the length of the document. Equivalent to calling
    +     * getDocument().getLength().
    +     */
    +    public final int getDocumentLength()
    +    {
    +        return document.getLength();
    +    }
     
    -/**
    - * Returns the start offset of the specified line.
    - * @param line The line
    - * @return The start offset of the specified line, or -1 if the line is
    - * invalid
    - */
    -   public int getLineStartOffset(int line)
    -   {
    -      Element lineElement = document.getDefaultRootElement()
    -         .getElement(line);
    -      if(lineElement == null)
    -         return -1;
    -      else
    -         return lineElement.getStartOffset();
    -   }
    +    /**
    +     * Returns the number of lines in the document.
    +     */
    +    public final int getLineCount()
    +    {
    +        return document.getDefaultRootElement().getElementCount();
    +    }
     
    -/**
    - * Returns the end offset of the specified line.
    - * @param line The line
    - * @return The end offset of the specified line, or -1 if the line is
    - * invalid.
    - */
    -   public int getLineEndOffset(int line)
    -   {
    -      Element lineElement = document.getDefaultRootElement()
    -         .getElement(line);
    -      if(lineElement == null)
    -         return -1;
    -      else
    -         return lineElement.getEndOffset();
    -   }
    +    /**
    +     * Returns the line containing the specified offset.
    +     *
    +     * @param offset The offset
    +     */
    +    public final int getLineOfOffset(int offset)
    +    {
    +        return document.getDefaultRootElement().getElementIndex(offset);
    +    }
     
    -/**
    - * Returns the length of the specified line.
    - * @param line The line
    - */
    -   public int getLineLength(int line)
    -   {
    -      Element lineElement = document.getDefaultRootElement()
    -         .getElement(line);
    -      if(lineElement == null)
    -         return -1;
    -      else
    -         return lineElement.getEndOffset()
    -            - lineElement.getStartOffset() - 1;
    -   }
    +    /**
    +     * Returns the start offset of the specified line.
    +     *
    +     * @param line The line
    +     * @return The start offset of the specified line, or -1 if the line is invalid
    +     */
    +    public int getLineStartOffset(int line)
    +    {
    +        Element lineElement = document.getDefaultRootElement()
    +            .getElement(line);
    +        if (lineElement == null)
    +        {
    +            return -1;
    +        }
    +        else
    +        {
    +            return lineElement.getStartOffset();
    +        }
    +    }
     
    -/**
    - * Returns the entire text of this text area.
    - */
    -   public String getText()
    -   {
    -      try
    -      {
    -         return document.getText(0,document.getLength());
    -      }
    -      catch(BadLocationException bl)
    -      {
    -         bl.printStackTrace();
    -         return null;
    -      }
    -   }
    +    /**
    +     * Returns the end offset of the specified line.
    +     *
    +     * @param line The line
    +     * @return The end offset of the specified line, or -1 if the line is invalid.
    +     */
    +    public int getLineEndOffset(int line)
    +    {
    +        Element lineElement = document.getDefaultRootElement()
    +            .getElement(line);
    +        if (lineElement == null)
    +        {
    +            return -1;
    +        }
    +        else
    +        {
    +            return lineElement.getEndOffset();
    +        }
    +    }
     
    -/**
    - * Sets the entire text of this text area.
    - */
    -   public void setText(String text)
    -   {
    -      try
    -      {
    -         document.beginCompoundEdit();
    -         document.remove(0,document.getLength());
    -         document.insertString(0,text,null);
    -      }
    -      catch(BadLocationException bl)
    -      {
    -         bl.printStackTrace();
    -      }
    -      finally
    -      {
    -         document.endCompoundEdit();
    -      }
    -   }
    +    /**
    +     * Returns the length of the specified line.
    +     *
    +     * @param line The line
    +     */
    +    public int getLineLength(int line)
    +    {
    +        Element lineElement = document.getDefaultRootElement()
    +            .getElement(line);
    +        if (lineElement == null)
    +        {
    +            return -1;
    +        }
    +        else
    +        {
    +            return lineElement.getEndOffset()
    +                - lineElement.getStartOffset() - 1;
    +        }
    +    }
     
    -/**
    - * Returns the specified substring of the document.
    - * @param start The start offset
    - * @param len The length of the substring
    - * @return The substring, or null if the offsets are invalid
    - */
    -   public final String getText(int start, int len)
    -   {
    -      try
    -      {
    -         return document.getText(start,len);
    -      }
    -      catch(BadLocationException bl)
    -      {
    -         bl.printStackTrace();
    -         return null;
    -      }
    -   }
    +    /**
    +     * Returns the entire text of this text area.
    +     */
    +    public String getText()
    +    {
    +        try
    +        {
    +            return document.getText(0, document.getLength());
    +        }
    +        catch (BadLocationException bl)
    +        {
    +            bl.printStackTrace();
    +            return null;
    +        }
    +    }
     
    -/**
    - * Copies the specified substring of the document into a segment.
    - * If the offsets are invalid, the segment will contain a null string.
    - * @param start The start offset
    - * @param len The length of the substring
    - * @param segment The segment
    - */
    -   public final void getText(int start, int len, Segment segment)
    -   {
    -      try
    -      {
    -         document.getText(start,len,segment);
    -      }
    -      catch(BadLocationException bl)
    -      {
    -         bl.printStackTrace();
    -         segment.offset = segment.count = 0;
    -      }
    -   }
    +    /**
    +     * Sets the entire text of this text area.
    +     */
    +    public void setText(String text)
    +    {
    +        try
    +        {
    +            document.beginCompoundEdit();
    +            document.remove(0, document.getLength());
    +            document.insertString(0, text, null);
    +        }
    +        catch (BadLocationException bl)
    +        {
    +            bl.printStackTrace();
    +        }
    +        finally
    +        {
    +            document.endCompoundEdit();
    +        }
    +    }
     
    -/**
    - * Returns the text on the specified line.
    - * @param lineIndex The line
    - * @return The text, or null if the line is invalid
    - */
    -   public final String getLineText(int lineIndex)
    -   {
    -      int start = getLineStartOffset(lineIndex);
    -      return getText(start,getLineEndOffset(lineIndex) - start - 1);
    -   }
    +    /**
    +     * Returns the specified substring of the document.
    +     *
    +     * @param start The start offset
    +     * @param len The length of the substring
    +     * @return The substring, or null if the offsets are invalid
    +     */
    +    public final String getText(int start, int len)
    +    {
    +        try
    +        {
    +            return document.getText(start, len);
    +        }
    +        catch (BadLocationException bl)
    +        {
    +            bl.printStackTrace();
    +            return null;
    +        }
    +    }
     
    -/**
    - * Copies the text on the specified line into a segment. If the line
    - * is invalid, the segment will contain a null string.
    - * @param lineIndex The line
    - */
    -   public final void getLineText(int lineIndex, Segment segment)
    -   {
    -      int start = getLineStartOffset(lineIndex);
    -      getText(start,getLineEndOffset(lineIndex) - start - 1,segment);
    -   }
    +    /**
    +     * Copies the specified substring of the document into a segment. If the offsets are invalid, the segment will
    +     * contain a null string.
    +     *
    +     * @param start The start offset
    +     * @param len The length of the substring
    +     * @param segment The segment
    +     */
    +    public final void getText(int start, int len, Segment segment)
    +    {
    +        try
    +        {
    +            document.getText(start, len, segment);
    +        }
    +        catch (BadLocationException bl)
    +        {
    +            bl.printStackTrace();
    +            segment.offset = segment.count = 0;
    +        }
    +    }
     
    -/**
    - * Returns the selection start offset.
    - */
    -   public final int getSelectionStart()
    -   {
    -      return selectionStart;
    -   }
    +    /**
    +     * Returns the text on the specified line.
    +     *
    +     * @param lineIndex The line
    +     * @return The text, or null if the line is invalid
    +     */
    +    public final String getLineText(int lineIndex)
    +    {
    +        int start = getLineStartOffset(lineIndex);
    +        return getText(start, getLineEndOffset(lineIndex) - start - 1);
    +    }
     
    -/**
    - * Returns the offset where the selection starts on the specified
    - * line.
    - */
    -   public int getSelectionStart(int line)
    -   {
    -      if(line == selectionStartLine)
    -         return selectionStart;
    -      else if(rectSelect)
    -      {
    -         Element map = document.getDefaultRootElement();
    -         int start = selectionStart - map.getElement(selectionStartLine)
    -            .getStartOffset();
    -      
    -         Element lineElement = map.getElement(line);
    -         int lineStart = lineElement.getStartOffset();
    -         int lineEnd = lineElement.getEndOffset() - 1;
    -         return Math.min(lineEnd,lineStart + start);
    -      }
    -      else
    -         return getLineStartOffset(line);
    -   }
    +    /**
    +     * Copies the text on the specified line into a segment. If the line is invalid, the segment will contain a null
    +     * string.
    +     *
    +     * @param lineIndex The line
    +     */
    +    public final void getLineText(int lineIndex, Segment segment)
    +    {
    +        int start = getLineStartOffset(lineIndex);
    +        getText(start, getLineEndOffset(lineIndex) - start - 1, segment);
    +    }
     
    -/**
    - * Returns the selection start line.
    - */
    -   public final int getSelectionStartLine()
    -   {
    -      return selectionStartLine;
    -   }
    +    /**
    +     * Returns the selection start offset.
    +     */
    +    public final int getSelectionStart()
    +    {
    +        return selectionStart;
    +    }
     
    -/**
    - * Sets the selection start. The new selection will be the new
    - * selection start and the old selection end.
    - * @param selectionStart The selection start
    - * @see #select(int,int)
    - */
    -   public final void setSelectionStart(int selectionStart)
    -   {
    -      select(selectionStart,selectionEnd);
    -   }
    +    /**
    +     * Sets the selection start. The new selection will be the new selection start and the old selection end.
    +     *
    +     * @param selectionStart The selection start
    +     * @see #select(int, int)
    +     */
    +    public final void setSelectionStart(int selectionStart)
    +    {
    +        select(selectionStart, selectionEnd);
    +    }
     
    -/**
    - * Returns the selection end offset.
    - */
    -   public final int getSelectionEnd()
    -   {
    -      return selectionEnd;
    -   }
    +    /**
    +     * Returns the offset where the selection starts on the specified line.
    +     */
    +    public int getSelectionStart(int line)
    +    {
    +        if (line == selectionStartLine)
    +        {
    +            return selectionStart;
    +        }
    +        else if (rectSelect)
    +        {
    +            Element map = document.getDefaultRootElement();
    +            int start = selectionStart - map.getElement(selectionStartLine)
    +                .getStartOffset();
     
    -/**
    - * Returns the offset where the selection ends on the specified
    - * line.
    - */
    -   public int getSelectionEnd(int line)
    -   {
    -      if(line == selectionEndLine)
    -         return selectionEnd;
    -      else if(rectSelect)
    -      {
    -         Element map = document.getDefaultRootElement();
    -         int end = selectionEnd - map.getElement(selectionEndLine)
    -            .getStartOffset();
    -      
    -         Element lineElement = map.getElement(line);
    -         int lineStart = lineElement.getStartOffset();
    -         int lineEnd = lineElement.getEndOffset() - 1;
    -         return Math.min(lineEnd,lineStart + end);
    -      }
    -      else
    -         return getLineEndOffset(line) - 1;
    -   }
    -
    -/**
    - * Returns the selection end line.
    - */
    -   public final int getSelectionEndLine()
    -   {
    -      return selectionEndLine;
    -   }
    -
    -/**
    - * Sets the selection end. The new selection will be the old
    - * selection start and the new selection end.
    - * @param selectionEnd The selection end
    - * @see #select(int,int)
    - */
    -   public final void setSelectionEnd(int selectionEnd)
    -   {
    -      select(selectionStart,selectionEnd);
    -   }
    -
    -/**
    - * Returns the caret position. This will either be the selection
    - * start or the selection end, depending on which direction the
    - * selection was made in.
    - */
    -   public final int getCaretPosition()
    -   {
    -      return (biasLeft ? selectionStart : selectionEnd);
    -   }
    -
    -/**
    - * Returns the caret line.
    - */
    -   public final int getCaretLine()
    -   {
    -      return (biasLeft ? selectionStartLine : selectionEndLine);
    -   }
    -
    -/**
    - * Returns the mark position. This will be the opposite selection
    - * bound to the caret position.
    - * @see #getCaretPosition()
    - */
    -   public final int getMarkPosition()
    -   {
    -      return (biasLeft ? selectionEnd : selectionStart);
    -   }
    -
    -/**
    - * Returns the mark line.
    - */
    -   public final int getMarkLine()
    -   {
    -      return (biasLeft ? selectionEndLine : selectionStartLine);
    -   }
    -
    -/**
    - * Sets the caret position. The new selection will consist of the
    - * caret position only (hence no text will be selected)
    - * @param caret The caret position
    - * @see #select(int,int)
    - */
    -   public final void setCaretPosition(int caret)
    -   {
    -      select(caret,caret);
    -   }
    -
    -/**
    - * Selects all text in the document.
    - */
    -   public final void selectAll()
    -   {
    -      select(0,getDocumentLength());
    -   }
    -
    -/**
    - * Moves the mark to the caret position.
    - */
    -   public final void selectNone()
    -   {
    -      select(getCaretPosition(),getCaretPosition());
    -   }
    -
    -/**
    - * Selects from the start offset to the end offset. This is the
    - * general selection method used by all other selecting methods.
    - * The caret position will be start if start < end, and end
    - * if end > start.
    - * @param start The start offset
    - * @param end The end offset
    - */
    -   public void select(int start, int end)
    -   {
    -      int newStart, newEnd;
    -      boolean newBias;
    -      if(start <= end)
    -      {
    -         newStart = start;
    -         newEnd = end;
    -         newBias = false;
    -      }
    -      else
    -      {
    -         newStart = end;
    -         newEnd = start;
    -         newBias = true;
    -      }
    -   
    -      if(newStart < 0 || newEnd > getDocumentLength())
    -      {
    -         throw new IllegalArgumentException("Bounds out of"
    -            + " range: " + newStart + "," +
    -            newEnd);
    -      }
    -   
    -   // If the new position is the same as the old, we don't
    -   // do all this crap, however we still do the stuff at
    -   // the end (clearing magic position, scrolling)
    -      if(newStart != selectionStart || newEnd != selectionEnd
    -      || newBias != biasLeft)
    -      {
    -         int newStartLine = getLineOfOffset(newStart);
    -         int newEndLine = getLineOfOffset(newEnd);
    -      
    -         if(painter.isBracketHighlightEnabled())
    -         {
    -            if(bracketLine != -1)
    -               painter.invalidateLine(bracketLine);
    -            updateBracketHighlight(end);
    -            if(bracketLine != -1)
    -               painter.invalidateLine(bracketLine);
    -         }
    -      
    -         painter.invalidateLineRange(selectionStartLine,selectionEndLine);
    -         painter.invalidateLineRange(newStartLine,newEndLine);
    -      
    -         document.addUndoableEdit(new CaretUndo(
    -            selectionStart,selectionEnd));
    -      
    -         selectionStart = newStart;
    -         selectionEnd = newEnd;
    -         selectionStartLine = newStartLine;
    -         selectionEndLine = newEndLine;
    -         biasLeft = newBias;
    -      
    -         fireCaretEvent();
    -      }
    -   
    -   // When the user is typing, etc, we don't want the caret
    -   // to blink
    -      blink = true;
    -      caretTimer.restart();
    -   
    -   // Disable rectangle select if selection start = selection end
    -      if(selectionStart == selectionEnd)
    -         rectSelect = false;
    -   
    -   // Clear the `magic' caret position used by up/down
    -      magicCaret = -1;      
    -      scrollToCaret();
    -   }
    -
    -/**
    - * Returns the selected text, or null if no selection is active.
    - */
    -   public final String getSelectedText()
    -   {
    -      if(selectionStart == selectionEnd)
    -         return null;
    -   
    -      if(rectSelect)
    -      {
    -      // Return each row of the selection on a new line
    -      
    -         Element map = document.getDefaultRootElement();
    -      
    -         int start = selectionStart - map.getElement(selectionStartLine)
    -            .getStartOffset();
    -         int end = selectionEnd - map.getElement(selectionEndLine)
    -            .getStartOffset();
    -      
    -      // Certain rectangles satisfy this condition...
    -         if(end < start)
    -         {
    -            int tmp = end;
    -            end = start;
    -            start = tmp;
    -         }
    -      
    -         StringBuffer buf = new StringBuffer();
    -         Segment seg = new Segment();
    -      
    -         for(int i = selectionStartLine; i <= selectionEndLine; i++)
    -         {
    -            Element lineElement = map.getElement(i);
    +            Element lineElement = map.getElement(line);
                 int lineStart = lineElement.getStartOffset();
                 int lineEnd = lineElement.getEndOffset() - 1;
    -            int lineLen = lineEnd - lineStart;
    -         
    -            lineStart = Math.min(lineStart + start,lineEnd);
    -            lineLen = Math.min(end - start,lineEnd - lineStart);
    -         
    -            getText(lineStart,lineLen,seg);
    -            buf.append(seg.array,seg.offset,seg.count);
    -         
    -            if(i != selectionEndLine)
    -               buf.append('\n');
    -         }
    -      
    -         return buf.toString();
    -      }
    -      else
    -      {
    -         return getText(selectionStart,
    -            selectionEnd - selectionStart);
    -      }
    -   }
    +            return Math.min(lineEnd, lineStart + start);
    +        }
    +        else
    +        {
    +            return getLineStartOffset(line);
    +        }
    +    }
     
    +    /**
    +     * Returns the selection start line.
    +     */
    +    public final int getSelectionStartLine()
    +    {
    +        return selectionStartLine;
    +    }
     
    -	
    -/**
    - * Replaces the selection with the specified text.
    - * @param selectedText The replacement text for the selection
    - */
    -   public void setSelectedText(String selectedText)
    -   {
    -      if(!editable)
    -      {
    -         throw new InternalError("Text component"
    -            + " read only");
    -      }
    -   
    -      document.beginCompoundEdit();
    -   
    -      try
    -      {
    -         if(rectSelect)
    -         {
    +    /**
    +     * Returns the selection end offset.
    +     */
    +    public final int getSelectionEnd()
    +    {
    +        return selectionEnd;
    +    }
    +
    +    /**
    +     * Sets the selection end. The new selection will be the old selection start and the new selection end.
    +     *
    +     * @param selectionEnd The selection end
    +     * @see #select(int, int)
    +     */
    +    public final void setSelectionEnd(int selectionEnd)
    +    {
    +        select(selectionStart, selectionEnd);
    +    }
    +
    +    /**
    +     * Returns the offset where the selection ends on the specified line.
    +     */
    +    public int getSelectionEnd(int line)
    +    {
    +        if (line == selectionEndLine)
    +        {
    +            return selectionEnd;
    +        }
    +        else if (rectSelect)
    +        {
                 Element map = document.getDefaultRootElement();
    -         
    -            int start = selectionStart - map.getElement(selectionStartLine)
    -               .getStartOffset();
                 int end = selectionEnd - map.getElement(selectionEndLine)
    -               .getStartOffset();
    -         
    -         // Certain rectangles satisfy this condition...
    -            if(end < start)
    +                .getStartOffset();
    +
    +            Element lineElement = map.getElement(line);
    +            int lineStart = lineElement.getStartOffset();
    +            int lineEnd = lineElement.getEndOffset() - 1;
    +            return Math.min(lineEnd, lineStart + end);
    +        }
    +        else
    +        {
    +            return getLineEndOffset(line) - 1;
    +        }
    +    }
    +
    +    /**
    +     * Returns the selection end line.
    +     */
    +    public final int getSelectionEndLine()
    +    {
    +        return selectionEndLine;
    +    }
    +
    +    /**
    +     * Returns the caret position. This will either be the selection start or the selection end, depending on which
    +     * direction the selection was made in.
    +     */
    +    public final int getCaretPosition()
    +    {
    +        return (biasLeft ? selectionStart : selectionEnd);
    +    }
    +
    +    /**
    +     * Sets the caret position. The new selection will consist of the caret position only (hence no text will be
    +     * selected)
    +     *
    +     * @param caret The caret position
    +     * @see #select(int, int)
    +     */
    +    public final void setCaretPosition(int caret)
    +    {
    +        select(caret, caret);
    +    }
    +
    +    /**
    +     * Returns the caret line.
    +     */
    +    public final int getCaretLine()
    +    {
    +        return (biasLeft ? selectionStartLine : selectionEndLine);
    +    }
    +
    +    /**
    +     * Returns the mark position. This will be the opposite selection bound to the caret position.
    +     *
    +     * @see #getCaretPosition()
    +     */
    +    public final int getMarkPosition()
    +    {
    +        return (biasLeft ? selectionEnd : selectionStart);
    +    }
    +
    +    /**
    +     * Returns the mark line.
    +     */
    +    public final int getMarkLine()
    +    {
    +        return (biasLeft ? selectionEndLine : selectionStartLine);
    +    }
    +
    +    /**
    +     * Selects all text in the document.
    +     */
    +    public final void selectAll()
    +    {
    +        select(0, getDocumentLength());
    +    }
    +
    +    /**
    +     * Moves the mark to the caret position.
    +     */
    +    public final void selectNone()
    +    {
    +        select(getCaretPosition(), getCaretPosition());
    +    }
    +
    +    /**
    +     * Selects from the start offset to the end offset. This is the general selection method used by all other selecting
    +     * methods. The caret position will be start if start < end, and end if end > start.
    +     *
    +     * @param start The start offset
    +     * @param end The end offset
    +     */
    +    public void select(int start, int end)
    +    {
    +        int newStart, newEnd;
    +        boolean newBias;
    +        if (start <= end)
    +        {
    +            newStart = start;
    +            newEnd = end;
    +            newBias = false;
    +        }
    +        else
    +        {
    +            newStart = end;
    +            newEnd = start;
    +            newBias = true;
    +        }
    +
    +        if (newStart < 0 || newEnd > getDocumentLength())
    +        {
    +            throw new IllegalArgumentException("Bounds out of"
    +                + " range: " + newStart + "," +
    +                newEnd);
    +        }
    +
    +        // If the new position is the same as the old, we don't
    +        // do all this crap, however we still do the stuff at
    +        // the end (clearing magic position, scrolling)
    +        if (newStart != selectionStart || newEnd != selectionEnd
    +            || newBias != biasLeft)
    +        {
    +            int newStartLine = getLineOfOffset(newStart);
    +            int newEndLine = getLineOfOffset(newEnd);
    +
    +            if (painter.isBracketHighlightEnabled())
                 {
    -               int tmp = end;
    -               end = start;
    -               start = tmp;
    +                if (bracketLine != -1)
    +                {
    +                    painter.invalidateLine(bracketLine);
    +                }
    +                updateBracketHighlight(end);
    +                if (bracketLine != -1)
    +                {
    +                    painter.invalidateLine(bracketLine);
    +                }
                 }
    -         
    -            int lastNewline = 0;
    -            int currNewline = 0;
    -         
    -            for(int i = selectionStartLine; i <= selectionEndLine; i++)
    +
    +            painter.invalidateLineRange(selectionStartLine, selectionEndLine);
    +            painter.invalidateLineRange(newStartLine, newEndLine);
    +
    +            document.addUndoableEdit(new CaretUndo(
    +                selectionStart, selectionEnd));
    +
    +            selectionStart = newStart;
    +            selectionEnd = newEnd;
    +            selectionStartLine = newStartLine;
    +            selectionEndLine = newEndLine;
    +            biasLeft = newBias;
    +
    +            fireCaretEvent();
    +        }
    +
    +        // When the user is typing, etc, we don't want the caret
    +        // to blink
    +        blink = true;
    +        caretTimer.restart();
    +
    +        // Disable rectangle select if selection start = selection end
    +        if (selectionStart == selectionEnd)
    +        {
    +            rectSelect = false;
    +        }
    +
    +        // Clear the `magic' caret position used by up/down
    +        magicCaret = -1;
    +        scrollToCaret();
    +    }
    +
    +    /**
    +     * Returns the selected text, or null if no selection is active.
    +     */
    +    public final String getSelectedText()
    +    {
    +        if (selectionStart == selectionEnd)
    +        {
    +            return null;
    +        }
    +
    +        if (rectSelect)
    +        {
    +            // Return each row of the selection on a new line
    +
    +            Element map = document.getDefaultRootElement();
    +
    +            int start = selectionStart - map.getElement(selectionStartLine)
    +                .getStartOffset();
    +            int end = selectionEnd - map.getElement(selectionEndLine)
    +                .getStartOffset();
    +
    +            // Certain rectangles satisfy this condition...
    +            if (end < start)
                 {
    -               Element lineElement = map.getElement(i);
    -               int lineStart = lineElement.getStartOffset();
    -               int lineEnd = lineElement.getEndOffset() - 1;
    -               int rectStart = Math.min(lineEnd,lineStart + start);
    -            
    -               document.remove(rectStart,Math.min(lineEnd - rectStart,
    -                  end - start));
    -            
    -               if(selectedText == null)
    -                  continue;
    -            
    -               currNewline = selectedText.indexOf('\n',lastNewline);
    -               if(currNewline == -1)
    -                  currNewline = selectedText.length();
    -            
    -               document.insertString(rectStart,selectedText
    -                  .substring(lastNewline,currNewline),null);
    -            
    -               lastNewline = Math.min(selectedText.length(),
    -                  currNewline + 1);
    +                int tmp = end;
    +                end = start;
    +                start = tmp;
                 }
    -         
    -            if(selectedText != null &&
    -            currNewline != selectedText.length())
    +
    +            StringBuffer buf = new StringBuffer();
    +            Segment seg = new Segment();
    +
    +            for (int i = selectionStartLine; i <= selectionEndLine; i++)
                 {
    -               int offset = map.getElement(selectionEndLine)
    -                  .getEndOffset() - 1;
    -               document.insertString(offset,"\n",null);
    -               document.insertString(offset + 1,selectedText
    -                  .substring(currNewline + 1),null);
    +                Element lineElement = map.getElement(i);
    +                int lineStart = lineElement.getStartOffset();
    +                int lineEnd = lineElement.getEndOffset() - 1;
    +                int lineLen = lineEnd - lineStart;
    +
    +                lineStart = Math.min(lineStart + start, lineEnd);
    +                lineLen = Math.min(end - start, lineEnd - lineStart);
    +
    +                getText(lineStart, lineLen, seg);
    +                buf.append(seg.array, seg.offset, seg.count);
    +
    +                if (i != selectionEndLine)
    +                {
    +                    buf.append('\n');
    +                }
                 }
    -         }
    -         else
    -         {
    -            document.remove(selectionStart,
    -               selectionEnd - selectionStart);
    -            if(selectedText != null)
    +
    +            return buf.toString();
    +        }
    +        else
    +        {
    +            return getText(selectionStart,
    +                selectionEnd - selectionStart);
    +        }
    +    }
    +
    +    /**
    +     * Replaces the selection with the specified text.
    +     *
    +     * @param selectedText The replacement text for the selection
    +     */
    +    public void setSelectedText(String selectedText)
    +    {
    +        if (!editable)
    +        {
    +            throw new InternalError("Text component"
    +                + " read only");
    +        }
    +
    +        document.beginCompoundEdit();
    +
    +        try
    +        {
    +            if (rectSelect)
                 {
    -               document.insertString(selectionStart,
    -                  selectedText,null);
    +                Element map = document.getDefaultRootElement();
    +
    +                int start = selectionStart - map.getElement(selectionStartLine)
    +                    .getStartOffset();
    +                int end = selectionEnd - map.getElement(selectionEndLine)
    +                    .getStartOffset();
    +
    +                // Certain rectangles satisfy this condition...
    +                if (end < start)
    +                {
    +                    int tmp = end;
    +                    end = start;
    +                    start = tmp;
    +                }
    +
    +                int lastNewline = 0;
    +                int currNewline = 0;
    +
    +                for (int i = selectionStartLine; i <= selectionEndLine; i++)
    +                {
    +                    Element lineElement = map.getElement(i);
    +                    int lineStart = lineElement.getStartOffset();
    +                    int lineEnd = lineElement.getEndOffset() - 1;
    +                    int rectStart = Math.min(lineEnd, lineStart + start);
    +
    +                    document.remove(rectStart, Math.min(lineEnd - rectStart,
    +                        end - start));
    +
    +                    if (selectedText == null)
    +                    {
    +                        continue;
    +                    }
    +
    +                    currNewline = selectedText.indexOf('\n', lastNewline);
    +                    if (currNewline == -1)
    +                    {
    +                        currNewline = selectedText.length();
    +                    }
    +
    +                    document.insertString(rectStart, selectedText
    +                        .substring(lastNewline, currNewline), null);
    +
    +                    lastNewline = Math.min(selectedText.length(),
    +                        currNewline + 1);
    +                }
    +
    +                if (selectedText != null &&
    +                    currNewline != selectedText.length())
    +                {
    +                    int offset = map.getElement(selectionEndLine)
    +                        .getEndOffset() - 1;
    +                    document.insertString(offset, "\n", null);
    +                    document.insertString(offset + 1, selectedText
    +                        .substring(currNewline + 1), null);
    +                }
                 }
    -         }
    -      }
    -      catch(BadLocationException bl)
    -      {
    -         bl.printStackTrace();
    -         throw new InternalError("Cannot replace"
    -               + " selection");
    -      }
    -      // No matter what happends... stops us from leaving document
    -      // in a bad state
    -      finally
    -      {
    -         document.endCompoundEdit();
    -      }
    -   
    -      setCaretPosition(selectionEnd);
    -   }
    +            else
    +            {
    +                document.remove(selectionStart,
    +                    selectionEnd - selectionStart);
    +                if (selectedText != null)
    +                {
    +                    document.insertString(selectionStart,
    +                        selectedText, null);
    +                }
    +            }
    +        }
    +        catch (BadLocationException bl)
    +        {
    +            bl.printStackTrace();
    +            throw new InternalError("Cannot replace"
    +                + " selection");
    +        }
    +        // No matter what happends... stops us from leaving document
    +        // in a bad state
    +        finally
    +        {
    +            document.endCompoundEdit();
    +        }
     
    -/**
    - * Returns true if this text area is editable, false otherwise.
    - */
    -   public final boolean isEditable()
    -   {
    -      return editable;
    -   }
    +        setCaretPosition(selectionEnd);
    +    }
     
    -/**
    - * Sets if this component is editable.
    - * @param editable True if this text area should be editable,
    - * false otherwise
    - */
    -   public final void setEditable(boolean editable)
    -   {
    -      this.editable = editable;
    -   }
    +    /**
    +     * Returns true if this text area is editable, false otherwise.
    +     */
    +    public final boolean isEditable()
    +    {
    +        return editable;
    +    }
     
    -/**
    - * Returns the right click popup menu.
    - */
    -   public final JPopupMenu getRightClickPopup()
    -   {
    -      return popup;
    -   }
    +    /**
    +     * Sets if this component is editable.
    +     *
    +     * @param editable True if this text area should be editable, false otherwise
    +     */
    +    public final void setEditable(boolean editable)
    +    {
    +        this.editable = editable;
    +    }
     
    -/**
    - * Sets the right click popup menu.
    - * @param popup The popup
    - */
    -   public final void setRightClickPopup(JPopupMenu popup)
    -   {
    -      this.popup = popup;
    -   }
    +    /**
    +     * Returns the right click popup menu.
    +     */
    +    public final JPopupMenu getRightClickPopup()
    +    {
    +        return popup;
    +    }
     
    -/**
    - * Returns the `magic' caret position. This can be used to preserve
    - * the column position when moving up and down lines.
    - */
    -   public final int getMagicCaretPosition()
    -   {
    -      return magicCaret;
    -   }
    +    /**
    +     * Sets the right click popup menu.
    +     *
    +     * @param popup The popup
    +     */
    +    public final void setRightClickPopup(JPopupMenu popup)
    +    {
    +        this.popup = popup;
    +    }
     
    -/**
    - * Sets the `magic' caret position. This can be used to preserve
    - * the column position when moving up and down lines.
    - * @param magicCaret The magic caret position
    - */
    -   public final void setMagicCaretPosition(int magicCaret)
    -   {
    -      this.magicCaret = magicCaret;
    -   }
    +    /**
    +     * Returns the `magic' caret position. This can be used to preserve the column position when moving up and down
    +     * lines.
    +     */
    +    public final int getMagicCaretPosition()
    +    {
    +        return magicCaret;
    +    }
     
    -/**
    - * Similar to setSelectedText(), but overstrikes the
    - * appropriate number of characters if overwrite mode is enabled.
    - * @param str The string
    - * @see #setSelectedText(String)
    - * @see #isOverwriteEnabled()
    - */
    -   public void overwriteSetSelectedText(String str)
    -   {
    -   // Don't overstrike if there is a selection
    -      if(!overwrite || selectionStart != selectionEnd)
    -      {
    -         setSelectedText(str);
    -         applySyntaxSensitiveHelp();
    -         return;
    -      }
    -   
    -   // Don't overstrike if we're on the end of
    -   // the line
    -      int caret = getCaretPosition();
    -      int caretLineEnd = getLineEndOffset(getCaretLine());
    -      if(caretLineEnd - caret <= str.length())
    -      {
    -         setSelectedText(str);
    -         applySyntaxSensitiveHelp();
    -         return;
    -      }
    -   
    -      document.beginCompoundEdit();
    -   
    -      try
    -      {
    -         document.remove(caret,str.length());
    -         document.insertString(caret,str,null);
    -      }
    -      catch(BadLocationException bl)
    -      {
    -         bl.printStackTrace();
    -      }
    -      finally
    -      {
    -         document.endCompoundEdit();
    -      }
    -      applySyntaxSensitiveHelp();
    -   	
    -   }
    +    /**
    +     * Sets the `magic' caret position. This can be used to preserve the column position when moving up and down lines.
    +     *
    +     * @param magicCaret The magic caret position
    +     */
    +    public final void setMagicCaretPosition(int magicCaret)
    +    {
    +        this.magicCaret = magicCaret;
    +    }
     
    -   JPopupMenu popupMenu;
    +    /**
    +     * Similar to setSelectedText(), but overstrikes the appropriate number of characters if overwrite mode
    +     * is enabled.
    +     *
    +     * @param str The string
    +     * @see #setSelectedText(String)
    +     * @see #isOverwriteEnabled()
    +     */
    +    public void overwriteSetSelectedText(String str)
    +    {
    +        // Don't overstrike if there is a selection
    +        if (!overwrite || selectionStart != selectionEnd)
    +        {
    +            setSelectedText(str);
    +            applySyntaxSensitiveHelp();
    +            return;
    +        }
     
    +        // Don't overstrike if we're on the end of
    +        // the line
    +        int caret = getCaretPosition();
    +        int caretLineEnd = getLineEndOffset(getCaretLine());
    +        if (caretLineEnd - caret <= str.length())
    +        {
    +            setSelectedText(str);
    +            applySyntaxSensitiveHelp();
    +            return;
    +        }
     
    +        document.beginCompoundEdit();
     
    -/**
    - * Returns true if overwrite mode is enabled, false otherwise.
    - */
    -   public final boolean isOverwriteEnabled()
    -   {
    -      return overwrite;
    -   }
    +        try
    +        {
    +            document.remove(caret, str.length());
    +            document.insertString(caret, str, null);
    +        }
    +        catch (BadLocationException bl)
    +        {
    +            bl.printStackTrace();
    +        }
    +        finally
    +        {
    +            document.endCompoundEdit();
    +        }
    +        applySyntaxSensitiveHelp();
     
    -/**
    - * Sets if overwrite mode should be enabled.
    - * @param overwrite True if overwrite mode should be enabled,
    - * false otherwise.
    - */
    -   public final void setOverwriteEnabled(boolean overwrite)
    -   {
    -      this.overwrite = overwrite;
    -      painter.invalidateSelectedLines();
    -   }
    +    }
     
    -/**
    - * Returns true if the selection is rectangular, false otherwise.
    - */
    -   public final boolean isSelectionRectangular()
    -   {
    -      return rectSelect;
    -   }
    +    /**
    +     * Returns true if overwrite mode is enabled, false otherwise.
    +     */
    +    public final boolean isOverwriteEnabled()
    +    {
    +        return overwrite;
    +    }
     
    -/**
    - * Sets if the selection should be rectangular.
    - * @param overwrite True if the selection should be rectangular,
    - * false otherwise.
    - */
    -   public final void setSelectionRectangular(boolean rectSelect)
    -   {
    -      this.rectSelect = rectSelect;
    -      painter.invalidateSelectedLines();
    -   }
    +    /**
    +     * Sets if overwrite mode should be enabled.
    +     *
    +     * @param overwrite True if overwrite mode should be enabled, false otherwise.
    +     */
    +    public final void setOverwriteEnabled(boolean overwrite)
    +    {
    +        this.overwrite = overwrite;
    +        painter.invalidateSelectedLines();
    +    }
     
    -/**
    - * Returns the position of the highlighted bracket (the bracket
    - * matching the one before the caret)
    - */
    -   public final int getBracketPosition()
    -   {
    -      return bracketPosition;
    -   }
    +    /**
    +     * Returns true if the selection is rectangular, false otherwise.
    +     */
    +    public final boolean isSelectionRectangular()
    +    {
    +        return rectSelect;
    +    }
     
    -/**
    - * Returns the line of the highlighted bracket (the bracket
    - * matching the one before the caret)
    - */
    -   public final int getBracketLine()
    -   {
    -      return bracketLine;
    -   }
    +    /**
    +     * Sets if the selection should be rectangular.
    +     *
    +     * @param overwrite True if the selection should be rectangular, false otherwise.
    +     */
    +    public final void setSelectionRectangular(boolean rectSelect)
    +    {
    +        this.rectSelect = rectSelect;
    +        painter.invalidateSelectedLines();
    +    }
     
    -/**
    - * Adds a caret change listener to this text area.
    - * @param listener The listener
    - */
    -   public final void addCaretListener(CaretListener listener)
    -   {
    -      listenerList.add(CaretListener.class,listener);
    -   }
    +    /**
    +     * Returns the position of the highlighted bracket (the bracket matching the one before the caret)
    +     */
    +    public final int getBracketPosition()
    +    {
    +        return bracketPosition;
    +    }
     
    -/**
    - * Removes a caret change listener from this text area.
    - * @param listener The listener
    - */
    -   public final void removeCaretListener(CaretListener listener)
    -   {
    -      listenerList.remove(CaretListener.class,listener);
    -   }
    +    /**
    +     * Returns the line of the highlighted bracket (the bracket matching the one before the caret)
    +     */
    +    public final int getBracketLine()
    +    {
    +        return bracketLine;
    +    }
     
    -/**
    - * Deletes the selected text from the text area and places it
    - * into the clipboard.
    - */
    -   public void cut()
    -   {
    -      if(editable)
    -      {
    -         copy();
    -         setSelectedText("");
    -      }
    -   }
    +    /**
    +     * Adds a caret change listener to this text area.
    +     *
    +     * @param listener The listener
    +     */
    +    public final void addCaretListener(CaretListener listener)
    +    {
    +        listenerList.add(CaretListener.class, listener);
    +    }
     
    -/**
    - * Places the selected text into the clipboard.
    - */
    -   public void copy()
    -   {
    -      if(selectionStart != selectionEnd)
    -      {
    -         Clipboard clipboard = getToolkit().getSystemClipboard();
    -      
    -         String selection = getSelectedText();
    -      
    -         int repeatCount = inputHandler.getRepeatCount();
    -         StringBuffer buf = new StringBuffer();
    -         for(int i = 0; i < repeatCount; i++)
    -            buf.append(selection);
    -      
    -         clipboard.setContents(new StringSelection(buf.toString()),null);
    -      }
    -   }
    +    /**
    +     * Removes a caret change listener from this text area.
    +     *
    +     * @param listener The listener
    +     */
    +    public final void removeCaretListener(CaretListener listener)
    +    {
    +        listenerList.remove(CaretListener.class, listener);
    +    }
    +
    +    /**
    +     * Deletes the selected text from the text area and places it into the clipboard.
    +     */
    +    public void cut()
    +    {
    +        if (editable)
    +        {
    +            copy();
    +            setSelectedText("");
    +        }
    +    }
    +
    +    /**
    +     * Places the selected text into the clipboard.
    +     */
    +    public void copy()
    +    {
    +        if (selectionStart != selectionEnd)
    +        {
    +            Clipboard clipboard = getToolkit().getSystemClipboard();
    +
    +            String selection = getSelectedText();
     
    -/**
    - * Inserts the clipboard contents into the text.
    - */
    -   public void paste()
    -   {
    -      if(editable)
    -      {
    -         Clipboard clipboard = getToolkit().getSystemClipboard();
    -         try
    -         {
    -         // The MacOS MRJ doesn't convert \r to \n,
    -         // so do it here
    -            String selection = ((String)clipboard
    -               .getContents(this).getTransferData(
    -               DataFlavor.stringFlavor))
    -               .replace('\r','\n');
    -         
                 int repeatCount = inputHandler.getRepeatCount();
                 StringBuffer buf = new StringBuffer();
    -            for(int i = 0; i < repeatCount; i++)
    -               buf.append(selection);
    -            selection = buf.toString();
    -            setSelectedText(selection);
    -         }
    -         catch(Exception e)
    -         {
    -            getToolkit().beep();
    -            System.err.println("Clipboard does not"
    -                  + " contain a string");
    -         }
    -      }
    -   }
    -
    -/**
    - * Called by the AWT when this component is removed from it's parent.
    - * This stops clears the currently focused component.
    - */
    -   public void removeNotify()
    -   {
    -      super.removeNotify();
    -      if(focusedComponent == this)
    -         focusedComponent = null;
    -   }
    -
    -/**
    - * Forwards key events directly to the input handler.
    - * This is slightly faster than using a KeyListener
    - * because some Swing overhead is avoided.
    - */
    -   public void processKeyEvent(KeyEvent evt)
    -   { 
    -      if(inputHandler == null)
    -         return;
    -      switch(evt.getID())
    -      {
    -         case KeyEvent.KEY_TYPED:
    -            inputHandler.keyTyped(evt);
    -            break;
    -         case KeyEvent.KEY_PRESSED:
    -            if (!checkPopupCompletion(evt)) {
    -               inputHandler.keyPressed(evt);
    -            }
    -            checkPopupMenu(evt);
    -            break;
    -         case KeyEvent.KEY_RELEASED:
    -            inputHandler.keyReleased(evt);
    -            break;
    -      }
    -   }
    -
    -// protected members
    -   protected static String CENTER = "center";
    -   protected static String RIGHT = "right";
    -   protected static String BOTTOM = "bottom";
    -
    -   protected static JEditTextArea focusedComponent;
    -   protected static Timer caretTimer;
    -
    -   protected TextAreaPainter painter;
    -
    -   protected JPopupMenu popup;
    -
    -   protected EventListenerList listenerList;
    -   protected MutableCaretEvent caretEvent;
    -
    -   protected boolean caretBlinks;
    -   protected boolean caretVisible;
    -   protected boolean blink;
    -
    -   protected boolean editable;
    -
    -   protected int caretBlinkRate;
    -   protected int firstLine;
    -   protected int visibleLines;
    -   protected int electricScroll;
    -
    -   protected int horizontalOffset;
    -
    -   protected JScrollBar vertical;
    -   protected JScrollBar horizontal;
    -   protected boolean scrollBarsInitialized;
    -
    -   protected InputHandler inputHandler;
    -   protected SyntaxDocument document;
    -   protected DocumentHandler documentHandler;
    -
    -   protected Segment lineSegment;
    -
    -   protected int selectionStart;
    -   protected int selectionStartLine;
    -   protected int selectionEnd;
    -   protected int selectionEndLine;
    -   protected boolean biasLeft;
    -
    -   protected int bracketPosition;
    -   protected int bracketLine;
    -
    -   protected int magicCaret;
    -   protected boolean overwrite;
    -   protected boolean rectSelect;
    -   // "unredoing" is mode used by DocumentHandler's insertUpdate() and removeUpdate()
    -   // to pleasingly select the text and location of the undo.   DPS 3-May-2010
    -   protected boolean unredoing = false; 
    -
    -
    -   protected void fireCaretEvent()
    -   {
    -      Object[] listeners = listenerList.getListenerList();
    -      for(int i = listeners.length - 2; i >= 0; i--)
    -      {
    -         if(listeners[i] == CaretListener.class)
    -         {
    -            ((CaretListener)listeners[i+1]).caretUpdate(caretEvent);
    -         }
    -      }
    -   }
    -
    -   protected void updateBracketHighlight(int newCaretPosition)
    -   {
    -      if(newCaretPosition == 0)
    -      {
    -         bracketPosition = bracketLine = -1;
    -         return;
    -      }
    -   
    -      try
    -      {
    -         int offset = TextUtilities.findMatchingBracket(
    -            document,newCaretPosition - 1);
    -         if(offset != -1)
    -         {
    -            bracketLine = getLineOfOffset(offset);
    -            bracketPosition = offset - getLineStartOffset(bracketLine);
    -            return;
    -         }
    -      }
    -      catch(BadLocationException bl)
    -      {
    -         bl.printStackTrace();
    -      }
    -   
    -      bracketLine = bracketPosition = -1;
    -   }
    -
    -   protected void documentChanged(DocumentEvent evt)
    -   {
    -      DocumentEvent.ElementChange ch = evt.getChange(
    -         document.getDefaultRootElement());
    -   
    -      int count;
    -      if(ch == null)
    -         count = 0;
    -      else
    -         count = ch.getChildrenAdded().length -
    -            ch.getChildrenRemoved().length;
    -   
    -      int line = getLineOfOffset(evt.getOffset());
    -      if(count == 0)
    -      {
    -         painter.invalidateLine(line);
    -      }
    -      // do magic stuff
    -      else if(line < firstLine)
    -      {
    -         setFirstLine(firstLine + count);
    -      }
    -      // end of magic stuff
    -      else
    -      {
    -         painter.invalidateLineRange(line,firstLine + visibleLines);
    -         updateScrollBars();
    -      }
    -   }
    -
    -   class ScrollLayout implements LayoutManager
    -   {
    -      public void addLayoutComponent(String name, Component comp)
    -      {
    -         if(name.equals(CENTER))
    -            center = comp;
    -         else if(name.equals(RIGHT))
    -            right = comp;
    -         else if(name.equals(BOTTOM))
    -            bottom = comp;
    -         else if(name.equals(LEFT_OF_SCROLLBAR))
    -            leftOfScrollBar.addElement(comp);
    -      }
    -   
    -      public void removeLayoutComponent(Component comp)
    -      {
    -         if(center == comp)
    -            center = null;
    -         if(right == comp)
    -            right = null;
    -         if(bottom == comp)
    -            bottom = null;
    -         else
    -            leftOfScrollBar.removeElement(comp);
    -      }
    -   
    -      public Dimension preferredLayoutSize(Container parent)
    -      {
    -         Dimension dim = new Dimension();
    -         Insets insets = getInsets();
    -         dim.width = insets.left + insets.right;
    -         dim.height = insets.top + insets.bottom;
    -      
    -         Dimension centerPref = center.getPreferredSize();
    -         dim.width += centerPref.width;
    -         dim.height += centerPref.height;
    -         Dimension rightPref = right.getPreferredSize();
    -         dim.width += rightPref.width;
    -         Dimension bottomPref = bottom.getPreferredSize();
    -         dim.height += bottomPref.height;
    -      
    -         return dim;
    -      }
    -   
    -      public Dimension minimumLayoutSize(Container parent)
    -      {
    -         Dimension dim = new Dimension();
    -         Insets insets = getInsets();
    -         dim.width = insets.left + insets.right;
    -         dim.height = insets.top + insets.bottom;
    -      
    -         Dimension centerPref = center.getMinimumSize();
    -         dim.width += centerPref.width; 
    -         dim.height += centerPref.height;
    -         Dimension rightPref = right.getMinimumSize();
    -         dim.width += rightPref.width;
    -         Dimension bottomPref = bottom.getMinimumSize();
    -         dim.height += bottomPref.height;
    -      
    -         return dim;
    -      }
    -   
    -      public void layoutContainer(Container parent)
    -      {
    -         Dimension size = parent.getSize();
    -         Insets insets = parent.getInsets();
    -         int itop = insets.top;
    -         int ileft = insets.left;
    -         int ibottom = insets.bottom;
    -         int iright = insets.right;
    -      
    -         int rightWidth = right.getPreferredSize().width;
    -         int bottomHeight = bottom.getPreferredSize().height;
    -         int centerWidth = size.width - rightWidth - ileft - iright;
    -         int centerHeight = size.height - bottomHeight - itop - ibottom;
    -      
    -         center.setBounds(
    -            ileft,
    -            itop,
    -            centerWidth,
    -            centerHeight);
    -      
    -         right.setBounds(
    -            ileft + centerWidth,
    -            itop,
    -            rightWidth,
    -            centerHeight);
    -      
    -      // Lay out all status components, in order
    -         Enumeration status = leftOfScrollBar.elements();
    -         while(status.hasMoreElements())
    -         {
    -            Component comp = (Component)status.nextElement();
    -            Dimension dim = comp.getPreferredSize();
    -            comp.setBounds(ileft,
    -               itop + centerHeight,
    -               dim.width,
    -               bottomHeight);
    -            ileft += dim.width;
    -         }
    -      
    -         bottom.setBounds(
    -            ileft,
    -            itop + centerHeight,
    -            size.width - rightWidth - ileft - iright,
    -            bottomHeight);
    -      }
    -   
    -   // private members
    -      private Component center;
    -      private Component right;
    -      private Component bottom;
    -      private Vector leftOfScrollBar = new Vector();
    -   }
    -
    -   static class CaretBlinker implements ActionListener
    -   {
    -      public void actionPerformed(ActionEvent evt)
    -      {
    -         if(focusedComponent != null
    -         && focusedComponent.hasFocus())
    -            focusedComponent.blinkCaret();
    -      }
    -   }
    -
    -   class MutableCaretEvent extends CaretEvent
    -   {
    -      MutableCaretEvent()
    -      {
    -         super(JEditTextArea.this);
    -      }
    -   
    -      public int getDot()
    -      {
    -         return getCaretPosition();
    -      }
    -   
    -      public int getMark()
    -      {
    -         return getMarkPosition();
    -      }
    -   }
    -
    -   class AdjustHandler implements AdjustmentListener
    -   {
    -      public void adjustmentValueChanged(final AdjustmentEvent evt)
    -      {
    -         if(!scrollBarsInitialized)
    -            return;
    -      
    -      // If this is not done, mousePressed events accumulate
    -      // and the result is that scrolling doesn't stop after
    -      // the mouse is released
    -         SwingUtilities.invokeLater(
    -               new Runnable() {
    -                  public void run()
    -                  {
    -                     if(evt.getAdjustable() == vertical)
    -                        setFirstLine(vertical.getValue());
    -                     else
    -                        setHorizontalOffset(-horizontal.getValue());
    -                  }
    -               });
    -      }
    -   }
    -
    -   class ComponentHandler extends ComponentAdapter
    -   {
    -      public void componentResized(ComponentEvent evt)
    -      {  
    -         recalculateVisibleLines();
    -         scrollBarsInitialized = true;
    -      }
    -   }
    -
    -
    -
    -   class DocumentHandler implements DocumentListener
    -   {
    -      public void insertUpdate(DocumentEvent evt)
    -      {
    -         documentChanged(evt);
    -      
    -         int offset = evt.getOffset();
    -         int length = evt.getLength();
    -      
    -         // If event fired because of undo or redo, select inserted text. DPS 3-May-2010
    -         if (unredoing) { 
    -            select(offset,offset+length); 
    -            return;
    -         }
    -      
    -         int newStart;
    -         int newEnd;
    -      
    -         if(selectionStart > offset || (selectionStart 
    -         == selectionEnd && selectionStart == offset))
    -            newStart = selectionStart + length;
    -         else
    -            newStart = selectionStart;
    -      
    -         if(selectionEnd >= offset)
    -            newEnd = selectionEnd + length;
    -         else
    -            newEnd = selectionEnd;
    -         select(newStart,newEnd);
    -      }
    -   
    -      public void removeUpdate(DocumentEvent evt)
    -      {
    -         documentChanged(evt);
    -      
    -         int offset = evt.getOffset();
    -         int length = evt.getLength();
    -      
    -         // If event fired because of undo or redo, move caret to position of removal. DPS 3-May-2010
    -         if (unredoing) {
    -            select(offset,offset);
    -            setCaretPosition(offset);
    -            return;
    -         }
    -      
    -         int newStart;
    -         int newEnd;
    -      
    -         if(selectionStart > offset)
    -         {
    -            if(selectionStart > offset + length)
    -               newStart = selectionStart - length;
    -            else
    -               newStart = offset;
    -         }
    -         else
    -            newStart = selectionStart;
    -      
    -         if(selectionEnd > offset)
    -         {
    -            if(selectionEnd > offset + length)
    -               newEnd = selectionEnd - length;
    -            else
    -               newEnd = offset;
    -         }
    -         else
    -            newEnd = selectionEnd;
    -         select(newStart,newEnd);
    -      }
    -   
    -      public void changedUpdate(DocumentEvent evt)
    -      {
    -      }
    -   }
    -
    -   class DragHandler implements MouseMotionListener
    -   {
    -      public void mouseDragged(MouseEvent evt)
    -      {
    -         if(popup != null && popup.isVisible())
    -            return;
    -      
    -         setSelectionRectangular((evt.getModifiers()
    -            & InputEvent.CTRL_MASK) != 0);
    -         select(getMarkPosition(),xyToOffset(evt.getX(),evt.getY()));
    -      }
    -   
    -      public void mouseMoved(MouseEvent evt) {}
    -   }
    -
    -   class FocusHandler implements FocusListener
    -   {
    -      public void focusGained(FocusEvent evt)
    -      {
    -         setCaretVisible(true);
    -         focusedComponent = JEditTextArea.this;
    -      }
    -   
    -      public void focusLost(FocusEvent evt)
    -      {
    -         setCaretVisible(false);
    -         focusedComponent = null;
    -      }
    -   }
    -
    -    // Added by DPS, 5-5-2010. Allows use of mouse wheel to scroll.
    -	 // Scrolling as fast as I could, the most notches I could get in
    -	 // one MouseWheelEvent was 3.  Normally it will be 1.  Nonetheless,
    -	 // this will scroll up to the number in the event, subject to
    -	 // scrollability of the text in its viewport. 
    -   class MouseWheelHandler implements MouseWheelListener
    -   {
    -      public void mouseWheelMoved(MouseWheelEvent e) { 
    -         int maxMotion = Math.abs(e.getWheelRotation()) * LINES_PER_MOUSE_WHEEL_NOTCH;
    -         if (e.getWheelRotation() < 0) { 
    -            setFirstLine( getFirstLine() - Math.min(maxMotion, getFirstLine()) ); 
    -         } 
    -         else {
    -            setFirstLine( getFirstLine() + (Math.min(maxMotion, Math.max(0,getLineCount()-(getFirstLine()+visibleLines)))));
    -         } 
    -      }
    -   }
    -
    -
    -   class MouseHandler extends MouseAdapter
    -   {
    -      public void mousePressed(MouseEvent evt)
    -      {
    -         requestFocus();
    -      
    -      // Focus events not fired sometimes?
    -         setCaretVisible(true);
    -         focusedComponent = JEditTextArea.this;
    -      
    -         if((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0
    -         && popup != null)
    -         {
    -            popup.show(painter,evt.getX(),evt.getY());
    -            return;
    -         }
    -      
    -         int line = yToLine(evt.getY());
    -         int offset = xToOffset(line,evt.getX());
    -         int dot = getLineStartOffset(line) + offset;
    -      
    -         switch(evt.getClickCount())
    -         {
    -            case 1:
    -               doSingleClick(evt,line,offset,dot);
    -               break;
    -            case 2:
    -            // It uses the bracket matching stuff, so
    -            // it can throw a BLE
    -               try
    -               {
    -                  doDoubleClick(evt,line,offset,dot);
    -               }
    -               catch(BadLocationException bl)
    -               {
    -                  bl.printStackTrace();
    -               }
    -               break;
    -            case 3:
    -               doTripleClick(evt,line,offset,dot);
    -               break;
    -         }
    -      }
    -   
    -      private void doSingleClick(MouseEvent evt, int line, 
    -       int offset, int dot)
    -      {
    -         if((evt.getModifiers() & InputEvent.SHIFT_MASK) != 0)
    -         {
    -            rectSelect = (evt.getModifiers() & InputEvent.CTRL_MASK) != 0;
    -            select(getMarkPosition(),dot);
    -         }
    -         else
    -            setCaretPosition(dot);
    -      }
    -   
    -      private void doDoubleClick(MouseEvent evt, int line,
    -       int offset, int dot) throws BadLocationException
    -      {
    -      // Ignore empty lines
    -         if(getLineLength(line) == 0)
    -            return;
    -      
    -         try
    -         {
    -            int bracket = TextUtilities.findMatchingBracket(
    -               document,Math.max(0,dot - 1));
    -            if(bracket != -1)
    +            for (int i = 0; i < repeatCount; i++)
                 {
    -               int mark = getMarkPosition();
    -            // Hack
    -               if(bracket > mark)
    -               {
    -                  bracket++;
    -                  mark--;
    -               }
    -               select(mark,bracket);
    -               return;
    +                buf.append(selection);
                 }
    -         }
    -         catch(BadLocationException bl)
    -         {
    +
    +            clipboard.setContents(new StringSelection(buf.toString()), null);
    +        }
    +    }
    +
    +    /**
    +     * Inserts the clipboard contents into the text.
    +     */
    +    public void paste()
    +    {
    +        if (editable)
    +        {
    +            Clipboard clipboard = getToolkit().getSystemClipboard();
    +            try
    +            {
    +                // The MacOS MRJ doesn't convert \r to \n,
    +                // so do it here
    +                String selection = ((String) clipboard
    +                    .getContents(this).getTransferData(
    +                        DataFlavor.stringFlavor))
    +                    .replace('\r', '\n');
    +
    +                int repeatCount = inputHandler.getRepeatCount();
    +                StringBuffer buf = new StringBuffer();
    +                for (int i = 0; i < repeatCount; i++)
    +                {
    +                    buf.append(selection);
    +                }
    +                selection = buf.toString();
    +                setSelectedText(selection);
    +            }
    +            catch (Exception e)
    +            {
    +                getToolkit().beep();
    +                System.err.println("Clipboard does not"
    +                    + " contain a string");
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Called by the AWT when this component is removed from it's parent. This stops clears the currently focused
    +     * component.
    +     */
    +    public void removeNotify()
    +    {
    +        super.removeNotify();
    +        if (focusedComponent == this)
    +        {
    +            focusedComponent = null;
    +        }
    +    }
    +
    +    /**
    +     * Forwards key events directly to the input handler. This is slightly faster than using a KeyListener because some
    +     * Swing overhead is avoided.
    +     */
    +    public void processKeyEvent(KeyEvent evt)
    +    {
    +        if (inputHandler == null)
    +        {
    +            return;
    +        }
    +        switch (evt.getID())
    +        {
    +            case KeyEvent.KEY_TYPED:
    +                inputHandler.keyTyped(evt);
    +                break;
    +            case KeyEvent.KEY_PRESSED:
    +                if (!checkPopupCompletion(evt))
    +                {
    +                    inputHandler.keyPressed(evt);
    +                }
    +                checkPopupMenu(evt);
    +                break;
    +            case KeyEvent.KEY_RELEASED:
    +                inputHandler.keyReleased(evt);
    +                break;
    +        }
    +    }
    +
    +    protected void fireCaretEvent()
    +    {
    +        Object[] listeners = listenerList.getListenerList();
    +        for (int i = listeners.length - 2; i >= 0; i--)
    +        {
    +            if (listeners[i] == CaretListener.class)
    +            {
    +                ((CaretListener) listeners[i + 1]).caretUpdate(caretEvent);
    +            }
    +        }
    +    }
    +
    +    protected void updateBracketHighlight(int newCaretPosition)
    +    {
    +        if (newCaretPosition == 0)
    +        {
    +            bracketPosition = bracketLine = -1;
    +            return;
    +        }
    +
    +        try
    +        {
    +            int offset = TextUtilities.findMatchingBracket(
    +                document, newCaretPosition - 1);
    +            if (offset != -1)
    +            {
    +                bracketLine = getLineOfOffset(offset);
    +                bracketPosition = offset - getLineStartOffset(bracketLine);
    +                return;
    +            }
    +        }
    +        catch (BadLocationException bl)
    +        {
                 bl.printStackTrace();
    -         }
    -      
    -      // Ok, it's not a bracket... select the word
    -         String lineText = getLineText(line);
    -         char ch = lineText.charAt(Math.max(0,offset - 1));
    -      
    -         String noWordSep = (String)document.getProperty("noWordSep");
    -         if(noWordSep == null)
    -            noWordSep = "";
    -      
    -      // If the user clicked on a non-letter char,
    -      // we select the surrounding non-letters
    -         boolean selectNoLetter = (!Character
    -            .isLetterOrDigit(ch)
    -            && noWordSep.indexOf(ch) == -1);
    -      
    -         int wordStart = 0;
    -      
    -         for(int i = offset - 1; i >= 0; i--)
    -         {
    -            ch = lineText.charAt(i);
    -            if(selectNoLetter ^ (!Character
    -            .isLetterOrDigit(ch) &&
    -            noWordSep.indexOf(ch) == -1))
    -            {
    -               wordStart = i + 1;
    -               break;
    -            }
    -         }
    -      
    -         int wordEnd = lineText.length();
    -         for(int i = offset; i < lineText.length(); i++)
    -         {
    -            ch = lineText.charAt(i);
    -            if(selectNoLetter ^ (!Character
    -            .isLetterOrDigit(ch) &&
    -            noWordSep.indexOf(ch) == -1))
    -            {
    -               wordEnd = i;
    -               break;
    -            }
    -         }
    -      
    -         int lineStart = getLineStartOffset(line);
    -         select(lineStart + wordStart,lineStart + wordEnd);
    -      }
    -   
    -      private void doTripleClick(MouseEvent evt, int line,
    -       int offset, int dot)
    -      {
    -         select(getLineStartOffset(line),getLineEndOffset(line)-1);
    -      }
    -   }
    +        }
     
    -   class CaretUndo extends AbstractUndoableEdit
    -   {
    -      private int start;
    -      private int end;
    -   
    -      CaretUndo(int start, int end)
    -      {
    -         this.start = start;
    -         this.end = end;
    -      }
    -   
    -      public boolean isSignificant()
    -      {
    -         return false;
    -      }
    -   
    -      public String getPresentationName()
    -      {
    -         return "caret move";
    -      }
    -   
    -      public void undo() throws CannotUndoException
    -      {
    -         super.undo();
    -      
    -         select(start,end);
    -      }
    -   
    -      public void redo() throws CannotRedoException
    -      {
    -         super.redo();
    -      
    -         select(start,end);
    -      }
    -   
    -      public boolean addEdit(UndoableEdit edit)
    -      {
    -         if(edit instanceof CaretUndo)
    -         {
    -            CaretUndo cedit = (CaretUndo)edit;
    -            start = cedit.start;
    -            end = cedit.end;
    -            cedit.die();
    -         
    -            return true;
    -         }
    -         else
    -            return false;
    -      }
    -   }
    +        bracketLine = bracketPosition = -1;
    +    }
     
    +    protected void documentChanged(DocumentEvent evt)
    +    {
    +        DocumentEvent.ElementChange ch = evt.getChange(
    +            document.getDefaultRootElement());
     
    -   /**
    -	 *  Return any relevant tool tip text for token at specified position. Keyword match
    -	 *  must be exact.  DPS 24-May-2010   
    -	 *  @param x x-coordinate of current position
    -	 *  @param y y-coordinate of current position
    -	 *  @return String containing appropriate tool tip text.  Possibly HTML-encoded.
    -	 */
    -	 // Is used for tool tip only (not popup menu)
    -   public String getSyntaxSensitiveToolTipText(int x, int y) {
    -      String result = null;
    -      int line = this.yToLine(y);
    -      ArrayList matches = getSyntaxSensitiveHelpAtLineOffset(line, this.xToOffset(line,x), true);
    -      if (matches == null) { 
    -         return null;
    -      }
    -      int length = PopupHelpItem.maxExampleLength(matches) + 2;
    -      result = "";
    -      for (int i=0; i") + "" + match.getExamplePaddedToLength(length).replaceAll(" "," ") + "" + match.getDescription();
    -      }
    -      return result + "";
    -   }
    +        int count;
    +        if (ch == null)
    +        {
    +            count = 0;
    +        }
    +        else
    +        {
    +            count = ch.getChildrenAdded().length -
    +                ch.getChildrenRemoved().length;
    +        }
     
    +        int line = getLineOfOffset(evt.getOffset());
    +        if (count == 0)
    +        {
    +            painter.invalidateLine(line);
    +        }
    +        // do magic stuff
    +        else if (line < firstLine)
    +        {
    +            setFirstLine(firstLine + count);
    +        }
    +        // end of magic stuff
    +        else
    +        {
    +            painter.invalidateLineRange(line, firstLine + visibleLines);
    +            updateScrollBars();
    +        }
    +    }
     
         /**
    -     * Constructs string for auto-indent feature.  Returns empty string
    -	  * if auto-intent is disabled or if line has no leading white space. 
    -	  * Uses getLeadingWhiteSpace(). Is used by InputHandler when processing
    -	  * key press for Enter key.   DPS 31-Dec-2010
    -	  * @return String containing auto-indent characters to be inserted into text
    -	  */    
    -   public String getAutoIndent() {
    -      return (Globals.getSettings().getBooleanSetting(Settings.AUTO_INDENT)) ? getLeadingWhiteSpace() : "";
    -   }
    +     * Return any relevant tool tip text for token at specified position. Keyword match must be exact.  DPS 24-May-2010
    +     *
    +     * @param x x-coordinate of current position
    +     * @param y y-coordinate of current position
    +     * @return String containing appropriate tool tip text.  Possibly HTML-encoded.
    +     */
    +    // Is used for tool tip only (not popup menu)
    +    public String getSyntaxSensitiveToolTipText(int x, int y)
    +    {
    +        String result = null;
    +        int line = this.yToLine(y);
    +        ArrayList matches = getSyntaxSensitiveHelpAtLineOffset(line, this.xToOffset(line, x), true);
    +        if (matches == null)
    +        {
    +            return null;
    +        }
    +        int length = PopupHelpItem.maxExampleLength(matches) + 2;
    +        result = "";
    +        for (int i = 0; i < matches.size(); i++)
    +        {
    +            PopupHelpItem match = (PopupHelpItem) matches.get(i);
    +            result += ((i == 0) ? "" : "
    ") + "" + match.getExamplePaddedToLength(length).replaceAll(" ", " ") + "" + match.getDescription(); + } + return result + ""; + } /** - * Makes a copy of leading white space (tab or space) from the current line and - * returns it. DPS 31-Dec-2010 - * @return String containing leading white space of current line. Empty string if none. - */ - public String getLeadingWhiteSpace() { - int line = getCaretLine(); - int lineLength = getLineLength(line); - String indent = ""; - if (lineLength > 0) { - String text = getText(getLineStartOffset(line), lineLength); - for (int position = 0; position < text.length(); position++) { - char character = text.charAt(position); - if (character == '\t' || character == ' ') { - indent += character; - } - else { - break; - } - } - } - return indent; - } + * Constructs string for auto-indent feature. Returns empty string if auto-intent is disabled or if line has no + * leading white space. Uses getLeadingWhiteSpace(). Is used by InputHandler when processing key press for Enter + * key. DPS 31-Dec-2010 + * + * @return String containing auto-indent characters to be inserted into text + */ + public String getAutoIndent() + { + return (Globals.getSettings().getBooleanSetting(Settings.AUTO_INDENT)) ? getLeadingWhiteSpace() : ""; + } - ////////////////////////////////////////////////////////////////////////////////// - // Get relevant help information at specified position. Returns ArrayList of - // PopupHelpItem with one per match, or null if no matches. - // The "exact" parameter is set depending on whether the match has to be - // exact or whether a prefix match will do. The token "s" will not match - // any instruction names if exact is true, but will match "sw", "sh", etc - // if exact is false. The former is helpful for mouse-movement-based tool - // tips (this is what you have). The latter is helpful for caret-based tool - // tips (this is what you can do). - private ArrayList getSyntaxSensitiveHelpAtLineOffset(int line, int offset, boolean exact) { - ArrayList matches = null; - TokenMarker tokenMarker = this.getTokenMarker(); - if (tokenMarker != null) { - Segment lineSegment = new Segment(); - this.getLineText(line,lineSegment); // fill segment with info from this line - Token tokens = tokenMarker.markTokens(lineSegment,line); - Token tokenList = tokens; - int tokenOffset = 0; - Token tokenAtOffset = null; - // cool for following the tokens... - //System.out.print("(JEditTextArea.java) Token Stream:"); - Token toke = tokens; - for (;;) { - //System.out.print(" "+toke.id+"("+toke.length+")"); - if (toke.id == Token.END) - break; - toke = toke.next; - } - //System.out.println(); - - for (;;) { - byte id = tokens.id; - if(id == Token.END) - break; - int length = tokens.length; - if (offset > tokenOffset && offset <= tokenOffset+length) { - tokenAtOffset = tokens; - break; + /** + * Makes a copy of leading white space (tab or space) from the current line and returns it. DPS 31-Dec-2010 + * + * @return String containing leading white space of current line. Empty string if none. + */ + public String getLeadingWhiteSpace() + { + int line = getCaretLine(); + int lineLength = getLineLength(line); + String indent = ""; + if (lineLength > 0) + { + String text = getText(getLineStartOffset(line), lineLength); + for (int position = 0; position < text.length(); position++) + { + char character = text.charAt(position); + if (character == '\t' || character == ' ') + { + indent += character; + } + else + { + break; + } } - tokenOffset += length; - tokens = tokens.next; - } - if (tokenAtOffset != null) { - String tokenText = lineSegment.toString().substring(tokenOffset, tokenOffset+tokenAtOffset.length); - if (exact) { - matches = tokenMarker.getTokenExactMatchHelp(tokenAtOffset, tokenText); - } - else { - matches = tokenMarker.getTokenPrefixMatchHelp(lineSegment.toString(), tokenList, tokenAtOffset, tokenText); - } - } - } - return matches; - } + } + return indent; + } + ////////////////////////////////////////////////////////////////////////////////// + // Get relevant help information at specified position. Returns ArrayList of + // PopupHelpItem with one per match, or null if no matches. + // The "exact" parameter is set depending on whether the match has to be + // exact or whether a prefix match will do. The token "s" will not match + // any instruction names if exact is true, but will match "sw", "sh", etc + // if exact is false. The former is helpful for mouse-movement-based tool + // tips (this is what you have). The latter is helpful for caret-based tool + // tips (this is what you can do). + private ArrayList getSyntaxSensitiveHelpAtLineOffset(int line, int offset, boolean exact) + { + ArrayList matches = null; + TokenMarker tokenMarker = this.getTokenMarker(); + if (tokenMarker != null) + { + Segment lineSegment = new Segment(); + this.getLineText(line, lineSegment); // fill segment with info from this line + Token tokens = tokenMarker.markTokens(lineSegment, line); + Token tokenList = tokens; + int tokenOffset = 0; + Token tokenAtOffset = null; + // cool for following the tokens... + //System.out.print("(JEditTextArea.java) Token Stream:"); + Token toke = tokens; + for (; ; ) + { + //System.out.print(" "+toke.id+"("+toke.length+")"); + if (toke.id == Token.END) + { + break; + } + toke = toke.next; + } + //System.out.println(); + + for (; ; ) + { + byte id = tokens.id; + if (id == Token.END) + { + break; + } + int length = tokens.length; + if (offset > tokenOffset && offset <= tokenOffset + length) + { + tokenAtOffset = tokens; + break; + } + tokenOffset += length; + tokens = tokens.next; + } + if (tokenAtOffset != null) + { + String tokenText = lineSegment.toString().substring(tokenOffset, tokenOffset + tokenAtOffset.length); + if (exact) + { + matches = tokenMarker.getTokenExactMatchHelp(tokenAtOffset, tokenText); + } + else + { + matches = tokenMarker.getTokenPrefixMatchHelp(lineSegment.toString(), tokenList, tokenAtOffset, tokenText); + } + } + } + return matches; + } //////////////////////////////////////////////////////////////////////////////////// // Compose and display syntax-sensitive help. Typically invoked upon typing a key. - // Results in popup menu. Is not used for creating tool tips. - private void applySyntaxSensitiveHelp() { - if (!mars.Globals.getSettings().getBooleanSetting(mars.Settings.POPUP_INSTRUCTION_GUIDANCE)) { - return; - } - int line = getCaretLine(); - int lineStart = getLineStartOffset(line); - int offset = Math.max(1,Math.min(getLineLength(line), - getCaretPosition() - lineStart)); - ArrayList helpItems = getSyntaxSensitiveHelpAtLineOffset(line,offset,false); - if (helpItems == null && popupMenu != null) { - popupMenu.setVisible(false); - popupMenu = null; - } - if (helpItems != null) { - popupMenu = new JPopupMenu(); - int length = PopupHelpItem.maxExampleLength(helpItems) + 2; - for (int i=0; i"+item.getExamplePaddedToLength(length).replaceAll(" "," ")+""+item.getDescription()+""); - if (item.getExact()) { - // The instruction name is completed so the role of the popup changes - // to that of floating help to assist in operand specification. - menuItem.setSelected(false); - // Want menu item to be disabled but that causes rendered text to be hard to see. - // Spent a couple hours on workaround with no success. The UI uses - // UIManager.get("MenuItem.disabledForeground") property to determine rendering - // color but this is done each time the text is rendered (paintText). There is - // no setter for the menu item itself. The UIManager property is used for all - // menus not just the editor's popup help menu, so you can't just set the disabled - // foreground color to, say, black and leave it. Tried several techniques without - // success. The only solution I found was a hack: writing a BasicMenuItem UI - // subclass that consists of hacked override of its paintText() method. But even - // this required use of "SwingUtilities2" class which has been deprecated for years - // So in the end I decided just to leave the menu item enabled. It will highlight - // but does nothing if selected. DPS 11-July-2014 - - // menuItem.setEnabled(false); - } - else { - // Typing of instruction/directive name is still in progress; the action listener - // will complete it when its menu item is selected. - menuItem.addActionListener(new PopupHelpActionListener(item.getTokenText(), item.getExample())); - } - popupMenu.add(menuItem); - } - popupMenu.pack(); - int y = lineToY(line); - int x = offsetToX(line, offset); - int height = painter.getFontMetrics(painter.getFont()).getHeight(); - int width = painter.getFontMetrics(painter.getFont()).charWidth('w'); - int menuXLoc = x+width+width+width; - int menuYLoc = y+height+height; // display below; - // Modified to always display popup BELOW the current line. - // This was done in response to negative student feedback about - // the popup blocking information they needed to (e.g. operands from - // previous instructions). Note that if menu is long enough and - // current cursor position is low enough, the menu will bottom out at the - // bottom of the screen and extend above the current line. DPS 23-Dec-2010 - popupMenu.show(this, menuXLoc, menuYLoc); - this.requestFocusInWindow(); // get cursor back from the menu - } - } - - - // Carries out the instruction/directive completion when popup menu - // item is selected. - private class PopupHelpActionListener implements ActionListener { - private String tokenText, text; - public PopupHelpActionListener(String tokenText, String text) { - this.tokenText = tokenText; - this.text = text.split(" ")[0]; - } - // Completion action will insert either a tab or space character following the - // completed instruction mnemonic. Inserts a tab if tab key was pressed; - // space otherwise. Get this information from the ActionEvent. - public void actionPerformed(ActionEvent e) { - String insert = (e.getActionCommand().charAt(0)=='\t') ? "\t" : " "; - if (this.tokenText.length() >= this.text.length()) { - overwriteSetSelectedText(insert); - } - else { - overwriteSetSelectedText(this.text.substring(this.tokenText.length())+insert); - } - } - } - - private void checkAutoIndent(KeyEvent evt) { - if (evt.getKeyCode()==KeyEvent.VK_ENTER) { - int line = getCaretLine(); - if (line <= 0) + // Results in popup menu. Is not used for creating tool tips. + private void applySyntaxSensitiveHelp() + { + if (!mars.Globals.getSettings().getBooleanSetting(mars.Settings.POPUP_INSTRUCTION_GUIDANCE)) + { return; - int previousLine = line - 1 ; - int previousLineLength = getLineLength(previousLine); - if (previousLineLength <= 0) - return; - String previous = getText(getLineStartOffset(previousLine), previousLineLength); - String indent = ""; - for (int position = 0; position < previous.length(); position++) { - char character = previous.charAt(position); - if (character == '\t' || character == ' ') { - indent += character; - } - else { - break; + } + int line = getCaretLine(); + int lineStart = getLineStartOffset(line); + int offset = Math.max(1, Math.min(getLineLength(line), + getCaretPosition() - lineStart)); + ArrayList helpItems = getSyntaxSensitiveHelpAtLineOffset(line, offset, false); + if (helpItems == null && popupMenu != null) + { + popupMenu.setVisible(false); + popupMenu = null; + } + if (helpItems != null) + { + popupMenu = new JPopupMenu(); + int length = PopupHelpItem.maxExampleLength(helpItems) + 2; + for (int i = 0; i < helpItems.size(); i++) + { + PopupHelpItem item = (PopupHelpItem) helpItems.get(i); + JMenuItem menuItem = new JMenuItem("" + item.getExamplePaddedToLength(length).replaceAll(" ", " ") + "" + item.getDescription() + ""); + if (item.getExact()) + { + // The instruction name is completed so the role of the popup changes + // to that of floating help to assist in operand specification. + menuItem.setSelected(false); + // Want menu item to be disabled but that causes rendered text to be hard to see. + // Spent a couple hours on workaround with no success. The UI uses + // UIManager.get("MenuItem.disabledForeground") property to determine rendering + // color but this is done each time the text is rendered (paintText). There is + // no setter for the menu item itself. The UIManager property is used for all + // menus not just the editor's popup help menu, so you can't just set the disabled + // foreground color to, say, black and leave it. Tried several techniques without + // success. The only solution I found was a hack: writing a BasicMenuItem UI + // subclass that consists of hacked override of its paintText() method. But even + // this required use of "SwingUtilities2" class which has been deprecated for years + // So in the end I decided just to leave the menu item enabled. It will highlight + // but does nothing if selected. DPS 11-July-2014 + + // menuItem.setEnabled(false); + } + else + { + // Typing of instruction/directive name is still in progress; the action listener + // will complete it when its menu item is selected. + menuItem.addActionListener(new PopupHelpActionListener(item.getTokenText(), item.getExample())); + } + popupMenu.add(menuItem); } - } - overwriteSetSelectedText(indent); - } - } - + popupMenu.pack(); + int y = lineToY(line); + int x = offsetToX(line, offset); + int height = painter.getFontMetrics(painter.getFont()).getHeight(); + int width = painter.getFontMetrics(painter.getFont()).charWidth('w'); + int menuXLoc = x + width + width + width; + int menuYLoc = y + height + height; // display below; + // Modified to always display popup BELOW the current line. + // This was done in response to negative student feedback about + // the popup blocking information they needed to (e.g. operands from + // previous instructions). Note that if menu is long enough and + // current cursor position is low enough, the menu will bottom out at the + // bottom of the screen and extend above the current line. DPS 23-Dec-2010 + popupMenu.show(this, menuXLoc, menuYLoc); + this.requestFocusInWindow(); // get cursor back from the menu + } + } + private void checkAutoIndent(KeyEvent evt) + { + if (evt.getKeyCode() == KeyEvent.VK_ENTER) + { + int line = getCaretLine(); + if (line <= 0) + { + return; + } + int previousLine = line - 1; + int previousLineLength = getLineLength(previousLine); + if (previousLineLength <= 0) + { + return; + } + String previous = getText(getLineStartOffset(previousLine), previousLineLength); + String indent = ""; + for (int position = 0; position < previous.length(); position++) + { + char character = previous.charAt(position); + if (character == '\t' || character == ' ') + { + indent += character; + } + else + { + break; + } + } + overwriteSetSelectedText(indent); + } + } //////////////////////////////////////////////////////////////////////////////////// // Called after processing a Key Pressed event. Will make popup menu disappear if - // Enter or Escape keys pressed. Will update if Backspace or Delete pressed. - // Not really concerned with modifiers here. - private void checkPopupMenu(KeyEvent evt) { - if ( evt.getKeyCode()==KeyEvent.VK_BACK_SPACE || evt.getKeyCode()==KeyEvent.VK_DELETE) - applySyntaxSensitiveHelp(); - if ( (evt.getKeyCode()== KeyEvent.VK_ENTER || evt.getKeyCode() == KeyEvent.VK_ESCAPE) - && popupMenu != null && popupMenu.isVisible() ) - popupMenu.setVisible(false); - } - - + // Enter or Escape keys pressed. Will update if Backspace or Delete pressed. + // Not really concerned with modifiers here. + private void checkPopupMenu(KeyEvent evt) + { + if (evt.getKeyCode() == KeyEvent.VK_BACK_SPACE || evt.getKeyCode() == KeyEvent.VK_DELETE) + { + applySyntaxSensitiveHelp(); + } + if ((evt.getKeyCode() == KeyEvent.VK_ENTER || evt.getKeyCode() == KeyEvent.VK_ESCAPE) + && popupMenu != null && popupMenu.isVisible()) + { + popupMenu.setVisible(false); + } + } + //////////////////////////////////////////////////////////////////////////////////// // Called before processing Key Pressed event. If popup menu is visible, will process - // tab and enter keys to select from the menu, and arrow keys to traverse the menu. - private boolean checkPopupCompletion(KeyEvent evt) { - if ((evt.getKeyCode()==KeyEvent.VK_UP || evt.getKeyCode()==KeyEvent.VK_DOWN) - && popupMenu != null && popupMenu.isVisible() && popupMenu.getComponentCount() > 0) - { - MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath(); - if (path.length < 1 || !(path[path.length-1] instanceof AbstractButton)) - return false; - AbstractButton item = (AbstractButton) path[path.length-1].getComponent(); - if (item.isEnabled()) { - int index = popupMenu.getComponentIndex(item); - if (index < 0) - return false; - if (evt.getKeyCode() == KeyEvent.VK_UP) { - index = (index==0) ? popupMenu.getComponentCount()-1 : index-1; - } - else { - index = (index==popupMenu.getComponentCount()-1) ? 0 : index+1; + // tab and enter keys to select from the menu, and arrow keys to traverse the menu. + private boolean checkPopupCompletion(KeyEvent evt) + { + if ((evt.getKeyCode() == KeyEvent.VK_UP || evt.getKeyCode() == KeyEvent.VK_DOWN) + && popupMenu != null && popupMenu.isVisible() && popupMenu.getComponentCount() > 0) + { + MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath(); + if (path.length < 1 || !(path[path.length - 1] instanceof AbstractButton)) + { + return false; } - // Neither popupMenu.setSelected() nor popupMenu.getSelectionModel().setSelectedIndex() - // have the desired effect (changing the menu item selected). Found references to - // this in a Sun forum. http://forums.sun.com/thread.jspa?forumID=57&threadID=641745 - // The solution, as shown here, is to use invokeLater. - final MenuElement[] newPath = new MenuElement[2]; - newPath[0] = path[0]; - newPath[1] = (MenuElement) popupMenu.getComponentAtIndex(index); + AbstractButton item = (AbstractButton) path[path.length - 1].getComponent(); + if (item.isEnabled()) + { + int index = popupMenu.getComponentIndex(item); + if (index < 0) + { + return false; + } + if (evt.getKeyCode() == KeyEvent.VK_UP) + { + index = (index == 0) ? popupMenu.getComponentCount() - 1 : index - 1; + } + else + { + index = (index == popupMenu.getComponentCount() - 1) ? 0 : index + 1; + } + // Neither popupMenu.setSelected() nor popupMenu.getSelectionModel().setSelectedIndex() + // have the desired effect (changing the menu item selected). Found references to + // this in a Sun forum. http://forums.sun.com/thread.jspa?forumID=57&threadID=641745 + // The solution, as shown here, is to use invokeLater. + final MenuElement[] newPath = new MenuElement[2]; + newPath[0] = path[0]; + newPath[1] = (MenuElement) popupMenu.getComponentAtIndex(index); + SwingUtilities.invokeLater( + new Runnable() + { + public void run() + { + MenuSelectionManager.defaultManager().setSelectedPath(newPath); + } + }); + return true; + } + else + { + return false; + } + } + if ((evt.getKeyCode() == KeyEvent.VK_TAB || evt.getKeyCode() == KeyEvent.VK_ENTER) + && popupMenu != null && popupMenu.isVisible() && popupMenu.getComponentCount() > 0) + { + MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath(); + if (path.length < 1 || !(path[path.length - 1] instanceof AbstractButton)) + { + return false; + } + AbstractButton item = (AbstractButton) path[path.length - 1].getComponent(); + if (item.isEnabled()) + { + ActionListener[] listeners = item.getActionListeners(); + if (listeners.length > 0) + { + listeners[0].actionPerformed(new ActionEvent(item, ActionEvent.ACTION_FIRST, + (evt.getKeyCode() == KeyEvent.VK_TAB) ? "\t" : " ")); + return true; + } + } + } + return false; + } + + static class CaretBlinker implements ActionListener + { + public void actionPerformed(ActionEvent evt) + { + if (focusedComponent != null + && focusedComponent.hasFocus()) + { + focusedComponent.blinkCaret(); + } + } + } + + class ScrollLayout implements LayoutManager + { + // private members + private Component center; + + private Component right; + + private Component bottom; + + private final Vector leftOfScrollBar = new Vector(); + + public void addLayoutComponent(String name, Component comp) + { + if (name.equals(CENTER)) + { + center = comp; + } + else if (name.equals(RIGHT)) + { + right = comp; + } + else if (name.equals(BOTTOM)) + { + bottom = comp; + } + else if (name.equals(LEFT_OF_SCROLLBAR)) + { + leftOfScrollBar.addElement(comp); + } + } + + public void removeLayoutComponent(Component comp) + { + if (center == comp) + { + center = null; + } + if (right == comp) + { + right = null; + } + if (bottom == comp) + { + bottom = null; + } + else + { + leftOfScrollBar.removeElement(comp); + } + } + + public Dimension preferredLayoutSize(Container parent) + { + Dimension dim = new Dimension(); + Insets insets = getInsets(); + dim.width = insets.left + insets.right; + dim.height = insets.top + insets.bottom; + + Dimension centerPref = center.getPreferredSize(); + dim.width += centerPref.width; + dim.height += centerPref.height; + Dimension rightPref = right.getPreferredSize(); + dim.width += rightPref.width; + Dimension bottomPref = bottom.getPreferredSize(); + dim.height += bottomPref.height; + + return dim; + } + + public Dimension minimumLayoutSize(Container parent) + { + Dimension dim = new Dimension(); + Insets insets = getInsets(); + dim.width = insets.left + insets.right; + dim.height = insets.top + insets.bottom; + + Dimension centerPref = center.getMinimumSize(); + dim.width += centerPref.width; + dim.height += centerPref.height; + Dimension rightPref = right.getMinimumSize(); + dim.width += rightPref.width; + Dimension bottomPref = bottom.getMinimumSize(); + dim.height += bottomPref.height; + + return dim; + } + + public void layoutContainer(Container parent) + { + Dimension size = parent.getSize(); + Insets insets = parent.getInsets(); + int itop = insets.top; + int ileft = insets.left; + int ibottom = insets.bottom; + int iright = insets.right; + + int rightWidth = right.getPreferredSize().width; + int bottomHeight = bottom.getPreferredSize().height; + int centerWidth = size.width - rightWidth - ileft - iright; + int centerHeight = size.height - bottomHeight - itop - ibottom; + + center.setBounds( + ileft, + itop, + centerWidth, + centerHeight); + + right.setBounds( + ileft + centerWidth, + itop, + rightWidth, + centerHeight); + + // Lay out all status components, in order + Enumeration status = leftOfScrollBar.elements(); + while (status.hasMoreElements()) + { + Component comp = (Component) status.nextElement(); + Dimension dim = comp.getPreferredSize(); + comp.setBounds(ileft, + itop + centerHeight, + dim.width, + bottomHeight); + ileft += dim.width; + } + + bottom.setBounds( + ileft, + itop + centerHeight, + size.width - rightWidth - ileft - iright, + bottomHeight); + } + } + + class MutableCaretEvent extends CaretEvent + { + MutableCaretEvent() + { + super(JEditTextArea.this); + } + + public int getDot() + { + return getCaretPosition(); + } + + public int getMark() + { + return getMarkPosition(); + } + } + + class AdjustHandler implements AdjustmentListener + { + public void adjustmentValueChanged(final AdjustmentEvent evt) + { + if (!scrollBarsInitialized) + { + return; + } + + // If this is not done, mousePressed events accumulate + // and the result is that scrolling doesn't stop after + // the mouse is released SwingUtilities.invokeLater( - new Runnable() { - public void run() { - MenuSelectionManager.defaultManager().setSelectedPath(newPath); - } - }); - return true; - } - else { - return false; - } - } - if ((evt.getKeyCode()==KeyEvent.VK_TAB || evt.getKeyCode()==KeyEvent.VK_ENTER) - && popupMenu != null && popupMenu.isVisible() && popupMenu.getComponentCount() > 0) - { - MenuElement[] path = MenuSelectionManager.defaultManager().getSelectedPath(); - if (path.length < 1 || !(path[path.length-1] instanceof AbstractButton)) - return false; - AbstractButton item = (AbstractButton) path[path.length-1].getComponent(); - if (item.isEnabled()) { - ActionListener[] listeners = item.getActionListeners(); - if (listeners.length > 0) { - listeners[0].actionPerformed(new ActionEvent(item, ActionEvent.ACTION_FIRST, - (evt.getKeyCode()==KeyEvent.VK_TAB) ? "\t" : " ")); - return true; + new Runnable() + { + public void run() + { + if (evt.getAdjustable() == vertical) + { + setFirstLine(vertical.getValue()); + } + else + { + setHorizontalOffset(-horizontal.getValue()); + } + } + }); + } + } + + class ComponentHandler extends ComponentAdapter + { + public void componentResized(ComponentEvent evt) + { + recalculateVisibleLines(); + scrollBarsInitialized = true; + } + } + + class DocumentHandler implements DocumentListener + { + public void insertUpdate(DocumentEvent evt) + { + documentChanged(evt); + + int offset = evt.getOffset(); + int length = evt.getLength(); + + // If event fired because of undo or redo, select inserted text. DPS 3-May-2010 + if (unredoing) + { + select(offset, offset + length); + return; } - } - } - return false; - } - - - static - { - caretTimer = new Timer(500,new CaretBlinker()); - caretTimer.setInitialDelay(500); - caretTimer.start(); - } + + int newStart; + int newEnd; + + if (selectionStart > offset || (selectionStart + == selectionEnd && selectionStart == offset)) + { + newStart = selectionStart + length; + } + else + { + newStart = selectionStart; + } + + if (selectionEnd >= offset) + { + newEnd = selectionEnd + length; + } + else + { + newEnd = selectionEnd; + } + select(newStart, newEnd); + } + + public void removeUpdate(DocumentEvent evt) + { + documentChanged(evt); + + int offset = evt.getOffset(); + int length = evt.getLength(); + + // If event fired because of undo or redo, move caret to position of removal. DPS 3-May-2010 + if (unredoing) + { + select(offset, offset); + setCaretPosition(offset); + return; + } + + int newStart; + int newEnd; + + if (selectionStart > offset) + { + if (selectionStart > offset + length) + { + newStart = selectionStart - length; + } + else + { + newStart = offset; + } + } + else + { + newStart = selectionStart; + } + + if (selectionEnd > offset) + { + if (selectionEnd > offset + length) + { + newEnd = selectionEnd - length; + } + else + { + newEnd = offset; + } + } + else + { + newEnd = selectionEnd; + } + select(newStart, newEnd); + } + + public void changedUpdate(DocumentEvent evt) + { + } + } + + class DragHandler implements MouseMotionListener + { + public void mouseDragged(MouseEvent evt) + { + if (popup != null && popup.isVisible()) + { + return; + } + + setSelectionRectangular((evt.getModifiers() + & InputEvent.CTRL_MASK) != 0); + select(getMarkPosition(), xyToOffset(evt.getX(), evt.getY())); + } + + public void mouseMoved(MouseEvent evt) + { + } + } + + class FocusHandler implements FocusListener + { + public void focusGained(FocusEvent evt) + { + setCaretVisible(true); + focusedComponent = JEditTextArea.this; + } + + public void focusLost(FocusEvent evt) + { + setCaretVisible(false); + focusedComponent = null; + } + } + + // Added by DPS, 5-5-2010. Allows use of mouse wheel to scroll. + // Scrolling as fast as I could, the most notches I could get in + // one MouseWheelEvent was 3. Normally it will be 1. Nonetheless, + // this will scroll up to the number in the event, subject to + // scrollability of the text in its viewport. + class MouseWheelHandler implements MouseWheelListener + { + public void mouseWheelMoved(MouseWheelEvent e) + { + int maxMotion = Math.abs(e.getWheelRotation()) * LINES_PER_MOUSE_WHEEL_NOTCH; + if (e.getWheelRotation() < 0) + { + setFirstLine(getFirstLine() - Math.min(maxMotion, getFirstLine())); + } + else + { + setFirstLine(getFirstLine() + (Math.min(maxMotion, Math.max(0, getLineCount() - (getFirstLine() + visibleLines))))); + } + } + } + + class MouseHandler extends MouseAdapter + { + public void mousePressed(MouseEvent evt) + { + requestFocus(); + + // Focus events not fired sometimes? + setCaretVisible(true); + focusedComponent = JEditTextArea.this; + + if ((evt.getModifiers() & InputEvent.BUTTON3_MASK) != 0 + && popup != null) + { + popup.show(painter, evt.getX(), evt.getY()); + return; + } + + int line = yToLine(evt.getY()); + int offset = xToOffset(line, evt.getX()); + int dot = getLineStartOffset(line) + offset; + + switch (evt.getClickCount()) + { + case 1: + doSingleClick(evt, line, offset, dot); + break; + case 2: + // It uses the bracket matching stuff, so + // it can throw a BLE + try + { + doDoubleClick(evt, line, offset, dot); + } + catch (BadLocationException bl) + { + bl.printStackTrace(); + } + break; + case 3: + doTripleClick(evt, line, offset, dot); + break; + } + } + + private void doSingleClick(MouseEvent evt, int line, + int offset, int dot) + { + if ((evt.getModifiers() & InputEvent.SHIFT_MASK) != 0) + { + rectSelect = (evt.getModifiers() & InputEvent.CTRL_MASK) != 0; + select(getMarkPosition(), dot); + } + else + { + setCaretPosition(dot); + } + } + + private void doDoubleClick(MouseEvent evt, int line, + int offset, int dot) throws BadLocationException + { + // Ignore empty lines + if (getLineLength(line) == 0) + { + return; + } + + try + { + int bracket = TextUtilities.findMatchingBracket( + document, Math.max(0, dot - 1)); + if (bracket != -1) + { + int mark = getMarkPosition(); + // Hack + if (bracket > mark) + { + bracket++; + mark--; + } + select(mark, bracket); + return; + } + } + catch (BadLocationException bl) + { + bl.printStackTrace(); + } + + // Ok, it's not a bracket... select the word + String lineText = getLineText(line); + char ch = lineText.charAt(Math.max(0, offset - 1)); + + String noWordSep = (String) document.getProperty("noWordSep"); + if (noWordSep == null) + { + noWordSep = ""; + } + + // If the user clicked on a non-letter char, + // we select the surrounding non-letters + boolean selectNoLetter = (!Character + .isLetterOrDigit(ch) + && noWordSep.indexOf(ch) == -1); + + int wordStart = 0; + + for (int i = offset - 1; i >= 0; i--) + { + ch = lineText.charAt(i); + if (selectNoLetter ^ (!Character + .isLetterOrDigit(ch) && + noWordSep.indexOf(ch) == -1)) + { + wordStart = i + 1; + break; + } + } + + int wordEnd = lineText.length(); + for (int i = offset; i < lineText.length(); i++) + { + ch = lineText.charAt(i); + if (selectNoLetter ^ (!Character + .isLetterOrDigit(ch) && + noWordSep.indexOf(ch) == -1)) + { + wordEnd = i; + break; + } + } + + int lineStart = getLineStartOffset(line); + select(lineStart + wordStart, lineStart + wordEnd); + } + + private void doTripleClick(MouseEvent evt, int line, + int offset, int dot) + { + select(getLineStartOffset(line), getLineEndOffset(line) - 1); + } + } + + class CaretUndo extends AbstractUndoableEdit + { + private int start; + + private int end; + + CaretUndo(int start, int end) + { + this.start = start; + this.end = end; + } + + public boolean isSignificant() + { + return false; + } + + public String getPresentationName() + { + return "caret move"; + } + + public void undo() throws CannotUndoException + { + super.undo(); + + select(start, end); + } + + public void redo() throws CannotRedoException + { + super.redo(); + + select(start, end); + } + + public boolean addEdit(UndoableEdit edit) + { + if (edit instanceof CaretUndo) + { + CaretUndo cedit = (CaretUndo) edit; + start = cedit.start; + end = cedit.end; + cedit.die(); + + return true; + } + else + { + return false; + } + } + } + + // Carries out the instruction/directive completion when popup menu + // item is selected. + private class PopupHelpActionListener implements ActionListener + { + private final String tokenText; + + private final String text; + + public PopupHelpActionListener(String tokenText, String text) + { + this.tokenText = tokenText; + this.text = text.split(" ")[0]; + } + + // Completion action will insert either a tab or space character following the + // completed instruction mnemonic. Inserts a tab if tab key was pressed; + // space otherwise. Get this information from the ActionEvent. + public void actionPerformed(ActionEvent e) + { + String insert = (e.getActionCommand().charAt(0) == '\t') ? "\t" : " "; + if (this.tokenText.length() >= this.text.length()) + { + overwriteSetSelectedText(insert); + } + else + { + overwriteSetSelectedText(this.text.substring(this.tokenText.length()) + insert); + } + } + } } diff --git a/src/main/java/mars/venus/editors/jeditsyntax/KeywordMap.java b/src/main/java/mars/venus/editors/jeditsyntax/KeywordMap.java index 484bd77..88e8d01 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/KeywordMap.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/KeywordMap.java @@ -8,136 +8,147 @@ * remains intact in all source distributions of this package. */ - package mars.venus.editors.jeditsyntax; +package mars.venus.editors.jeditsyntax; - import mars.venus.editors.jeditsyntax.tokenmarker.*; - import javax.swing.text.Segment; +import mars.venus.editors.jeditsyntax.tokenmarker.Token; + +import javax.swing.text.Segment; /** - * A KeywordMap is similar to a hashtable in that it maps keys - * to values. However, the `keys' are Swing segments. This allows lookups of - * text substrings without the overhead of creating a new string object. + * A KeywordMap is similar to a hashtable in that it maps keys to values. However, the `keys' are Swing + * segments. This allows lookups of text substrings without the overhead of creating a new string object. *

    * This class is used by CTokenMarker to map keywords to ids. * * @author Slava Pestov, Mike Dillon * @version $Id: KeywordMap.java,v 1.16 1999/12/13 03:40:30 sp Exp $ */ - public class KeywordMap - { - /** - * Creates a new KeywordMap. - * @param ignoreCase True if keys are case insensitive - */ - public KeywordMap(boolean ignoreCase) - { - this(ignoreCase, 52); - this.ignoreCase = ignoreCase; - } - - /** - * Creates a new KeywordMap. - * @param ignoreCase True if the keys are case insensitive - * @param mapLength The number of `buckets' to create. - * A value of 52 will give good performance for most maps. - */ - public KeywordMap(boolean ignoreCase, int mapLength) - { - this.mapLength = mapLength; - this.ignoreCase = ignoreCase; - map = new Keyword[mapLength]; - } - - /** - * Looks up a key. - * @param text The text segment - * @param offset The offset of the substring within the text segment - * @param length The length of the substring - */ - public byte lookup(Segment text, int offset, int length) - { - if(length == 0) +public class KeywordMap +{ + // protected members + protected int mapLength; + + private final Keyword[] map; + + private boolean ignoreCase; + + /** + * Creates a new KeywordMap. + * + * @param ignoreCase True if keys are case insensitive + */ + public KeywordMap(boolean ignoreCase) + { + this(ignoreCase, 52); + this.ignoreCase = ignoreCase; + } + + /** + * Creates a new KeywordMap. + * + * @param ignoreCase True if the keys are case insensitive + * @param mapLength The number of `buckets' to create. A value of 52 will give good performance for most maps. + */ + public KeywordMap(boolean ignoreCase, int mapLength) + { + this.mapLength = mapLength; + this.ignoreCase = ignoreCase; + map = new Keyword[mapLength]; + } + + /** + * Looks up a key. + * + * @param text The text segment + * @param offset The offset of the substring within the text segment + * @param length The length of the substring + */ + public byte lookup(Segment text, int offset, int length) + { + if (length == 0) + { return Token.NULL; - if (text.array[offset]=='%') + } + if (text.array[offset] == '%') + { return Token.MACRO_ARG; // added 12/12 M. Sekhavat - Keyword k = map[getSegmentMapKey(text, offset, length)]; - while(k != null) - { - if(length != k.keyword.length) + } + Keyword k = map[getSegmentMapKey(text, offset, length)]; + while (k != null) + { + if (length != k.keyword.length) { - k = k.next; - continue; + k = k.next; + continue; + } + if (SyntaxUtilities.regionMatches(ignoreCase, text, offset, + k.keyword)) + { + return k.id; } - if(SyntaxUtilities.regionMatches(ignoreCase,text,offset, - k.keyword)) - return k.id; k = k.next; - } - return Token.NULL; - } - - /** - * Adds a key-value mapping. - * @param keyword The key - * @Param id The value - */ - public void add(String keyword, byte id) - { - int key = getStringMapKey(keyword); - map[key] = new Keyword(keyword.toCharArray(),id,map[key]); - } - - /** - * Returns true if the keyword map is set to be case insensitive, - * false otherwise. - */ - public boolean getIgnoreCase() - { - return ignoreCase; - } - - /** - * Sets if the keyword map should be case insensitive. - * @param ignoreCase True if the keyword map should be case - * insensitive, false otherwise - */ - public void setIgnoreCase(boolean ignoreCase) - { - this.ignoreCase = ignoreCase; - } - - // protected members - protected int mapLength; - - protected int getStringMapKey(String s) - { - return (Character.toUpperCase(s.charAt(0)) + - Character.toUpperCase(s.charAt(s.length()-1))) + } + return Token.NULL; + } + + /** + * Adds a key-value mapping. + * + * @param keyword The key + * @Param id The value + */ + public void add(String keyword, byte id) + { + int key = getStringMapKey(keyword); + map[key] = new Keyword(keyword.toCharArray(), id, map[key]); + } + + /** + * Returns true if the keyword map is set to be case insensitive, false otherwise. + */ + public boolean getIgnoreCase() + { + return ignoreCase; + } + + /** + * Sets if the keyword map should be case insensitive. + * + * @param ignoreCase True if the keyword map should be case insensitive, false otherwise + */ + public void setIgnoreCase(boolean ignoreCase) + { + this.ignoreCase = ignoreCase; + } + + protected int getStringMapKey(String s) + { + return (Character.toUpperCase(s.charAt(0)) + + Character.toUpperCase(s.charAt(s.length() - 1))) % mapLength; - } - - protected int getSegmentMapKey(Segment s, int off, int len) - { - return (Character.toUpperCase(s.array[off]) + + } + + protected int getSegmentMapKey(Segment s, int off, int len) + { + return (Character.toUpperCase(s.array[off]) + Character.toUpperCase(s.array[off + len - 1])) % mapLength; - } - - // private members - class Keyword - { - public Keyword(char[] keyword, byte id, Keyword next) - { + } + + // private members + class Keyword + { + public char[] keyword; + + public byte id; + + public Keyword next; + + public Keyword(char[] keyword, byte id, Keyword next) + { this.keyword = keyword; this.id = id; this.next = next; - } - - public char[] keyword; - public byte id; - public Keyword next; - } - - private Keyword[] map; - private boolean ignoreCase; - } + } + } +} diff --git a/src/main/java/mars/venus/editors/jeditsyntax/PopupHelpItem.java b/src/main/java/mars/venus/editors/jeditsyntax/PopupHelpItem.java index 8af8f61..7dd8139 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/PopupHelpItem.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/PopupHelpItem.java @@ -1,4 +1,3 @@ - /* Copyright (c) 2003-2010, Pete Sanderson and Kenneth Vollmar @@ -27,118 +26,148 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - package mars.venus.editors.jeditsyntax; - - /** - * Handly little class to contain help information for a popupMenu or - * tool tip item. - */ - public class PopupHelpItem { - private String tokenText; - private String example; - private String description; - private boolean exact; // from exact match? - private int exampleLength; - private static final String spaces = " "; // 40 spaces +package mars.venus.editors.jeditsyntax; + +/** + * Handly little class to contain help information for a popupMenu or tool tip item. + */ +public class PopupHelpItem +{ + private static final String spaces = " "; // 40 spaces + + private final String tokenText; + + private String example; + + private String description; + + private final boolean exact; // from exact match? + + private int exampleLength; /** - * Create popup help item. This is created as result of either an exact-match or - * prefix-match search. Note that prefix-match search includes exact as well as partial matches. + * Create popup help item. This is created as result of either an exact-match or prefix-match search. Note that + * prefix-match search includes exact as well as partial matches. + * * @param tokenText The document text that matched * @param example An example instruction * @param description A textual description of the instruction * @param exact True if match occurred as result of exact-match search, false otherwise. */ - public PopupHelpItem(String tokenText, String example, String description, boolean exact) { - this.tokenText = tokenText; - this.example = example; - if (exact) { - this.description = description; - } else { - int detailPosition = description.indexOf(mars.venus.HelpHelpAction.descriptionDetailSeparator); - this.description = (detailPosition == -1) ? description : description.substring(0,detailPosition); - } - this.exampleLength = this.example.length(); - this.exact = exact; - } - + public PopupHelpItem(String tokenText, String example, String description, boolean exact) + { + this.tokenText = tokenText; + this.example = example; + if (exact) + { + this.description = description; + } + else + { + int detailPosition = description.indexOf(mars.venus.HelpHelpAction.descriptionDetailSeparator); + this.description = (detailPosition == -1) ? description : description.substring(0, detailPosition); + } + this.exampleLength = this.example.length(); + this.exact = exact; + } + /** * Create popup help item, where match is result of an exact-match search. + * * @param tokenText The document text that matched * @param example An example instruction * @param description A textual description of the instruction */ - public PopupHelpItem(String tokenText, String example, String description) { - this(tokenText, example, description, true); - - } - - /** The document text that mached this item */ - public String getTokenText() { - return this.tokenText; - } - - public String getExample() { - return this.example; - } - - public String getDescription() { - return this.description; - } - - /** Determines whether match occurred in an exact-match or prefix-match search. - * Note this can return false even if the match is exact because prefix-match also - * includes exact match results. E.g. prefix match on "lw" will match both "lwl" and "lw". - * @return True if exact-match search, false otherwise. - */ - public boolean getExact() { - return this.exact; - } - - public int getExampleLength() { - return this.exampleLength; - } - - // for performance purposes, length limited to example length + 40 - public String getExamplePaddedToLength(int length) { - String result = null; - if (length > this.exampleLength) { + public PopupHelpItem(String tokenText, String example, String description) + { + this(tokenText, example, description, true); + + } + + // Utility method. Traverse ArrayList of PopupHelpItem objects + // and return String length of longest example. + public static int maxExampleLength(java.util.ArrayList matches) + { + int length = 0; + if (matches != null) + { + for (int i = 0; i < matches.size(); i++) + { + Object match = matches.get(i); + if (match instanceof PopupHelpItem) + { + length = Math.max(length, ((PopupHelpItem) match).getExampleLength()); + } + } + } + return length; + } + + /** The document text that mached this item */ + public String getTokenText() + { + return this.tokenText; + } + + public String getExample() + { + return this.example; + } + + public void setExample(String example) + { + this.example = example; + this.exampleLength = example.length(); + } + + public String getDescription() + { + return this.description; + } + + public void setDescription(String description) + { + this.description = description; + } + + /** + * Determines whether match occurred in an exact-match or prefix-match search. Note this can return false even if + * the match is exact because prefix-match also includes exact match results. E.g. prefix match on "lw" will match + * both "lwl" and "lw". + * + * @return True if exact-match search, false otherwise. + */ + public boolean getExact() + { + return this.exact; + } + + public int getExampleLength() + { + return this.exampleLength; + } + + // for performance purposes, length limited to example length + 40 + public String getExamplePaddedToLength(int length) + { + String result = null; + if (length > this.exampleLength) + { int numSpaces = length - this.exampleLength; - if (numSpaces > spaces.length()) { - numSpaces = spaces.length(); + if (numSpaces > spaces.length()) + { + numSpaces = spaces.length(); } - result = this.example + spaces.substring(0,numSpaces); - } - else if (length == this.exampleLength) { + result = this.example + spaces.substring(0, numSpaces); + } + else if (length == this.exampleLength) + { result = this.example; - } - else { - result = this.example.substring(0,length); - } - return result; - } - - public void setExample(String example) { - this.example = example; - this.exampleLength = example.length(); - } - - public void setDescription(String description) { - this.description = description; - } - - // Utility method. Traverse ArrayList of PopupHelpItem objects - // and return String length of longest example. - public static int maxExampleLength(java.util.ArrayList matches) { - int length = 0; - if (matches != null) { - for (int i=0; iSegment is equal to a - * string. - * @param ignoreCase True if case should be ignored, false otherwise - * @param text The segment - * @param offset The offset into the segment - * @param match The string to match - */ - public static boolean regionMatches(boolean ignoreCase, Segment text, - int offset, String match) - { - int length = offset + match.length(); - char[] textArray = text.array; - if(length > text.offset + text.count) +public class SyntaxUtilities +{ + /** + * Paints the specified line onto the graphics context. Note that this method munges the offset and count values of + * the segment. + * + * @param line The line segment + * @param tokens The token list for the line + * @param styles The syntax style list + * @param expander The tab expander used to determine tab stops. May be null + * @param gfx The graphics context + * @param x The x co-ordinate + * @param y The y co-ordinate + * @return The x co-ordinate, plus the width of the painted string + */ + public static boolean popupShowing = false; + + public static Popup popup; + + // private members + private SyntaxUtilities() + { + } + + /** + * Checks if a subregion of a Segment is equal to a string. + * + * @param ignoreCase True if case should be ignored, false otherwise + * @param text The segment + * @param offset The offset into the segment + * @param match The string to match + */ + public static boolean regionMatches(boolean ignoreCase, Segment text, + int offset, String match) + { + int length = offset + match.length(); + char[] textArray = text.array; + if (length > text.offset + text.count) + { return false; - for(int i = offset, j = 0; i < length; i++, j++) - { + } + for (int i = offset, j = 0; i < length; i++, j++) + { char c1 = textArray[i]; char c2 = match.charAt(j); - if(ignoreCase) + if (ignoreCase) { - c1 = Character.toUpperCase(c1); - c2 = Character.toUpperCase(c2); + c1 = Character.toUpperCase(c1); + c2 = Character.toUpperCase(c2); } - if(c1 != c2) - return false; - } - return true; - } - - /** - * Checks if a subregion of a Segment is equal to a - * character array. - * @param ignoreCase True if case should be ignored, false otherwise - * @param text The segment - * @param offset The offset into the segment - * @param match The character array to match - */ - public static boolean regionMatches(boolean ignoreCase, Segment text, - int offset, char[] match) - { - int length = offset + match.length; - char[] textArray = text.array; - if(length > text.offset + text.count) + if (c1 != c2) + { + return false; + } + } + return true; + } + + /** + * Checks if a subregion of a Segment is equal to a character array. + * + * @param ignoreCase True if case should be ignored, false otherwise + * @param text The segment + * @param offset The offset into the segment + * @param match The character array to match + */ + public static boolean regionMatches(boolean ignoreCase, Segment text, + int offset, char[] match) + { + int length = offset + match.length; + char[] textArray = text.array; + if (length > text.offset + text.count) + { return false; - for(int i = offset, j = 0; i < length; i++, j++) - { + } + for (int i = offset, j = 0; i < length; i++, j++) + { char c1 = textArray[i]; char c2 = match[j]; - if(ignoreCase) + if (ignoreCase) { - c1 = Character.toUpperCase(c1); - c2 = Character.toUpperCase(c2); + c1 = Character.toUpperCase(c1); + c2 = Character.toUpperCase(c2); } - if(c1 != c2) - return false; - } - return true; - } - - /** - * Returns the default style table. This can be passed to the - * setStyles() method of SyntaxDocument - * to use the default syntax styles. - */ - public static SyntaxStyle[] getDefaultSyntaxStyles() - { - SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT]; - - // SyntaxStyle constructor params: color, italic?, bold? - // All need to be assigned even if not used by language (no gaps in array) - styles[Token.NULL] = new SyntaxStyle(Color.black,false,false); - styles[Token.COMMENT1] = new SyntaxStyle(new Color(0x00CC33),true,false);//(Color.black,true,false); - styles[Token.COMMENT2] = new SyntaxStyle(new Color(0x990033),true,false); - styles[Token.KEYWORD1] = new SyntaxStyle(Color.blue,false,false);//(Color.black,false,true); - styles[Token.KEYWORD2] = new SyntaxStyle(Color.magenta,false,false); - styles[Token.KEYWORD3] = new SyntaxStyle(Color.red,false,false);//(new Color(0x009600),false,false); - styles[Token.LITERAL1] = new SyntaxStyle(new Color(0x00CC33),false,false);//(new Color(0x650099),false,false); - styles[Token.LITERAL2] = new SyntaxStyle(new Color(0x00CC33),false,false);//(new Color(0x650099),false,true); - styles[Token.LABEL] = new SyntaxStyle(Color.black,true,false);//(new Color(0x990033),false,true); - styles[Token.OPERATOR] = new SyntaxStyle(Color.black,false,true); - styles[Token.INVALID] = new SyntaxStyle(Color.red,false,false); - styles[Token.MACRO_ARG]= new SyntaxStyle(new Color(150, 150,0), false, false); - return styles; - } - - /** - * Returns the CURRENT style table. This can be passed to the - * setStyles() method of SyntaxDocument - * to use the current syntax styles. If changes have been made - * via MARS Settings menu, the current settings will not be the - * same as the default settings. - */ - public static SyntaxStyle[] getCurrentSyntaxStyles() - { - SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT]; - - styles[Token.NULL] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.NULL); - styles[Token.COMMENT1] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.COMMENT1); - styles[Token.COMMENT2] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.COMMENT2); - styles[Token.KEYWORD1] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.KEYWORD1); - styles[Token.KEYWORD2] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.KEYWORD2); - styles[Token.KEYWORD3] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.KEYWORD3); - styles[Token.LITERAL1] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.LITERAL1); - styles[Token.LITERAL2] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.LITERAL2); - styles[Token.LABEL] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.LABEL); - styles[Token.OPERATOR] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.OPERATOR); - styles[Token.INVALID] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.INVALID); - styles[Token.MACRO_ARG]= Globals.getSettings().getEditorSyntaxStyleByPosition(Token.MACRO_ARG); - return styles; - } - - /** - * Paints the specified line onto the graphics context. Note that this - * method munges the offset and count values of the segment. - * @param line The line segment - * @param tokens The token list for the line - * @param styles The syntax style list - * @param expander The tab expander used to determine tab stops. May - * be null - * @param gfx The graphics context - * @param x The x co-ordinate - * @param y The y co-ordinate - * @return The x co-ordinate, plus the width of the painted string - */ public static boolean popupShowing = false; - public static Popup popup; - public static int paintSyntaxLine(Segment line, Token tokens, - SyntaxStyle[] styles, TabExpander expander, Graphics gfx, - int x, int y) - { - Font defaultFont = gfx.getFont(); - Color defaultColor = gfx.getColor(); - - int offset = 0; - for(;;) - { - byte id = tokens.id; - if(id == Token.END) - break; - - int length = tokens.length; - if(id == Token.NULL) + if (c1 != c2) { - if(!defaultColor.equals(gfx.getColor())) - gfx.setColor(defaultColor); - if(!defaultFont.equals(gfx.getFont())) - gfx.setFont(defaultFont); + return false; + } + } + return true; + } + + /** + * Returns the default style table. This can be passed to the + * setStyles() method of SyntaxDocument + * to use the default syntax styles. + */ + public static SyntaxStyle[] getDefaultSyntaxStyles() + { + SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT]; + + // SyntaxStyle constructor params: color, italic?, bold? + // All need to be assigned even if not used by language (no gaps in array) + styles[Token.NULL] = new SyntaxStyle(Color.black, false, false); + styles[Token.COMMENT1] = new SyntaxStyle(new Color(0x00CC33), true, false);//(Color.black,true,false); + styles[Token.COMMENT2] = new SyntaxStyle(new Color(0x990033), true, false); + styles[Token.KEYWORD1] = new SyntaxStyle(Color.blue, false, false);//(Color.black,false,true); + styles[Token.KEYWORD2] = new SyntaxStyle(Color.magenta, false, false); + styles[Token.KEYWORD3] = new SyntaxStyle(Color.red, false, false);//(new Color(0x009600),false,false); + styles[Token.LITERAL1] = new SyntaxStyle(new Color(0x00CC33), false, false);//(new Color(0x650099),false,false); + styles[Token.LITERAL2] = new SyntaxStyle(new Color(0x00CC33), false, false);//(new Color(0x650099),false,true); + styles[Token.LABEL] = new SyntaxStyle(Color.black, true, false);//(new Color(0x990033),false,true); + styles[Token.OPERATOR] = new SyntaxStyle(Color.black, false, true); + styles[Token.INVALID] = new SyntaxStyle(Color.red, false, false); + styles[Token.MACRO_ARG] = new SyntaxStyle(new Color(150, 150, 0), false, false); + return styles; + } + + /** + * Returns the CURRENT style table. This can be passed to the + * setStyles() method of SyntaxDocument + * to use the current syntax styles. If changes have been made via MARS Settings menu, the current settings will + * not be the same as the default settings. + */ + public static SyntaxStyle[] getCurrentSyntaxStyles() + { + SyntaxStyle[] styles = new SyntaxStyle[Token.ID_COUNT]; + + styles[Token.NULL] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.NULL); + styles[Token.COMMENT1] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.COMMENT1); + styles[Token.COMMENT2] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.COMMENT2); + styles[Token.KEYWORD1] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.KEYWORD1); + styles[Token.KEYWORD2] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.KEYWORD2); + styles[Token.KEYWORD3] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.KEYWORD3); + styles[Token.LITERAL1] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.LITERAL1); + styles[Token.LITERAL2] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.LITERAL2); + styles[Token.LABEL] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.LABEL); + styles[Token.OPERATOR] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.OPERATOR); + styles[Token.INVALID] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.INVALID); + styles[Token.MACRO_ARG] = Globals.getSettings().getEditorSyntaxStyleByPosition(Token.MACRO_ARG); + return styles; + } + + public static int paintSyntaxLine(Segment line, Token tokens, + SyntaxStyle[] styles, TabExpander expander, Graphics gfx, + int x, int y) + { + Font defaultFont = gfx.getFont(); + Color defaultColor = gfx.getColor(); + + int offset = 0; + for (; ; ) + { + byte id = tokens.id; + if (id == Token.END) + { + break; + } + + int length = tokens.length; + if (id == Token.NULL) + { + if (!defaultColor.equals(gfx.getColor())) + { + gfx.setColor(defaultColor); + } + if (!defaultFont.equals(gfx.getFont())) + { + gfx.setFont(defaultFont); + } } else - styles[id].setGraphicsFlags(gfx,defaultFont); - line.count = length; - - if (id == Token.KEYWORD1){ - //System.out.println("Instruction: "+line); - if (!popupShowing) {// System.out.println("creating popup"); -// JComponent paintArea = (JComponent) expander; -// JToolTip tip = paintArea.createToolTip(); -// tip.setTipText("Instruction: "+line); -// Point screenLocation = paintArea.getLocationOnScreen(); -// PopupFactory popupFactory = PopupFactory.getSharedInstance(); -// popup = popupFactory.getPopup(paintArea, tip, screenLocation.x + x, screenLocation.y + y); -// popupShowing = true; -// popup.show(); -// int delay = 200; //milliseconds -// ActionListener taskPerformer = -// new ActionListener() { -// public void actionPerformed(ActionEvent evt) { -// //popupShowing = false; -// if (popup!= null) { -// popup.hide(); -// } -// } -// }; -// Timer popupTimer = new Timer(delay, taskPerformer); -// popupTimer.setRepeats(false); -// popupTimer.start(); - - } - - // ToolTipManager.sharedInstance().mouseMoved( - // new MouseEvent((Component)expander, MouseEvent.MOUSE_MOVED, new java.util.Date().getTime(), 0, x, y, 0, false)); - // new InstructionMouseEvent((Component)expander, x, y, line)); + { + styles[id].setGraphicsFlags(gfx, defaultFont); } - - x = Utilities.drawTabbedText(line,x,y,gfx,expander,0); + line.count = length; + + if (id == Token.KEYWORD1) + { + //System.out.println("Instruction: "+line); + if (!popupShowing) + {// System.out.println("creating popup"); + // JComponent paintArea = (JComponent) expander; + // JToolTip tip = paintArea.createToolTip(); + // tip.setTipText("Instruction: "+line); + // Point screenLocation = paintArea.getLocationOnScreen(); + // PopupFactory popupFactory = PopupFactory.getSharedInstance(); + // popup = popupFactory.getPopup(paintArea, tip, screenLocation.x + x, screenLocation.y + y); + // popupShowing = true; + // popup.show(); + // int delay = 200; //milliseconds + // ActionListener taskPerformer = + // new ActionListener() { + // public void actionPerformed(ActionEvent evt) { + // //popupShowing = false; + // if (popup!= null) { + // popup.hide(); + // } + // } + // }; + // Timer popupTimer = new Timer(delay, taskPerformer); + // popupTimer.setRepeats(false); + // popupTimer.start(); + + } + + // ToolTipManager.sharedInstance().mouseMoved( + // new MouseEvent((Component)expander, MouseEvent.MOUSE_MOVED, new java.util.Date().getTime(), 0, x, y, 0, false)); + // new InstructionMouseEvent((Component)expander, x, y, line)); + } + + x = Utilities.drawTabbedText(line, x, y, gfx, expander, 0); line.offset += length; offset += length; - + tokens = tokens.next; - } - - return x; - } - - // private members - private SyntaxUtilities() {} - } - - class InstructionMouseEvent extends MouseEvent { - private Segment line; - public InstructionMouseEvent(Component component, int x, int y, Segment line) { - super(component, MouseEvent.MOUSE_MOVED, new java.util.Date().getTime(), 0, x, y, 0, false); - System.out.println("Create InstructionMouseEvent "+x+" "+y+" "+line); - this.line = line; - } - public Segment getLine() { - return this.line; - } - } + } + + return x; + } +} + +class InstructionMouseEvent extends MouseEvent +{ + private final Segment line; + + public InstructionMouseEvent(Component component, int x, int y, Segment line) + { + super(component, MouseEvent.MOUSE_MOVED, new java.util.Date().getTime(), 0, x, y, 0, false); + System.out.println("Create InstructionMouseEvent " + x + " " + y + " " + line); + this.line = line; + } + + public Segment getLine() + { + return this.line; + } +} diff --git a/src/main/java/mars/venus/editors/jeditsyntax/TextAreaDefaults.java b/src/main/java/mars/venus/editors/jeditsyntax/TextAreaDefaults.java index fa6c2e4..2d43997 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/TextAreaDefaults.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/TextAreaDefaults.java @@ -7,80 +7,97 @@ * remains intact in all source distributions of this package. */ - package mars.venus.editors.jeditsyntax; +package mars.venus.editors.jeditsyntax; - import mars.Settings; - import javax.swing.JPopupMenu; - import java.awt.Color; +import mars.Settings; + +import javax.swing.*; +import java.awt.*; /** - * Encapsulates default settings for a text area. This can be passed - * to the constructor once the necessary fields have been filled out. - * The advantage of doing this over calling lots of set() methods after - * creating the text area is that this method is faster. + * Encapsulates default settings for a text area. This can be passed to the constructor once the necessary fields have + * been filled out. The advantage of doing this over calling lots of set() methods after creating the text area is that + * this method is faster. */ - public class TextAreaDefaults - { - private static TextAreaDefaults DEFAULTS; - - public InputHandler inputHandler; - public SyntaxDocument document; - public boolean editable; - - public boolean caretVisible; - public boolean caretBlinks; - public boolean blockCaret; - public int caretBlinkRate; - public int electricScroll; - public int tabSize; - - public int cols; - public int rows; - public SyntaxStyle[] styles; - public Color caretColor; - public Color selectionColor; - public Color lineHighlightColor; - public boolean lineHighlight; - public Color bracketHighlightColor; - public boolean bracketHighlight; - public Color eolMarkerColor; - public boolean eolMarkers; - public boolean paintInvalid; - - public JPopupMenu popup; - - /** - * Returns a new TextAreaDefaults object with the default values filled - * in. - */ - public static TextAreaDefaults getDefaults() - { - DEFAULTS = new TextAreaDefaults(); - - DEFAULTS.inputHandler = new DefaultInputHandler(); - DEFAULTS.inputHandler.addDefaultKeyBindings(); - DEFAULTS.editable = true; - - DEFAULTS.blockCaret = false; - DEFAULTS.caretVisible = true; - DEFAULTS.caretBlinks = (mars.Globals.getSettings().getCaretBlinkRate() != 0); - DEFAULTS.caretBlinkRate = mars.Globals.getSettings().getCaretBlinkRate(); - DEFAULTS.tabSize = mars.Globals.getSettings().getEditorTabSize(); - DEFAULTS.electricScroll = 0;// was 3. Will begin scrolling when cursor is this many lines from the edge. - - DEFAULTS.cols = 80; - DEFAULTS.rows = 25; - DEFAULTS.styles = SyntaxUtilities.getCurrentSyntaxStyles(); // was getDefaultSyntaxStyles() - DEFAULTS.caretColor = Color.black; // Color.red; - DEFAULTS.selectionColor = new Color(0xccccff); - DEFAULTS.lineHighlightColor = new Color(0xeeeeee);//0xe0e0e0); - DEFAULTS.lineHighlight = mars.Globals.getSettings().getBooleanSetting(Settings.EDITOR_CURRENT_LINE_HIGHLIGHTING); - DEFAULTS.bracketHighlightColor = Color.black; - DEFAULTS.bracketHighlight = false; // assembly language doesn't need this. - DEFAULTS.eolMarkerColor = new Color(0x009999); - DEFAULTS.eolMarkers = false; // true; - DEFAULTS.paintInvalid = false; //true; - DEFAULTS.document = new SyntaxDocument(); - return DEFAULTS; - } - } +public class TextAreaDefaults +{ + private static TextAreaDefaults DEFAULTS; + + public InputHandler inputHandler; + + public SyntaxDocument document; + + public boolean editable; + + public boolean caretVisible; + + public boolean caretBlinks; + + public boolean blockCaret; + + public int caretBlinkRate; + + public int electricScroll; + + public int tabSize; + + public int cols; + + public int rows; + + public SyntaxStyle[] styles; + + public Color caretColor; + + public Color selectionColor; + + public Color lineHighlightColor; + + public boolean lineHighlight; + + public Color bracketHighlightColor; + + public boolean bracketHighlight; + + public Color eolMarkerColor; + + public boolean eolMarkers; + + public boolean paintInvalid; + + public JPopupMenu popup; + + /** + * Returns a new TextAreaDefaults object with the default values filled in. + */ + public static TextAreaDefaults getDefaults() + { + DEFAULTS = new TextAreaDefaults(); + + DEFAULTS.inputHandler = new DefaultInputHandler(); + DEFAULTS.inputHandler.addDefaultKeyBindings(); + DEFAULTS.editable = true; + + DEFAULTS.blockCaret = false; + DEFAULTS.caretVisible = true; + DEFAULTS.caretBlinks = (mars.Globals.getSettings().getCaretBlinkRate() != 0); + DEFAULTS.caretBlinkRate = mars.Globals.getSettings().getCaretBlinkRate(); + DEFAULTS.tabSize = mars.Globals.getSettings().getEditorTabSize(); + DEFAULTS.electricScroll = 0;// was 3. Will begin scrolling when cursor is this many lines from the edge. + + DEFAULTS.cols = 80; + DEFAULTS.rows = 25; + DEFAULTS.styles = SyntaxUtilities.getCurrentSyntaxStyles(); // was getDefaultSyntaxStyles() + DEFAULTS.caretColor = Color.black; // Color.red; + DEFAULTS.selectionColor = new Color(0xccccff); + DEFAULTS.lineHighlightColor = new Color(0xeeeeee);//0xe0e0e0); + DEFAULTS.lineHighlight = mars.Globals.getSettings().getBooleanSetting(Settings.EDITOR_CURRENT_LINE_HIGHLIGHTING); + DEFAULTS.bracketHighlightColor = Color.black; + DEFAULTS.bracketHighlight = false; // assembly language doesn't need this. + DEFAULTS.eolMarkerColor = new Color(0x009999); + DEFAULTS.eolMarkers = false; // true; + DEFAULTS.paintInvalid = false; //true; + DEFAULTS.document = new SyntaxDocument(); + return DEFAULTS; + } +} diff --git a/src/main/java/mars/venus/editors/jeditsyntax/TextAreaPainter.java b/src/main/java/mars/venus/editors/jeditsyntax/TextAreaPainter.java index df70fbb..d1610e0 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/TextAreaPainter.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/TextAreaPainter.java @@ -9,745 +9,788 @@ * remains intact in all source distributions of this package. */ - package mars.venus.editors.jeditsyntax; +package mars.venus.editors.jeditsyntax; - import mars.venus.editors.jeditsyntax.tokenmarker.*; - import javax.swing.ToolTipManager; - import javax.swing.text.*; - import javax.swing.JComponent; - import java.awt.event.MouseEvent; - import java.awt.*; +import mars.venus.editors.jeditsyntax.tokenmarker.Token; +import mars.venus.editors.jeditsyntax.tokenmarker.TokenMarker; + +import javax.swing.*; +import javax.swing.text.Segment; +import javax.swing.text.TabExpander; +import javax.swing.text.Utilities; +import java.awt.*; +import java.awt.event.MouseEvent; /** - * The text area repaint manager. It performs double buffering and paints - * lines of text. + * The text area repaint manager. It performs double buffering and paints lines of text. + * * @author Slava Pestov * @version $Id: TextAreaPainter.java,v 1.24 1999/12/13 03:40:30 sp Exp $ */ - public class TextAreaPainter extends JComponent implements TabExpander - { - /** - * Creates a new repaint manager. This should be not be called - * directly. - */ - public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults) - { - this.textArea = textArea; - - setAutoscrolls(true); - setDoubleBuffered(true); - setOpaque(true); - - - - ToolTipManager.sharedInstance().registerComponent(this); - - currentLine = new Segment(); - currentLineIndex = -1; - - setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); - - setFont(new Font("Courier New" /*"Monospaced"*/,Font.PLAIN,14)); - setForeground(Color.black); - setBackground(Color.white); - - tabSizeChars = defaults.tabSize; - blockCaret = defaults.blockCaret; - styles = defaults.styles; - cols = defaults.cols; - rows = defaults.rows; - caretColor = defaults.caretColor; - selectionColor = defaults.selectionColor; - lineHighlightColor = defaults.lineHighlightColor; - lineHighlight = defaults.lineHighlight; - bracketHighlightColor = defaults.bracketHighlightColor; - bracketHighlight = defaults.bracketHighlight; - paintInvalid = defaults.paintInvalid; - eolMarkerColor = defaults.eolMarkerColor; - eolMarkers = defaults.eolMarkers; - } - - /** - * Returns if this component can be traversed by pressing the - * Tab key. This returns false. - * - * NOTE: as of Java 1.4 this method is deprecated and no longer - * has the desired effect because the focus subsystem does not - * call it. I've implemented a KeyEventDispatcher in JEditTextArea - * to handle Tabs. DPS 12-May-2010 - */ - public final boolean isManagingFocus() - { - return false; - } - - /** - * Fetch the tab size in characters. DPS 12-May-2010. - * @return int tab size in characters - */ - public int getTabSize() { - return tabSizeChars; - } - - /** - * Set the tab size in characters. DPS 12-May-2010. - * Originally it was fixed at PlainDocument property - * value (8). - * @param size tab size in characters - */ - public void setTabSize(int size) { - tabSizeChars = size; - } - - /** - * Returns the syntax styles used to paint colorized text. Entry n - * will be used to paint tokens with id = n. - * @see org.syntax.jedit.Token - */ - public final SyntaxStyle[] getStyles() - { - return styles; - } - - /** - * Sets the syntax styles used to paint colorized text. Entry n - * will be used to paint tokens with id = n. - * @param styles The syntax styles - * @see org.syntax.jedit.Token - */ - public final void setStyles(SyntaxStyle[] styles) - { - this.styles = styles; - repaint(); - } - - /** - * Returns the caret color. - */ - public final Color getCaretColor() - { - return caretColor; - } - - /** - * Sets the caret color. - * @param caretColor The caret color - */ - public final void setCaretColor(Color caretColor) - { - this.caretColor = caretColor; - invalidateSelectedLines(); - } - - /** - * Returns the selection color. - */ - public final Color getSelectionColor() - { - return selectionColor; - } - - /** - * Sets the selection color. - * @param selectionColor The selection color - */ - public final void setSelectionColor(Color selectionColor) - { - this.selectionColor = selectionColor; - invalidateSelectedLines(); - } - - /** - * Returns the line highlight color. - */ - public final Color getLineHighlightColor() - { - return lineHighlightColor; - } - - /** - * Sets the line highlight color. - * @param lineHighlightColor The line highlight color - */ - public final void setLineHighlightColor(Color lineHighlightColor) - { - this.lineHighlightColor = lineHighlightColor; - invalidateSelectedLines(); - } - - /** - * Returns true if line highlight is enabled, false otherwise. - */ - public final boolean isLineHighlightEnabled() - { - return lineHighlight; - } - - /** - * Enables or disables current line highlighting. - * @param lineHighlight True if current line highlight should be enabled, - * false otherwise - */ - public final void setLineHighlightEnabled(boolean lineHighlight) - { - this.lineHighlight = lineHighlight; - invalidateSelectedLines(); - } - - /** - * Returns the bracket highlight color. - */ - public final Color getBracketHighlightColor() - { - return bracketHighlightColor; - } - - /** - * Sets the bracket highlight color. - * @param bracketHighlightColor The bracket highlight color - */ - public final void setBracketHighlightColor(Color bracketHighlightColor) - { - this.bracketHighlightColor = bracketHighlightColor; - invalidateLine(textArea.getBracketLine()); - } - - /** - * Returns true if bracket highlighting is enabled, false otherwise. - * When bracket highlighting is enabled, the bracket matching the - * one before the caret (if any) is highlighted. - */ - public final boolean isBracketHighlightEnabled() - { - return bracketHighlight; - } - - /** - * Enables or disables bracket highlighting. - * When bracket highlighting is enabled, the bracket matching the - * one before the caret (if any) is highlighted. - * @param bracketHighlight True if bracket highlighting should be - * enabled, false otherwise - */ - public final void setBracketHighlightEnabled(boolean bracketHighlight) - { - this.bracketHighlight = bracketHighlight; - invalidateLine(textArea.getBracketLine()); - } - - /** - * Returns true if the caret should be drawn as a block, false otherwise. - */ - public final boolean isBlockCaretEnabled() - { - return blockCaret; - } - - /** - * Sets if the caret should be drawn as a block, false otherwise. - * @param blockCaret True if the caret should be drawn as a block, - * false otherwise. - */ - public final void setBlockCaretEnabled(boolean blockCaret) - { - this.blockCaret = blockCaret; - invalidateSelectedLines(); - } - - /** - * Returns the EOL marker color. - */ - public final Color getEOLMarkerColor() - { - return eolMarkerColor; - } - - /** - * Sets the EOL marker color. - * @param eolMarkerColor The EOL marker color - */ - public final void setEOLMarkerColor(Color eolMarkerColor) - { - this.eolMarkerColor = eolMarkerColor; - repaint(); - } - - /** - * Returns true if EOL markers are drawn, false otherwise. - */ - public final boolean getEOLMarkersPainted() - { - return eolMarkers; - } - - /** - * Sets if EOL markers are to be drawn. - * @param eolMarkers True if EOL markers should be drawn, false otherwise - */ - public final void setEOLMarkersPainted(boolean eolMarkers) - { - this.eolMarkers = eolMarkers; - repaint(); - } - - /** - * Returns true if invalid lines are painted as red tildes (~), - * false otherwise. - */ - public boolean getInvalidLinesPainted() - { - return paintInvalid; - } - - /** - * Sets if invalid lines are to be painted as red tildes. - * @param paintInvalid True if invalid lines should be drawn, false otherwise - */ - public void setInvalidLinesPainted(boolean paintInvalid) - { - this.paintInvalid = paintInvalid; - } - - /** - * Adds a custom highlight painter. - * @param highlight The highlight - */ - public void addCustomHighlight(Highlight highlight) - { - highlight.init(textArea,highlights); - highlights = highlight; - } - - /** - * Highlight interface. - */ - public interface Highlight - { - /** - * Called after the highlight painter has been added. - * @param textArea The text area - * @param next The painter this one should delegate to - */ - void init(JEditTextArea textArea, Highlight next); - - /** - * This should paint the highlight and delgate to the - * next highlight painter. - * @param gfx The graphics context - * @param line The line number - * @param y The y co-ordinate of the line - */ - void paintHighlight(Graphics gfx, int line, int y); - - /** - * Returns the tool tip to display at the specified - * location. If this highlighter doesn't know what to - * display, it should delegate to the next highlight - * painter. - * @param evt The mouse event - */ - String getToolTipText(MouseEvent evt); - } - - /** - * Returns the tool tip to display at the specified location. - * @param evt The mouse event - */ - public String getToolTipText(MouseEvent evt) - { - // if(highlights != null) - // return highlights.getToolTipText(evt); - // else - // return null; - if (highlights != null) +public class TextAreaPainter extends JComponent implements TabExpander +{ + // protected members + protected JEditTextArea textArea; + + protected SyntaxStyle[] styles; + + protected Color caretColor; + + protected Color selectionColor; + + protected Color lineHighlightColor; + + protected Color bracketHighlightColor; + + protected Color eolMarkerColor; + + protected boolean blockCaret; + + protected boolean lineHighlight; + + protected boolean bracketHighlight; + + protected boolean paintInvalid; + + protected boolean eolMarkers; + + protected int cols; + + protected int rows; + + protected int tabSize, tabSizeChars; + + protected FontMetrics fm; + + protected Highlight highlights; + + // package-private members + int currentLineIndex; + + Token currentLineTokens; + + Segment currentLine; + + /** + * Creates a new repaint manager. This should be not be called directly. + */ + public TextAreaPainter(JEditTextArea textArea, TextAreaDefaults defaults) + { + this.textArea = textArea; + + setAutoscrolls(true); + setDoubleBuffered(true); + setOpaque(true); + + + ToolTipManager.sharedInstance().registerComponent(this); + + currentLine = new Segment(); + currentLineIndex = -1; + + setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR)); + + setFont(new Font("Courier New" /*"Monospaced"*/, Font.PLAIN, 14)); + setForeground(Color.black); + setBackground(Color.white); + + tabSizeChars = defaults.tabSize; + blockCaret = defaults.blockCaret; + styles = defaults.styles; + cols = defaults.cols; + rows = defaults.rows; + caretColor = defaults.caretColor; + selectionColor = defaults.selectionColor; + lineHighlightColor = defaults.lineHighlightColor; + lineHighlight = defaults.lineHighlight; + bracketHighlightColor = defaults.bracketHighlightColor; + bracketHighlight = defaults.bracketHighlight; + paintInvalid = defaults.paintInvalid; + eolMarkerColor = defaults.eolMarkerColor; + eolMarkers = defaults.eolMarkers; + } + + /** + * Returns if this component can be traversed by pressing the Tab key. This returns false. + *

    + * NOTE: as of Java 1.4 this method is deprecated and no longer has the desired effect because the focus subsystem + * does not call it. I've implemented a KeyEventDispatcher in JEditTextArea to handle Tabs. DPS 12-May-2010 + */ + public final boolean isManagingFocus() + { + return false; + } + + /** + * Fetch the tab size in characters. DPS 12-May-2010. + * + * @return int tab size in characters + */ + public int getTabSize() + { + return tabSizeChars; + } + + /** + * Set the tab size in characters. DPS 12-May-2010. Originally it was fixed at PlainDocument property value (8). + * + * @param size tab size in characters + */ + public void setTabSize(int size) + { + tabSizeChars = size; + } + + /** + * Returns the syntax styles used to paint colorized text. Entry n will be used to paint tokens with id = + * n. + * + * @see org.syntax.jedit.Token + */ + public final SyntaxStyle[] getStyles() + { + return styles; + } + + /** + * Sets the syntax styles used to paint colorized text. Entry n will be used to paint tokens with id = + * n. + * + * @param styles The syntax styles + * @see org.syntax.jedit.Token + */ + public final void setStyles(SyntaxStyle[] styles) + { + this.styles = styles; + repaint(); + } + + /** + * Returns the caret color. + */ + public final Color getCaretColor() + { + return caretColor; + } + + /** + * Sets the caret color. + * + * @param caretColor The caret color + */ + public final void setCaretColor(Color caretColor) + { + this.caretColor = caretColor; + invalidateSelectedLines(); + } + + /** + * Returns the selection color. + */ + public final Color getSelectionColor() + { + return selectionColor; + } + + /** + * Sets the selection color. + * + * @param selectionColor The selection color + */ + public final void setSelectionColor(Color selectionColor) + { + this.selectionColor = selectionColor; + invalidateSelectedLines(); + } + + /** + * Returns the line highlight color. + */ + public final Color getLineHighlightColor() + { + return lineHighlightColor; + } + + /** + * Sets the line highlight color. + * + * @param lineHighlightColor The line highlight color + */ + public final void setLineHighlightColor(Color lineHighlightColor) + { + this.lineHighlightColor = lineHighlightColor; + invalidateSelectedLines(); + } + + /** + * Returns true if line highlight is enabled, false otherwise. + */ + public final boolean isLineHighlightEnabled() + { + return lineHighlight; + } + + /** + * Enables or disables current line highlighting. + * + * @param lineHighlight True if current line highlight should be enabled, false otherwise + */ + public final void setLineHighlightEnabled(boolean lineHighlight) + { + this.lineHighlight = lineHighlight; + invalidateSelectedLines(); + } + + /** + * Returns the bracket highlight color. + */ + public final Color getBracketHighlightColor() + { + return bracketHighlightColor; + } + + /** + * Sets the bracket highlight color. + * + * @param bracketHighlightColor The bracket highlight color + */ + public final void setBracketHighlightColor(Color bracketHighlightColor) + { + this.bracketHighlightColor = bracketHighlightColor; + invalidateLine(textArea.getBracketLine()); + } + + /** + * Returns true if bracket highlighting is enabled, false otherwise. When bracket highlighting is enabled, the + * bracket matching the one before the caret (if any) is highlighted. + */ + public final boolean isBracketHighlightEnabled() + { + return bracketHighlight; + } + + /** + * Enables or disables bracket highlighting. When bracket highlighting is enabled, the bracket matching the one + * before the caret (if any) is highlighted. + * + * @param bracketHighlight True if bracket highlighting should be enabled, false otherwise + */ + public final void setBracketHighlightEnabled(boolean bracketHighlight) + { + this.bracketHighlight = bracketHighlight; + invalidateLine(textArea.getBracketLine()); + } + + /** + * Returns true if the caret should be drawn as a block, false otherwise. + */ + public final boolean isBlockCaretEnabled() + { + return blockCaret; + } + + /** + * Sets if the caret should be drawn as a block, false otherwise. + * + * @param blockCaret True if the caret should be drawn as a block, false otherwise. + */ + public final void setBlockCaretEnabled(boolean blockCaret) + { + this.blockCaret = blockCaret; + invalidateSelectedLines(); + } + + /** + * Returns the EOL marker color. + */ + public final Color getEOLMarkerColor() + { + return eolMarkerColor; + } + + /** + * Sets the EOL marker color. + * + * @param eolMarkerColor The EOL marker color + */ + public final void setEOLMarkerColor(Color eolMarkerColor) + { + this.eolMarkerColor = eolMarkerColor; + repaint(); + } + + /** + * Returns true if EOL markers are drawn, false otherwise. + */ + public final boolean getEOLMarkersPainted() + { + return eolMarkers; + } + + /** + * Sets if EOL markers are to be drawn. + * + * @param eolMarkers True if EOL markers should be drawn, false otherwise + */ + public final void setEOLMarkersPainted(boolean eolMarkers) + { + this.eolMarkers = eolMarkers; + repaint(); + } + + /** + * Returns true if invalid lines are painted as red tildes (~), false otherwise. + */ + public boolean getInvalidLinesPainted() + { + return paintInvalid; + } + + /** + * Sets if invalid lines are to be painted as red tildes. + * + * @param paintInvalid True if invalid lines should be drawn, false otherwise + */ + public void setInvalidLinesPainted(boolean paintInvalid) + { + this.paintInvalid = paintInvalid; + } + + /** + * Adds a custom highlight painter. + * + * @param highlight The highlight + */ + public void addCustomHighlight(Highlight highlight) + { + highlight.init(textArea, highlights); + highlights = highlight; + } + + /** + * Returns the tool tip to display at the specified location. + * + * @param evt The mouse event + */ + public String getToolTipText(MouseEvent evt) + { + // if(highlights != null) + // return highlights.getToolTipText(evt); + // else + // return null; + if (highlights != null) + { return highlights.getToolTipText(evt); - else if (this.textArea.getTokenMarker()==null) - return null; - else - return this.textArea.getSyntaxSensitiveToolTipText(evt.getX(),evt.getY()); - // int line = yToLine(evt.getY()); - // int offset = xToOffset(line,evt.getX()); - // { - // if (evt instanceof InstructionMouseEvent) { - // System.out.println("get Tool Tip Text for InstructionMouseEvent"); - // return "Instruction: "+ ((InstructionMouseEvent)evt).getLine().toString(); - // } - // else { - // return "Not a fake?";//null; - // } - // } - } - /** - * Returns the font metrics used by this component. - */ - public FontMetrics getFontMetrics() - { - return fm; - } - - /** - * Sets the font for this component. This is overridden to update the - * cached font metrics and to recalculate which lines are visible. - * @param font The font - */ - public void setFont(Font font) - { - super.setFont(font); - fm = Toolkit.getDefaultToolkit().getFontMetrics(font); - textArea.recalculateVisibleLines(); - } - - - - /** - * Repaints the text. - * @param gfx The graphics context - */ - public void paint(Graphics gfx) - { - - // Added 4/6/10 DPS to set antialiasing for text rendering - smoother letters - // Second one says choose algorithm for quality over speed - ((Graphics2D)gfx).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); - ((Graphics2D)gfx).setRenderingHint( RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY ); - - - tabSize = fm.charWidth(' ') * tabSizeChars; // was: ((Integer)textArea.getDocument().getProperty(PlainDocument.tabSizeAttribute)).intValue(); - - Rectangle clipRect = gfx.getClipBounds(); - - gfx.setColor(getBackground()); - gfx.fillRect(clipRect.x,clipRect.y,clipRect.width,clipRect.height); - - // We don't use yToLine() here because that method doesn't - // return lines past the end of the document - int height = fm.getHeight(); - int firstLine = textArea.getFirstLine(); - int firstInvalid = firstLine + clipRect.y / height; - // Because the clipRect's height is usually an even multiple - // of the font height, we subtract 1 from it, otherwise one - // too many lines will always be painted. - int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height; - - try - { - TokenMarker tokenMarker = ((SyntaxDocument)textArea.getDocument()) - .getTokenMarker(); + } + else if (this.textArea.getTokenMarker() == null) + { + return null; + } + else + { + return this.textArea.getSyntaxSensitiveToolTipText(evt.getX(), evt.getY()); + } + // int line = yToLine(evt.getY()); + // int offset = xToOffset(line,evt.getX()); + // { + // if (evt instanceof InstructionMouseEvent) { + // System.out.println("get Tool Tip Text for InstructionMouseEvent"); + // return "Instruction: "+ ((InstructionMouseEvent)evt).getLine().toString(); + // } + // else { + // return "Not a fake?";//null; + // } + // } + } + + /** + * Returns the font metrics used by this component. + */ + public FontMetrics getFontMetrics() + { + return fm; + } + + /** + * Sets the font for this component. This is overridden to update the cached font metrics and to recalculate which + * lines are visible. + * + * @param font The font + */ + public void setFont(Font font) + { + super.setFont(font); + fm = Toolkit.getDefaultToolkit().getFontMetrics(font); + textArea.recalculateVisibleLines(); + } + + /** + * Repaints the text. + * + * @param gfx The graphics context + */ + public void paint(Graphics gfx) + { + + // Added 4/6/10 DPS to set antialiasing for text rendering - smoother letters + // Second one says choose algorithm for quality over speed + ((Graphics2D) gfx).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + ((Graphics2D) gfx).setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + + + tabSize = fm.charWidth(' ') * tabSizeChars; // was: ((Integer)textArea.getDocument().getProperty(PlainDocument.tabSizeAttribute)).intValue(); + + Rectangle clipRect = gfx.getClipBounds(); + + gfx.setColor(getBackground()); + gfx.fillRect(clipRect.x, clipRect.y, clipRect.width, clipRect.height); + + // We don't use yToLine() here because that method doesn't + // return lines past the end of the document + int height = fm.getHeight(); + int firstLine = textArea.getFirstLine(); + int firstInvalid = firstLine + clipRect.y / height; + // Because the clipRect's height is usually an even multiple + // of the font height, we subtract 1 from it, otherwise one + // too many lines will always be painted. + int lastInvalid = firstLine + (clipRect.y + clipRect.height - 1) / height; + + try + { + TokenMarker tokenMarker = ((SyntaxDocument) textArea.getDocument()) + .getTokenMarker(); int x = textArea.getHorizontalOffset(); - - for(int line = firstInvalid; line <= lastInvalid; line++) + + for (int line = firstInvalid; line <= lastInvalid; line++) { - paintLine(gfx,tokenMarker,line,x); + paintLine(gfx, tokenMarker, line, x); } - - if(tokenMarker != null && tokenMarker.isNextLineRequested()) + + if (tokenMarker != null && tokenMarker.isNextLineRequested()) { - int h = clipRect.y + clipRect.height; - repaint(0,h,getWidth(),getHeight() - h); + int h = clipRect.y + clipRect.height; + repaint(0, h, getWidth(), getHeight() - h); } - } - catch(Exception e) - { - System.err.println("Error repainting line" - + " range {" + firstInvalid + "," - + lastInvalid + "}:"); - e.printStackTrace(); - } - } - - /** - * Marks a line as needing a repaint. - * @param line The line to invalidate - */ - public final void invalidateLine(int line) - { - repaint(0,textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(), - getWidth(),fm.getHeight()); - } - - /** - * Marks a range of lines as needing a repaint. - * @param firstLine The first line to invalidate - * @param lastLine The last line to invalidate - */ - public final void invalidateLineRange(int firstLine, int lastLine) - { - repaint(0,textArea.lineToY(firstLine) + fm.getMaxDescent() + fm.getLeading(), - getWidth(),(lastLine - firstLine + 1) * fm.getHeight()); - } - - /** - * Repaints the lines containing the selection. - */ - public final void invalidateSelectedLines() - { - invalidateLineRange(textArea.getSelectionStartLine(), + } + catch (Exception e) + { + System.err.println("Error repainting line" + + " range {" + firstInvalid + "," + + lastInvalid + "}:"); + e.printStackTrace(); + } + } + + /** + * Marks a line as needing a repaint. + * + * @param line The line to invalidate + */ + public final void invalidateLine(int line) + { + repaint(0, textArea.lineToY(line) + fm.getMaxDescent() + fm.getLeading(), + getWidth(), fm.getHeight()); + } + + /** + * Marks a range of lines as needing a repaint. + * + * @param firstLine The first line to invalidate + * @param lastLine The last line to invalidate + */ + public final void invalidateLineRange(int firstLine, int lastLine) + { + repaint(0, textArea.lineToY(firstLine) + fm.getMaxDescent() + fm.getLeading(), + getWidth(), (lastLine - firstLine + 1) * fm.getHeight()); + } + + /** + * Repaints the lines containing the selection. + */ + public final void invalidateSelectedLines() + { + invalidateLineRange(textArea.getSelectionStartLine(), textArea.getSelectionEndLine()); - } - - /** - * Implementation of TabExpander interface. Returns next tab stop after - * a specified point. - * @param x The x co-ordinate - * @param tabOffset Ignored - * @return The next tab stop after x - */ - public float nextTabStop(float x, int tabOffset) - { - int offset = textArea.getHorizontalOffset(); - int ntabs = ((int)x - offset) / tabSize; - return (ntabs + 1) * tabSize + offset; - } - - /** - * Returns the painter's preferred size. - */ - public Dimension getPreferredSize() - { - Dimension dim = new Dimension(); - dim.width = fm.charWidth('w') * cols; - dim.height = fm.getHeight() * rows; - return dim; - } - - - /** - * Returns the painter's minimum size. - */ - public Dimension getMinimumSize() - { - return getPreferredSize(); - } - - // package-private members - int currentLineIndex; - Token currentLineTokens; - Segment currentLine; - - // protected members - protected JEditTextArea textArea; - - protected SyntaxStyle[] styles; - protected Color caretColor; - protected Color selectionColor; - protected Color lineHighlightColor; - protected Color bracketHighlightColor; - protected Color eolMarkerColor; - - protected boolean blockCaret; - protected boolean lineHighlight; - protected boolean bracketHighlight; - protected boolean paintInvalid; - protected boolean eolMarkers; - protected int cols; - protected int rows; - - protected int tabSize, tabSizeChars; - protected FontMetrics fm; - - protected Highlight highlights; - - protected void paintLine(Graphics gfx, TokenMarker tokenMarker, - int line, int x) - {//System.out.println("paintLine "+ (++count)); - Font defaultFont = getFont(); - Color defaultColor = getForeground(); - - currentLineIndex = line; - int y = textArea.lineToY(line); - - if(line < 0 || line >= textArea.getLineCount()) - { - if(paintInvalid) + } + + /** + * Implementation of TabExpander interface. Returns next tab stop after a specified point. + * + * @param x The x co-ordinate + * @param tabOffset Ignored + * @return The next tab stop after x + */ + public float nextTabStop(float x, int tabOffset) + { + int offset = textArea.getHorizontalOffset(); + int ntabs = ((int) x - offset) / tabSize; + return (ntabs + 1) * tabSize + offset; + } + + /** + * Returns the painter's preferred size. + */ + public Dimension getPreferredSize() + { + Dimension dim = new Dimension(); + dim.width = fm.charWidth('w') * cols; + dim.height = fm.getHeight() * rows; + return dim; + } + + /** + * Returns the painter's minimum size. + */ + public Dimension getMinimumSize() + { + return getPreferredSize(); + } + + protected void paintLine(Graphics gfx, TokenMarker tokenMarker, + int line, int x) + {//System.out.println("paintLine "+ (++count)); + Font defaultFont = getFont(); + Color defaultColor = getForeground(); + + currentLineIndex = line; + int y = textArea.lineToY(line); + + if (line < 0 || line >= textArea.getLineCount()) + { + if (paintInvalid) { - paintHighlight(gfx,line,y); - styles[Token.INVALID].setGraphicsFlags(gfx,defaultFont); - gfx.drawString("~",0,y + fm.getHeight()); + paintHighlight(gfx, line, y); + styles[Token.INVALID].setGraphicsFlags(gfx, defaultFont); + gfx.drawString("~", 0, y + fm.getHeight()); } - } - else if(tokenMarker == null) - { - paintPlainLine(gfx,line,defaultFont,defaultColor,x,y); - } - else - { - paintSyntaxLine(gfx,tokenMarker,line,defaultFont, - defaultColor,x,y); - } - } - - protected void paintPlainLine(Graphics gfx, int line, Font defaultFont, - Color defaultColor, int x, int y) - { - paintHighlight(gfx,line,y); - textArea.getLineText(line,currentLine); - - gfx.setFont(defaultFont); - gfx.setColor(defaultColor); - - y += fm.getHeight(); - x = Utilities.drawTabbedText(currentLine,x,y,gfx,this,0); - - if(eolMarkers) - { + } + else if (tokenMarker == null) + { + paintPlainLine(gfx, line, defaultFont, defaultColor, x, y); + } + else + { + paintSyntaxLine(gfx, tokenMarker, line, defaultFont, + defaultColor, x, y); + } + } + + protected void paintPlainLine(Graphics gfx, int line, Font defaultFont, + Color defaultColor, int x, int y) + { + paintHighlight(gfx, line, y); + textArea.getLineText(line, currentLine); + + gfx.setFont(defaultFont); + gfx.setColor(defaultColor); + + y += fm.getHeight(); + x = Utilities.drawTabbedText(currentLine, x, y, gfx, this, 0); + + if (eolMarkers) + { gfx.setColor(eolMarkerColor); - gfx.drawString(".",x,y); - } - } - // private int count=0; - protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker, - int line, Font defaultFont, Color defaultColor, int x, int y) - {//System.out.println("paintSyntaxLine line "+ line); - textArea.getLineText(currentLineIndex,currentLine); - currentLineTokens = tokenMarker.markTokens(currentLine, + gfx.drawString(".", x, y); + } + } + + // private int count=0; + protected void paintSyntaxLine(Graphics gfx, TokenMarker tokenMarker, + int line, Font defaultFont, Color defaultColor, int x, int y) + {//System.out.println("paintSyntaxLine line "+ line); + textArea.getLineText(currentLineIndex, currentLine); + currentLineTokens = tokenMarker.markTokens(currentLine, currentLineIndex); - - paintHighlight(gfx,line,y); - - gfx.setFont(defaultFont); - gfx.setColor(defaultColor); - y += fm.getHeight(); - x = SyntaxUtilities.paintSyntaxLine(currentLine, - currentLineTokens,styles,this,gfx,x,y); - // count++; - // if (count % 100 == 10) { - // textArea.setToolTipText("Setting Text at Count of "+count); System.out.println("set tool tip"); - // } - // if (count % 100 == 60) { - // textArea.setToolTipText(null);System.out.println("reset tool tip"); - // } - //System.out.println("SyntaxUtilities.paintSyntaxLine "+ (++count)); - if(eolMarkers) - { + + paintHighlight(gfx, line, y); + + gfx.setFont(defaultFont); + gfx.setColor(defaultColor); + y += fm.getHeight(); + x = SyntaxUtilities.paintSyntaxLine(currentLine, + currentLineTokens, styles, this, gfx, x, y); + // count++; + // if (count % 100 == 10) { + // textArea.setToolTipText("Setting Text at Count of "+count); System.out.println("set tool tip"); + // } + // if (count % 100 == 60) { + // textArea.setToolTipText(null);System.out.println("reset tool tip"); + // } + //System.out.println("SyntaxUtilities.paintSyntaxLine "+ (++count)); + if (eolMarkers) + { gfx.setColor(eolMarkerColor); - gfx.drawString(".",x,y); - } - } - - protected void paintHighlight(Graphics gfx, int line, int y) - {//System.out.println("paintHighlight "+ (++count)); - if(line >= textArea.getSelectionStartLine() - && line <= textArea.getSelectionEndLine()) - paintLineHighlight(gfx,line,y); - - if(highlights != null) - highlights.paintHighlight(gfx,line,y); - - if(bracketHighlight && line == textArea.getBracketLine()) - paintBracketHighlight(gfx,line,y); - - if(line == textArea.getCaretLine()) - paintCaret(gfx,line,y); - } - - protected void paintLineHighlight(Graphics gfx, int line, int y) - {//System.out.println("paintLineHighlight "+ (++count)); - int height = fm.getHeight(); - y += fm.getLeading() + fm.getMaxDescent(); - - int selectionStart = textArea.getSelectionStart(); - int selectionEnd = textArea.getSelectionEnd(); - - if(selectionStart == selectionEnd) - { - if(lineHighlight) + gfx.drawString(".", x, y); + } + } + + protected void paintHighlight(Graphics gfx, int line, int y) + {//System.out.println("paintHighlight "+ (++count)); + if (line >= textArea.getSelectionStartLine() + && line <= textArea.getSelectionEndLine()) + { + paintLineHighlight(gfx, line, y); + } + + if (highlights != null) + { + highlights.paintHighlight(gfx, line, y); + } + + if (bracketHighlight && line == textArea.getBracketLine()) + { + paintBracketHighlight(gfx, line, y); + } + + if (line == textArea.getCaretLine()) + { + paintCaret(gfx, line, y); + } + } + + protected void paintLineHighlight(Graphics gfx, int line, int y) + {//System.out.println("paintLineHighlight "+ (++count)); + int height = fm.getHeight(); + y += fm.getLeading() + fm.getMaxDescent(); + + int selectionStart = textArea.getSelectionStart(); + int selectionEnd = textArea.getSelectionEnd(); + + if (selectionStart == selectionEnd) + { + if (lineHighlight) { - gfx.setColor(lineHighlightColor); - gfx.fillRect(0,y,getWidth(),height); + gfx.setColor(lineHighlightColor); + gfx.fillRect(0, y, getWidth(), height); } - } - else - { + } + else + { gfx.setColor(selectionColor); - + int selectionStartLine = textArea.getSelectionStartLine(); int selectionEndLine = textArea.getSelectionEndLine(); int lineStart = textArea.getLineStartOffset(line); - + int x1, x2; - if(textArea.isSelectionRectangular()) + if (textArea.isSelectionRectangular()) { - int lineLen = textArea.getLineLength(line); - x1 = textArea._offsetToX(line,Math.min(lineLen, - selectionStart - textArea.getLineStartOffset( - selectionStartLine))); - x2 = textArea._offsetToX(line,Math.min(lineLen, - selectionEnd - textArea.getLineStartOffset( - selectionEndLine))); - if(x1 == x2) - x2++; + int lineLen = textArea.getLineLength(line); + x1 = textArea._offsetToX(line, Math.min(lineLen, + selectionStart - textArea.getLineStartOffset( + selectionStartLine))); + x2 = textArea._offsetToX(line, Math.min(lineLen, + selectionEnd - textArea.getLineStartOffset( + selectionEndLine))); + if (x1 == x2) + { + x2++; + } } - else if(selectionStartLine == selectionEndLine) + else if (selectionStartLine == selectionEndLine) { - x1 = textArea._offsetToX(line, - selectionStart - lineStart); - x2 = textArea._offsetToX(line, - selectionEnd - lineStart); + x1 = textArea._offsetToX(line, + selectionStart - lineStart); + x2 = textArea._offsetToX(line, + selectionEnd - lineStart); } - else if(line == selectionStartLine) + else if (line == selectionStartLine) { - x1 = textArea._offsetToX(line, - selectionStart - lineStart); - x2 = getWidth(); + x1 = textArea._offsetToX(line, + selectionStart - lineStart); + x2 = getWidth(); } - else if(line == selectionEndLine) + else if (line == selectionEndLine) { - x1 = 0; - x2 = textArea._offsetToX(line, - selectionEnd - lineStart); + x1 = 0; + x2 = textArea._offsetToX(line, + selectionEnd - lineStart); } else { - x1 = 0; - x2 = getWidth(); + x1 = 0; + x2 = getWidth(); } - - // "inlined" min/max() - gfx.fillRect(x1 > x2 ? x2 : x1,y,x1 > x2 ? - (x1 - x2) : (x2 - x1),height); - } - - } - - protected void paintBracketHighlight(Graphics gfx, int line, int y) - { - int position = textArea.getBracketPosition(); - if(position == -1) + + // "inlined" min/max() + gfx.fillRect(x1 > x2 ? x2 : x1, y, x1 > x2 ? + (x1 - x2) : (x2 - x1), height); + } + + } + + protected void paintBracketHighlight(Graphics gfx, int line, int y) + { + int position = textArea.getBracketPosition(); + if (position == -1) + { return; - y += fm.getLeading() + fm.getMaxDescent(); - int x = textArea._offsetToX(line,position); - gfx.setColor(bracketHighlightColor); - // Hack!!! Since there is no fast way to get the character - // from the bracket matching routine, we use ( since all - // brackets probably have the same width anyway - gfx.drawRect(x,y,fm.charWidth('(') - 1, + } + y += fm.getLeading() + fm.getMaxDescent(); + int x = textArea._offsetToX(line, position); + gfx.setColor(bracketHighlightColor); + // Hack!!! Since there is no fast way to get the character + // from the bracket matching routine, we use ( since all + // brackets probably have the same width anyway + gfx.drawRect(x, y, fm.charWidth('(') - 1, fm.getHeight() - 1); - } - - protected void paintCaret(Graphics gfx, int line, int y) - { - if(textArea.isCaretVisible()) - { - int offset = textArea.getCaretPosition() - - textArea.getLineStartOffset(line); - int caretX = textArea._offsetToX(line,offset); + } + + protected void paintCaret(Graphics gfx, int line, int y) + { + if (textArea.isCaretVisible()) + { + int offset = textArea.getCaretPosition() + - textArea.getLineStartOffset(line); + int caretX = textArea._offsetToX(line, offset); int caretWidth = ((blockCaret || - textArea.isOverwriteEnabled()) ? - fm.charWidth('w') : 1); + textArea.isOverwriteEnabled()) ? + fm.charWidth('w') : 1); y += fm.getLeading() + fm.getMaxDescent(); int height = fm.getHeight(); - + gfx.setColor(caretColor); - - if(textArea.isOverwriteEnabled()) + + if (textArea.isOverwriteEnabled()) { - gfx.fillRect(caretX,y + height - 1, - caretWidth,1); + gfx.fillRect(caretX, y + height - 1, + caretWidth, 1); } else { - gfx.drawRect(caretX,y,caretWidth, height - 1); + gfx.drawRect(caretX, y, caretWidth, height - 1); } - } - } - } + } + } + + /** + * Highlight interface. + */ + public interface Highlight + { + /** + * Called after the highlight painter has been added. + * + * @param textArea The text area + * @param next The painter this one should delegate to + */ + void init(JEditTextArea textArea, Highlight next); + + /** + * This should paint the highlight and delgate to the next highlight painter. + * + * @param gfx The graphics context + * @param line The line number + * @param y The y co-ordinate of the line + */ + void paintHighlight(Graphics gfx, int line, int y); + + /** + * Returns the tool tip to display at the specified location. If this highlighter doesn't know what to display, + * it should delegate to the next highlight painter. + * + * @param evt The mouse event + */ + String getToolTipText(MouseEvent evt); + } +} diff --git a/src/main/java/mars/venus/editors/jeditsyntax/TextUtilities.java b/src/main/java/mars/venus/editors/jeditsyntax/TextUtilities.java index f751e1c..90e74e5 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/TextUtilities.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/TextUtilities.java @@ -9,176 +9,212 @@ package mars.venus.editors.jeditsyntax; -import javax.swing.text.*; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; /** * Class with several utility functions used by the text area component. + * * @author Slava Pestov * @version $Id: TextUtilities.java,v 1.4 1999/12/13 03:40:30 sp Exp $ */ public class TextUtilities { - /** - * Returns the offset of the bracket matching the one at the - * specified offset of the document, or -1 if the bracket is - * unmatched (or if the character is not a bracket). - * @param doc The document - * @param offset The offset - * @exception BadLocationException If an out-of-bounds access - * was attempted on the document text - */ - public static int findMatchingBracket(Document doc, int offset) - throws BadLocationException - { - if(doc.getLength() == 0) - return -1; - char c = doc.getText(offset,1).charAt(0); - char cprime; // c` - corresponding character - boolean direction; // true = back, false = forward + /** + * Returns the offset of the bracket matching the one at the specified offset of the document, or -1 if the bracket + * is unmatched (or if the character is not a bracket). + * + * @param doc The document + * @param offset The offset + * @throws BadLocationException If an out-of-bounds access was attempted on the document text + */ + public static int findMatchingBracket(Document doc, int offset) + throws BadLocationException + { + if (doc.getLength() == 0) + { + return -1; + } + char c = doc.getText(offset, 1).charAt(0); + char cprime; // c` - corresponding character + boolean direction; // true = back, false = forward - switch(c) - { - case '(': cprime = ')'; direction = false; break; - case ')': cprime = '('; direction = true; break; - case '[': cprime = ']'; direction = false; break; - case ']': cprime = '['; direction = true; break; - case '{': cprime = '}'; direction = false; break; - case '}': cprime = '{'; direction = true; break; - default: return -1; - } + switch (c) + { + case '(': + cprime = ')'; + direction = false; + break; + case ')': + cprime = '('; + direction = true; + break; + case '[': + cprime = ']'; + direction = false; + break; + case ']': + cprime = '['; + direction = true; + break; + case '{': + cprime = '}'; + direction = false; + break; + case '}': + cprime = '{'; + direction = true; + break; + default: + return -1; + } - int count; + int count; - // How to merge these two cases is left as an exercise - // for the reader. + // How to merge these two cases is left as an exercise + // for the reader. - // Go back or forward - if(direction) - { - // Count is 1 initially because we have already - // `found' one closing bracket - count = 1; + // Go back or forward + if (direction) + { + // Count is 1 initially because we have already + // `found' one closing bracket + count = 1; - // Get text[0,offset-1]; - String text = doc.getText(0,offset); + // Get text[0,offset-1]; + String text = doc.getText(0, offset); - // Scan backwards - for(int i = offset - 1; i >= 0; i--) - { - // If text[i] == c, we have found another - // closing bracket, therefore we will need - // two opening brackets to complete the - // match. - char x = text.charAt(i); - if(x == c) - count++; + // Scan backwards + for (int i = offset - 1; i >= 0; i--) + { + // If text[i] == c, we have found another + // closing bracket, therefore we will need + // two opening brackets to complete the + // match. + char x = text.charAt(i); + if (x == c) + { + count++; + } - // If text[i] == cprime, we have found a - // opening bracket, so we return i if - // --count == 0 - else if(x == cprime) - { - if(--count == 0) - return i; - } - } - } - else - { - // Count is 1 initially because we have already - // `found' one opening bracket - count = 1; + // If text[i] == cprime, we have found a + // opening bracket, so we return i if + // --count == 0 + else if (x == cprime) + { + if (--count == 0) + { + return i; + } + } + } + } + else + { + // Count is 1 initially because we have already + // `found' one opening bracket + count = 1; - // So we don't have to + 1 in every loop - offset++; + // So we don't have to + 1 in every loop + offset++; - // Number of characters to check - int len = doc.getLength() - offset; + // Number of characters to check + int len = doc.getLength() - offset; - // Get text[offset+1,len]; - String text = doc.getText(offset,len); + // Get text[offset+1,len]; + String text = doc.getText(offset, len); - // Scan forwards - for(int i = 0; i < len; i++) - { - // If text[i] == c, we have found another - // opening bracket, therefore we will need - // two closing brackets to complete the - // match. - char x = text.charAt(i); + // Scan forwards + for (int i = 0; i < len; i++) + { + // If text[i] == c, we have found another + // opening bracket, therefore we will need + // two closing brackets to complete the + // match. + char x = text.charAt(i); - if(x == c) - count++; + if (x == c) + { + count++; + } - // If text[i] == cprime, we have found an - // closing bracket, so we return i if - // --count == 0 - else if(x == cprime) - { - if(--count == 0) - return i + offset; - } - } - } + // If text[i] == cprime, we have found an + // closing bracket, so we return i if + // --count == 0 + else if (x == cprime) + { + if (--count == 0) + { + return i + offset; + } + } + } + } - // Nothing found - return -1; - } + // Nothing found + return -1; + } - /** - * Locates the start of the word at the specified position. - * @param line The text - * @param pos The position - */ - public static int findWordStart(String line, int pos, String noWordSep) - { - char ch = line.charAt(pos - 1); + /** + * Locates the start of the word at the specified position. + * + * @param line The text + * @param pos The position + */ + public static int findWordStart(String line, int pos, String noWordSep) + { + char ch = line.charAt(pos - 1); - if(noWordSep == null) - noWordSep = ""; - boolean selectNoLetter = (!Character.isLetterOrDigit(ch) - && noWordSep.indexOf(ch) == -1); + if (noWordSep == null) + { + noWordSep = ""; + } + boolean selectNoLetter = (!Character.isLetterOrDigit(ch) + && noWordSep.indexOf(ch) == -1); - int wordStart = 0; - for(int i = pos - 1; i >= 0; i--) - { - ch = line.charAt(i); - if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) && - noWordSep.indexOf(ch) == -1)) - { - wordStart = i + 1; - break; - } - } + int wordStart = 0; + for (int i = pos - 1; i >= 0; i--) + { + ch = line.charAt(i); + if (selectNoLetter ^ (!Character.isLetterOrDigit(ch) && + noWordSep.indexOf(ch) == -1)) + { + wordStart = i + 1; + break; + } + } - return wordStart; - } + return wordStart; + } - /** - * Locates the end of the word at the specified position. - * @param line The text - * @param pos The position - */ - public static int findWordEnd(String line, int pos, String noWordSep) - { - char ch = line.charAt(pos); + /** + * Locates the end of the word at the specified position. + * + * @param line The text + * @param pos The position + */ + public static int findWordEnd(String line, int pos, String noWordSep) + { + char ch = line.charAt(pos); - if(noWordSep == null) - noWordSep = ""; - boolean selectNoLetter = (!Character.isLetterOrDigit(ch) - && noWordSep.indexOf(ch) == -1); + if (noWordSep == null) + { + noWordSep = ""; + } + boolean selectNoLetter = (!Character.isLetterOrDigit(ch) + && noWordSep.indexOf(ch) == -1); - int wordEnd = line.length(); - for(int i = pos; i < line.length(); i++) - { - ch = line.charAt(i); - if(selectNoLetter ^ (!Character.isLetterOrDigit(ch) && - noWordSep.indexOf(ch) == -1)) - { - wordEnd = i; - break; - } - } - return wordEnd; - } + int wordEnd = line.length(); + for (int i = pos; i < line.length(); i++) + { + ch = line.charAt(i); + if (selectNoLetter ^ (!Character.isLetterOrDigit(ch) && + noWordSep.indexOf(ch) == -1)) + { + wordEnd = i; + break; + } + } + return wordEnd; + } } diff --git a/src/main/java/mars/venus/editors/jeditsyntax/tokenmarker/MIPSTokenMarker.java b/src/main/java/mars/venus/editors/jeditsyntax/tokenmarker/MIPSTokenMarker.java index c1efae8..64a603b 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/tokenmarker/MIPSTokenMarker.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/tokenmarker/MIPSTokenMarker.java @@ -7,125 +7,190 @@ * remains intact in all source distributions of this package. */ - package mars.venus.editors.jeditsyntax.tokenmarker; +package mars.venus.editors.jeditsyntax.tokenmarker; - import mars.venus.editors.jeditsyntax.*; - import mars.mips.instructions.*; - import mars.assembler.*; - import javax.swing.text.Segment; - import java.util.*; +import mars.assembler.Directives; +import mars.mips.instructions.BasicInstruction; +import mars.mips.instructions.Instruction; +import mars.venus.editors.jeditsyntax.KeywordMap; +import mars.venus.editors.jeditsyntax.PopupHelpItem; + +import javax.swing.text.Segment; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.TreeSet; /** * MIPS token marker. * * @author Pete Sanderson (2010) and Slava Pestov (1999) */ - public class MIPSTokenMarker extends TokenMarker - { - public MIPSTokenMarker() - { - this(getKeywords()); - } - - public MIPSTokenMarker(KeywordMap keywords) - { - this.keywords = keywords; - } - - public static String[] getMIPSTokenLabels() - { - if (tokenLabels == null) - { +public class MIPSTokenMarker extends TokenMarker +{ + // private members + private static KeywordMap cKeywords; + + private static String[] tokenLabels, tokenExamples; + + private final KeywordMap keywords; + + private int lastOffset; + + private int lastKeyword; + + + public MIPSTokenMarker() + { + this(getKeywords()); + } + + + public MIPSTokenMarker(KeywordMap keywords) + { + this.keywords = keywords; + } + + public static String[] getMIPSTokenLabels() + { + if (tokenLabels == null) + { tokenLabels = new String[Token.ID_COUNT]; tokenLabels[Token.COMMENT1] = "Comment"; tokenLabels[Token.LITERAL1] = "String literal"; tokenLabels[Token.LITERAL2] = "Character literal"; - tokenLabels[Token.LABEL] = "Label"; + tokenLabels[Token.LABEL] = "Label"; tokenLabels[Token.KEYWORD1] = "MIPS instruction"; tokenLabels[Token.KEYWORD2] = "Assembler directive"; tokenLabels[Token.KEYWORD3] = "Register"; - tokenLabels[Token.INVALID] = "In-progress, invalid"; - tokenLabels[Token.MACRO_ARG]= "Macro parameter"; - } - return tokenLabels; - } - - public static String[] getMIPSTokenExamples() - { - if (tokenExamples == null) - { + tokenLabels[Token.INVALID] = "In-progress, invalid"; + tokenLabels[Token.MACRO_ARG] = "Macro parameter"; + } + return tokenLabels; + } + + public static String[] getMIPSTokenExamples() + { + if (tokenExamples == null) + { tokenExamples = new String[Token.ID_COUNT]; tokenExamples[Token.COMMENT1] = "# Load"; tokenExamples[Token.LITERAL1] = "\"First\""; tokenExamples[Token.LITERAL2] = "'\\n'"; - tokenExamples[Token.LABEL] = "main:"; + tokenExamples[Token.LABEL] = "main:"; tokenExamples[Token.KEYWORD1] = "lui"; tokenExamples[Token.KEYWORD2] = ".text"; tokenExamples[Token.KEYWORD3] = "$zero"; - tokenExamples[Token.INVALID] = "\"Regi"; - tokenExamples[Token.MACRO_ARG]= "%arg"; - } - return tokenExamples; - } - - - - public byte markTokensImpl(byte token, Segment line, int lineIndex) - { - char[] array = line.array; - int offset = line.offset; - lastOffset = offset; - lastKeyword = offset; - int length = line.count + offset; - boolean backslash = false; - - loop: - for(int i = offset; i < length; i++) - { - int i1 = (i+1); - - char c = array[i]; - if(c == '\\') + tokenExamples[Token.INVALID] = "\"Regi"; + tokenExamples[Token.MACRO_ARG] = "%arg"; + } + return tokenExamples; + } + + /** + * Get KeywordMap containing all MIPS key words. This includes all instruction mnemonics, assembler directives, and + * register names. + * + * @return KeywordMap where key is the keyword and associated value is the token type (e.g. Token.KEYWORD1). + */ + + + public static KeywordMap getKeywords() + { + if (cKeywords == null) + { + cKeywords = new KeywordMap(false); + // add Instruction mnemonics + java.util.ArrayList instructionSet = mars.Globals.instructionSet.getInstructionList(); + for (int i = 0; i < instructionSet.size(); i++) { - backslash = !backslash; - continue; + cKeywords.add(((mars.mips.instructions.Instruction) instructionSet.get(i)).getName(), Token.KEYWORD1); } - - switch(token) + // add assembler directives + java.util.ArrayList directiveSet = mars.assembler.Directives.getDirectiveList(); + for (int i = 0; i < directiveSet.size(); i++) { - case Token.NULL: - switch(c) - { - case '"': - doKeyword(line,i,c); - if(backslash) - backslash = false; - else - { - addToken(i - lastOffset,token); - token = Token.LITERAL1; - lastOffset = lastKeyword = i; - } - break; - case '\'': - doKeyword(line,i,c); - if(backslash) - backslash = false; - else - { - addToken(i - lastOffset,token); - token = Token.LITERAL2; - lastOffset = lastKeyword = i; - } - break; - case ':': + cKeywords.add(((mars.assembler.Directives) directiveSet.get(i)).getName(), Token.KEYWORD2); + } + // add integer register file + mars.mips.hardware.Register[] registerFile = mars.mips.hardware.RegisterFile.getRegisters(); + for (int i = 0; i < registerFile.length; i++) + { + cKeywords.add(registerFile[i].getName(), Token.KEYWORD3); + cKeywords.add("$" + i, Token.KEYWORD3); // also recognize $0, $1, $2, etc + } + // add Coprocessor 1 (floating point) register file + mars.mips.hardware.Register[] coprocessor1RegisterFile = mars.mips.hardware.Coprocessor1.getRegisters(); + for (int i = 0; i < coprocessor1RegisterFile.length; i++) + { + cKeywords.add(coprocessor1RegisterFile[i].getName(), Token.KEYWORD3); + } + // Note: Coprocessor 0 registers referenced only by number: $8, $12, $13, $14. These are already in the map + + } + return cKeywords; + } + + public byte markTokensImpl(byte token, Segment line, int lineIndex) + { + char[] array = line.array; + int offset = line.offset; + lastOffset = offset; + lastKeyword = offset; + int length = line.count + offset; + boolean backslash = false; + + loop: + for (int i = offset; i < length; i++) + { + int i1 = (i + 1); + + char c = array[i]; + if (c == '\\') + { + backslash = !backslash; + continue; + } + + switch (token) + { + case Token.NULL: + switch (c) + { + case '"': + doKeyword(line, i, c); + if (backslash) + { + backslash = false; + } + else + { + addToken(i - lastOffset, token); + token = Token.LITERAL1; + lastOffset = lastKeyword = i; + } + break; + case '\'': + doKeyword(line, i, c); + if (backslash) + { + backslash = false; + } + else + { + addToken(i - lastOffset, token); + token = Token.LITERAL2; + lastOffset = lastKeyword = i; + } + break; + case ':': /* Original code for ':' case, replaced 3 Aug 2010. Details below. if(lastKeyword == offset) { // Commenting out this IF statement permits recognition of keywords // used as labels when terminated with ":". The most common example // is "b:" (where b is mnemonic for branch instruction). DPS 6-July-2010. // - // if(doKeyword(line,i,c)) + // if(doKeyword(line,i,c)) // break; backslash = false; addToken(i1 - lastOffset,Token.LABEL); @@ -135,416 +200,429 @@ break; break; */ - // Replacement code 3 Aug 2010. Will recognize label definitions when: - // (1) label is same as instruction name, (2) label begins after column 1, - // (3) there are spaces between label name and colon, (4) label is valid - // MIPS identifier (otherwise would catch, say, 0 (zero) in .word 0:10) + // Replacement code 3 Aug 2010. Will recognize label definitions when: + // (1) label is same as instruction name, (2) label begins after column 1, + // (3) there are spaces between label name and colon, (4) label is valid + // MIPS identifier (otherwise would catch, say, 0 (zero) in .word 0:10) + backslash = false; + //String lab = new String(array, lastOffset, i1-lastOffset-1).trim(); + boolean validIdentifier = false; + try + { + validIdentifier = mars.assembler.TokenTypes.isValidIdentifier(new String(array, lastOffset, i1 - lastOffset - 1).trim()); + } + catch (StringIndexOutOfBoundsException e) + { + validIdentifier = false; + } + if (validIdentifier) + { + addToken(i1 - lastOffset, Token.LABEL); + lastOffset = lastKeyword = i1; + } + break; + case '#': + backslash = false; + doKeyword(line, i, c); + if (length - i >= 1) + { + addToken(i - lastOffset, token); + addToken(length - i, Token.COMMENT1); + lastOffset = lastKeyword = length; + break loop; + } + break; + default: + backslash = false; + // . and $ added 4/6/10 DPS; % added 12/12 M.Sekhavat + if (!Character.isLetterOrDigit(c) + && c != '_' && c != '.' && c != '$' && c != '%') + { + doKeyword(line, i, c); + } + break; + } + break; + case Token.LITERAL1: + if (backslash) + { backslash = false; - //String lab = new String(array, lastOffset, i1-lastOffset-1).trim(); - boolean validIdentifier = false; - try { - validIdentifier = mars.assembler.TokenTypes.isValidIdentifier(new String(array, lastOffset, i1-lastOffset-1).trim()); - } - catch (StringIndexOutOfBoundsException e) { - validIdentifier = false; - } - if (validIdentifier) { - addToken(i1 - lastOffset,Token.LABEL); - lastOffset = lastKeyword = i1; - } - break; - case '#': + } + else if (c == '"') + { + addToken(i1 - lastOffset, token); + token = Token.NULL; + lastOffset = lastKeyword = i1; + } + break; + case Token.LITERAL2: + if (backslash) + { backslash = false; - doKeyword(line,i,c); - if(length - i >= 1) - { - addToken(i - lastOffset,token); - addToken(length - i,Token.COMMENT1); - lastOffset = lastKeyword = length; - break loop; - } - break; - default: - backslash = false; - // . and $ added 4/6/10 DPS; % added 12/12 M.Sekhavat - if(!Character.isLetterOrDigit(c) - && c != '_' && c != '.' && c != '$' && c!='%') - doKeyword(line,i,c); - break; - } - break; - case Token.LITERAL1: - if(backslash) - backslash = false; - else if(c == '"') - { - addToken(i1 - lastOffset,token); - token = Token.NULL; - lastOffset = lastKeyword = i1; - } - break; - case Token.LITERAL2: - if(backslash) - backslash = false; - else if(c == '\'') - { - addToken(i1 - lastOffset,Token.LITERAL1); - token = Token.NULL; - lastOffset = lastKeyword = i1; - } - break; - default: - throw new InternalError("Invalid state: " - + token); + } + else if (c == '\'') + { + addToken(i1 - lastOffset, Token.LITERAL1); + token = Token.NULL; + lastOffset = lastKeyword = i1; + } + break; + default: + throw new InternalError("Invalid state: " + + token); } - } - - if(token == Token.NULL) - doKeyword(line,length,'\0'); - - switch(token) - { + } + + if (token == Token.NULL) + { + doKeyword(line, length, '\0'); + } + + switch (token) + { case Token.LITERAL1: case Token.LITERAL2: - addToken(length - lastOffset,Token.INVALID); - token = Token.NULL; - break; + addToken(length - lastOffset, Token.INVALID); + token = Token.NULL; + break; case Token.KEYWORD2: - addToken(length - lastOffset,token); - if(!backslash) - token = Token.NULL; + addToken(length - lastOffset, token); + if (!backslash) + { + token = Token.NULL; + } default: - addToken(length - lastOffset,token); - break; - } - - return token; - } - - - /** - * Construct and return any appropriate help information for - * the given token. - * @param token the pertinent Token object - * @param tokenText the source String that matched to the token - * @return ArrayList of PopupHelpItem objects, one per match. - */ - public ArrayList getTokenExactMatchHelp(Token token, String tokenText) - { - ArrayList matches = null; - if (token != null && token.id == Token.KEYWORD1) { - ArrayList instrMatches = mars.Globals.instructionSet.matchOperator(tokenText); - if (instrMatches.size() > 0) { - int realMatches = 0; - matches = new ArrayList(); - for (int i=0; i 0) + { + int realMatches = 0; + matches = new ArrayList(); + for (int i = 0; i < instrMatches.size(); i++) + { + Instruction inst = (Instruction) instrMatches.get(i); + if (mars.Globals.getSettings().getExtendedAssemblerEnabled() || inst instanceof BasicInstruction) + { + matches.add(new PopupHelpItem(tokenText, inst.getExampleFormat(), inst.getDescription())); + realMatches++; + } + } + if (realMatches == 0) + { + matches.add(new PopupHelpItem(tokenText, tokenText, "(is not a basic instruction)")); + } } - } - if (token != null && token.id == Token.KEYWORD2) { + } + if (token != null && token.id == Token.KEYWORD2) + { Directives dir = Directives.matchDirective(tokenText); - if (dir != null) { - matches = new ArrayList(); - matches.add(new PopupHelpItem(tokenText, dir.getName(),dir.getDescription())); + if (dir != null) + { + matches = new ArrayList(); + matches.add(new PopupHelpItem(tokenText, dir.getName(), dir.getDescription())); } - } - return matches; - } - - - /** - * Construct and return any appropriate help information for - * prefix match based on current line's token list. - * @param line String containing current line - * @param tokenList first Token on current line (head of linked list) - * @param token the pertinent Token object - * @param tokenText the source String that matched to the token in previous parameter - * @return ArrayList of PopupHelpItem objects, one per match. - */ - - public ArrayList getTokenPrefixMatchHelp(String line, Token tokenList, Token token, String tokenText) - { - ArrayList matches = null; - - // CASE: Unlikely boundary case... - if (tokenList == null || tokenList.id == Token.END) { + } + return matches; + } + + /** + * Construct and return any appropriate help information for prefix match based on current line's token list. + * + * @param line String containing current line + * @param tokenList first Token on current line (head of linked list) + * @param token the pertinent Token object + * @param tokenText the source String that matched to the token in previous parameter + * @return ArrayList of PopupHelpItem objects, one per match. + */ + + public ArrayList getTokenPrefixMatchHelp(String line, Token tokenList, Token token, String tokenText) + { + ArrayList matches = null; + + // CASE: Unlikely boundary case... + if (tokenList == null || tokenList.id == Token.END) + { return null; - } - - // CASE: if current token is a comment, turn off the text. - if (token != null && token.id == Token.COMMENT1) { + } + + // CASE: if current token is a comment, turn off the text. + if (token != null && token.id == Token.COMMENT1) + { return null; - } - - // Let's see if the line already contains an instruction or directive. If so, we need its token - // text as well so we can do the match. Also need to distinguish the case where current - // token is also an instruction/directive (moreThanOneKeyword variable). - - Token tokens = tokenList; - String keywordTokenText = null; - byte keywordType = -1; - int offset = 0; - boolean moreThanOneKeyword = false; - while (tokens.id != Token.END) { - if (tokens.id == Token.KEYWORD1 || tokens.id == Token.KEYWORD2) { - if (keywordTokenText != null) { - moreThanOneKeyword = true; - break; - } - keywordTokenText = line.substring(offset, offset+tokens.length); - keywordType = tokens.id; - } + } + + // Let's see if the line already contains an instruction or directive. If so, we need its token + // text as well so we can do the match. Also need to distinguish the case where current + // token is also an instruction/directive (moreThanOneKeyword variable). + + Token tokens = tokenList; + String keywordTokenText = null; + byte keywordType = -1; + int offset = 0; + boolean moreThanOneKeyword = false; + while (tokens.id != Token.END) + { + if (tokens.id == Token.KEYWORD1 || tokens.id == Token.KEYWORD2) + { + if (keywordTokenText != null) + { + moreThanOneKeyword = true; + break; + } + keywordTokenText = line.substring(offset, offset + tokens.length); + keywordType = tokens.id; + } offset += tokens.length; tokens = tokens.next; - } - - // CASE: Current token is valid KEYWORD1 (MIPS instruction). If this line contains a previous KEYWORD1 or KEYWORD2 - // token, then we ignore this one and do exact match on the first one. If it does not, there may be longer - // instructions for which this is a prefix, so do a prefix match on current token. - if (token != null && token.id == Token.KEYWORD1) { - if (moreThanOneKeyword) { - return (keywordType == Token.KEYWORD1) ? getTextFromInstructionMatch(keywordTokenText, true) - : getTextFromDirectiveMatch(keywordTokenText,true); - } - else { - return getTextFromInstructionMatch(tokenText, false); + } + + // CASE: Current token is valid KEYWORD1 (MIPS instruction). If this line contains a previous KEYWORD1 or KEYWORD2 + // token, then we ignore this one and do exact match on the first one. If it does not, there may be longer + // instructions for which this is a prefix, so do a prefix match on current token. + if (token != null && token.id == Token.KEYWORD1) + { + if (moreThanOneKeyword) + { + return (keywordType == Token.KEYWORD1) ? getTextFromInstructionMatch(keywordTokenText, true) + : getTextFromDirectiveMatch(keywordTokenText, true); } - } - - // CASE: Current token is valid KEYWORD2 (MIPS directive). If this line contains a previous KEYWORD1 or KEYWORD2 - // token, then we ignore this one and do exact match on the first one. If it does not, there may be longer - // directives for which this is a prefix, so do a prefix match on current token. - if (token != null && token.id == Token.KEYWORD2) { - if (moreThanOneKeyword) { - return (keywordType == Token.KEYWORD1) ? getTextFromInstructionMatch(keywordTokenText, true) - : getTextFromDirectiveMatch(keywordTokenText,true); - } - else { - return getTextFromDirectiveMatch(tokenText, false); + else + { + return getTextFromInstructionMatch(tokenText, false); } - } - - // CASE: line already contains KEYWORD1 or KEYWORD2 and current token is something other - // than KEYWORD1 or KEYWORD2. Generate text based on exact match of that token. - if (keywordTokenText != null) { - if (keywordType == Token.KEYWORD1) { - return getTextFromInstructionMatch(keywordTokenText, true); + } + + // CASE: Current token is valid KEYWORD2 (MIPS directive). If this line contains a previous KEYWORD1 or KEYWORD2 + // token, then we ignore this one and do exact match on the first one. If it does not, there may be longer + // directives for which this is a prefix, so do a prefix match on current token. + if (token != null && token.id == Token.KEYWORD2) + { + if (moreThanOneKeyword) + { + return (keywordType == Token.KEYWORD1) ? getTextFromInstructionMatch(keywordTokenText, true) + : getTextFromDirectiveMatch(keywordTokenText, true); } - if (keywordType == Token.KEYWORD2) { - return getTextFromDirectiveMatch(keywordTokenText, true); + else + { + return getTextFromDirectiveMatch(tokenText, false); } - } - - // CASE: Current token is NULL, which can be any number of things. Think of it as being either white space - // or an in-progress token possibly preceded by white space. We'll do a trim on the token. Now there - // are two subcases to consider: - // SUBCASE: The line does not contain any KEYWORD1 or KEYWORD2 tokens but nothing remains after trimming the - // current token's text. This means it consists only of white space and there is nothing more to do - // but return. - // SUBCASE: The line does not contain any KEYWORD1 or KEYWORD2 tokens. This means we do a prefix match of - // of the current token to either instruction or directive names. Easy to distinguish since - // directives start with "." - - - - if (token != null && token.id == Token.NULL) { - + } + + // CASE: line already contains KEYWORD1 or KEYWORD2 and current token is something other + // than KEYWORD1 or KEYWORD2. Generate text based on exact match of that token. + if (keywordTokenText != null) + { + if (keywordType == Token.KEYWORD1) + { + return getTextFromInstructionMatch(keywordTokenText, true); + } + if (keywordType == Token.KEYWORD2) + { + return getTextFromDirectiveMatch(keywordTokenText, true); + } + } + + // CASE: Current token is NULL, which can be any number of things. Think of it as being either white space + // or an in-progress token possibly preceded by white space. We'll do a trim on the token. Now there + // are two subcases to consider: + // SUBCASE: The line does not contain any KEYWORD1 or KEYWORD2 tokens but nothing remains after trimming the + // current token's text. This means it consists only of white space and there is nothing more to do + // but return. + // SUBCASE: The line does not contain any KEYWORD1 or KEYWORD2 tokens. This means we do a prefix match of + // of the current token to either instruction or directive names. Easy to distinguish since + // directives start with "." + + + if (token != null && token.id == Token.NULL) + { + String trimmedTokenText = tokenText.trim(); - - // Subcase: no KEYWORD1 or KEYWORD2 but current token contains nothing but white space. We're done. - if (keywordTokenText == null && trimmedTokenText.length() == 0) { - return null; - } - - // Subcase: no KEYWORD1 or KEYWORD2. Generate text based on prefix match of trimmed current token. - if (keywordTokenText == null && trimmedTokenText.length() > 0) { - if (trimmedTokenText.charAt(0)=='.') { - return getTextFromDirectiveMatch(trimmedTokenText, false); - } - else - if (trimmedTokenText.length() >= mars.Globals.getSettings().getEditorPopupPrefixLength()) { - return getTextFromInstructionMatch(trimmedTokenText, false); - } + + // Subcase: no KEYWORD1 or KEYWORD2 but current token contains nothing but white space. We're done. + if (keywordTokenText == null && trimmedTokenText.length() == 0) + { + return null; } - } - // should never get here... - return null; - } - - - /////////////////////////////////////////////////////////////////////////// - // Return ArrayList of PopupHelpItem for match of directives. If second argument - // true, will do exact match. If false, will do prefix match. Returns null - // if no matches. - private ArrayList getTextFromDirectiveMatch(String tokenText, boolean exact) { - ArrayList matches = null; - ArrayList directiveMatches = null; - if (exact) { + + // Subcase: no KEYWORD1 or KEYWORD2. Generate text based on prefix match of trimmed current token. + if (keywordTokenText == null && trimmedTokenText.length() > 0) + { + if (trimmedTokenText.charAt(0) == '.') + { + return getTextFromDirectiveMatch(trimmedTokenText, false); + } + else if (trimmedTokenText.length() >= mars.Globals.getSettings().getEditorPopupPrefixLength()) + { + return getTextFromInstructionMatch(trimmedTokenText, false); + } + } + } + // should never get here... + return null; + } + + /////////////////////////////////////////////////////////////////////////// + // Return ArrayList of PopupHelpItem for match of directives. If second argument + // true, will do exact match. If false, will do prefix match. Returns null + // if no matches. + private ArrayList getTextFromDirectiveMatch(String tokenText, boolean exact) + { + ArrayList matches = null; + ArrayList directiveMatches = null; + if (exact) + { Object dir = Directives.matchDirective(tokenText); - if (dir != null) { - directiveMatches = new ArrayList(); - directiveMatches.add(dir); + if (dir != null) + { + directiveMatches = new ArrayList(); + directiveMatches.add(dir); } - } - else { - directiveMatches = Directives.prefixMatchDirectives(tokenText); - } - if (directiveMatches != null) { + } + else + { + directiveMatches = Directives.prefixMatchDirectives(tokenText); + } + if (directiveMatches != null) + { matches = new ArrayList(); - for (int i=0; iSyntaxDocument.getColors() - * to get a color value, a length value which is the length of the - * token in the text, and a pointer to the next token in the list. + * A linked list of tokens. Each token has three fields - a token identifier, which is a byte value that can be looked + * up in the array returned by SyntaxDocument.getColors() to get a color value, a length value which is the + * length of the token in the text, and a pointer to the next token in the list. * * @author Slava Pestov * @version $Id: Token.java,v 1.12 1999/12/13 03:40:30 sp Exp $ @@ -22,142 +20,130 @@ package mars.venus.editors.jeditsyntax.tokenmarker; public class Token { - // NOTE from DPS 13-May-2010. - // Please do not modify any of these constants! It's not fatal or - // anything, but will cause funny results in the MARS Settings - // mechanism (at least temporarily until changed). The - // associated values here are appended into the key names for - // persistent storage (e.g. registry) of syntax style information - // for the various tokens. - - /** - * Normal text token id. This should be used to mark - * normal text. - */ - public static final byte NULL = 0; + // NOTE from DPS 13-May-2010. + // Please do not modify any of these constants! It's not fatal or + // anything, but will cause funny results in the MARS Settings + // mechanism (at least temporarily until changed). The + // associated values here are appended into the key names for + // persistent storage (e.g. registry) of syntax style information + // for the various tokens. - /** - * Comment 1 token id. This can be used to mark a comment. - */ - public static final byte COMMENT1 = 1; + /** + * Normal text token id. This should be used to mark normal text. + */ + public static final byte NULL = 0; - /** - * Comment 2 token id. This can be used to mark a comment. - */ - public static final byte COMMENT2 = 2; + /** + * Comment 1 token id. This can be used to mark a comment. + */ + public static final byte COMMENT1 = 1; - - /** - * Literal 1 token id. This can be used to mark a string - * literal (eg, C mode uses this to mark "..." literals) - */ - public static final byte LITERAL1 = 3; + /** + * Comment 2 token id. This can be used to mark a comment. + */ + public static final byte COMMENT2 = 2; - /** - * Literal 2 token id. This can be used to mark an object - * literal (eg, Java mode uses this to mark true, false, etc) - */ - public static final byte LITERAL2 = 4; - /** - * Label token id. This can be used to mark labels - * (eg, C mode uses this to mark ...: sequences) - */ - public static final byte LABEL = 5; + /** + * Literal 1 token id. This can be used to mark a string literal (eg, C mode uses this to mark "..." literals) + */ + public static final byte LITERAL1 = 3; - /** - * Keyword 1 token id. This can be used to mark a - * keyword. This should be used for general language - * constructs. - */ - public static final byte KEYWORD1 = 6; + /** + * Literal 2 token id. This can be used to mark an object literal (eg, Java mode uses this to mark true, false, + * etc) + */ + public static final byte LITERAL2 = 4; - /** - * Keyword 2 token id. This can be used to mark a - * keyword. This should be used for preprocessor - * commands, or variables. - */ - public static final byte KEYWORD2 = 7; + /** + * Label token id. This can be used to mark labels (eg, C mode uses this to mark ...: sequences) + */ + public static final byte LABEL = 5; - /** - * Keyword 3 token id. This can be used to mark a - * keyword. This should be used for data types. - */ - public static final byte KEYWORD3 = 8; + /** + * Keyword 1 token id. This can be used to mark a keyword. This should be used for general language constructs. + */ + public static final byte KEYWORD1 = 6; - /** - * Operator token id. This can be used to mark an - * operator. (eg, SQL mode marks +, -, etc with this - * token type) - */ - public static final byte OPERATOR = 9; + /** + * Keyword 2 token id. This can be used to mark a keyword. This should be used for preprocessor commands, or + * variables. + */ + public static final byte KEYWORD2 = 7; - /** - * Invalid token id. This can be used to mark invalid - * or incomplete tokens, so the user can easily spot - * syntax errors. - */ - public static final byte INVALID = 10; + /** + * Keyword 3 token id. This can be used to mark a keyword. This should be used for data types. + */ + public static final byte KEYWORD3 = 8; - /** - * Macro parameter token. Added for MARS 4.3. - */ - public static final byte MACRO_ARG = 11; - - /** - * The total number of defined token ids. - */ - public static final byte ID_COUNT = 12; + /** + * Operator token id. This can be used to mark an operator. (eg, SQL mode marks +, -, etc with this token type) + */ + public static final byte OPERATOR = 9; - /** - * The first id that can be used for internal state - * in a token marker. - */ - public static final byte INTERNAL_FIRST = 100; + /** + * Invalid token id. This can be used to mark invalid or incomplete tokens, so the user can easily spot syntax + * errors. + */ + public static final byte INVALID = 10; - /** - * The last id that can be used for internal state - * in a token marker. - */ - public static final byte INTERNAL_LAST = 126; + /** + * Macro parameter token. Added for MARS 4.3. + */ + public static final byte MACRO_ARG = 11; - /** - * The token type, that along with a length of 0 - * marks the end of the token list. - */ - public static final byte END = 127; + /** + * The total number of defined token ids. + */ + public static final byte ID_COUNT = 12; - /** - * The length of this token. - */ - public int length; + /** + * The first id that can be used for internal state in a token marker. + */ + public static final byte INTERNAL_FIRST = 100; - /** - * The id of this token. - */ - public byte id; + /** + * The last id that can be used for internal state in a token marker. + */ + public static final byte INTERNAL_LAST = 126; - /** - * The next token in the linked list. - */ - public Token next; + /** + * The token type, that along with a length of 0 marks the end of the token list. + */ + public static final byte END = 127; - /** - * Creates a new token. - * @param length The length of the token - * @param id The id of the token - */ - public Token(int length, byte id) - { - this.length = length; - this.id = id; - } + /** + * The length of this token. + */ + public int length; - /** - * Returns a string representation of this token. - */ - public String toString() - { - return "[id=" + id + ",length=" + length + "]"; - } + /** + * The id of this token. + */ + public byte id; + + /** + * The next token in the linked list. + */ + public Token next; + + /** + * Creates a new token. + * + * @param length The length of the token + * @param id The id of the token + */ + public Token(int length, byte id) + { + this.length = length; + this.id = id; + } + + /** + * Returns a string representation of this token. + */ + public String toString() + { + return "[id=" + id + ",length=" + length + "]"; + } } diff --git a/src/main/java/mars/venus/editors/jeditsyntax/tokenmarker/TokenMarker.java b/src/main/java/mars/venus/editors/jeditsyntax/tokenmarker/TokenMarker.java index 6665968..33c3f26 100644 --- a/src/main/java/mars/venus/editors/jeditsyntax/tokenmarker/TokenMarker.java +++ b/src/main/java/mars/venus/editors/jeditsyntax/tokenmarker/TokenMarker.java @@ -1,346 +1,368 @@ /* - * TokenMarker.java - Generic token marker - * Copyright (C) 1998, 1999 Slava Pestov - * - * You may use and modify this package for any purpose. Redistribution is - * permitted, in both source and binary form, provided that this notice - * remains intact in all source distributions of this package. - */ + * TokenMarker.java - Generic token marker + * Copyright (C) 1998, 1999 Slava Pestov + * + * You may use and modify this package for any purpose. Redistribution is + * permitted, in both source and binary form, provided that this notice + * remains intact in all source distributions of this package. + */ - package mars.venus.editors.jeditsyntax.tokenmarker; + package mars.venus.editors.jeditsyntax.tokenmarker; - import javax.swing.text.Segment; - import java.util.*; + import javax.swing.text.Segment; + import java.util.ArrayList; -/** - * A token marker that splits lines of text into tokens. Each token carries - * a length field and an indentification tag that can be mapped to a color - * for painting that token.

    - * - * For performance reasons, the linked list of tokens is reused after each - * line is tokenized. Therefore, the return value of markTokens - * should only be used for immediate painting. Notably, it cannot be - * cached. - * - * @author Slava Pestov - * @version $Id: TokenMarker.java,v 1.32 1999/12/13 03:40:30 sp Exp $ - * - * @see org.syntax.jedit.Token - */ - public abstract class TokenMarker { - /** - * A wrapper for the lower-level markTokensImpl method - * that is called to split a line up into tokens. - * @param line The line - * @param lineIndex The line number - */ - public Token markTokens(Segment line, int lineIndex) { - if(lineIndex >= length) { - throw new IllegalArgumentException("Tokenizing invalid line: " - + lineIndex); + /** + * A token marker that splits lines of text into tokens. Each token carries a length field and an indentification tag + * that can be mapped to a color for painting that token.

    + *

    + * For performance reasons, the linked list of tokens is reused after each line is tokenized. Therefore, the return + * value of markTokens should only be used for immediate painting. Notably, it cannot be cached. + * + * @author Slava Pestov + * @version $Id: TokenMarker.java,v 1.32 1999/12/13 03:40:30 sp Exp $ + * @see org.syntax.jedit.Token + */ + public abstract class TokenMarker + { + /** + * The first token in the list. This should be used as the return value from markTokens(). + */ + protected Token firstToken; + + /** + * The last token in the list. New tokens are added here. This should be set to null before a new line is to be + * tokenized. + */ + protected Token lastToken; + + /** + * An array for storing information about lines. It is enlarged and shrunk automatically by the + * insertLines() and + * deleteLines() methods. + */ + protected LineInfo[] lineInfo; + + /** + * The number of lines in the model being tokenized. This can be less than the length of the lineInfo + * array. + */ + protected int length; + + /** + * The last tokenized line. + */ + protected int lastLine; + + /** + * True if the next line should be painted. + */ + protected boolean nextLineRequested; + + /** + * Creates a new TokenMarker. This DOES NOT create a lineInfo array; an initial call to + * insertLines() does that. + */ + protected TokenMarker() + { + lastLine = -1; + } + + /** + * A wrapper for the lower-level markTokensImpl method that is called to split a line up into tokens. + * + * @param line The line + * @param lineIndex The line number + */ + public Token markTokens(Segment line, int lineIndex) + { + if (lineIndex >= length) + { + throw new IllegalArgumentException("Tokenizing invalid line: " + + lineIndex); } - + lastToken = null; - + LineInfo info = lineInfo[lineIndex]; LineInfo prev; - if(lineIndex == 0) - prev = null; + if (lineIndex == 0) + { + prev = null; + } else - prev = lineInfo[lineIndex - 1]; - + { + prev = lineInfo[lineIndex - 1]; + } + byte oldToken = info.token; byte token = markTokensImpl(prev == null ? - Token.NULL : prev.token,line,lineIndex); - + Token.NULL : prev.token, line, lineIndex); + info.token = token; - - /* - * This is a foul hack. It stops nextLineRequested from being cleared if - * the same line is marked twice. - * - * Why is this necessary? It's all JEditTextArea's fault. When something - * is inserted into the text, firing a document event, the - * insertUpdate() method shifts the caret (if necessary) by the amount - * inserted. - * - * All caret movement is handled by the select() method, which - * eventually pipes the new position to scrollTo() and calls repaint(). - * - * Note that at this point in time, the new line hasn't yet been - * painted; the caret is moved first. - * - * scrollTo() calls offsetToX(), which tokenizes the line unless it is - * being called on the last line painted (in which case it uses the text - * area's painter cached token list). What scrollTo() does next is - * irrelevant. - * - * After scrollTo() has done it's job, repaint() is called, and - * eventually we end up in paintLine(), whose job is to paint the - * changed line. It, too, calls markTokens(). - * - * The problem was that if the line started a multiline token, the first - * markTokens() (done in offsetToX()) would set nextLineRequested - * (because the line end token had changed) but the second would clear - * it (because the line was the same that time) and therefore - * paintLine() would never know that it needed to repaint subsequent - * lines. - * - * This bug took me ages to track down, that's why I wrote all the - * relevant info down so that others wouldn't duplicate it. - */ - if(!(lastLine == lineIndex && nextLineRequested)) - nextLineRequested = (oldToken != token); - + + /* + * This is a foul hack. It stops nextLineRequested from being cleared if + * the same line is marked twice. + * + * Why is this necessary? It's all JEditTextArea's fault. When something + * is inserted into the text, firing a document event, the + * insertUpdate() method shifts the caret (if necessary) by the amount + * inserted. + * + * All caret movement is handled by the select() method, which + * eventually pipes the new position to scrollTo() and calls repaint(). + * + * Note that at this point in time, the new line hasn't yet been + * painted; the caret is moved first. + * + * scrollTo() calls offsetToX(), which tokenizes the line unless it is + * being called on the last line painted (in which case it uses the text + * area's painter cached token list). What scrollTo() does next is + * irrelevant. + * + * After scrollTo() has done it's job, repaint() is called, and + * eventually we end up in paintLine(), whose job is to paint the + * changed line. It, too, calls markTokens(). + * + * The problem was that if the line started a multiline token, the first + * markTokens() (done in offsetToX()) would set nextLineRequested + * (because the line end token had changed) but the second would clear + * it (because the line was the same that time) and therefore + * paintLine() would never know that it needed to repaint subsequent + * lines. + * + * This bug took me ages to track down, that's why I wrote all the + * relevant info down so that others wouldn't duplicate it. + */ + if (!(lastLine == lineIndex && nextLineRequested)) + { + nextLineRequested = (oldToken != token); + } + lastLine = lineIndex; - - addToken(0,Token.END); - + + addToken(0, Token.END); + return firstToken; - } - - /** - * An abstract method that splits a line up into tokens. It - * should parse the line, and call addToken() to - * add syntax tokens to the token list. Then, it should return - * the initial token type for the next line.

    - * - * For example if the current line contains the start of a - * multiline comment that doesn't end on that line, this method - * should return the comment token type so that it continues on - * the next line. - * - * @param token The initial token type for this line - * @param line The line to be tokenized - * @param lineIndex The index of the line in the document, starting at 0 - * @return The initial token type for the next line - */ - protected abstract byte markTokensImpl(byte token, Segment line, - int lineIndex); - - /** - * Returns if the token marker supports tokens that span multiple - * lines. If this is true, the object using this token marker is - * required to pass all lines in the document to the - * markTokens() method (in turn).

    - * - * The default implementation returns true; it should be overridden - * to return false on simpler token markers for increased speed. - */ - public boolean supportsMultilineTokens() { + } + + /** + * An abstract method that splits a line up into tokens. It should parse the line, and call addToken() + * to add syntax tokens to the token list. Then, it should return the initial token type for the next line.

    + *

    + * For example if the current line contains the start of a multiline comment that doesn't end on that line, this + * method should return the comment token type so that it continues on the next line. + * + * @param token The initial token type for this line + * @param line The line to be tokenized + * @param lineIndex The index of the line in the document, starting at 0 + * @return The initial token type for the next line + */ + protected abstract byte markTokensImpl(byte token, Segment line, + int lineIndex); + + // protected members + + /** + * Returns if the token marker supports tokens that span multiple lines. If this is true, the object using this + * token marker is required to pass all lines in the document to the + * markTokens() method (in turn).

    + *

    + * The default implementation returns true; it should be overridden to return false on simpler token markers for + * increased speed. + */ + public boolean supportsMultilineTokens() + { return true; - } - - /** - * Informs the token marker that lines have been inserted into - * the document. This inserts a gap in the lineInfo - * array. - * @param index The first line number - * @param lines The number of lines - */ - public void insertLines(int index, int lines) { - if(lines <= 0) - return; + } + + /** + * Informs the token marker that lines have been inserted into the document. This inserts a gap in the + * lineInfo array. + * + * @param index The first line number + * @param lines The number of lines + */ + public void insertLines(int index, int lines) + { + if (lines <= 0) + { + return; + } length += lines; ensureCapacity(length); int len = index + lines; - System.arraycopy(lineInfo,index,lineInfo,len, lineInfo.length - len); - - for(int i = index + lines - 1; i >= index; i--){ - lineInfo[i] = new LineInfo(); + System.arraycopy(lineInfo, index, lineInfo, len, lineInfo.length - len); + + for (int i = index + lines - 1; i >= index; i--) + { + lineInfo[i] = new LineInfo(); } - } - - /** - * Informs the token marker that line have been deleted from - * the document. This removes the lines in question from the - * lineInfo array. - * @param index The first line number - * @param lines The number of lines - */ - public void deleteLines(int index, int lines) { + } + + /** + * Informs the token marker that line have been deleted from the document. This removes the lines in question from + * the + * lineInfo array. + * + * @param index The first line number + * @param lines The number of lines + */ + public void deleteLines(int index, int lines) + { if (lines <= 0) - return; + { + return; + } int len = index + lines; length -= lines; - System.arraycopy(lineInfo,len,lineInfo, - index,lineInfo.length - len); - } - - /** - * Returns the number of lines in this token marker. - */ - public int getLineCount() { + System.arraycopy(lineInfo, len, lineInfo, + index, lineInfo.length - len); + } + + /** + * Returns the number of lines in this token marker. + */ + public int getLineCount() + { return length; - } - - /** - * Returns true if the next line should be repainted. This - * will return true after a line has been tokenized that starts - * a multiline token that continues onto the next line. - */ - public boolean isNextLineRequested() { + } + + /** + * Returns true if the next line should be repainted. This will return true after a line has been tokenized that + * starts a multiline token that continues onto the next line. + */ + public boolean isNextLineRequested() + { return nextLineRequested; - } - - /** - * Construct and return any appropriate help information for - * the given token. This default definition returns null; - * override it in language-specific subclasses. - * @param token the pertinent Token object - * @param tokenText the source String that matched to the token - * @return ArrayList containing PopupHelpItem objects, one per match. - */ - public ArrayList getTokenExactMatchHelp(Token token, String tokenText) { + } + + /** + * Construct and return any appropriate help information for the given token. This default definition returns null; + * override it in language-specific subclasses. + * + * @param token the pertinent Token object + * @param tokenText the source String that matched to the token + * @return ArrayList containing PopupHelpItem objects, one per match. + */ + public ArrayList getTokenExactMatchHelp(Token token, String tokenText) + { return null; - } - - /** - * Construct and return any appropriate help information for - * the given token or "token prefix". Will match instruction prefixes, e.g. "s" matches "sw". - * This default definition returns null; - * override it in language-specific subclasses. - * @param line String containing current line - * @param tokenList first Token on the current line - * @param token the pertinent Token object - * @param tokenText the source String that matched to the token - * @return ArrayList containing PopupHelpItem objects, one per match. - */ - public ArrayList getTokenPrefixMatchHelp(String line, Token tokenList, Token tokenAtOffset, String tokenText) { + } + + /** + * Construct and return any appropriate help information for the given token or "token prefix". Will match + * instruction prefixes, e.g. "s" matches "sw". This default definition returns null; override it in + * language-specific subclasses. + * + * @param line String containing current line + * @param tokenList first Token on the current line + * @param token the pertinent Token object + * @param tokenText the source String that matched to the token + * @return ArrayList containing PopupHelpItem objects, one per match. + */ + public ArrayList getTokenPrefixMatchHelp(String line, Token tokenList, Token tokenAtOffset, String tokenText) + { return null; - } - - // protected members - - /** - * The first token in the list. This should be used as the return - * value from markTokens(). - */ - protected Token firstToken; - - /** - * The last token in the list. New tokens are added here. - * This should be set to null before a new line is to be tokenized. - */ - protected Token lastToken; - - /** - * An array for storing information about lines. It is enlarged and - * shrunk automatically by the insertLines() and - * deleteLines() methods. - */ - protected LineInfo[] lineInfo; - - /** - * The number of lines in the model being tokenized. This can be - * less than the length of the lineInfo array. - */ - protected int length; - - /** - * The last tokenized line. - */ - protected int lastLine; - - /** - * True if the next line should be painted. - */ - protected boolean nextLineRequested; - - /** - * Creates a new TokenMarker. This DOES NOT create - * a lineInfo array; an initial call to insertLines() - * does that. - */ - protected TokenMarker() { - lastLine = -1; - } - - /** - * Ensures that the lineInfo array can contain the - * specified index. This enlarges it if necessary. No action is - * taken if the array is large enough already.

    - * - * It should be unnecessary to call this under normal - * circumstances; insertLine() should take care of - * enlarging the line info array automatically. - * - * @param index The array index - */ - protected void ensureCapacity(int index) { - if(lineInfo == null) - lineInfo = new LineInfo[index + 1]; - else if(lineInfo.length <= index) { - LineInfo[] lineInfoN = new LineInfo[(index + 1) * 2]; - System.arraycopy(lineInfo,0,lineInfoN,0, - lineInfo.length); - lineInfo = lineInfoN; + } + + /** + * Ensures that the lineInfo array can contain the specified index. This enlarges it if necessary. No + * action is taken if the array is large enough already.

    + *

    + * It should be unnecessary to call this under normal circumstances; insertLine() should take care of + * enlarging the line info array automatically. + * + * @param index The array index + */ + protected void ensureCapacity(int index) + { + if (lineInfo == null) + { + lineInfo = new LineInfo[index + 1]; } - } - - /** - * Adds a token to the token list. - * @param length The length of the token - * @param id The id of the token - */ - protected void addToken(int length, byte id) { - if(id >= Token.INTERNAL_FIRST && id <= Token.INTERNAL_LAST) - throw new InternalError("Invalid id: " + id); - - if(length == 0 && id != Token.END) - return; - - if(firstToken == null) { - firstToken = new Token(length,id); - lastToken = firstToken; + else if (lineInfo.length <= index) + { + LineInfo[] lineInfoN = new LineInfo[(index + 1) * 2]; + System.arraycopy(lineInfo, 0, lineInfoN, 0, + lineInfo.length); + lineInfo = lineInfoN; } - else if(lastToken == null) { - lastToken = firstToken; - firstToken.length = length; - firstToken.id = id; + } + + /** + * Adds a token to the token list. + * + * @param length The length of the token + * @param id The id of the token + */ + protected void addToken(int length, byte id) + { + if (id >= Token.INTERNAL_FIRST && id <= Token.INTERNAL_LAST) + { + throw new InternalError("Invalid id: " + id); } - else if(lastToken.next == null) { - lastToken.next = new Token(length,id); - lastToken = lastToken.next; + + if (length == 0 && id != Token.END) + { + return; } - else { - lastToken = lastToken.next; - lastToken.length = length; - lastToken.id = id; + + if (firstToken == null) + { + firstToken = new Token(length, id); + lastToken = firstToken; } - } - - /** - * Inner class for storing information about tokenized lines. - */ - public class LineInfo { - /** - * Creates a new LineInfo object with token = Token.NULL - * and obj = null. - */ - public LineInfo() { + else if (lastToken == null) + { + lastToken = firstToken; + firstToken.length = length; + firstToken.id = id; } - - /** - * Creates a new LineInfo object with the specified - * parameters. - */ - public LineInfo(byte token, Object obj) { - this.token = token; - this.obj = obj; + else if (lastToken.next == null) + { + lastToken.next = new Token(length, id); + lastToken = lastToken.next; } - - /** - * The id of the last token of the line. - */ + else + { + lastToken = lastToken.next; + lastToken.length = length; + lastToken.id = id; + } + } + + /** + * Inner class for storing information about tokenized lines. + */ + public class LineInfo + { + /** + * The id of the last token of the line. + */ public byte token; - - /** - * This is for use by the token marker implementations - * themselves. It can be used to store anything that - * is an object and that needs to exist on a per-line - * basis. - */ + + /** + * This is for use by the token marker implementations themselves. It can be used to store anything that is an + * object and that needs to exist on a per-line basis. + */ public Object obj; - } - } + + /** + * Creates a new LineInfo object with token = Token.NULL and obj = null. + */ + public LineInfo() + { + } + + /** + * Creates a new LineInfo object with the specified parameters. + */ + public LineInfo(byte token, Object obj) + { + this.token = token; + this.obj = obj; + } + } + }