diff --git a/src/mars/ErrorList.java b/src/mars/ErrorList.java index 0b8dd28..080ca7c 100644 --- a/src/mars/ErrorList.java +++ b/src/mars/ErrorList.java @@ -1,6 +1,7 @@ - package mars; - import java.util.*; - import java.io.*; +package mars; +import java.util.*; +import java.io.*; + /* Copyright (c) 2003-2012, Pete Sanderson and Kenneth Vollmar @@ -37,162 +38,160 @@ a copy of this software and associated documentation files (the * @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 { + 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()) { return; - } - if (errorCount == getErrorLimit()) { + } + 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); + } + } + + + /** + * 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 = 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); + 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 - + } + return report.toString(); + } +} // ErrorList diff --git a/src/mars/ErrorMessage.java b/src/mars/ErrorMessage.java index 74d056b..d962216 100644 --- a/src/mars/ErrorMessage.java +++ b/src/mars/ErrorMessage.java @@ -1,8 +1,9 @@ - package mars; - - import java.util.regex.Pattern; - import java.util.regex.Matcher; - import java.util.ArrayList; +package mars; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.ArrayList; + /* Copyright (c) 2003-2012, Pete Sanderson and Kenneth Vollmar @@ -37,249 +38,247 @@ a copy of this software and associated documentation files (the * @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 { + 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. + **/ // 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 { + } + else { if (sourceMIPSprogram.getSourceLineList() == null) { - this.filename = sourceMIPSprogram.getFilename(); - this.line = line; + 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(); + 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.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()) { + } + } + + 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()) { 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(); + 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; + 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. - */ + } + 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 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 sourceMIPSprogram.getLocalMacroPool().getExpansionHistory(); + } + +} // ErrorMessage diff --git a/src/mars/Globals.java b/src/mars/Globals.java index d7aa115..6fdee07 100644 --- a/src/mars/Globals.java +++ b/src/mars/Globals.java @@ -1,13 +1,13 @@ - package mars; - import mars.mips.instructions.syscalls.*; - import mars.mips.instructions.*; - import mars.mips.hardware.*; - import mars.assembler.*; - import mars.venus.*; - import mars.util.*; - import java.io.*; - import java.util.*; - +package mars; +import mars.mips.instructions.syscalls.*; +import mars.mips.instructions.*; +import mars.mips.hardware.*; +import mars.assembler.*; +import mars.venus.*; +import mars.util.*; +import java.io.*; +import java.util.*; + /* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar @@ -76,7 +76,7 @@ public class Globals /** The current MARS version number. Can't wait for "initialize()" call to get it. */ public static final String version = "4.5"; /** List of accepted file extensions for MIPS assembly source files. */ - public static final ArrayList fileExtensions = getFileExtensions(); + 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 */ @@ -188,8 +188,8 @@ private static int getIntegerProperty(String propertiesFile, String propertyName // Read assembly language file extensions from properties file. Resulting // string is tokenized into array list (assume StringTokenizer default delimiters). - private static ArrayList getFileExtensions() { - ArrayList extensionsList = new ArrayList(); + private static ArrayList getFileExtensions() { + ArrayList extensionsList = new ArrayList<>(); String extensions = getPropertyEntry(configPropertiesFile,"Extensions"); if (extensions != null) { StringTokenizer st = new StringTokenizer(extensions); @@ -207,8 +207,8 @@ private static ArrayList getFileExtensions() { * @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() { - ArrayList toolsList = new ArrayList(); + public static ArrayList getExternalTools() { + ArrayList toolsList = new ArrayList<>(); String delimiter = ";"; String tools = getPropertyEntry(configPropertiesFile,"ExternalTools"); if (tools != null) { @@ -235,10 +235,10 @@ public static String getPropertyEntry(String propertiesFile, String propertyName * Read any syscall number assignment overrides from config file. * @return ArrayList of SyscallNumberOverride objects */ - public ArrayList getSyscallOverrides() { - ArrayList overrides = new ArrayList(); + public ArrayList getSyscallOverrides() { + ArrayList overrides = new ArrayList<>(); Properties properties = PropertiesFile.loadPropertiesFromFile(syscallPropertiesFile); - Enumeration keys = properties.keys(); + Enumeration keys = properties.keys(); while (keys.hasMoreElements()) { String key = (String) keys.nextElement(); overrides.add(new SyscallNumberOverride(key,properties.getProperty(key))); diff --git a/src/mars/assembler/Assembler.java b/src/mars/assembler/Assembler.java index 6bd85e0..141ae2a 100644 --- a/src/mars/assembler/Assembler.java +++ b/src/mars/assembler/Assembler.java @@ -1,22 +1,22 @@ - package mars.assembler; - - import java.util.ArrayList; - import java.util.Collections; - import java.util.Comparator; - - 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; +package mars.assembler; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +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; /* Copyright (c) 2003-2012, Pete Sanderson and Kenneth Vollmar @@ -55,1461 +55,1417 @@ a copy of this software and associated documentation files (the * @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, - Memory.kernelTextBaseAddress); - 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) + textAddress = new UserKernelAddressSpace(Memory.textBaseAddress, + Memory.kernelTextBaseAddress); + 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) 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 = 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 + ArrayList tokenList = fileCurrentlyBeingAssembled.getTokenList(); + ArrayList parsedList = fileCurrentlyBeingAssembled.createParsedList(); + // 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); - } + if (errors.errorLimitExceeded()) + break; + for (int z = 0; z < tokenList.get(i).size(); z++) { + Token t = 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.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)")); + 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; - this.fileCurrentlyBeingAssembled = (MIPSprogram) tokenizedProgramFiles.get(fileIndex); - ArrayList parsedList = fileCurrentlyBeingAssembled.getParsedList(); + break; + this.fileCurrentlyBeingAssembled = 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. - + statement = 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, + 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; - statement = (ProgramStatement) this.machineList.get(i); + break; + statement = this.machineList.get(i); statement.buildMachineStatementFromBasicStatement(errors); if (Globals.debug) - System.out.println(statement); + 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) { + 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++) { - ProgramStatement ps1 = (ProgramStatement) instructions.get(i); - ProgramStatement ps2 = (ProgramStatement) instructions.get(i + 1); + } + 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 = instructions.get(i); + ProgramStatement ps2 = 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)")); + 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(); + 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) { - ArrayList instrMatches = this.matchInstruction(token); + } + + // 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. + // 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.")); + 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; + 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 - && 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()) + } + + // 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()) 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 (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; } - } - } // parseLabel() - - private boolean tokenListBeginsWithLabel(TokenList tokens) { - // 2-July-2010. DPS. Remove prohibition of operator names as labels - if (tokens.size() < 2) + } + } // 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) - && 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) + 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) 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")); + .getStartPos(), "\"" + token.getValue() + + "\" directive is invalid or not implemented in MARS")); return; - } - 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) { + } 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; + 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; + 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; + 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()); + 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) { + } 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; + 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; + 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 + } else if (inMacroSegment) { + // should not parse lines even directives in macro segment return; - } - else if (direct == Directives.DATA || direct == Directives.KDATA) { + } 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); + : 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.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); + : 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.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); + // 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); + storeStrings(tokens, direct, errors); } - } - else if (direct == Directives.ALIGN) { + } 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))); - } + 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) { + } 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); + 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) { + } 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; + 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 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; + 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) { + .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; + 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? + // 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); + // 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.")); + .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++) { + } + } // 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()); + 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); - } + 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); + } } - } - } - - // ////////////////////////////////////////////////////////////////////////////////// - // 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) { + } + } + + // ////////////////////////////////////////////////////////////////////////////////// + // 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); + storeNumeric(tokens, direct, errors); } - } - else if (direct == Directives.ASCII || direct == Directives.ASCIIZ) { + } else if (direct == Directives.ASCII || direct == Directives.ASCIIZ) { if (passesDataSegmentCheck(tokens.get(0))) { - storeStrings(tokens, direct, errors); + 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) { + } + } // 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() + "\"")); + .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")); + 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; + 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.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); + storeInteger(token, directive, errors); } if (Directives.isFloatingDirective(directive)) { - storeRealNumber(token, directive, errors); + 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()); + } + 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()); 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. + // 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; + value = value & 0x000000FF; + } else if (directive == Directives.HALF) { + value = value & 0x0000FFFF; } - + 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")); + 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); + 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) - ********/ + * 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); + 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); } - } // end of "if integer token type" - else if (token.getType() == TokenTypes.IDENTIFIER) { + } // 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); - } + 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. + // 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")); + 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())) { + .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; - } + 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; + errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(), + token.getStartPos(), "\"" + token.getValue() + + "\" is an out-of-range value")); + return; } - } - else { + } 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(), "\"" + 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) { + .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); - } + .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) { + } + } // 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(); - } - } - - // Take a hard line. - public boolean equals(Object obj) { + } + } + + // /////////////////////////////////////////////////////////////////////////////////// + // 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(ProgramStatement ps1, ProgramStatement ps2) { + int addr1 = ps1.getAddress(); + int addr2 = ps2.getAddress(); + return (addr1 < 0 && addr2 >= 0 || addr1 >= 0 && addr2 < 0) ? addr2 : addr1 - addr2; + } + + // 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 { + int[] address; + int currentAddressSpace; + private final int USER = 0, KERNEL = 1; + + // 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) { + } + + private void setAddressSpace(int addressSpace) { if (addressSpace == USER || addressSpace == KERNEL) { - currentAddressSpace = addressSpace; - } - else { - throw new IllegalArgumentException(); + currentAddressSpace = addressSpace; + } 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 ArrayList forwardReferenceList; - - private DataSegmentForwardReferences() { - forwardReferenceList = new ArrayList(); - } - - private int size() { + } + } + + // ////////////////////////////////////////////////////////////////////////// + // 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 once resolved + // - 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() { + forwardReferenceList = new ArrayList<>(); + } + + 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++; - } + entry = 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.")); + entry = 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; + this.patchAddress = patchAddress; + this.length = length; + this.token = token; } - } - - } - } + } + + } +} diff --git a/src/mars/assembler/DataTypes.java b/src/mars/assembler/DataTypes.java index 175e393..316d2b0 100644 --- a/src/mars/assembler/DataTypes.java +++ b/src/mars/assembler/DataTypes.java @@ -9,7 +9,7 @@ and Kenneth Vollmar (kenvollmar@missouristate.edu) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, +but not limited to the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/src/mars/assembler/Directives.java b/src/mars/assembler/Directives.java index cc025ac..bb4c698 100644 --- a/src/mars/assembler/Directives.java +++ b/src/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 @@ -40,151 +40,145 @@ a copy of this software and associated documentation files (the * @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; i 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; i < directiveList.size(); i++) { + match = directiveList.get(i); if (str.equalsIgnoreCase(match.descriptor)) { - return match; + 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" + } + 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; i < directiveList.size(); i++) { + if (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 * - * @param str A String - * @return If match is found, returns ArrayList of matching Directives objects, else returns null. + * @return String representing Directive: its MIPS name **/ - - public static ArrayList prefixMatchDirectives(String str) { - ArrayList matches = null; - for (int i=0; i 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; + } + +} diff --git a/src/mars/assembler/MacroPool.java b/src/mars/assembler/MacroPool.java index c524309..f3a5d00 100644 --- a/src/mars/assembler/MacroPool.java +++ b/src/mars/assembler/MacroPool.java @@ -1,10 +1,10 @@ - package mars.assembler; +package mars.assembler; - import java.util.ArrayList; - import java.util.Stack; +import java.util.ArrayList; +import java.util.Stack; - import mars.ErrorList; - import mars.MIPSprogram; +import mars.ErrorList; +import mars.MIPSprogram; /* Copyright (c) 2013. @@ -46,157 +46,150 @@ a copy of this software and associated documentation files (the * * @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 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) 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+="->"; ret+=callStackOrigLines.get(i).toString(); - } - return ret; - } - } + } + return ret; + } +} diff --git a/src/mars/assembler/OperandFormat.java b/src/mars/assembler/OperandFormat.java index 0965edc..a581f7d 100644 --- a/src/mars/assembler/OperandFormat.java +++ b/src/mars/assembler/OperandFormat.java @@ -69,17 +69,17 @@ static boolean tokenOperandMatch(TokenList candidateList, Instruction inst, Erro * 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) { + static Instruction bestOperandMatch(TokenList tokenList, ArrayList instrMatches) { if (instrMatches == null) return null; if (instrMatches.size() == 1) - return (Instruction) instrMatches.get(0); + return instrMatches.get(0); for (int i=0; i table; + // Note -1 is legal 32 bit address (0xFFFFFFFF) but it is the high address in + // kernel address space so highly unlikely that any symbol will have this as + // its associated address! + public static final int NOT_FOUND = -1; - public class SymbolTable { - private static String startLabel = "main"; - private String filename; - private ArrayList table; - // Note -1 is legal 32 bit address (0xFFFFFFFF) but it is the high address in - // kernel address space so highly unlikely that any symbol will have this as - // its associated address! - public static final int NOT_FOUND = -1; - - /** - * Create a new empty symbol table for given file - * @param filename name of file this symbol table is associated with. Will be - * used only for output/display so it can be any descriptive string. - */ - public SymbolTable(String filename) { - this.filename = filename; - this.table = new ArrayList(); - } - /** - * Adds a Symbol object into the array of Symbols. - * @param token The token representing the Symbol. - * @param address The address of the Symbol. - * @param b The type of Symbol, true for data, false for text. - * @param errors List to which to add any processing errors that occur. - **/ - - public void addSymbol(Token token, int address, boolean b, ErrorList errors) { - String label = token.getValue(); - if (getSymbol(label) != null) { + /** + * Create a new empty symbol table for given file + * @param filename name of file this symbol table is associated with. Will be + * used only for output/display so it can be any descriptive string. + */ + public SymbolTable(String filename) { + this.filename = filename; + this.table = new ArrayList<>(); + } + /** + * Adds a Symbol object into the array of Symbols. + * @param token The token representing the Symbol. + * @param address The address of the Symbol. + * @param b The type of Symbol, true for data, false for text. + * @param errors List to which to add any processing errors that occur. + **/ + + public void addSymbol(Token token, int address, boolean b, ErrorList errors) { + String label = token.getValue(); + if (getSymbol(label) != null) { errors.add(new ErrorMessage(token.getSourceMIPSprogram(), token.getSourceLine(),token.getStartPos(),"label \""+label+"\" already defined")); - } - else { + } + else { Symbol s= new Symbol(label, address, b); table.add(s); if (Globals.debug) System.out.println("The symbol " + label + " with address " + address + " has been added to the "+this.filename+" symbol table."); - } - } - - - /** - * Removes a symbol from the Symbol table. If not found, it does nothing. - * This will rarely happen (only when variable is declared .globl after already - * being defined in the local symbol table). - * @param token The token representing the Symbol. - **/ - - public void removeSymbol(Token token) { - String label = token.getValue(); - for (int i=0; i < table.size(); i++) { + } + } + + + /** + * Removes a symbol from the Symbol table. If not found, it does nothing. + * This will rarely happen (only when variable is declared .globl after already + * being defined in the local symbol table). + * @param token The token representing the Symbol. + **/ + + public void removeSymbol(Token token) { + String label = token.getValue(); + for (int i=0; i < table.size(); i++) { if (((Symbol)(table.get(i))).getName().equals(label)){ - table.remove(i); - if (Globals.debug) System.out.println("The symbol " + label + " has been removed from the "+this.filename+" symbol table."); - break; + table.remove(i); + if (Globals.debug) System.out.println("The symbol " + label + " has been removed from the "+this.filename+" symbol table."); + break; } - } - return; - } - - - /** - * Method to return the address associated with the given label. - * @param s The label. - * @return The memory address of the label given, or NOT_FOUND if not found in symbol table. - **/ - public int getAddress(String s){ - for(int i=0; i < table.size(); i++){ + } + return; + } + + + /** + * Method to return the address associated with the given label. + * @param s The label. + * @return The memory address of the label given, or NOT_FOUND if not found in symbol table. + **/ + public int getAddress(String s){ + for(int i=0; i < table.size(); i++){ if (((Symbol)(table.get(i))).getName().equals(s)){ - return((Symbol) table.get(i)).getAddress(); + return((Symbol) table.get(i)).getAddress(); } - } - return NOT_FOUND; - } - - /** - * Method to return the address associated with the given label. Look first - * in this (local) symbol table then in symbol table of labels declared - * global (.globl directive). - * @param s The label. - * @return The memory address of the label given, or NOT_FOUND if not found in symbol table. - **/ - public int getAddressLocalOrGlobal(String s) { - int address = this.getAddress(s); - return (address==NOT_FOUND) ? Globals.symbolTable.getAddress(s) : address ; - } - - - /** - * Produce Symbol object from symbol table that corresponds to given String. - * @param s target String - * @return Symbol object for requested target, null if not found in symbol table. - **/ - - public Symbol getSymbol(String s){ - for(int i=0; i < table.size(); i++){ + } + return NOT_FOUND; + } + + /** + * Method to return the address associated with the given label. Look first + * in this (local) symbol table then in symbol table of labels declared + * global (.globl directive). + * @param s The label. + * @return The memory address of the label given, or NOT_FOUND if not found in symbol table. + **/ + public int getAddressLocalOrGlobal(String s) { + int address = this.getAddress(s); + return (address==NOT_FOUND) ? Globals.symbolTable.getAddress(s) : address ; + } + + + /** + * Produce Symbol object from symbol table that corresponds to given String. + * @param s target String + * @return Symbol object for requested target, null if not found in symbol table. + **/ + + public Symbol getSymbol(String s){ + for(int i=0; i < table.size(); i++){ if (((Symbol)(table.get(i))).getName().equals(s)){ - return (Symbol) table.get(i); + return (Symbol) table.get(i); } - } - return null; - } - - /** - * Produce Symbol object from symbol table that has the given address. - * @param s String representing address - * @return Symbol object having requested address, null if address not found in symbol table. - **/ - - public Symbol getSymbolGivenAddress(String s){ - int address = 0; - try { + } + return null; + } + + /** + * Produce Symbol object from symbol table that has the given address. + * @param s String representing address + * @return Symbol object having requested address, null if address not found in symbol table. + **/ + + public Symbol getSymbolGivenAddress(String s){ + int address = 0; + try { address = mars.util.Binary.stringToInt(s);// DPS 2-Aug-2010: was Integer.parseInt(s) but croaked on hex - } - catch (NumberFormatException e) { - return null; + } + catch (NumberFormatException e) { + return null; } - for(int i=0; i < table.size(); i++){ + for(int i=0; i < table.size(); i++){ if (((Symbol)(table.get(i))).getAddress() == address){ - return (Symbol) table.get(i); + return (Symbol) table.get(i); } - } - return null; - } - - /** - * Produce Symbol object from either local or global symbol table that has the - * given address. - * @param s String representing address - * @return Symbol object having requested address, null if address not found in symbol table. - **/ - public Symbol getSymbolGivenAddressLocalOrGlobal(String s){ - Symbol sym = this.getSymbolGivenAddress(s); - return (sym==null) ? Globals.symbolTable.getSymbolGivenAddress(s) : sym ; - } - - - - /** - * For obtaining the Data Symbols. - * @return An ArrayList of Symbol objects. - **/ - - public ArrayList getDataSymbols(){ - ArrayList list= new ArrayList(); - for(int i=0; i getDataSymbols(){ + ArrayList list= new ArrayList<>(); + for(int i=0; i getTextSymbols(){ + ArrayList list= new ArrayList<>(); + for(int i=0; i getAllSymbols(){ + ArrayList list= new ArrayList<>(); + for(int i=0; i(); + } + +/** + * Fix address in symbol table entry. Any and all entries that match the original + * address will be modified to contain the replacement address. There is no effect, + * if none of the addresses matches. + * @param originalAddress Address associated with 0 or more symtab entries. + * @param replacementAddress Any entry that has originalAddress will have its + * address updated to this value. Does nothing if none do. + */ + + public void fixSymbolTableAddress(int originalAddress, int replacementAddress) { + Symbol label = getSymbolGivenAddress(Integer.toString(originalAddress)); + while (label != null) { label.setAddress(replacementAddress); label = getSymbolGivenAddress(Integer.toString(originalAddress)); - } - return; - } - - /** - * Fetches the text segment label (symbol) which, if declared global, indicates - * the starting address for execution. - * @return String containing global label whose text segment address is starting address for program execution. - **/ - public static String getStartLabel() { - return startLabel; - } - } \ No newline at end of file + } + return; + } + + /** + * Fetches the text segment label (symbol) which, if declared global, indicates + * the starting address for execution. + * @return String containing global label whose text segment address is starting address for program execution. + **/ + public static String getStartLabel() { + return startLabel; + } +} diff --git a/src/mars/assembler/TokenList.java b/src/mars/assembler/TokenList.java index 1438000..0f7c44d 100644 --- a/src/mars/assembler/TokenList.java +++ b/src/mars/assembler/TokenList.java @@ -39,14 +39,14 @@ a copy of this software and associated documentation files (the public class TokenList implements Cloneable { - private ArrayList tokenList; + private ArrayList tokenList; private String processedLine;// DPS 03-Jan-2013 /** * Constructor for objects of class TokenList */ public TokenList() { - tokenList = new ArrayList(); + tokenList = new ArrayList<>(); processedLine = ""; // DPS 03-Jan-2013 } @@ -79,7 +79,7 @@ public String getProcessedLine() { * @return the requested token, or ArrayIndexOutOfBounds exception */ public Token get(int pos) { - return (Token) tokenList.get(pos); + return tokenList.get(pos); } /** @@ -156,7 +156,7 @@ public String toString() { public String toTypeString() { String stringified = ""; for (int i=0; i) tokenList.clone(); return t; } catch (CloneNotSupportedException e) { return null; diff --git a/src/mars/assembler/TokenTypes.java b/src/mars/assembler/TokenTypes.java index ed25b0e..bd7a17a 100644 --- a/src/mars/assembler/TokenTypes.java +++ b/src/mars/assembler/TokenTypes.java @@ -1,9 +1,10 @@ - package mars.assembler; - import mars.*; - import mars.util.*; - import mars.mips.hardware.*; - - /* +package mars.assembler; +import mars.*; +import mars.util.*; +import mars.mips.hardware.*; +import java.util.ArrayList; + +/* Copyright (c) 2003-2008, Pete Sanderson and Kenneth Vollmar Developed by Pete Sanderson (psanderson@otterbein.edu) @@ -30,7 +31,7 @@ a copy of this software and associated documentation files (the (MIT license, http://www.opensource.org/licenses/mit-license.html) */ - + /** * Constants to identify the types of tokens found in MIPS programs. If Java had * enumerated types, that's how these would probably be implemented. @@ -39,57 +40,57 @@ a copy of this software and associated documentation files (the * @version August 2003 **/ - public final class TokenTypes { - - 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 String descriptor; - - private TokenTypes() { - // private ctor assures no objects can be created other than those above. - descriptor = "generic"; - } - - private TokenTypes(String name) { - descriptor = name; - } - - /** +public final class TokenTypes { + + 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 String descriptor; + + private TokenTypes() { + // private ctor assures no objects can be created other than those above. + descriptor = "generic"; + } + + private TokenTypes(String name) { + descriptor = name; + } + + /** * Produces String equivalent of this token type, which is its name. * * @return String containing descriptive name for token type. **/ - public String toString() { - return descriptor; - } - - /** + public String toString() { + return descriptor; + } + + /** * Classifies the given token into one of the MIPS types. * * @param value String containing candidate language element, extracted from MIPS program. @@ -98,196 +99,196 @@ public String toString() { * 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) == '#') + 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) { + + // 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; + 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) + // 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; + 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 + + /*************************************************************************** + * 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; + 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_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 { + } + 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) - { - // NO ACTION -- exception suppressed - } - - // See if it is an instruction operator - if (Globals.instructionSet.matchOperator(value) != null) + } + catch (NumberFormatException e) + { + // NO ACTION -- exception suppressed + } + + // 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 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()) { + /** + * + * 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; + result = false; index++; - } - return result; - } - - } + } + return result; + } + +}