public static void GenerateIncomingCalls(IEnumerable <ProgramLine> programLines, MemoryMap memoryMap) { foreach (ProgramLine instructionLine in programLines) { if (instructionLine.OutgoingCall != null) { // Find the memory description of the call target address int targetAddress = instructionLine.OutgoingCall.Address; MemoryCellDescription memDescription = memoryMap.MemoryCellDescriptions[targetAddress]; // This description should be a instruction line starting at the same address if (memDescription == null || memDescription.Type != MemoryDescriptionType.ProgramLine) { throw new Exception("Outgoing call at address " + targetAddress + " points to an unknown area in memory"); } ProgramLine targetLine = (ProgramLine)memDescription.Description; if (targetLine.Type != ProgramLineType.OpCodeInstruction || targetLine.LineAddress != targetAddress) { throw new Exception("Outgoing call from program line " + instructionLine.Text + " at address " + instructionLine.LineAddress + " points in the middle of the instruction " + targetLine.Text + " starting at address " + targetLine.LineAddress + " in memory"); } else { // Connect the source and the target instructionLine.OutgoingCall.Line = targetLine; if (targetLine.IncomingCalls == null) { targetLine.IncomingCalls = new List <CallSource>(); } targetLine.IncomingCalls.Add(instructionLine.OutgoingCall.Source); } } } }
public void CommentStringChars(int startAddress, DecodeCharacter decodeChar, StringTerminationType terminationType, int expectedLength, byte terminationChar) { int stringLength = 0; StringTerminationTest terminationTest = b => stringLength == expectedLength; if (terminationType == StringTerminationType.InvertedChar) { terminationTest = b => b >= 128; } else if (terminationType == StringTerminationType.SpecialChar) { terminationTest = b => b == terminationChar; } int firstLineNumber = GetLineFromAddress(startAddress).LineNumber; for (int lineNumber = firstLineNumber; ; lineNumber++) { ProgramLine currentLine = Lines[lineNumber - 1]; byte charCode = currentLine.MemoryBytes[0]; stringLength++; if (terminationTest(charCode)) { return; } else { AppendCommentToProgramLine(currentLine.LineAddress, decodeChar(charCode)); } } }
public void RenameSymbolInProgram(string existingSymbol, string newSymbol) { if (!Variables.ContainsKey(existingSymbol)) { throw new Exception("Symbol " + existingSymbol + " does not exist, it can not be replaced"); } foreach (ProgramLine line in Lines) { if (line.Text.Contains(existingSymbol)) { StringBuilder newLineTextWithRenamedSymbol = new StringBuilder(); foreach (AsmToken token in line.AllTokens) { if (token.Type == AsmTokenType.SYMBOL && token.Text == existingSymbol) { if (token.LeadingWhiteSpace != null) { newLineTextWithRenamedSymbol.Append(token.LeadingWhiteSpace.Text); } newLineTextWithRenamedSymbol.Append(newSymbol); } else { newLineTextWithRenamedSymbol.Append(token.TextWithLeadingWhiteSpace); } } ProgramLine newLineWithRenamedSymbol = Assembler.ParseProgramLine(newLineTextWithRenamedSymbol.ToString()); line.CopyTextualRepresentationFrom(newLineWithRenamedSymbol); } } Variables.Add(newSymbol, Variables[existingSymbol]); Variables.Remove(existingSymbol); }
public void AppendCommentToProgramLine(int commentedLineAddress, string comment) { ProgramLine commentedLine = GetLineFromAddress(commentedLineAddress); string newLineTextWithComment = commentedLine.Text + " ; " + comment; ProgramLine newLineWithComment = Assembler.ParseProgramLine(newLineTextWithComment); commentedLine.CopyTextualRepresentationFrom(newLineWithComment); }
public void InsertCommentAboveProgramLine(int commentedLineAddress, string comment) { ProgramLine commentedLine = GetLineFromAddress(commentedLineAddress); int insertedLineNumber = commentedLine.LineNumber; ProgramLine newCommentLine = Assembler.ParseProgramLine("; " + comment, insertedLineNumber, false); Lines.Insert(insertedLineNumber - 1, newCommentLine); RenumberLinesAfterLineNumber(insertedLineNumber); }
public static void PrefixLabelToProgramLine(string labelName, ProgramLine programLine) { if (programLine.Label == null) { // Parse new line from text ProgramLine newTextLine = Assembler.ParseProgramLine(labelName + ":" + programLine.Text); // Copy textual representation to the original program Line programLine.CopyTextualRepresentationFrom(newTextLine); } else { throw new InvalidOperationException("Program line is already prefixed by a Label"); } }
public static void ReplaceAddressWithSymbolInProgramLine(ProgramLine instructionLine, int addressParameterIndexToReplace, bool addressIsRelative, string symbolName) { if (addressParameterIndexToReplace == 0 || addressParameterIndexToReplace == 1) { if (instructionLine.OpCodeParameters != null && instructionLine.OpCodeParameters.Count > 0) { InstructionLineParam paramToReplace = instructionLine.OpCodeParameters[addressParameterIndexToReplace]; // Find token to replace AsmToken tokenToReplace = paramToReplace.Tokens.FirstOrDefault <AsmToken>(t => t.Type == AsmTokenType.NUMBER); if (tokenToReplace != null) { // Generate new text for the line, replacing only the address token StringBuilder lineText = new StringBuilder(); foreach (AsmToken token in instructionLine.AllTokens) { if ((!addressIsRelative && token != tokenToReplace) || !paramToReplace.Tokens.Contains(token)) { lineText.Append(token.TextWithLeadingWhiteSpace); } else if (token == tokenToReplace) { if (tokenToReplace.LeadingWhiteSpace != null) { lineText.Append(tokenToReplace.LeadingWhiteSpace.Text); } else if (addressIsRelative && (paramToReplace.Tokens[0] != tokenToReplace)) { if (paramToReplace.Tokens[0].LeadingWhiteSpace != null) { lineText.Append(paramToReplace.Tokens[0].LeadingWhiteSpace.Text); } } lineText.Append(symbolName); } } // Parse new line from text ProgramLine newTextLine = Assembler.ParseProgramLine(lineText.ToString()); // Copy textual representation to the original program Line instructionLine.CopyTextualRepresentationFrom(newTextLine); } } } }
// Utility methods for the refactoring internal void CopyTextualRepresentationFrom(ProgramLine newTextLine) { Text = newTextLine.Text; Label = newTextLine.Label; LabelToken = newTextLine.LabelToken; Comment = newTextLine.Comment; CommentToken = newTextLine.CommentToken; OpCodeName = newTextLine.OpCodeName; OpCodeNameToken = newTextLine.OpCodeNameToken; OpCodeBytes = newTextLine.OpCodeBytes; OpCodeBytesTokens = newTextLine.OpCodeBytesTokens; OpCodeParameters = newTextLine.OpCodeParameters; DirectiveName = newTextLine.DirectiveName; DirectiveNameToken = newTextLine.DirectiveNameToken; DirectiveParameters = newTextLine.DirectiveParameters; AllTokens = newTextLine.AllTokens; }
public void PrefixLabelToProgramLine(int lineAddress, string labelName, bool extendSearchToAllNumbers) { ProgramLine programLine = GetLineFromAddress(lineAddress); if (programLine.Label == null) { // Parse new line from text ProgramLine newTextLine = Assembler.ParseProgramLine(labelName + ":" + programLine.Text); // Copy textual representation to the original program Line programLine.CopyTextualRepresentationFrom(newTextLine); // Update all references in the program ReplaceAddressWithSymbol(lineAddress, labelName, extendSearchToAllNumbers); } else { throw new InvalidOperationException("Program line is already prefixed by a Label"); } }
public void DefineWordData(int startAddress) { ProgramLine firstLine = GetLineFromAddress(startAddress); if (firstLine.Type != ProgramLineType.AssemblerDirective || firstLine.DirectiveName != "DEFB") { throw new Exception("Address should point to a DEFB directive"); } ProgramLine secondLine = GetLineFromAddress(startAddress + 1); if (secondLine.Type != ProgramLineType.AssemblerDirective || secondLine.DirectiveName != "DEFB") { throw new Exception("Address + 1 should point to a DEFB directive"); } int lsb = firstLine.DirectiveParameters[0].NumberExpression.GetValue(Variables, firstLine); int msb = secondLine.DirectiveParameters[0].NumberExpression.GetValue(Variables, secondLine); int word = msb * 256 + lsb; string newDirectiveText = "DEFW " + word.ToString("X4") + "H"; bool hasLeadingWhitespace = firstLine.DirectiveNameToken.LeadingWhiteSpace != null; if (hasLeadingWhitespace) { newDirectiveText = firstLine.DirectiveNameToken.LeadingWhiteSpace.Text + newDirectiveText; } if (firstLine.LabelToken != null) { newDirectiveText = firstLine.LabelToken.TextWithLeadingWhiteSpace + (hasLeadingWhitespace ? "" : "\t") + newDirectiveText; } ProgramLine newDirectiveLine = Assembler.ParseProgramLine(newDirectiveText, firstLine.LineNumber, false); newDirectiveLine.LineAddress = firstLine.LineAddress; Lines.RemoveAt(firstLine.LineNumber - 1); Lines.RemoveAt(firstLine.LineNumber - 1); Lines.Insert(firstLine.LineNumber - 1, newDirectiveLine); RenumberLinesAfterLineNumber(firstLine.LineNumber); }
public override int GetValue(IDictionary <string, NumberExpression> variables, ProgramLine prgLine) { int leftValue = left.GetValue(variables, prgLine); int rightValue = right.GetValue(variables, prgLine); switch (operation) { case NumberOperationType.Addition: return(leftValue + rightValue); case NumberOperationType.Subtraction: return(leftValue - rightValue); //case NumberOperationType.Multiplication: default: return(leftValue * rightValue); } }
public abstract int GetValue(IDictionary <string, NumberExpression> variables, ProgramLine prgLine);
public CallSource(CallSourceType type, int sourceInstructionAddress, ProgramLine programLine) { Type = type; Address = sourceInstructionAddress; Line = programLine; }
public static void ReplaceAddressWithSymbolInProgramLine(ProgramLine instructionLine, int addressParameterIndexToReplace, bool addressIsRelative, string symbolName) { if (addressParameterIndexToReplace == 0 || addressParameterIndexToReplace == 1) { if (instructionLine.OpCodeParameters != null && instructionLine.OpCodeParameters.Count > 0) { InstructionLineParam paramToReplace = instructionLine.OpCodeParameters[addressParameterIndexToReplace]; // Find token to replace AsmToken tokenToReplace = paramToReplace.Tokens.FirstOrDefault<AsmToken>(t => t.Type == AsmTokenType.NUMBER); if (tokenToReplace != null) { // Generate new text for the line, replacing only the address token StringBuilder lineText = new StringBuilder(); foreach (AsmToken token in instructionLine.AllTokens) { if ((!addressIsRelative && token != tokenToReplace) || !paramToReplace.Tokens.Contains(token)) { lineText.Append(token.TextWithLeadingWhiteSpace); } else if (token == tokenToReplace) { if (tokenToReplace.LeadingWhiteSpace != null) { lineText.Append(tokenToReplace.LeadingWhiteSpace.Text); } else if (addressIsRelative && (paramToReplace.Tokens[0] != tokenToReplace)) { if (paramToReplace.Tokens[0].LeadingWhiteSpace != null) { lineText.Append(paramToReplace.Tokens[0].LeadingWhiteSpace.Text); } } lineText.Append(symbolName); } } // Parse new line from text ProgramLine newTextLine = Assembler.ParseProgramLine(lineText.ToString()); // Copy textual representation to the original program Line instructionLine.CopyTextualRepresentationFrom(newTextLine); } } } }
public static InstructionFlowBehavior GenerateOutgoingCall(ProgramLine programLine, IDictionary <string, NumberExpression> programVariables) { if (programLine.Type == ProgramLineType.OpCodeInstruction) { // Analyse instruction effect on the program flow InstructionFlowBehavior instructionFlowBehavior = ProgramFlowAnalyzer.GetInstructionFlowBehavior(programLine.InstructionCode.InstructionType); // - check if the program flow continues to the next line programLine.ContinueToNextLine = (instructionFlowBehavior & InstructionFlowBehavior.ContinueToNextInstruction) > 0; // - check if the current program line can jump elswhere (either always or depending on a condition) if ((instructionFlowBehavior & InstructionFlowBehavior.JumpToKnownLocation) > 0) { // Register the outgoing call address in the program line CallSourceType callInstructionType; InstructionLineParam targetAddressParameter; AddressingMode targetAddressParameterType; switch (programLine.InstructionType.Index) { // -- Call and jump instructions -- // Instruction_18_CALL_Address PC => stack, PC = Param1 (ODh|ODl) case 18: callInstructionType = CallSourceType.CallInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; // Instruction_19_CALL_FlagCondition_Address (PC += instruction size) OR (PC => stack, PC = Param2 (ODh|ODl)) case 19: callInstructionType = CallSourceType.CallInstruction; targetAddressParameter = programLine.OpCodeParameters[1]; targetAddressParameterType = programLine.InstructionType.Param2Type.Value; break; // Instruction_36_DJNZ_RelativeDisplacement (PC += instruction size) OR (PC += Param1 (OD)) case 36: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; // Instruction_54_JP_Address PC = Param1 (ODh|ODl) case 54: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; // Instruction_56_JP_FlagCondition_Address (PC += instruction size) OR (PC = Param2 (ODh|ODl)) case 56: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[1]; targetAddressParameterType = programLine.InstructionType.Param2Type.Value; break; // Instruction_57_JR_RelativeDisplacement PC += Param1 (OD) case 57: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; // Instruction_58_JR_FlagCondition_RelativeDisplacement (PC += instruction size) OR (PC += Param2 (OD)) case 58: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[1]; targetAddressParameterType = programLine.InstructionType.Param2Type.Value; break; // Instruction_122_RST_ResetAddress PC => stack, PC = (AddressModifiedPageZero)Param1 case 122: callInstructionType = CallSourceType.CallInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; default: throw new NotImplementedException("Unexpected instruction type " + programLine.InstructionType.Index); } // Compute target address int targetAddress; if (targetAddressParameterType == AddressingMode.Relative && !(targetAddressParameter.NumberExpression is SymbolOperand)) { targetAddress = programLine.LineAddress + targetAddressParameter.NumberExpression.GetValue(programVariables, programLine) /* + 2 not necessary because the assembler already adjusts when parsing the expression */; } else { targetAddress = targetAddressParameter.NumberExpression.GetValue(programVariables, programLine); } // Register call source and call target CallSource callSource = new CallSource(callInstructionType, programLine.LineAddress, programLine); CallTarget callTarget = new CallTarget(callSource, targetAddress); programLine.OutgoingCall = callTarget; } return(instructionFlowBehavior); } else { return(0); } }
public override int GetValue(IDictionary <string, NumberExpression> variables, ProgramLine prgLine) { return(prgLine.LineAddress); }
public static ProgramLine ParseProgramLine(string programLineText, int lineNumber, bool ignoreCase) { // Create a new program line from text ProgramLine programLine = new ProgramLine() { LineNumber = lineNumber, Text = programLineText }; // Ignore custom assembler directives if (programLineText.Length > 0) { string trimText = programLineText.Trim(); if (trimText.Length > 0 && (trimText[0] == '#' || trimText[0] == '.')) { programLine.Comment = programLineText; return programLine; } } // Parse the program line AsmLexer lexer = new AsmLexer(programLine.Text, programLine.LineNumber, ignoreCase); AsmParser parser = new AsmParser(lexer, programLine); try { parser.ParseProgramLine(); } catch (Exception e) { programLine.Error = new ProgramLineError() { ErrorMessage = e.Message, StartColumnIndex = 0, EndColumnIndex = programLine.Text.Length - 1 }; } return programLine; }
public override int GetValue(IDictionary<string, NumberExpression> variables, ProgramLine prgLine) { return value; }
public override int GetValue(IDictionary<string, NumberExpression> variables, ProgramLine prgLine) { // Compute relative displacement from program line address if (((SymbolOperand)left).IsLabel(variables)) { return base.GetValue(variables, prgLine); } // Return the original value else { return left.GetValue(variables, prgLine); } }
public static Program GenerateProgram(string sourcePath, Stream machinecodeStream, int programStartAddress, CallTarget[] entryPoints, MemoryMap memoryMap) { // Load machine code in memory int b = -1; int currentAddress = programStartAddress; while ((b = machinecodeStream.ReadByte()) >= 0) { memoryMap.MemoryCells[currentAddress] = (byte)b; memoryMap.MemoryCellDescriptions[currentAddress] = null; // Reset previous cell descriptions currentAddress++; } int programEndAddress = currentAddress - 1; // Check entry points if (entryPoints == null || entryPoints.Length == 0) { throw new Exception("Entry point adresses are mandatory to try to decompile machine code"); } foreach (CallTarget entryPoint in entryPoints) { if (entryPoint.Address < programStartAddress || entryPoint.Address > programEndAddress) { throw new Exception("Entry point adrress : " + entryPoint.Address + " is out of the range of the program"); } } // Initialize program Program program = new Program(sourcePath, ProgramSource.ObjectCodeBinary); program.BaseAddress = programStartAddress; program.MaxAddress = programEndAddress; memoryMap.Programs.Add(program); program.Lines.Add(Assembler.ParseProgramLine("; **************************************************************************")); program.Lines.Add(Assembler.ParseProgramLine("; * Decompiled assembly program for : " + sourcePath)); program.Lines.Add(Assembler.ParseProgramLine("; **************************************************************************")); program.Lines.Add(Assembler.ParseProgramLine("")); program.Lines.Add(Assembler.ParseProgramLine("ORG " + FormatHexAddress(programStartAddress))); program.Lines.Add(Assembler.ParseProgramLine("")); // Initialize code addresses to explore with entry points received as parameters Queue <CallTarget> callTargetsToExplore = new Queue <CallTarget>(); foreach (CallTarget entryPoint in entryPoints) { callTargetsToExplore.Enqueue(entryPoint); } // The tracing decompiler will follow all the code paths // and it will generate the program lines completely out of order. // We can't add them to the program in the ascending // order of the addresses during the first pass. // So we store them in a temporary map for the second pass. IDictionary <int, ProgramLine> generatedProgramLines = new SortedDictionary <int, ProgramLine>(); // Start from each call target and follow code path while (callTargetsToExplore.Count > 0) { // Dequeue one code address to explore CallTarget entryPoint = callTargetsToExplore.Dequeue(); currentAddress = entryPoint.Address; // If this code address was already explored, do nothing if (memoryMap.MemoryCellDescriptions[currentAddress] != null) { continue; } // Check if there is a limit in the number of opcodes to disassemble bool stopDisassemblyAfterMaxNumberOfBytes = entryPoint.Source.CodeRelocationBytesCount > 0; int maxNumberOfBytes = entryPoint.Source.CodeRelocationBytesCount; InstructionFlowBehavior instructionFlowBehavior = 0; do { // Check if instruction start address stays in the boundaries of the current program if (currentAddress < programStartAddress || currentAddress > programEndAddress) { throw new Exception("Entry point address : " + currentAddress + " is out of the range of the program"); } // Check if current address was already explored if (memoryMap.MemoryCellDescriptions[currentAddress] != null) { break; } // Check if the maximum number of bytes to disassemble after the current entry point was reached if (stopDisassemblyAfterMaxNumberOfBytes && (currentAddress - entryPoint.Address) >= maxNumberOfBytes) { break; } // Read one instruction code int instructionStartAddress = currentAddress; byte displacement; InstructionCode instructionCode = ReadOneInstructionCode(memoryMap, ref currentAddress, out displacement); // Check that instruction end address stays in the boundaries of the current program int instructionEndAddress = instructionStartAddress + instructionCode.OpCodeByteCount + instructionCode.OperandsByteCount - 1; if (instructionEndAddress > programEndAddress) { throw new Exception("End of instruction : " + instructionCode.InstructionType.OpCodeName + ", at address : " + instructionStartAddress + " is out of the range of the program"); } // Read instruction code operands byte operandByte1 = 0; byte operandByte2 = 0; if (instructionCode.OpCodeByteCount == 3) { operandByte1 = displacement; } else { if (instructionCode.OperandsByteCount >= 1) { operandByte1 = memoryMap.MemoryCells[currentAddress++]; } if (instructionCode.OperandsByteCount == 2) { operandByte2 = memoryMap.MemoryCells[currentAddress++]; } } // Generate assembly text for the current instruction string instructionLineText = GenerateInstructionLineText(instructionCode, operandByte1, operandByte2); // Create a new program line from its textual representation ProgramLine programLine = Assembler.ParseProgramLine(instructionLineText); // Register the binary representation of the program line programLine.LineAddress = instructionStartAddress; programLine.InstructionCode = instructionCode; programLine.InstructionType = instructionCode.InstructionType; programLine.OperandByte1 = operandByte1; programLine.OperandByte2 = operandByte2; // Store the generated program line in the temporay map generatedProgramLines.Add(instructionStartAddress, programLine); // Register the signification of all the bytes of the instruction in the memory map MemoryCellDescription instructionDescription = new MemoryCellDescription(MemoryDescriptionType.ProgramLine, programLine); for (int instructionAddress = instructionStartAddress; instructionAddress <= instructionEndAddress; instructionAddress++) { memoryMap.MemoryCellDescriptions[instructionAddress] = instructionDescription; } // Analyse instruction effect on the program flow // - check if the program flow continues to the next line // - check if the current program line can jump elswhere (either always or depending on a condition) // => register the outgoing call address in the program line instructionFlowBehavior = ProgramFlowAnalyzer.GenerateOutgoingCall(programLine, null); if (programLine.OutgoingCall != null) { // If target address was not explored before, add a new entry point to the list if (memoryMap.MemoryCellDescriptions[programLine.OutgoingCall.Address] == null) { callTargetsToExplore.Enqueue(programLine.OutgoingCall); } } } // Continue to next instruction if the current instruction enables it while ((instructionFlowBehavior & InstructionFlowBehavior.ContinueToNextInstruction) > 0); } // Find all the outgoing calls and register them as incoming calls on their target lines ProgramFlowAnalyzer.GenerateIncomingCalls(generatedProgramLines.Values, memoryMap); // To enhance the readability of the generated program // Find all the incoming calls : // - generate labels for the target lines // Find all the outgoing calls // - replace address references with labels in the source lines Assembler.GenerateLabelsForIncomingAndOutgoingCalls(generatedProgramLines.Values, program.Variables); // All the memory cells which were not hit during the exploration // of all the code paths must represent data MemoryCellDescription dataDescription = new MemoryCellDescription(MemoryDescriptionType.Data, null); for (int programAddress = programStartAddress; programAddress <= programEndAddress; programAddress++) { if (memoryMap.MemoryCellDescriptions[programAddress] == null) { // Register in the memory map that this address contains data memoryMap.MemoryCellDescriptions[programAddress] = dataDescription; // Generate a program line to insert this byte of data ProgramLine dataDirectiveLine = Assembler.ParseProgramLine("DEFB " + String.Format("{0:X2}H", memoryMap.MemoryCells[programAddress])); // Register the binary representation of the program line dataDirectiveLine.LineAddress = programAddress; dataDirectiveLine.MemoryBytes = new byte[] { memoryMap.MemoryCells[programAddress] }; // Store the generated program line in the temporay map generatedProgramLines.Add(programAddress, dataDirectiveLine); } } // Add all generated program lines in the ascending order of their addresses foreach (ProgramLine programLine in generatedProgramLines.Values) { programLine.LineNumber = program.Lines.Count + 1; program.Lines.Add(programLine); } // Try to compile the generated program and check that it produces the right machine code output // NB : !! remove this step when the decompiler is well tested and mature !! MemoryMap generatedProgramMachineCode = new MemoryMap(memoryMap.MemoryCells.Length); Assembler.CompileProgram(program, programStartAddress, generatedProgramMachineCode); for (int programAddress = programStartAddress; programAddress <= programEndAddress; programAddress++) { if (generatedProgramMachineCode.MemoryCells[programAddress] != memoryMap.MemoryCells[programAddress]) { throw new Exception("Error in decompiled program at address " + programAddress + " : source byte = " + memoryMap.MemoryCells[programAddress] + ", decompiled program line + " + ((ProgramLine)memoryMap.MemoryCellDescriptions[programAddress].Description).Text + " produced byte = " + generatedProgramMachineCode.MemoryCells[programAddress]); } } return(program); }
public override int GetValue(IDictionary<string, NumberExpression> variables, ProgramLine prgLine) { int leftValue = left.GetValue(variables, prgLine); int rightValue = right.GetValue(variables, prgLine); switch (operation) { case NumberOperationType.Addition: return leftValue + rightValue; case NumberOperationType.Subtraction: return leftValue - rightValue; //case NumberOperationType.Multiplication: default: return leftValue * rightValue; } }
public override int GetValue(IDictionary <string, NumberExpression> variables, ProgramLine prgLine) { // Compute relative displacement from program line address if (((SymbolOperand)left).IsLabel(variables)) { return(base.GetValue(variables, prgLine)); } // Return the original value else { return(left.GetValue(variables, prgLine)); } }
private static void CompileCommentLine(ProgramLine programLine) { if (!String.IsNullOrEmpty(programLine.Label)) { throw new Exception(String.Format("Line {0} : a label is not allowed for a comments or empty line", programLine.LineNumber)); } }
public override int GetValue(IDictionary <string, NumberExpression> variables, ProgramLine prgLine) { NumberExpression valueExpression; if (variables == null || !variables.TryGetValue(symbol, out valueExpression)) { throw new Exception(String.Format("Line {0} : No value has been assigned to symbol {1} in {2}", prgLine.LineNumber, symbol, prgLine.Text)); } return(valueExpression.GetValue(variables, prgLine)); }
private static void CompileInstructionLine(Program program, MemoryMap memoryMap, ref int address, IList<Operand> operands, ProgramLine programLine) { if (!String.IsNullOrEmpty(programLine.Label)) { string label = programLine.Label; if (!program.Variables.ContainsKey(label)) { program.Variables.Add(label, new LabelAddress(address)); } } // Find instruction code and instruction type string instructionText = GenerateInstructionText(programLine); InstructionCode instructionCode = null; if (!Z80OpCodes.Dictionary.TryGetValue(instructionText, out instructionCode)) { throw new Exception(String.Format("Line {0} : invalid instruction type {1}", programLine.LineNumber, instructionText)); } InstructionType instructionType = Z80InstructionTypes.Table[instructionCode.InstructionTypeIndex]; // Register binary representation of program line programLine.InstructionCode = instructionCode; programLine.InstructionType = instructionType; MemoryCellDescription programLineDescription = new MemoryCellDescription(MemoryDescriptionType.ProgramLine, programLine); // One or two byte opcodes if (instructionCode.OpCodeByteCount <= 2) { // Optional one byte prefix if (instructionCode.Prefix != null) { memoryMap.MemoryCellDescriptions[address] = programLineDescription; memoryMap.MemoryCells[address++] = instructionCode.Prefix[0]; } // Opcode memoryMap.MemoryCellDescriptions[address] = programLineDescription; memoryMap.MemoryCells[address++] = instructionCode.OpCode; // Parameters AddressingMode? paramType1 = instructionType.Param1Type; if (paramType1.HasValue) { InstructionLineParam lineParam1 = programLine.OpCodeParameters[0]; AddOperandForLineParam(ref address, operands, programLine, paramType1, lineParam1); } AddressingMode? paramType2 = instructionType.Param2Type; if (paramType2.HasValue) { InstructionLineParam lineParam2 = programLine.OpCodeParameters[1]; AddOperandForLineParam(ref address, operands, programLine, paramType2, lineParam2); } } // Four bytes opcodes else { // Two bytes prefix memoryMap.MemoryCellDescriptions[address] = programLineDescription; memoryMap.MemoryCells[address++] = instructionCode.Prefix[0]; memoryMap.MemoryCellDescriptions[address] = programLineDescription; memoryMap.MemoryCells[address++] = instructionCode.Prefix[1]; // Displacement AddressingMode? paramType1 = instructionType.Param1Type; if (paramType1.HasValue) { InstructionLineParam lineParam1 = programLine.OpCodeParameters[0]; AddOperandForLineParam(ref address, operands, programLine, paramType1, lineParam1); } AddressingMode? paramType2 = instructionType.Param2Type; if (paramType2.HasValue) { InstructionLineParam lineParam2 = programLine.OpCodeParameters[1]; AddOperandForLineParam(ref address, operands, programLine, paramType2, lineParam2); } // Opcode memoryMap.MemoryCellDescriptions[address] = programLineDescription; memoryMap.MemoryCells[address++] = instructionCode.OpCode; } }
public int GetAddressFromLine(int programLineNumber) { ProgramLine programLine = Lines[programLineNumber - 1]; return(programLine.LineAddress); }
private static void TryReplaceAddressWithSystemVariables(SpectrumMemoryMap spectrumMemory, Program program, ProgramLine line, int paramIndex) { if ((paramIndex == 0 && line.InstructionType.Param1Type == AddressingMode.Extended) || (paramIndex == 1 && line.InstructionType.Param2Type == AddressingMode.Extended)) { NumberOperand operand = line.OpCodeParameters[paramIndex].NumberExpression as NumberOperand; if (operand != null) { int address = operand.GetValue(null, line); if (address >= 23552 && address <= 23733) { SpectrumSystemVariable systemVariable = spectrumMemory.MemoryCellDescriptions[address].Description as SpectrumSystemVariable; if (systemVariable != null) { if (!program.Variables.ContainsKey(systemVariable.Name)) { program.Variables.Add(systemVariable.Name, new NumberOperand(address)); } Program.ReplaceAddressWithSymbolInProgramLine(line, paramIndex, false, systemVariable.Name); program.AppendCommentToProgramLine(line.LineAddress, systemVariable.Name + " : " + systemVariable.Description); } } } } }
public static InstructionFlowBehavior GenerateOutgoingCall(ProgramLine programLine, IDictionary<string, NumberExpression> programVariables) { if (programLine.Type == ProgramLineType.OpCodeInstruction) { // Analyse instruction effect on the program flow InstructionFlowBehavior instructionFlowBehavior = ProgramFlowAnalyzer.GetInstructionFlowBehavior(programLine.InstructionCode.InstructionType); // - check if the program flow continues to the next line programLine.ContinueToNextLine = (instructionFlowBehavior & InstructionFlowBehavior.ContinueToNextInstruction) > 0; // - check if the current program line can jump elswhere (either always or depending on a condition) if ((instructionFlowBehavior & InstructionFlowBehavior.JumpToKnownLocation) > 0) { // Register the outgoing call address in the program line CallSourceType callInstructionType; InstructionLineParam targetAddressParameter; AddressingMode targetAddressParameterType; switch (programLine.InstructionType.Index) { // -- Call and jump instructions -- // Instruction_18_CALL_Address PC => stack, PC = Param1 (ODh|ODl) case 18: callInstructionType = CallSourceType.CallInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; // Instruction_19_CALL_FlagCondition_Address (PC += instruction size) OR (PC => stack, PC = Param2 (ODh|ODl)) case 19: callInstructionType = CallSourceType.CallInstruction; targetAddressParameter = programLine.OpCodeParameters[1]; targetAddressParameterType = programLine.InstructionType.Param2Type.Value; break; // Instruction_36_DJNZ_RelativeDisplacement (PC += instruction size) OR (PC += Param1 (OD)) case 36: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; // Instruction_54_JP_Address PC = Param1 (ODh|ODl) case 54: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; // Instruction_56_JP_FlagCondition_Address (PC += instruction size) OR (PC = Param2 (ODh|ODl)) case 56: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[1]; targetAddressParameterType = programLine.InstructionType.Param2Type.Value; break; // Instruction_57_JR_RelativeDisplacement PC += Param1 (OD) case 57: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; // Instruction_58_JR_FlagCondition_RelativeDisplacement (PC += instruction size) OR (PC += Param2 (OD)) case 58: callInstructionType = CallSourceType.JumpInstruction; targetAddressParameter = programLine.OpCodeParameters[1]; targetAddressParameterType = programLine.InstructionType.Param2Type.Value; break; // Instruction_122_RST_ResetAddress PC => stack, PC = (AddressModifiedPageZero)Param1 case 122: callInstructionType = CallSourceType.CallInstruction; targetAddressParameter = programLine.OpCodeParameters[0]; targetAddressParameterType = programLine.InstructionType.Param1Type.Value; break; default: throw new NotImplementedException("Unexpected instruction type " + programLine.InstructionType.Index); } // Compute target address int targetAddress; if (targetAddressParameterType == AddressingMode.Relative && !(targetAddressParameter.NumberExpression is SymbolOperand)) { targetAddress = programLine.LineAddress + targetAddressParameter.NumberExpression.GetValue(programVariables, programLine) /* + 2 not necessary because the assembler already adjusts when parsing the expression */; } else { targetAddress = targetAddressParameter.NumberExpression.GetValue(programVariables, programLine); } // Register call source and call target CallSource callSource = new CallSource(callInstructionType, programLine.LineAddress, programLine); CallTarget callTarget = new CallTarget(callSource, targetAddress); programLine.OutgoingCall = callTarget; } return instructionFlowBehavior; } else { return 0; } }
public void ReplaceAddressWithSymbol(int addressToReplace, string symbolName, bool extendSearchToAllNumbers) { if (Variables.ContainsKey(symbolName)) { if (Variables[symbolName].GetValue(Variables, null) != addressToReplace) { throw new Exception("Symbol " + symbolName + " already has a different meaning"); } } else { Variables[symbolName] = new NumberOperand(addressToReplace); } foreach (ProgramLine line in Lines) { if (line.Type == ProgramLineType.OpCodeInstruction) { if (line.InstructionType.Param1Type == AddressingMode.Extended || line.InstructionType.Param1Type == AddressingMode.Relative || (extendSearchToAllNumbers && line.InstructionType.Param1Type == AddressingMode.Immediate16)) { if (line.OpCodeParameters[0].NumberExpression.GetValue(Variables, line) == addressToReplace) { ReplaceAddressWithSymbolInProgramLine(line, 0, line.InstructionType.Param1Type == AddressingMode.Relative, symbolName); } } if (line.InstructionType.Param2Type == AddressingMode.Extended || line.InstructionType.Param2Type == AddressingMode.Relative || (extendSearchToAllNumbers && line.InstructionType.Param2Type == AddressingMode.Immediate16)) { if (line.OpCodeParameters[1].NumberExpression.GetValue(Variables, line) == addressToReplace) { ReplaceAddressWithSymbolInProgramLine(line, 1, line.InstructionType.Param2Type == AddressingMode.Relative, symbolName); } } } else if (extendSearchToAllNumbers && line.Type == ProgramLineType.AssemblerDirective) { if (line.DirectiveName == "DEFW" && line.DirectiveParameters[0].NumberExpression.GetValue(Variables, line) == addressToReplace) { // Parse new line from text string newDirectiveText = line.DirectiveNameToken.TextWithLeadingWhiteSpace; if (line.LabelToken != null) { newDirectiveText = line.LabelToken.TextWithLeadingWhiteSpace + newDirectiveText; } if (line.DirectiveParameters[0].Tokens[0].LeadingWhiteSpace != null) { newDirectiveText += line.DirectiveParameters[0].Tokens[0].LeadingWhiteSpace.Text; } else { newDirectiveText += " "; } newDirectiveText += symbolName; ProgramLine newDirectiveLine = Assembler.ParseProgramLine(newDirectiveText); // Copy textual representation to the original program Line line.CopyTextualRepresentationFrom(newDirectiveLine); } } } }
private static void AddOperandForLineParam(ref int address, IList<Operand> operands, ProgramLine programLine, AddressingMode? paramType, InstructionLineParam lineParam) { Operand operand = null; switch (paramType.Value) { // 8 bit unsigned operand case AddressingMode.Immediate: case AddressingMode.IOPortImmediate: operand = new Operand() { Type = OperandType.Unsigned8, Address = address, Expression = lineParam.NumberExpression, Line = programLine}; address += 1; break; // 8 bit signed operand case AddressingMode.Indexed: case AddressingMode.Relative: operand = new Operand() { Type = OperandType.Signed8, Address = address, Expression = lineParam.NumberExpression, Line = programLine }; address += 1; if (paramType.Value == AddressingMode.Relative && operand.Expression is SymbolOperand) { // If a label address is used where a relative address is expected, // we have to compute the relative displacement from the current addres to the label address operand.Expression = new RelativeDisplacementFromLabelAddressExpression((SymbolOperand)operand.Expression, NumberOperationType.Subtraction, new NumberOperand(address)); } else if (paramType.Value == AddressingMode.Relative && operand.Expression is NumberOperand) { // Relative adressing mode definition (DJNZ and JR instructions) : // The jump is measured from the address of the instruction OpCode and has a range of -126 to +129 bytes. // The assembler automatically adjusts for the twice incremented PC. operand.Expression = new NumberOperationExpression(operand.Expression, NumberOperationType.Subtraction, new NumberOperand(2)); } break; // 16 bit unsigned operand case AddressingMode.Immediate16: case AddressingMode.Extended: operand = new Operand() { Type = OperandType.Unsigned16, Address = address, Expression = lineParam.NumberExpression, Line = programLine}; address += 2; break; } if (operand != null) { operands.Add(operand); } }
public AsmParser(AsmLexer lexer, ProgramLine prgLine) { this.lexer = lexer; this.lineNumber = prgLine.LineNumber; this.prgLine = prgLine; }
private static bool CompileDirectiveLine(Program program, MemoryMap memoryMap, ref int address, IList<Operand> operands, ProgramLine programLine) { switch (programLine.DirectiveName) { case "EQU": // label EQU nn : this directive is used to assign a value to a label. if (String.IsNullOrEmpty(programLine.Label)) { throw new Exception(String.Format("Line {0} : EQU directive needs a label", programLine.LineNumber)); } else if (programLine.DirectiveParameters.Count != 1) { throw new Exception(String.Format("Line {0} : EQU directive needs exactly one parameter", programLine.LineNumber)); } else { DirectiveLineParam param = programLine.DirectiveParameters[0]; string label = programLine.Label; if (program.Variables.ContainsKey(label)) { throw new Exception(String.Format("Line {0} : EQU directive - this label can not be defined twice", programLine.LineNumber)); } program.Variables.Add(label, param.NumberExpression); } break; case "DEFL": // label DEFL nn : this directive also assigns a value nn to a label, // but may be repeated whithin the program with different values for // the same label, whereas EQU may be used only once. // ==> Symbol table is much more complicated to handle in incremental parsing scenarios if it does not contain constants only throw new Exception(String.Format("Line {0} : DEFL directive is not supported", programLine.LineNumber)); case "ORG": // ORG nn : this directive will set the address counter to the value nn. // In other words, the first executable instruction encountered after // this directive will reside at the value nn. It can be used to locate // different segments of a program at different memory locations. if (!String.IsNullOrEmpty(programLine.Label)) { throw new Exception(String.Format("Line {0} : ORG directive does not allow a label", programLine.LineNumber)); } else if (programLine.DirectiveParameters.Count != 1) { throw new Exception(String.Format("Line {0} : ORG directive needs exactly one parameter", programLine.LineNumber)); } else { DirectiveLineParam param = programLine.DirectiveParameters[0]; address = param.NumberExpression.GetValue(program.Variables, programLine); } break; case "DEFS": // DEFS nn : reserves a bloc of memory size nn bytes, starting at the // current value of the reference counter. case "RESERVE": // label RESERVE nn : using the RESERVE pseudo-operation, you assign a // name to the memory area and declare the number of locations to be assigned. if (String.IsNullOrEmpty(programLine.Label)) { string label = programLine.Label; program.Variables.Add(label, new LabelAddress(address)); } if (programLine.DirectiveParameters.Count != 1) { throw new Exception(String.Format("Line {0} : DEFS or RESERVE directive needs exactly one parameter", programLine.LineNumber)); } else { string label = programLine.Label; program.Variables.Add(label, new NumberOperand(address)); DirectiveLineParam param = programLine.DirectiveParameters[0]; int increment = param.NumberExpression.GetValue(program.Variables, programLine); address += increment; } break; case "DEFB": // DEFB n,n,... : this directive assigns eight-bit contents to a byte // residing at the current reference counter. // DEFB 'S',... : assigns the ASCII value of 'S' to the byte. if (!String.IsNullOrEmpty(programLine.Label)) { string label = programLine.Label; program.Variables.Add(label, new LabelAddress(address)); } if (programLine.DirectiveParameters == null) { throw new Exception(String.Format("Line {0} : DEFB directive needs at least one parameter", programLine.LineNumber)); } else { foreach (DirectiveLineParam param in programLine.DirectiveParameters) { Operand operand = new Operand() { Type = OperandType.Unsigned8, Address = address, Expression = param.NumberExpression, Line = programLine }; address += 1; operands.Add(operand); } } break; case "DEFW": // DEFW nn,nn,... : this assigns the value nn to the two-byte word residing at // the current reference counter and the following location. if (!String.IsNullOrEmpty(programLine.Label)) { string label = programLine.Label; program.Variables.Add(label, new LabelAddress(address)); } if (programLine.DirectiveParameters == null) { throw new Exception(String.Format("Line {0} : DEFW directive at least one parameter", programLine.LineNumber)); } else { foreach (DirectiveLineParam param in programLine.DirectiveParameters) { Operand operand = new Operand() { Type = OperandType.Unsigned16, Address = address, Expression = param.NumberExpression, Line = programLine }; address += 2; operands.Add(operand); } } break; case "DEFM": // DEFM "S" : stores into memory the string "S" starting at the current // reference counter. It must less than 63 in length. if (!String.IsNullOrEmpty(programLine.Label)) { string label = programLine.Label; program.Variables.Add(label, new LabelAddress(address)); } if (programLine.DirectiveParameters.Count != 1) { throw new Exception(String.Format("Line {0} : DEFM directive needs exactly one parameter", programLine.LineNumber)); } else { DirectiveLineParam param = programLine.DirectiveParameters[0]; string stringValue = param.StringValue; MemoryCellDescription programLineDescription = new MemoryCellDescription(MemoryDescriptionType.ProgramLine, programLine); foreach (char chr in stringValue.ToCharArray()) { memoryMap.MemoryCellDescriptions[address] = programLineDescription; memoryMap.MemoryCells[address] = (byte)chr; address += 1; } } break; case "DATA": // label DATA n,nn,'S' : the DATA pseudo-operation allows the programmer // to enter fixed data into memory. Most assemblers allow more elaborate // DATA instructions that handle a large amount of data at one time. // => Not useful since we already have DEFB / DEFW / DEFM, and one type of value per line is better for readability throw new Exception(String.Format("Line {0} : DATA directive is not supported", programLine.LineNumber)); case "END": // END : indicates the end of the program. Any other statements following // it will be ignored. if (!String.IsNullOrEmpty(programLine.Label)) { throw new Exception(String.Format("Line {0} : END directive does not allow a label", programLine.LineNumber)); } else { return true; } // ==> Not supported yet, spectrum programs remain short by nature case "MACRO": // MACRO P0 P1 .. Pn : is used to define a label as a macro, and to define // its formal parameter list. throw new Exception(String.Format("Line {0} : MACRO directive is not supported", programLine.LineNumber)); case "ENDM": // ENDM : is used to mark the end of macro definition. throw new Exception(String.Format("Line {0} : ENDM directive is not supported", programLine.LineNumber)); } return false; }
public abstract int GetValue(IDictionary<string, NumberExpression> variables, ProgramLine prgLine);
private static string GenerateInstructionText(ProgramLine programLine) { StringBuilder sb = new StringBuilder(); sb.Append(programLine.OpCodeName); if (programLine.OpCodeBytes !=null) { sb.Append("["); for (int i = 0; i < programLine.OpCodeBytes.Length; i++) { if (i > 0) sb.Append(' '); sb.Append(String.Format("{0:X2}H", programLine.OpCodeBytes[i])); } sb.Append("]"); } if (programLine.OpCodeParameters != null) { if (programLine.OpCodeParameters.Count > 0) { sb.Append(' '); } int paramIndex = 0; foreach (InstructionLineParam param in programLine.OpCodeParameters) { if (paramIndex > 0) { sb.Append(','); } paramIndex++; switch (param.Type) { case InstructionLineParamType.Address: // At parsing time, a number between parentheses can be used for the following addressing modes : //->Extended //->IOPortImmediate sb.Append("(n)"); break; case InstructionLineParamType.Bit: sb.Append(param.Bit.ToString()[1]); break; case InstructionLineParamType.FlagCondition: sb.Append(param.FlagCondition.ToString()); break; case InstructionLineParamType.Flags: sb.Append("F"); break; case InstructionLineParamType.IOPortRegister: sb.Append(String.Format("({0})", param.Register.ToString())); break; case InstructionLineParamType.Indexed: if (param.Register16 == Register16.IX) { sb.Append("(IX+d)"); } else if (param.Register16 == Register16.IY) { sb.Append("(IY+d)"); } break; case InstructionLineParamType.InterruptMode: sb.Append(param.InterruptMode.ToString()[2]); break; case InstructionLineParamType.Number: // At parsing time, a simple number can be used for the following addressing modes : //->ModifiedPageZero if (programLine.OpCodeName == "RST") { sb.Append(String.Format("{0:X}H", param.NumberExpression.GetValue(null, programLine))); } //->Immediate //->Relative //->Immediate16 //->Extended else { sb.Append("n"); } break; case InstructionLineParamType.Register: sb.Append(param.Register.ToString()); break; case InstructionLineParamType.Register16: if (param.Register16 == Register16.AF2) { sb.Append("AF'"); } else { sb.Append(param.Register16.ToString()); } break; case InstructionLineParamType.RegisterIndirect: sb.Append(String.Format("({0})", param.Register16.ToString())); break; } } } return sb.ToString(); }
private static string FindPreviousLabel(Program program, ProgramLine referenceLine) { for (int lineNumber = referenceLine.LineNumber; lineNumber >= 1; lineNumber--) { string candidateLabel = program.Lines[lineNumber-1].Label; if (!String.IsNullOrEmpty(candidateLabel)) { return candidateLabel; } } return null; }
public override int GetValue(IDictionary<string, NumberExpression> variables, ProgramLine prgLine) { NumberExpression valueExpression; if (variables == null || !variables.TryGetValue(symbol, out valueExpression)) { throw new Exception(String.Format("Line {0} : No value has been assigned to symbol {1} in {2}", prgLine.LineNumber, symbol, prgLine.Text)); } return valueExpression.GetValue(variables, prgLine); }