/** * Add a modification record to this object file. * * @param record The modification record to add * @refcode OB * @errtest * N/A * @errmsg * N/A * @author Jacob Peddicord * @creation May 10, 2011 * @modlog * @teststandard Andrew Buelow * @codestandard Mark Mathis */ private void AddRecord(ModificationRecord record) { this.modificationRecords.Add(record); }
/** * Scan and the source (pass 2) and generate text and modification * records as apporpriate. * * @refcode OB * @errtest * N/A * @errmsg * N/A * @author Jacob Peddicord * @creation May 9, 2011 * @modlog * - May 10, 2011 - Jacob - Implemented some modification record things * - May 10, 2011 - Jacob - Refactored line iteration a bit * - May 11, 2011 - Jacob - Fix how we determine the type and number of modifications * - May 13, 2011 - Mark - Implement expression processing * - May 13, 2011 - Mark - Fix one of the expression cases with modifications * - May 14, 2011 - Mark - Implement ADC/e! * - May 14, 2011 - Mark - Adjust error handling code * @teststandard Andrew Buelow * @codestandard Mark Mathis */ private void GenerateSourceRecords() { foreach (IntermediateLine line in this.input) { //Console.WriteLine(this.SourceLine) // skip lines that won't be in the object file if (line.ProgramCounter == null) { // special case: RESET directive should generate a linking record if (line.Directive == "RESET" && line.GetThreeErrors().Count == 0) { var record = new LinkingRecord(this.symb.ProgramName); record.EntryName = line.Label; // set the location to the target of the RESET record.ProgramLocation = line.DirectiveOperand; this.AddRecord(record); } // store the rest in the report List<Errors.Error> errors = line.GetThreeErrors(); this.report.Add(null, null, ' ', line.SourceLineNumber, line.SourceLine, errors); continue; } // create the text record TextRecord rec = new TextRecord(this.symb.ProgramName); rec.ProgramLocation = line.ProgramCounter; rec.StatusFlag = 'A'; rec.Adjustments = "0"; string bin = line.Bytecode; // do we have an instruction? if (line.OpCategory != null) { // equated symbols may need to be relocated if (line.Equated != null) { Symbol symb = this.symb.GetSymbol(line.Equated); if (symb.relocations == 0) { rec.StatusFlag = 'A'; rec.Adjustments = "0"; } else if (symb.relocations == 1) { rec.StatusFlag = 'R'; rec.Adjustments = "0"; } else { ModificationRecord mod = new ModificationRecord(this.symb.ProgramName); mod.ProgramLocation = line.ProgramCounter; mod.Word = Convert.ToString(Convert.ToInt32(line.Bytecode, 2), 16); for (int i = 0; i < symb.relocations; i++) { mod.AddAdjustment(true, this.symb.ProgramName); } this.AddRecord(mod); rec.StatusFlag = 'M'; rec.Adjustments = Convert.ToString(symb.relocations, 16); } } // or a plain label else if (line.OpLitOperand == OperandParser.Literal.NONE) { // get the symbol that is being referenced if (line.OpOperand != null && this.symb.ContainsSymbol(line.OpOperand)) { Symbol symb = this.symb.GetSymbol(line.OpOperand); // external labels are processed in the linker if (symb.usage == Usage.EXTERNAL) { // create a modification record ModificationRecord mod = new ModificationRecord(this.symb.ProgramName); mod.ProgramLocation = line.ProgramCounter; mod.Word = Convert.ToString(Convert.ToInt32(line.Bytecode, 2), 16); mod.AddAdjustment(true, symb.rlabel); this.AddRecord(mod); // set the status to 1 modify rec.StatusFlag = 'M'; rec.Adjustments = "1"; } // otherwise we can resolve the symbol else { // get the binary encoding padded to 10 bits bin = BinaryHelper.BinaryString(symb.lc).PadLeft(10, '0'); // prefix it with the original instruction bytecode bin = line.Bytecode.Substring(0, 6) + bin; // relocatable label rec.StatusFlag = 'R'; } } // was it empty? else if (line.OpOperand == null || line.OpOperand.Length == 0) { bin = line.Bytecode; rec.StatusFlag = 'A'; } // error, otherwise else { line.AddError(Errors.Category.Serious, 34); line.NOPificate(); bin = line.Bytecode; } } // otherwise if it is (was) an expression else if (line.OpLitOperand == OperandParser.Literal.EXPRESSION) { // then is is relocatable // get the expression to be evaluated string expr = line.OpOperand; // create the modification record, this will have at least one // adjustment because these expressions are required to have a * ModificationRecord mod = new ModificationRecord(symb.ProgramName); // this is a garbage variable int rel; // evaluate the expression bool worked; worked = OperandParser.ParseExpression(ref expr, OperandParser.Expressions.Operand, line, ref symb, mod, out rel); if (worked) { // adjustments and such rec.StatusFlag = 'M'; rec.Adjustments = Convert.ToString(mod.Adjustments, 16); // get the hex sorted out int adj = Convert.ToInt32(expr, 16); int bytecode = Convert.ToInt32(bin, 2) + adj; // check the range int start = Convert.ToInt32(this.input.Line(1).DirectiveOperand, 16); int eof = start + Convert.ToInt32(this.input.ModuleLength, 16); if (Convert.ToInt32(expr, 16) > eof || Convert.ToInt32(expr, 16) < start) { line.AddError(Errors.Category.Serious, 38); line.NOPificate(); } else { bin = Convert.ToString(bytecode, 2); mod.Word = Convert.ToString(bytecode, 16); mod.ProgramLocation = line.ProgramCounter; this.AddRecord(mod); } } else { } } // special case: numeric values with GOTO, JUMP, MOPER else if (line.OpLitOperand == OperandParser.Literal.NUMBER && (line.OpFunction == "GOTO" || line.OpCategory == "JUMP" || line.OpCategory == "MOPER")) { rec.StatusFlag = 'R'; } // otherwise, it was a literal else { rec.StatusFlag = 'A'; } } // or a DAT directive? else if (line.Directive == "DAT") { // DAT fields shouldn't need to be modified rec.StatusFlag = 'A'; } // or an ADC directive? else if (line.Directive == "ADC" || line.Directive == "ADCE") { // make the modification record ModificationRecord mod = new ModificationRecord(symb.ProgramName); bool worked = true; string expr = line.DirectiveOperand; int rel = 0; int maxOp = 1; // this differentiates between ADC and ADCe if (line.Directive.EndsWith("E")) { maxOp = 3; } if (line.DirectiveLitOperand == OperandParser.Literal.EXPRESSION) { // wat do if expressions worked = OperandParser.ParseExpression(ref expr, OperandParser.Expressions.ADC, line, ref symb, mod, out rel, maxOp); // catching errors if (worked) { bin = Convert.ToString(Convert.ToInt32(expr, 16), 2); } } else if (line.DirectiveLitOperand == OperandParser.Literal.NUMBER) { // wat do if just a number mod.AddAdjustment(true, symb.ProgramName); rel = 1; bin = Convert.ToString(Convert.ToInt32(expr, 16), 2); } // check the range int start = Convert.ToInt32(this.input.Line(1).DirectiveOperand, 16); int eof = start + Convert.ToInt32(this.input.ModuleLength, 16); if (Convert.ToInt32(expr, 16) > eof || Convert.ToInt32(expr, 16) < start) { line.AddError(Errors.Category.Serious, 38); line.NOPificate(); bin = "0000000000000000"; } // if there are modifications to be made, add them if (mod.Adjustments > 0 && rel > 0 && worked) { rec.StatusFlag = 'M'; rec.Adjustments = Convert.ToString(mod.Adjustments, 16); mod.Word = Convert.ToString(Convert.ToInt32(bin, 2), 16); mod.ProgramLocation = line.ProgramCounter; this.AddRecord(mod); } } // anything else with a LC shouldn't be here... else { // this code *should* be unreachable throw new NotImplementedException(); } // convert bytecode to hex and add the record rec.HexCode = Convert.ToString(Convert.ToInt32(bin, 2), 16).ToUpper(); this.AddRecord(rec); // list of errors List<Errors.Error> errorlist = line.GetThreeErrors(); // add a line to the assembly report this.report.Add(line.ProgramCounter, rec.HexCode, rec.StatusFlag, line.SourceLineNumber, line.SourceLine, errorlist); } }
/** * Reads an expression and evaluates it as much as it can. If the parsing is * successful, the operand variable is replaced with the most fully parsed * version of the expression possible. If the expression cannot be parsed due * to an error in the expression, thie function returns false and an error is * added to the passed in IntermediateLine parameter. * * @refcode EX1, EX2, EX3 * @errtest N/A * @errmsg ES.2, ES.19, ES.20, ES.22, ES.23, ES.24, ES.27, ES.30, ES.31, ES.32, ES.33 * @author Mark Mathis * @creation April 18, 2011 * @modlog * - April 18, 2011 - Mark - Parses operand expressions (EX1). * - April 19, 2011 - Mark - Parses EQU expressions (EX2). * - April 19, 2011 - Mark - Parses ADC expressions (EX3). * - April 19, 2011 - Mark - Errors are caught and reported in all expressions. * - May 13, 2011 - Mark - Added rec and modifications parameters. * - May 14, 2011 - Mark - Now keeps track of adjustments required for pass 2. * @teststandard Andrew Buelow * @codestandard Mark Mathis * * @param operand the expression to parse, will contain the outgoing value of the expression * @param type the type of expression to be parsed * @param interLine the intermediate line containing this expression * @param symb the symbol table for this source code file * @param rec the modification record for keeping track of adjustments in instruction expressions * and ADC/ADCe expressions * @param modifications the number of adjustments needed for a EQU/EQUe expression * @param maxOperators the maximum number of operators allowed in this type of expression * * @return true if the expression is successfully parsed and evaluated. * false if an error is found. */ public static bool ParseExpression(ref string operand, OperandParser.Expressions type, IntermediateLine interLine, ref SymbolTable symb, ModificationRecord rec, out int modifications, int maxOperators = 1) { modifications = 0; if (operand != null && operand.Length > 0) { // if the expression is just a star, take care of it and return if (operand.Length == 1 && operand[0] == '*') { modifications++; rec.AddAdjustment(true, symb.ProgramName); operand = interLine.ProgramCounter; return true; } char[] validOperators = { '+', '-' }; // if there are too many operators, give an error List<string> operands = operand.Split(validOperators, StringSplitOptions.RemoveEmptyEntries).ToList(); if (operands.Count - 1 > maxOperators) { // error, too many operators interLine.AddError(Errors.Category.Serious, 22); return false; } // check for absolute ADC/ADCe bool absolute = false; if (operand[0] == '+' && type == Expressions.ADC) { absolute = true; operand = operand.Substring(1); } List<char> operators = new List<char>(); int pos = operand.IndexOfAny(validOperators, 0); while (pos != -1) { operators.Add(operand[pos]); pos = operand.IndexOfAny(validOperators, pos + 1); } // check that we have the correct number of operands if (operators.Count != operands.Count - 1) { // error, wrong number of operators interLine.AddError(Errors.Category.Serious, 24); return false; } // it can't always be that easy switch (type) { case OperandParser.Expressions.Operand: { char oprtr; string opr2; string star; if (operand[0] == '*') { star = interLine.ProgramCounter; oprtr = operand[1]; opr2 = operand.Substring(2); rec.AddAdjustment(true, symb.ProgramName); Tokenizer.TokenKinds valid; Tokenizer.GetTokenKind(opr2, out valid); if (valid == Tokenizer.TokenKinds.Label_Or_Command) { if (2 <= opr2.Length && opr2.Length <= 32) { if (symb.ContainsSymbol(opr2)) { if (symb.GetSymbol(opr2).usage == Usage.EQUATED) { opr2 = Convert.ToInt32(symb.GetSymbol(opr2).val, 16).ToString(); } else if (symb.GetSymbol(opr2).usage == Usage.LABEL) { opr2 = Convert.ToInt32(symb.GetSymbol(opr2).lc, 16).ToString(); rec.AddAdjustment(oprtr == '+', symb.ProgramName); } } } else { // error:label is too long Logger.Log("ERROR: ES.2 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 2); return false; } } else if (valid == Tokenizer.TokenKinds.Number) { if (!(0 <= Convert.ToInt32(opr2) && Convert.ToInt32(opr2) <= 1023)) { // error, the number is out of bounds Logger.Log("ERROR: ES.28 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 27); return false; } } else if (opr2 == "*") { // error: star used multiple times interLine.AddError(Errors.Category.Serious, 19); return false; } else { //error, must be number or previously equated symbol Logger.Log("ERROR: ES.29 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 27); return false; } Tokenizer.GetTokenKind(opr2, out valid); if (valid == Tokenizer.TokenKinds.Number) { int result = -1; // if the method gets here, then it's using a number or // symbol we can deal with switch (oprtr) { case '+': { result = Convert.ToInt32(star, 16) + Convert.ToInt32(opr2); } break; case '-': { result = Convert.ToInt32(star, 16) - Convert.ToInt32(opr2); } break; default: { // error invalid operator in expression Logger.Log("ERROR: ES.30 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 30); return false; } } if (0 <= result && result <= 1023) { operand = Convert.ToString(result,16); } else { // error: computation out of bounds Logger.Log("ERROR: ES.27 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 27); return false; } } } else { //error invalid operand expression Logger.Log("ERROR: ES.31 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 31); return false; } } break; case Expressions.EQU: { for (int i = 0; i < operands.Count; i++) { string label = operands[i]; Tokenizer.TokenKinds tokenkind; Tokenizer.GetTokenKind(label, out tokenkind); if (label == "*") { if (i == 0) { operands[i] = Convert.ToInt32(Parser.LC, 16).ToString(); modifications++; } else { // error, star must be first operand Logger.Log("ERROR: invalid star notation in expression.", "OperandParser"); interLine.AddError(Errors.Category.Serious, 19); return false; } } else if (symb.ContainsSymbol(label)) { Symbol operSym = symb.GetSymbol(label); if (operSym.usage == Usage.EQUATED) { operands[i] = Convert.ToInt32(operSym.val, 16).ToString(); modifications += operSym.relocations; } else if (operSym.usage == Usage.LABEL) { operands[i] = Convert.ToInt32(operSym.lc, 16).ToString(); modifications++; } else { // error, can only use equated symbols or local references Logger.Log("ERROR: ES.32 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 32); return false; } } else if (tokenkind == Tokenizer.TokenKinds.Number) { // this needs to be caught, so the next else doesn't get // tripped. but there's nothing to do herp. // I will tell you stories of my people. } else { // undefined symbol Logger.Log("ERROR: ES.20 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 20); return false; } } operands.Reverse(); operators.Reverse(); string possibleOperand = EvaluateExpression(new Stack<string>(operands), new Stack<char>(operators)); if (0 <= int.Parse(possibleOperand) && int.Parse(possibleOperand) <= 1023) { operand = Convert.ToString(int.Parse(possibleOperand), 16); } else { // error, calculation must be within the range of 0 to 1023 Logger.Log("ERROR: ES.27 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 27); return false; } } break; case Expressions.ADC: { bool noLabels = true; for (int i = 0; i < operands.Count; i++) { string label = operands[i]; if (label == "*") { noLabels = false; if (i == 0) { operands[i] = Convert.ToInt32(interLine.ProgramCounter,16).ToString(); rec.AddAdjustment(true, symb.ProgramName); modifications++; } else { // error, star must be first operand Logger.Log("ERROR: ES.19 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 19); return false; } } else if (symb.ContainsSymbol(label)) { noLabels = false; Symbol operSym = symb.GetSymbol(label); if (operSym.usage == Usage.LABEL) { operands[i] = Convert.ToInt32(operSym.lc, 16).ToString(); modifications++; if (i > 0) { rec.AddAdjustment(operators[i - 1] == '+', symb.ProgramName); } else { rec.AddAdjustment(true, symb.ProgramName); } } else if (operSym.usage == Usage.EXTERNAL) { noLabels = false; operands[i] = "0"; modifications++; if (i > 0) { rec.AddAdjustment(operators[i - 1] == '+', operSym.rlabel); } else { rec.AddAdjustment(true, operSym.rlabel); } } else { // error: symbols can only be external, local reference // in ADC/ADCe expressions Logger.Log("ERROR: ES.33 encountered", "OperandParser"); interLine.AddError(Errors.Category.Serious, 33); return false; } } } if (noLabels) { rec.AddAdjustment(true, symb.ProgramName); modifications++; } bool allNumbers = true; foreach (string op in operands) { Tokenizer.TokenKinds tokenKind; Tokenizer.GetTokenKind(op, out tokenKind); if (tokenKind == Tokenizer.TokenKinds.Number) { allNumbers = allNumbers && true; } else { allNumbers = false; } } if (allNumbers) { operands.Reverse(); operators.Reverse(); operand = EvaluateExpression(new Stack<string>(operands), new Stack<char>(operators)); operand = Convert.ToString(Convert.ToInt32(operand), 16); } else { operand = operands[0]; for (int i = 0; i + 1 < operands.Count; i++) { operand += operators[i] + operands[i + 1]; } } if (absolute) modifications = 0; } break; } } return true; }