package mars; import mars.assembler.*; import mars.mips.instructions.*; import mars.mips.hardware.*; import mars.util.*; import java.util.*; /* Copyright (c) 2003-2013, Pete Sanderson and Kenneth Vollmar Developed by Pete Sanderson (psanderson@otterbein.edu) and Kenneth Vollmar (kenvollmar@missouristate.edu) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. (MIT license, http://www.opensource.org/licenses/mit-license.html) */ /** * Represents one assembly/machine statement. This represents the "bare machine" level. * Pseudo-instructions have already been processed at this point and each assembly * statement generated by them is one of these. * * @author Pete Sanderson and Jason Bumgarner * @version August 2003 */ public class ProgramStatement { private MIPSprogram sourceMIPSprogram; private String source, basicAssemblyStatement, machineStatement; private TokenList originalTokenList, strippedTokenList; private BasicStatementList basicStatementList; private int[] operands; private int numOperands; private Instruction instruction; private int textAddress; private int sourceLine; private int binaryStatement; private boolean altered; private static final String invalidOperator = ""; ////////////////////////////////////////////////////////////////////////////////// /** * Constructor for ProgramStatement when there are links back to all source and token * information. These can be used by a debugger later on. * @param sourceMIPSprogram The MIPSprogram object that contains this statement * @param source The corresponding MIPS source statement. * @param origTokenList Complete list of Token objects (includes labels, comments, parentheses, etc) * @param strippedTokenList List of Token objects with all but operators and operands removed. * @param inst The Instruction object for this statement's operator. * @param textAddress The Text Segment address in memory where the binary machine code for this statement * is stored. **/ public ProgramStatement(MIPSprogram sourceMIPSprogram, String source, TokenList origTokenList, TokenList strippedTokenList, Instruction inst, int textAddress, int sourceLine) { this.sourceMIPSprogram = sourceMIPSprogram; this.source = source; this.originalTokenList = origTokenList; this.strippedTokenList = strippedTokenList; this.operands = new int[4]; this.numOperands = 0; this.instruction = inst; this.textAddress = textAddress; this.sourceLine = sourceLine; this.basicAssemblyStatement = null; this.basicStatementList = new BasicStatementList(); this.machineStatement = null; this.binaryStatement = 0; // nop, or sll $0, $0, 0 (32 bits of 0's) this.altered = false; } ////////////////////////////////////////////////////////////////////////////////// /** * Constructor for ProgramStatement used only for writing a binary machine * instruction with no source code to refer back to. Originally supported * only NOP instruction (all zeroes), but extended in release 4.4 to support * all basic instructions. This was required for the self-modifying code * feature. * @param binaryStatement The 32-bit machine code. * @param textAddress The Text Segment address in memory where the binary machine code for this statement * is stored. **/ public ProgramStatement(int binaryStatement, int textAddress) { this.sourceMIPSprogram = null; this.binaryStatement = binaryStatement; this.textAddress = textAddress; this.originalTokenList = this.strippedTokenList = null; this.source = ""; this.machineStatement = this.basicAssemblyStatement = null; BasicInstruction instr = Globals.instructionSet.findByBinaryCode(binaryStatement); if (instr == null) { this.operands = null; this.numOperands = 0; this.instruction = (binaryStatement==0) // this is a "nop" statement ? (Instruction) Globals.instructionSet.matchOperator("nop").get(0) : null; } else { this.operands = new int[4]; this.numOperands = 0; this.instruction = instr; String opandCodes = "fst"; String fmt = instr.getOperationMask(); BasicInstructionFormat instrFormat = instr.getInstructionFormat(); int numOps = 0; for (int i = 0; i < opandCodes.length(); i++) { int code = opandCodes.charAt(i); int j = fmt.indexOf(code); if (j >= 0) { int k0 = 31 - fmt.lastIndexOf(code); int k1 = 31 - j; int opand = (binaryStatement >> k0) & ((1 << (k1 - k0 + 1)) - 1); if (instrFormat.equals(BasicInstructionFormat.I_BRANCH_FORMAT) && numOps == 2) { opand = opand << 16 >> 16; } else if (instrFormat.equals(BasicInstructionFormat.J_FORMAT) && numOps == 0) { opand |= (textAddress >> 2) & 0x3C000000; } this.operands[numOps] = opand; numOps++; } } this.numOperands = numOps; } this.altered = false; this.basicStatementList = buildBasicStatementListFromBinaryCode(binaryStatement, instr, operands, numOperands); } ///////////////////////////////////////////////////////////////////////////// /** * Given specification of BasicInstruction for this operator, build the * corresponding assembly statement in basic assembly format (e.g. substituting * register numbers for register names, replacing labels by values). * @param errors The list of assembly errors encountered so far. May add to it here. **/ public void buildBasicStatementFromBasicInstruction(ErrorList errors) { Token token = strippedTokenList.get(0); String basicStatementElement = token.getValue()+" ";; String basic = basicStatementElement; basicStatementList.addString(basicStatementElement); // the operator TokenTypes tokenType, nextTokenType; String tokenValue; int registerNumber; this.numOperands = 0; for (int i=1; i> 2; address = (address - (this.textAddress+Instruction.INSTRUCTION_LENGTH)) >> 2; absoluteAddress = false; } } ////////////////////////////////////////////////////////////////////// basic += address; if (absoluteAddress) { // record as address if absolute, value if relative basicStatementList.addAddress(address); } else { basicStatementList.addValue(address); } this.operands[this.numOperands++] = address; } else if (tokenType == TokenTypes.INTEGER_5 || tokenType == TokenTypes.INTEGER_16 || tokenType == TokenTypes.INTEGER_16U || tokenType == TokenTypes.INTEGER_32) { int tempNumeric = Binary.stringToInt(tokenValue); /*************************************************************************** * 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 (tokenType != TokenTypes.INTEGER_16U) { // part of the Berkeley mod... * if ( Binary.isHex(tokenValue) && * (tempNumeric >= 32768) && * (tempNumeric <= 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. * tempNumeric = tempNumeric - 65536; * // Note: no action needed for range 0xffff8000 ... 0xffffffff * } * } ************************** END DPS 3-July-2008 COMMENTS *******************************/ basic += tempNumeric; basicStatementList.addValue(tempNumeric); this.operands[this.numOperands++] = tempNumeric; ///// End modification 1/7/05 KENV /////////////////////////////////////////// } else { basicStatementElement = tokenValue; basic += basicStatementElement; basicStatementList.addString(basicStatementElement); } // add separator if not at end of token list AND neither current nor // next token is a parenthesis if ((i < strippedTokenList.size()-1)) { nextTokenType = strippedTokenList.get(i+1).getType(); if (tokenType != TokenTypes.LEFT_PAREN && tokenType != TokenTypes.RIGHT_PAREN && nextTokenType != TokenTypes.LEFT_PAREN && nextTokenType != TokenTypes.RIGHT_PAREN) { basicStatementElement = ","; basic += basicStatementElement; basicStatementList.addString(basicStatementElement); } } } this.basicAssemblyStatement = basic; } //buildBasicStatementFromBasicInstruction() ///////////////////////////////////////////////////////////////////////////// /** * Given the current statement in Basic Assembly format (see above), build the * 32-bit binary machine code statement. * @param errors The list of assembly errors encountered so far. May add to it here. **/ public void buildMachineStatementFromBasicStatement(ErrorList errors) { try { //mask indicates bit positions for 'f'irst, 's'econd, 't'hird operand this.machineStatement = ((BasicInstruction)instruction).getOperationMask(); } // This means the pseudo-instruction expansion generated another // pseudo-instruction (expansion must be to all basic instructions). // This is an error on the part of the pseudo-instruction author. catch (ClassCastException cce) { errors.add(new ErrorMessage(this.sourceMIPSprogram,this.sourceLine,0, "INTERNAL ERROR: pseudo-instruction expansion contained a pseudo-instruction")); return; } BasicInstructionFormat format = ((BasicInstruction)instruction).getInstructionFormat(); if (format == BasicInstructionFormat.J_FORMAT) { if ((this.textAddress & 0xF0000000) != (this.operands[0] & 0xF0000000)) { // attempt to jump beyond 28-bit byte (26-bit word) address range. // SPIM flags as warning, I'll flag as error b/c MARS text segment not long enough for it to be OK. errors.add(new ErrorMessage(this.sourceMIPSprogram, this.sourceLine, 0, "Jump target word address beyond 26-bit range")); return; } // Note the bit shift to make this a word address. this.operands[0] = this.operands[0] >>> 2; this.insertBinaryCode(this.operands[0], Instruction.operandMask[0], errors); } else if (format == BasicInstructionFormat.I_BRANCH_FORMAT) { for (int i=0; i= numOperands, it returns -1. **/ public int getOperand(int i) { if (i >= 0 && i < this.numOperands) { return operands[i]; } else { return -1; } } ////////////////////////////////////////////////////////////////////////////// // Given operand (register or integer) and mask character ('f', 's', or 't'), // generate the correct sequence of bits and replace the mask with them. private void insertBinaryCode(int value, char mask, ErrorList errors) { int startPos = this.machineStatement.indexOf(mask); int endPos = this.machineStatement.lastIndexOf(mask); if (startPos == -1 || endPos == -1) { // should NEVER occur errors.add(new ErrorMessage(this.sourceMIPSprogram,this.sourceLine,0, "INTERNAL ERROR: mismatch in number of operands in statement vs mask")); return; } String bitString = Binary.intToBinaryString(value, endPos-startPos+1); String state = this.machineStatement.substring(0, startPos) + bitString; if (endPos < this.machineStatement.length()-1) state = state + this.machineStatement.substring(endPos+1); this.machineStatement = state; return; } // insertBinaryCode() ////////////////////////////////////////////////////////////////////////////// /* * Given a model BasicInstruction and the assembled (not source) operand array for a statement, * this method will construct the corresponding basic instruction list. This method is * used by the constructor that is given only the int address and binary code. It is not * intended to be used when source code is available. DPS 11-July-2013 */ private BasicStatementList buildBasicStatementListFromBinaryCode(int binary, BasicInstruction instr, int[] operands, int numOperands) { BasicStatementList statementList = new BasicStatementList(); int tokenListCounter = 1; // index 0 is operator; operands start at index 1 if (instr == null) { statementList.addString(invalidOperator); return statementList; } else { statementList.addString(instr.getName()+" "); } for (int i=0; i 1 && tokenListCounter