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);
                    }
                }
            }
        }
Example #2
0
        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));
                }
            }
        }
Example #3
0
        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);
        }
Example #4
0
        public void AppendCommentToProgramLine(int commentedLineAddress, string comment)
        {
            ProgramLine commentedLine = GetLineFromAddress(commentedLineAddress);

            string      newLineTextWithComment = commentedLine.Text + " ; " + comment;
            ProgramLine newLineWithComment     = Assembler.ParseProgramLine(newLineTextWithComment);

            commentedLine.CopyTextualRepresentationFrom(newLineWithComment);
        }
Example #5
0
        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);
        }
Example #6
0
        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");
            }
        }
Example #7
0
        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);
                    }
                }
            }
        }
Example #8
0
        // 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;
        }
Example #9
0
        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");
            }
        }
Example #10
0
        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);
        }
Example #11
0
        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);
            }
        }
Example #12
0
 public abstract int GetValue(IDictionary <string, NumberExpression> variables, ProgramLine prgLine);
Example #13
0
 public CallSource(CallSourceType type, int sourceInstructionAddress, ProgramLine programLine)
 {
     Type    = type;
     Address = sourceInstructionAddress;
     Line    = programLine;
 }
 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);
            }
        }
Example #17
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;
 }
 // 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 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);
     }
 }
Example #22
0
        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;
     }
 }
Example #24
0
 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));
     }
 }
Example #26
0
        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;
            }
        }
Example #28
0
        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;
            }
        }
Example #31
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();
 }
        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"); }
        }
 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);
 }