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);
                    }
                }
            }
        }
예제 #2
0
 public MemoryMap(int size)
 {
     MemoryCells            = new byte[size];
     MemoryCellDescriptions = new MemoryCellDescription[size];
     Programs = new List <Program>();
 }
        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;
            }
        }
        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 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;
        }
예제 #6
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 MemoryMap(int size)
 {
     MemoryCells = new byte[size];
     MemoryCellDescriptions = new MemoryCellDescription[size];
     Programs = new List<Program>();
 }
        public SpectrumMemoryMap()
            : base(65536)
        {
            // Load ROM assembly source code
            string sourcePath = "zxspectrum.asm";
            string romProgramText = LoadROMAssemblySource(sourcePath);

            // Parse ROM program
            using (Stream romProgramStream = new MemoryStream(Encoding.UTF8.GetBytes(romProgramText)))
            {
                ROM = Assembler.ParseProgram(sourcePath, romProgramStream, Encoding.UTF8, false);
            }

            // Compile ROM program and store it in memeory
            Assembler.CompileProgram(ROM, 0, this);

            // Analyze ROM ProgramFlow
            Assembler.AnalyzeProgramFlow(ROM, this);

            // Register system variables descriptions
            foreach (SpectrumSystemVariable variable in SystemVariables.Values)
            {
                for (int i = variable.Address; i < (variable.Address + variable.Length); i++)
                {
                    MemoryCellDescriptions[i] = new MemoryCellDescription(MemoryDescriptionType.SystemVariable, variable);
                }
            }
        }