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 ROM()
        {
            cells = new byte[Memory.BYTES_16K];

            Address = new BusConnector<ushort>();
            Data = new BusConnector<byte>();

            string sourcePath = "zxspectrum.asm";
            Stream romProgramFileStream = PlatformSpecific.GetStreamForProjectFile(sourcePath);
            Program = Assembler.ParseProgram(sourcePath, romProgramFileStream, Encoding.UTF8, false);
            MemoryMap programBytes = new MemoryMap(cells.Length);
            Assembler.CompileProgram(Program, 0, programBytes);
            cells = programBytes.MemoryCells;
        }
        public static void CheckMemoryDump(string filename)
        {
            string objFilepath = String.Format("Assembly/Samples/{0}.obj", filename);
            Stream objStream = PlatformSpecific.GetStreamForProjectFile(objFilepath);

            string asmFilepath = String.Format("Assembly/DisassemblerResults/{0}.disasm", filename);
            Stream asmStream = PlatformSpecific.GetStreamForProjectFile(asmFilepath);

            string referenceAsmText = null;
            using (StreamReader sr = new StreamReader(asmStream))
            {
                referenceAsmText = sr.ReadToEnd();
            }

            MemoryMap memoryMap = new MemoryMap(65536);
            CallTarget entryPoint = new CallTarget(new CallSource(CallSourceType.Boot, 0, null), 0);
            Program program = Disassembler.GenerateProgram(objFilepath, objStream, 0, new CallTarget[] { entryPoint }, memoryMap);

            if (program.ToString() != referenceAsmText)
            {
                throw new Exception(String.Format("Disassembly of program {0} does not match reference text", filename));
            }
        }
        public static void CheckProgram(string filename)
        {
            string objFilePath = String.Format("Assembly/Samples/{0}.obj", filename);
            Stream objStream = PlatformSpecific.GetStreamForProjectFile(objFilePath);

            string asmFilePath = String.Format("Assembly/Samples/{0}.asm", filename);
            Stream asmStream = PlatformSpecific.GetStreamForProjectFile(asmFilePath);

            byte[] referenceMemory = ReadToEnd(objStream);

            Program program = Assembler.ParseProgram(filename, asmStream, Encoding.UTF8, false);
            MemoryMap compiledMemory = new MemoryMap(referenceMemory.Length);
            Assembler.CompileProgram(program, 0, compiledMemory);

            for (int i = 0; i < referenceMemory.Length; i++)
            {
                if (compiledMemory.MemoryCells[i] != referenceMemory[i])
                {
                    ProgramLine errorLine = null;
                    foreach (ProgramLine prgLine in program.Lines)
                    {
                        if (prgLine.LineAddress > i)
                            break;
                        else if (prgLine.Type != ProgramLineType.CommentOrWhitespace)
                            errorLine = prgLine;
                    }
                    if (errorLine.Type == ProgramLineType.OpCodeInstruction)
                    {
                        throw new Exception(String.Format("Compilation result different from reference at address {0} - compiled {1:X} / expected {2:X} - line {3} starting at address {4}, {5}+{6} bytes : {7}", i, compiledMemory.MemoryCells[i], referenceMemory[i], errorLine.LineNumber, errorLine.LineAddress, errorLine.InstructionCode.OpCodeByteCount, errorLine.InstructionCode.OperandsByteCount, errorLine.Text));
                    }
                    else
                    {
                        throw new Exception(String.Format("Compilation result different from reference at address {0} - compiled {1:X} / expected {2:X} - line {3} starting at address {4}, {5}+{6} bytes : {7}", i, compiledMemory.MemoryCells[i], referenceMemory[i], errorLine.LineNumber, errorLine.LineAddress, errorLine.Text));
                    }
                }
            }
        }
Пример #5
0
        private static InstructionCode ReadOneInstructionCode(MemoryMap memoryMap, ref int currentAddress, out byte displacement)
        {
            byte currentOpCodeTable = 0;

            displacement = 0;
            InstructionCode instrCode = null;

            do
            {
                // Read one code byte in memory and look for a coresponding instruction code in the instructions table
                byte codeByte = memoryMap.MemoryCells[currentAddress++];
                instrCode = Z80OpCodes.Tables[currentOpCodeTable, codeByte];

                // If more code bytes are expected, switch to another instruction decoding table
                if (instrCode.SwitchToTableNumber.HasValue)
                {
                    currentOpCodeTable = instrCode.SwitchToTableNumber.Value;
                    if (currentOpCodeTable >= 5)
                    {
                        // 4 bytes opcode, next byte is displacement value
                        displacement = memoryMap.MemoryCells[currentAddress++];
                    }
                }
            }
            // Continue to read code bytes until a complete instruction has been recognized
            while(instrCode.FetchMoreBytes > 0);

            // Special case : NONI[DDH] and NONI[FDH]
            if (instrCode.SwitchToTableNumber.HasValue)
            {
                // A second byte was read, which did not yield a valid instruction code => backtrack
                currentAddress--;
            }

            return(instrCode);
        }
        private static void CompileInstructionLine(Program program, MemoryMap memoryMap, ref int address, IList<Operand> operands, ProgramLine programLine)
        {
            if (!String.IsNullOrEmpty(programLine.Label))
            {
                string label = programLine.Label;
                if (!program.Variables.ContainsKey(label))
                {
                    program.Variables.Add(label, new LabelAddress(address));
                }
            }

            // Find instruction code and instruction type
            string instructionText = GenerateInstructionText(programLine);
            InstructionCode instructionCode = null;
            if (!Z80OpCodes.Dictionary.TryGetValue(instructionText, out instructionCode))
            {
                throw new Exception(String.Format("Line {0} : invalid instruction type {1}", programLine.LineNumber, instructionText));
            }
            InstructionType instructionType = Z80InstructionTypes.Table[instructionCode.InstructionTypeIndex];

            // Register binary representation of program line
            programLine.InstructionCode = instructionCode;
            programLine.InstructionType = instructionType;
            MemoryCellDescription programLineDescription = new MemoryCellDescription(MemoryDescriptionType.ProgramLine, programLine);

            // One or two byte opcodes
            if (instructionCode.OpCodeByteCount <= 2)
            {
                //  Optional one byte prefix
                if (instructionCode.Prefix != null)
                {
                    memoryMap.MemoryCellDescriptions[address] = programLineDescription;
                    memoryMap.MemoryCells[address++] = instructionCode.Prefix[0];
                }

                // Opcode
                memoryMap.MemoryCellDescriptions[address] = programLineDescription;
                memoryMap.MemoryCells[address++] = instructionCode.OpCode;

                // Parameters
                AddressingMode? paramType1 = instructionType.Param1Type;
                if (paramType1.HasValue)
                {
                    InstructionLineParam lineParam1 = programLine.OpCodeParameters[0];
                    AddOperandForLineParam(ref address, operands, programLine, paramType1, lineParam1);
                }
                AddressingMode? paramType2 = instructionType.Param2Type;
                if (paramType2.HasValue)
                {
                    InstructionLineParam lineParam2 = programLine.OpCodeParameters[1];
                    AddOperandForLineParam(ref address, operands, programLine, paramType2, lineParam2);
                }
            }
            // Four bytes opcodes
            else
            {
                // Two bytes prefix
                memoryMap.MemoryCellDescriptions[address] = programLineDescription;
                memoryMap.MemoryCells[address++] = instructionCode.Prefix[0];
                memoryMap.MemoryCellDescriptions[address] = programLineDescription;
                memoryMap.MemoryCells[address++] = instructionCode.Prefix[1];

                // Displacement
                AddressingMode? paramType1 = instructionType.Param1Type;
                if (paramType1.HasValue)
                {
                    InstructionLineParam lineParam1 = programLine.OpCodeParameters[0];
                    AddOperandForLineParam(ref address, operands, programLine, paramType1, lineParam1);
                }
                AddressingMode? paramType2 = instructionType.Param2Type;
                if (paramType2.HasValue)
                {
                    InstructionLineParam lineParam2 = programLine.OpCodeParameters[1];
                    AddOperandForLineParam(ref address, operands, programLine, paramType2, lineParam2);
                }

                // Opcode
                memoryMap.MemoryCellDescriptions[address] = programLineDescription;
                memoryMap.MemoryCells[address++] = instructionCode.OpCode;
            }
        }
        public static void CompileProgram(Program program, int baseAddress, MemoryMap memoryMap)
        {
            // 1. First pass :
            // - execute assembler directives
            // - recognize and generate opcodes
            // => compute all adresses for labels
            // - collect all operands and their adresses, for delayed evaluation during the second pass

            int address = baseAddress;
            IList<Operand> operands = new List<Operand>();

            foreach(ProgramLine programLine in program.Lines)
            {
                // Skip lines with parsing errors
                if(programLine.HasError)
                {
                    continue;
                }

                // Set line address
                programLine.LineAddress = address;

                try
                {
                    // --------------------------------------
                    // Assembler directives
                    // --------------------------------------
                    if (programLine.Type == ProgramLineType.AssemblerDirective)
                    {
                        bool isEndDirective = CompileDirectiveLine(program, memoryMap, ref address, operands, programLine);
                        if (isEndDirective)
                        {
                            // END : indicates the end of the program. Any other statements following it will be ignored.
                            break;
                        }
                    }
                    // --------------------------------------
                    // Instructions opcodes
                    // --------------------------------------
                    else if (programLine.Type == ProgramLineType.OpCodeInstruction)
                    {
                        CompileInstructionLine(program, memoryMap, ref address, operands, programLine);
                    }
                    // --------------------------------------
                    // Comments
                    // --------------------------------------
                    else if (programLine.Type == ProgramLineType.CommentOrWhitespace)
                    {
                        CompileCommentLine(programLine);
                    }
                }
                catch (Exception e)
                {
                    programLine.Error = new ProgramLineError() { ErrorMessage = e.Message, StartColumnIndex = 0, EndColumnIndex = programLine.Text.Length - 1 };
                    program.ErrorCount++;
                    program.LinesWithErrors.Add(programLine);
                }
            }

            // 2. Second pass
            // - compute and generate values for all operands
            try
            {
                ComputeAndGenerateOperands(program, memoryMap, operands);
            }
            catch (Exception e)
            {
                ProgramLine firstProgramLine = program.Lines[0];
                firstProgramLine.Error = new ProgramLineError() { ErrorMessage = "Failed to compute values for all operands : " + e.Message, StartColumnIndex = 0, EndColumnIndex = firstProgramLine.Text.Length - 1 };
                program.ErrorCount++;
                program.LinesWithErrors.Add(firstProgramLine);
            }
        }
        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;
        }
        private static InstructionCode ReadOneInstructionCode(MemoryMap memoryMap, ref int currentAddress, out byte displacement)
        {
            byte currentOpCodeTable = 0;
            displacement = 0;
            InstructionCode instrCode = null;
            do
            {
                // Read one code byte in memory and look for a coresponding instruction code in the instructions table
                byte codeByte = memoryMap.MemoryCells[currentAddress++];
                instrCode = Z80OpCodes.Tables[currentOpCodeTable, codeByte];

                // If more code bytes are expected, switch to another instruction decoding table
                if(instrCode.SwitchToTableNumber.HasValue)
                {
                    currentOpCodeTable = instrCode.SwitchToTableNumber.Value;
                    if (currentOpCodeTable >= 5)
                    {
                        // 4 bytes opcode, next byte is displacement value
                        displacement = memoryMap.MemoryCells[currentAddress++];
                    }
                }
            }
            // Continue to read code bytes until a complete instruction has been recognized
            while(instrCode.FetchMoreBytes > 0);

            // Special case : NONI[DDH] and NONI[FDH]
            if (instrCode.SwitchToTableNumber.HasValue)
            {
                // A second byte was read, which did not yield a valid instruction code => backtrack
                currentAddress--;
            }

            return instrCode;
        }
        public static void AnalyzeProgramFlow(Program program, MemoryMap memoryMap)
        {
            // Generate all outgoing calls
            foreach (ProgramLine programLine in program.Lines)
            {
                ProgramFlowAnalyzer.GenerateOutgoingCall(programLine, program.Variables);
            }

            // Generate all incoming calls
            ProgramFlowAnalyzer.GenerateIncomingCalls(program.Lines, memoryMap);
        }
        public static void DecompileZ80tests()
        {
            string fileName = "z80tests";
            TapFile tapFile = TapFileReader.ReadTapFile(PlatformSpecific.GetStreamForProjectFile("TestTapes/" + fileName + ".tap"), fileName);

            ProgramHeader loaderHeader = (ProgramHeader)tapFile.DataBlocks[0];
            int programLength = loaderHeader.ProgramLength;
            int variablesLength = loaderHeader.FollowingDataLength - loaderHeader.ProgramLength;

            TapDataBlock loaderBlock = tapFile.DataBlocks[1];
            MemoryStream binaryMemoryStream = new MemoryStream(loaderBlock.TapeData);
            /* Read flag */ binaryMemoryStream.ReadByte();
            BasicProgram basicLoader = BasicReader.ReadMemoryFormat(fileName, binaryMemoryStream, programLength, variablesLength);

            ByteArrayHeader codeHeader = (ByteArrayHeader)tapFile.DataBlocks[2];
            int dataLength = codeHeader.DataLength;

            TapDataBlock codeBlock = tapFile.DataBlocks[3];
            binaryMemoryStream = new MemoryStream(codeBlock.TapeData);
            /* Read flag */ binaryMemoryStream.ReadByte();

            int baseAddress = codeHeader.StartAddress;
            int startAddress = codeHeader.StartAddress;

            SpectrumMemoryMap spectrumMemory = new SpectrumMemoryMap();

            // Load machine code in memory to generate entry points BEFORE dissassembly
            int b = -1;
            int currentAddress = baseAddress;
            long streamPositionBefore = binaryMemoryStream.Position;
            while ((b = binaryMemoryStream.ReadByte()) >= 0)
            {
                spectrumMemory.MemoryCells[currentAddress] = (byte)b;
                spectrumMemory.MemoryCellDescriptions[currentAddress] = null; // Reset previous cell descriptions
                currentAddress++;
            }
            binaryMemoryStream.Position = streamPositionBefore;

            List<CallTarget> entryPoints = new List<CallTarget>();
            entryPoints.Add(new CallTarget(new CallSource(CallSourceType.ExternalEntryPoint, 0, null), startAddress));
            entryPoints.Add(new CallTarget(new CallSource(CallSourceType.CallInstruction, 0, null), 0x80B8));
            entryPoints.Add(new CallTarget(new CallSource(CallSourceType.CallInstruction, 0, null), 0x80C0));
            entryPoints.Add(new CallTarget(new CallSource(CallSourceType.JumpInstruction, 0x94CE, null), 0x94D1));
            ReadEntryPointsInTestsParametersTable(0x822B, spectrumMemory, entryPoints);
            ReadEntryPointsInTestsParametersTable(0x8407, spectrumMemory, entryPoints);

            Program program = Disassembler.GenerateProgram(fileName, binaryMemoryStream, baseAddress, entryPoints.ToArray(), spectrumMemory);

            foreach (ProgramLine line in program.Lines)
            {
                if (line.Type == ProgramLineType.OpCodeInstruction)
                {
                    TryReplaceAddressWithSystemVariables(spectrumMemory, program, line, 0);
                    TryReplaceAddressWithSystemVariables(spectrumMemory, program, line, 1);
                }
            }

            program.Lines.Insert(5, Assembler.ParseProgramLine("VAR_MEMPTR_CHECKSUM EQU FFFFH"));
            program.Lines.Insert(5, Assembler.ParseProgramLine("LAST_K EQU 5C08H"));
            program.Lines.Insert(5, Assembler.ParseProgramLine("CHAN_OPEN EQU 1601H"));
            program.Lines.Insert(5, Assembler.ParseProgramLine("CLS EQU 0D6BH"));
            program.Lines.Insert(5, Assembler.ParseProgramLine("START EQU 0000H"));
            program.Lines.Insert(5, Assembler.ParseProgramLine(""));
            program.RenumberLinesAfterLineNumber(0);

            program.PrefixLabelToProgramLine(0x8000, "MAIN");
            program.RenameSymbolInProgram("L8003", "EXEC_LDIR");
            program.RenameSymbolInProgram("L800A", "EXEC_LDDR");
            program.RenameSymbolInProgram("L8011", "EXEC_CPIR");
            program.RenameSymbolInProgram("L801A", "EXEC_CPDR");
            program.RenameSymbolInProgram("L8023", "EXEC_DJNZ_FF");
            program.RenameSymbolInProgram("L802B", "EXEC_DJNZ_FF_LOOP");
            program.RenameSymbolInProgram("L802E", "EXEC_DJNZ_01");
            program.RenameSymbolInProgram("L8036", "EXEC_DJNZ_01_LOOP");
            program.RenameSymbolInProgram("L8039", "MENU_SCREEN");
            program.RenameSymbolInProgram("L8042", "MENU_SCREEN_INPUT");
            program.RenameSymbolInProgram("L80DA", "INIT_SCREEN");
            program.AppendCommentToProgramLine(0x803C, "Menu screen string start address");
            program.InsertCommentAboveProgramLine(0x8114, "--- Menu screen string ---");
            CharDecoder decoder = new CharDecoder();
            program.CommentStringChars(0x8114, decoder.DecodeChar, Program.StringTerminationType.SpecialChar, 0, 0xFF);
            program.AppendCommentToProgramLine(0x8045, "If Key 1 was pressed");
            program.RenameSymbolInProgram("L8057", "FLAGS_TESTS");
            program.AppendCommentToProgramLine(0x8049, "If Key 2 was pressed");
            program.RenameSymbolInProgram("L805F", "MEMPTR_TESTS");
            program.AppendCommentToProgramLine(0x804D, "If Key 3 was pressed");
            program.InsertCommentAboveProgramLine(0x8057, "=> exit to BASIC");
            program.AppendCommentToProgramLine(0x80DA, "Clear the display");
            program.AppendCommentToProgramLine(0x80DF, "Open a channel : user stream 02");
            program.InsertCommentAboveProgramLine(0x80E2, "=> return to MENU_SCREEN");
            program.RenameSymbolInProgram("L80E2", "WAIT_KEYPRESS");
            program.RenameSymbolInProgram("L80E6", "WAIT_KEYPRESS_LOOP");
            program.RenameSymbolInProgram("L80ED", "DISPLAY_STRING");
            program.RenameSymbolInProgram("L80D2", "SETIY_DISPLAY_STRING");
            program.RenameSymbolInProgram("L80F5", "DISPLAY_TWO_HEXBYTES");
            program.RenameSymbolInProgram("L80FE", "DISPLAY_TWO_HEXCHARS");
            program.RenameSymbolInProgram("L810B", "DISPLAY_ONE_HEXCHAR");
            program.InsertCommentAboveProgramLine(0x80ED, "Display all chars starting from address DE upwards, until char FF");
            program.RenameSymbolInProgram("L8072", "TEST_SCREEN");
            program.RenameSymbolInProgram("L807D", "TEST_SCREEN_LAUNCH_TEST");
            program.RenameSymbolInProgram("L80AF", "TEST_SCREEN_NEXT_TEST");
            program.PrefixLabelToProgramLine(0x80B8, "TEST_SCREEN_TEST_PASSED", true);
            Program.ReplaceAddressWithSymbolInProgramLine(program.GetLineFromAddress(0x80A6), 1, false, "TEST_SCREEN_TEST_PASSED");
            program.PrefixLabelToProgramLine(0x80C0, "TEST_SCREEN_TEST_FAILED", true);
            Program.ReplaceAddressWithSymbolInProgramLine(program.GetLineFromAddress(0x80AA), 1, false, "TEST_SCREEN_TEST_FAILED");
            program.AppendCommentToProgramLine(0x805A, "Flags test screen string start address");
            program.InsertCommentAboveProgramLine(0x81CD, "--- Flags test screen string ---");
            program.CommentStringChars(0x81CD, decoder.DecodeChar, Program.StringTerminationType.SpecialChar, 0, 0xFF);
            program.AppendCommentToProgramLine(0x8057, "Flags tests parameter table start address");
            program.InsertCommentAboveProgramLine(0x822B, "--- Flags tests parameter table ---");
            program.AppendCommentToProgramLine(0x8062, "MEMPTR test screen string start address");
            program.InsertCommentAboveProgramLine(0x81EA, "--- MEMPTR test screen string ---");
            program.CommentStringChars(0x81EA, decoder.DecodeChar, Program.StringTerminationType.SpecialChar, 0, 0xFF);
            program.AppendCommentToProgramLine(0x805F, "MEMPTR tests parameter table start address");
            program.InsertCommentAboveProgramLine(0x8407, "--- MEMPTR tests parameter table ---");
            program.RenameSymbolInProgram("L8067", "END_OF_TESTS");
            program.AppendCommentToProgramLine(0x8067, "End of tests string start address");
            program.InsertCommentAboveProgramLine(0x81AB, "--- End of tests string ---");
            program.CommentStringChars(0x81AB, decoder.DecodeChar, Program.StringTerminationType.SpecialChar, 0, 0xFF);
            program.AppendCommentToProgramLine(0x8226, "Space reserved to save HL in routine TEST_SCREEN");
            program.AppendCommentToProgramLine(0x8227, "Space reserved to save HL in routine TEST_SCREEN");
            program.AppendCommentToProgramLine(0x8228, "Space reserved to save SP in routine TEST_SCREEN");
            program.AppendCommentToProgramLine(0x8229, "Space reserved to save SP in routine TEST_SCREEN");
            program.InsertCommentAboveProgramLine(0x8085, "Here HL points to an entry in the tests parameters table");
            program.InsertCommentAboveProgramLine(0x8088, "Test params structure 0 - 1 : Pointer to an executable machine code function : the test launcher. 00H|00H marks the end of the parameters table.");
            program.InsertCommentAboveProgramLine(0x8088, "Test params structure 2 -> FFH : starting with the third byte, the parameter table entry contains a string describing the test");
            program.AppendCommentToProgramLine(0x8091, "Display tested opcode name");
            program.AppendCommentToProgramLine(0x8094, "TAB");
            program.AppendCommentToProgramLine(0x8097, "BRIGHT ...");
            program.AppendCommentToProgramLine(0x809A, "... 0");
            program.AppendCommentToProgramLine(0x809C, ":");
            program.AppendCommentToProgramLine(0x809F, "space");
            program.InsertCommentAboveProgramLine(0x80AF, "=> jump to test launcher, start address found at the beginning the current parameter table entry");
            program.RenameSymbolInProgram("L892D", "FLAGS_TEST_COMMON_EXEC");
            program.RenameSymbolInProgram("L9423", "MEMPTR_TEST_COMMON_EXEC");
            program.RenameSymbolInProgram("L89B9", "FLAGS_TEST_REPEAT_EXEC");
            program.RenameSymbolInProgram("L8A0B", "FLAGS_TEST_DDCB_EXEC");
            program.RenameSymbolInProgram("L8A6D", "FLAGS_TEST_FDCB_EXEC");
            program.RenameSymbolInProgram("L8B03", "FLAGS_TEST_CB_EXEC");
            program.CommentStringChars(0x8208, decoder.DecodeChar, Program.StringTerminationType.SpecialChar, 0, 0xFF);
            program.CommentStringChars(0x820F, decoder.DecodeChar, Program.StringTerminationType.SpecialChar, 0, 0xFF);
            program.AppendCommentToProgramLine(0x8083, "Set white border around the screen");
            program.AppendCommentToProgramLine(0x808C, "Here : HL points to the address of the test laucher routine, DE points to the string describing the test");
            program.AppendCommentToProgramLine(0x808F, "If HL = 00 : end of the tests table, else :");
            program.AppendCommentToProgramLine(0x80A2, "After the end of the string, save the address of the next entry in tests table for next iteration");
            program.InsertCommentAboveProgramLine(0x8208, "--- Passed test string ---");
            program.InsertCommentAboveProgramLine(0x820F, "--- Failed test string ---");
            program.InsertCommentAboveProgramLine(0x892D, "Generic execution environment for flags test");
            program.RenameSymbolInProgram("L9520", "ADD_FLAGS_TO_CHECKSUM");
            program.AppendCommentToProgramLine(0x954A, "Flags Checksum value");
            program.AppendCommentToProgramLine(0x892F, "Replaces instruction CALL START below with CALL (test EXEC address)");
            program.InsertCommentAboveProgramLine(0x894E, "==> call to one of the FLAGS_TEST_nn_EXEC routine");
            program.AppendCommentToProgramLine(0x895C, "Display Flags checksum value");
            program.AppendCommentToProgramLine(0x8964, "Compare Flags checksum with expected value");
            program.AppendCommentToProgramLine(0x8966, "==> return to TEST_SCREEN_TEST_FAILED");
            program.AppendCommentToProgramLine(0x8968, "==> return to TEST_SCREEN_TEST_PASSED");
            program.RenameSymbolInProgram("L8949", "FLAGS_TEST_CMN_EXEC_AF_VAL");
            program.AppendCommentToProgramLine(0x89BA, "Replaces instruction LDI below with one of the repeated instructions from the test table");
            program.InsertCommentAboveProgramLine(0x89D1, "==> executes one of the repeated instructions from the test table");
            program.RenameSymbolInProgram("L89C3", "FLAGS_TEST_REPEAT_EXEC_EXT_LOOP");
            program.RenameSymbolInProgram("L89CD", "FLAGS_TEST_REPEAT_EXEC_INN_LOOP");
            program.AppendCommentToProgramLine(0x89E1, "Display Flags checksum value");
            program.AppendCommentToProgramLine(0x89E9, "Compare Flags checksum with expected value");
            program.AppendCommentToProgramLine(0x89EB, "==> return to TEST_SCREEN_TEST_FAILED");
            program.AppendCommentToProgramLine(0x89ED, "==> return to TEST_SCREEN_TEST_PASSED");
            program.RenameSymbolInProgram("L8A1C", "FLAGS_TEST_DDCB_EXEC_LOOP");
            program.AppendCommentToProgramLine(0x8A1F, "<= third byte of opcode replaced with all values between 00 and FF by the line above");
            program.AppendCommentToProgramLine(0x8A45, "Display Flags checksum value");
            program.AppendCommentToProgramLine(0x8A4D, "Compare Flags checksum with expected value");
            program.AppendCommentToProgramLine(0x8A4F, "==> return to TEST_SCREEN_TEST_FAILED");
            program.AppendCommentToProgramLine(0x8A51, "==> return to TEST_SCREEN_TEST_PASSED");
            program.RenameSymbolInProgram("L8A81", "FLAGS_TEST_FDCB_EXEC_LOOP");
            program.AppendCommentToProgramLine(0x8A84, "<= third byte of opcode replaced with all values between 00 and FF by the line above");
            program.AppendCommentToProgramLine(0x8AAA, "Display Flags checksum value");
            program.AppendCommentToProgramLine(0x8AB2, "Compare Flags checksum with expected value");
            program.AppendCommentToProgramLine(0x8AB4, "==> return to TEST_SCREEN_TEST_FAILED");
            program.AppendCommentToProgramLine(0x8AB6, "==> return to TEST_SCREEN_TEST_PASSED");
            program.RenameSymbolInProgram("L8B14", "FLAGS_TEST_CB_EXEC_LOOP");
            program.RenameSymbolInProgram("L8B2A", "FLAGS_TEST_CB_EXEC_CASE1");
            program.RenameSymbolInProgram("L8B2C", "FLAGS_TEST_CB_EXEC_CASE2");
            program.RenameSymbolInProgram("L8B3A", "FLAGS_TEST_CB_EXEC_CASE3");
            program.AppendCommentToProgramLine(0x8B24, "<= third byte of opcode replaced with all values between 00 and FF by the line above at 8B17");
            program.AppendCommentToProgramLine(0x8B2A, "<= third byte of opcode replaced with all values between 00 and FF by the line above at 8B14");
            program.AppendCommentToProgramLine(0x8B51, "Display Flags checksum value");
            program.AppendCommentToProgramLine(0x8B59, "Compare Flags checksum with expected value");
            program.AppendCommentToProgramLine(0x8B5B, "==> return to TEST_SCREEN_TEST_FAILED");
            program.AppendCommentToProgramLine(0x8B5D, "==> return to TEST_SCREEN_TEST_PASSED");
            program.RenameSymbolInProgram("L942E", "MEMPTR_CMN_EXEC_START");
            program.AppendCommentToProgramLine(0x94CF, "==> loop back to MEMPTR_CMN_EXEC_LOOP");
            program.AppendCommentToProgramLine(0x94CE, "==> jump to MEMPTR_CMN_EXEC_CHECKSUM");
            program.PrefixLabelToProgramLine(0x94D1, "MEMPTR_CMN_EXEC_CHECKSUM", true);
            program.RenameSymbolInProgram("L94E1", "MEMPTR_CMN_EXEC_RET");
            program.AppendCommentToProgramLine(0x9429, "Compare Flags checksum with expected value");
            program.AppendCommentToProgramLine(0x942B, "==> return to TEST_SCREEN_TEST_FAILED");
            program.AppendCommentToProgramLine(0x942D, "==> return to TEST_SCREEN_TEST_PASSED");
            program.InsertCommentAboveProgramLine(0x9445, "--- NOPs below will be replaced by MEMPTR_TEST_nn_EXEC (instruction LDIR at address 9435 above) ---");
            program.InsertCommentAboveProgramLine(0x9459, "--- End of replaced code ---");
            program.AppendCommentToProgramLine(0x94DB, "Display Flags checksum value");
            program.RenameSymbolInProgram("L94E2", "MEMPTR_CHECKSUM");
            program.RenameSymbolInProgram("L94EE", "MEMPTR_CHECKSUM_LOOP1");
            program.RenameSymbolInProgram("L94F0", "MEMPTR_CHECKSUM_LOOP2");
            program.RenameSymbolInProgram("L94FB", "MEMPTR_CHECKSUM_CASE1");
            program.RenameSymbolInProgram("L94FE", "MEMPTR_CHECKSUM_CASE2");
            program.RenameSymbolInProgram("L9500", "MEMPTR_CHECKSUM_LOOP3");
            program.RenameSymbolInProgram("L950B", "MEMPTR_CHECKSUM_CASE3");
            program.RenameSymbolInProgram("L950C", "MEMPTR_CHECKSUM_CASE4");
            program.RenameSymbolInProgram("L9515", "MEMPTR_CHECKSUM_CASE5");
            program.RenameSymbolInProgram("L9517", "RESET_FLAGS_CHECKSUM");
            program.AppendCommentToProgramLine(0x8B5E, "Used to save and restore accumulator in FLAGS_TEST_CB_EXEC");
            program.AppendCommentToProgramLine(0x822A, "Used to restore accumulator in MEMPTR_TEST_COMMON_EXEC");
            program.AppendCommentToProgramLine(0x954B, "Not used ?");
            program.AppendCommentToProgramLine(0x954C, "Not used ?");
            program.PrefixLabelToProgramLine(0x954A, "VAR_FLAGS_CHECKSUM", true);
            program.ReplaceAddressWithSymbol(0xFFFF, "VAR_MEMPTR_CHECKSUM", true);
            program.PrefixLabelToProgramLine(0x8226, "VAR_SAVE_HL", true);
            program.PrefixLabelToProgramLine(0x8228, "VAR_SAVE_SP", true);
            program.PrefixLabelToProgramLine(0x822A, "VAR_SAVE_A", true);
            program.PrefixLabelToProgramLine(0x8114, "STRING_MENU_SCREEN", true);
            program.PrefixLabelToProgramLine(0x81AB, "STRING_END_OF_TESTS", true);
            program.PrefixLabelToProgramLine(0x81CD, "STRING_FLAGS_TESTS_SCREEN", true);
            program.PrefixLabelToProgramLine(0x81EA, "STRING_MEMPTR_TESTS_SCREEN", true);
            program.PrefixLabelToProgramLine(0x8208, "STRING_PASSED_TEST", true);
            program.PrefixLabelToProgramLine(0x820F, "STRING_FAILED_TEST", true);
            program.PrefixLabelToProgramLine(0x822B, "TABLE_FLAGS_TESTS", true);
            program.PrefixLabelToProgramLine(0x8407, "TABLE_MEMPTR_TESTS", true);
            program.PrefixLabelToProgramLine(0x8B5E, "VAR_SAVE_A2", true);
            program.PrefixLabelToProgramLine(0x9445, "MEMPTR_CMN_EXEC_LOOP", true);
            program.PrefixLabelToProgramLine(0x945C, "AREA_MEMPTR_TEST_INSERT", true);

            AddCommentsInTestsParametersTable(0x822B, spectrumMemory, decoder, program);
            AddCommentsInTestsParametersTable(0x8407, spectrumMemory, decoder, program);

            MemoryMap testMemoryMap = new MemoryMap(65536);
            program.Variables.Clear();
            Assembler.CompileProgram(program, 0x8000, testMemoryMap);
            for (int programAddress = program.BaseAddress; programAddress <= program.MaxAddress; programAddress++)
            {
                if (testMemoryMap.MemoryCells[programAddress] != spectrumMemory.MemoryCells[programAddress])
                {
                    throw new Exception("Error in decompiled program at address " + programAddress + " : source byte = " + spectrumMemory.MemoryCells[programAddress] + ", decompiled program line + " + ((ProgramLine)spectrumMemory.MemoryCellDescriptions[programAddress].Description).Text + " produced byte = " + testMemoryMap.MemoryCells[programAddress]);
                }
            }

            string htmlProgram  = AsmHtmlFormatter.BeautifyAsmCode(fileName, program);
        }
        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 static void DecompileZexall2()
        {
            string fileName = "zexall2";
            TapFile tapFile = TapFileReader.ReadTapFile(PlatformSpecific.GetStreamForProjectFile("TestTapes/" + fileName + ".tap"), fileName);

            ProgramHeader loaderHeader = (ProgramHeader)tapFile.DataBlocks[0];
            int programLength = loaderHeader.ProgramLength;
            int variablesLength = loaderHeader.FollowingDataLength - loaderHeader.ProgramLength;

            TapDataBlock loaderBlock = tapFile.DataBlocks[1];
            MemoryStream binaryMemoryStream = new MemoryStream(loaderBlock.TapeData);
            /* Read flag */
            binaryMemoryStream.ReadByte();
            BasicProgram basicLoader = BasicReader.ReadMemoryFormat(fileName, binaryMemoryStream, programLength, variablesLength);

            ByteArrayHeader codeHeader = (ByteArrayHeader)tapFile.DataBlocks[2];
            int dataLength = codeHeader.DataLength;

            TapDataBlock codeBlock = tapFile.DataBlocks[3];
            binaryMemoryStream = new MemoryStream(codeBlock.TapeData);
            /* Read flag */
            binaryMemoryStream.ReadByte();

            int baseAddress = codeHeader.StartAddress;
            int startAddress = codeHeader.StartAddress;

            SpectrumMemoryMap spectrumMemory = new SpectrumMemoryMap();

            // Load machine code in memory to generate entry points BEFORE dissassembly
            int b = -1;
            int currentAddress = baseAddress;
            long streamPositionBefore = binaryMemoryStream.Position;
            while ((b = binaryMemoryStream.ReadByte()) >= 0)
            {
                spectrumMemory.MemoryCells[currentAddress] = (byte)b;
                spectrumMemory.MemoryCellDescriptions[currentAddress] = null; // Reset previous cell descriptions
                currentAddress++;
            }
            binaryMemoryStream.Position = streamPositionBefore;

            List<CallTarget> entryPoints = new List<CallTarget>();
            entryPoints.Add(new CallTarget(new CallSource(CallSourceType.ExternalEntryPoint, 0, null), startAddress));

            Program program = Disassembler.GenerateProgram(fileName, binaryMemoryStream, baseAddress, entryPoints.ToArray(), spectrumMemory);

            program.Lines.Insert(5, Assembler.ParseProgramLine("PRINT_A EQU 0010H"));
            program.Lines.Insert(5, Assembler.ParseProgramLine("CHAN_OPEN EQU 1601H"));
            program.Lines.Insert(5, Assembler.ParseProgramLine(""));
            program.RenumberLinesAfterLineNumber(0);

            program.PrefixLabelToProgramLine(0x8000, "MAIN");
            program.RenameSymbolInProgram("L8013", "START");
            program.PrefixLabelToProgramLine(0x9F69, "MSG1");
            program.RenameSymbolInProgram("L9F41", "BDOS");
            program.PrefixLabelToProgramLine(0x8043, "TESTS");
            program.RenameSymbolInProgram("L802B", "LOOP");
            program.RenameSymbolInProgram("L8038", "DONE");
            program.RenameSymbolInProgram("L9C4F", "STT");
            program.RenameSymbolInProgram("L8040", "STOP");
            program.PrefixLabelToProgramLine(0x9F8B, "MSG2");
            program.PrefixLabelToProgramLine(0x9ED7, "FLGMSK");
            program.PrefixLabelToProgramLine(0x9E49, "COUNTER");
            program.RenameSymbolInProgram("L9DB8", "INITMASK");
            program.PrefixLabelToProgramLine(0x9E71, "SHIFTER");
            program.PrefixLabelToProgramLine(0x9EB3, "IUT");
            program.PrefixLabelToProgramLine(0x8003, "MSBT");
            program.RenameSymbolInProgram("L9FF4", "INITCRC");
            program.RenameSymbolInProgram("L9C94", "TLP");
            program.RenameSymbolInProgram("L9CA8", "TLP1");
            program.RenameSymbolInProgram("L9CAB", "TLP2");
            program.RenameSymbolInProgram("L9E99", "TEST");
            program.RenameSymbolInProgram("L9DF8", "COUNT");
            program.RenameSymbolInProgram("L9E1C", "SHIFT");
            program.RenameSymbolInProgram("L9CE9", "TLP3");
            program.RenameSymbolInProgram("L9FB5", "CMPCRC");
            program.PrefixLabelToProgramLine(0x9F9A, "OKMSG");
            program.RenameSymbolInProgram("L9CE0", "TLPOK");
            program.PrefixLabelToProgramLine(0x9F9F, "ERMSG1");
            program.RenameSymbolInProgram("L9F0C", "PHEX8");
            program.PrefixLabelToProgramLine(0x9FA7, "ERMSG2");
            program.PrefixLabelToProgramLine(0x9FB2, "CRLF");
            program.PrefixLabelToProgramLine(0x9D5F, "CNBIT");
            program.PrefixLabelToProgramLine(0x9D83, "SHFBIT");
            program.PrefixLabelToProgramLine(0x9D60, "CNTBYT");
            program.PrefixLabelToProgramLine(0x9D84, "SHBYT");
            program.RenameSymbolInProgram("L9D13", "SETUP");
            program.RenameSymbolInProgram("L9D1C", "SUBYTE");
            program.RenameSymbolInProgram("L9D3D", "SUBSHF");
            program.RenameSymbolInProgram("L9D2C", "SUBCLP");
            program.RenameSymbolInProgram("L9D62", "NXTCBIT");
            program.RenameSymbolInProgram("L9D58", "SUBSTR");
            program.RenameSymbolInProgram("L9D49", "SBSHF1");
            program.RenameSymbolInProgram("L9D86", "NXTSBIT");
            program.RenameSymbolInProgram("L9D7B", "NCB1");
            program.RenameSymbolInProgram("L9D9F", "NSB1");
            program.RenameSymbolInProgram("L9DA7", "CLRMEM");
            program.RenameSymbolInProgram("L9DC7", "IMLP");
            program.RenameSymbolInProgram("L9DC8", "IMLP1");
            program.RenameSymbolInProgram("L9DCE", "IMLP2");
            program.RenameSymbolInProgram("L9DEB", "IMLP3");
            program.RenameSymbolInProgram("L9E04", "CNTLP");
            program.RenameSymbolInProgram("L9E13", "CNTEND");
            program.RenameSymbolInProgram("L9E17", "CNTLP1");
            program.RenameSymbolInProgram("L9E28", "SHFLP");
            program.RenameSymbolInProgram("L9E3E", "SHFLP2");
            program.RenameSymbolInProgram("L9E40", "SHFLPE");
            program.RenameSymbolInProgram("L9E44", "SHFLP1");
            program.PrefixLabelToProgramLine(0x9F00, "SPSAV");
            program.PrefixLabelToProgramLine(0x8011, "SPBT");
            program.PrefixLabelToProgramLine(0x9EFE, "SPAT");
            program.PrefixLabelToProgramLine(0x9EF0, "MSAT");
            program.PrefixLabelToProgramLine(0x9EFC, "FLGSAT");
            program.PrefixLabelToProgramLine(0xA008, "CRCVAL");
            program.RenameSymbolInProgram("L9EE2", "TCRC");
            program.RenameSymbolInProgram("L9FCC", "UPDCRC");
            program.PrefixLabelToProgramLine(0x9F04, "HEXSTR");
            program.RenameSymbolInProgram("L9F11", "PH8LP");
            program.RenameSymbolInProgram("L9F1E", "PHEX2");
            program.RenameSymbolInProgram("L9F27", "PHEX1");
            program.RenameSymbolInProgram("L9F34", "PH11");
            program.RenameSymbolInProgram("L9F54", "PRCHAR");
            program.RenameSymbolInProgram("L9F5C", "PRSTRING");
            program.RenameSymbolInProgram("L9F4F", "BDOSDONE");
            program.RenameSymbolInProgram("L9FBD", "CCLP");
            program.RenameSymbolInProgram("L9FC8", "CCE");
            program.PrefixLabelToProgramLine(0xA00C, "CRCTAB");
            program.RenameSymbolInProgram("L9FE5", "CRCLP");
            program.RenameSymbolInProgram("L9FFE", "ICRCLP");

            string[] testNames = new string[] { "SCFOP","CCFOP","SCFCCF","CCFSCF","BITA","BITHL","BITX","BITZ80","DAAOP","CPLOP","ADC16","ADD16","ADD16X",
            "ADD16Y","ALU8I","ALU8R","ALU8RX","ALU8X","CPD1","CPI1","INCA","INCB","INCBC","INCC","INCD","INCDE","INCE","INCH","INCHL","INCIX","INCIY",
            "INCL","INCM","INCSP","INCX","INCXH","INCXL","INCYH","INCYL","LD161","LD162","LD163","LD164","LD165","LD166","LD167","LD168","LD16IM","LD16IX",
            "LD8BD","LD8IM","LD8IMX","LD8IX1","LD8IX2","LD8IX3","LD8IXY","LD8RR","LD8RRX","LDA","LDD1","LDD2","LDI1","LDI2","NEGOP","RLDOP","ROT8080",
            "ROTXY","ROTZ80","SRZ80","SRZX","ST8IX1","ST8IX2","ST8IX3","STABD" };

            int testTableAddress = 0x8043;
            foreach(string testName in testNames)
            {
                int testAddress = spectrumMemory.MemoryCells[testTableAddress++] + 256 * spectrumMemory.MemoryCells[testTableAddress++];
                program.PrefixLabelToProgramLine(testAddress, testName);
            }

            MemoryMap testMemoryMap = new MemoryMap(65536);
            program.Variables.Clear();
            Assembler.CompileProgram(program, 0x8000, testMemoryMap);
            for (int programAddress = program.BaseAddress; programAddress <= program.MaxAddress; programAddress++)
            {
                if (testMemoryMap.MemoryCells[programAddress] != spectrumMemory.MemoryCells[programAddress])
                {
                    throw new Exception("Error in decompiled program at address " + programAddress + " : source byte = " + spectrumMemory.MemoryCells[programAddress] + ", decompiled program line + " + ((ProgramLine)spectrumMemory.MemoryCellDescriptions[programAddress].Description).Text + " produced byte = " + testMemoryMap.MemoryCells[programAddress]);
                }
            }

            string htmlProgram = AsmHtmlFormatter.BeautifyAsmCode(fileName, program);
        }
 private static void ComputeAndGenerateOperands(Program program, MemoryMap memoryMap, IList<Operand> operands)
 {
     foreach (Operand operand in operands)
     {
         switch (operand.Type)
         {
             // 8 bit unsigned operand
             case OperandType.Unsigned8:
                 byte uOperand = (byte)operand.Expression.GetValue(program.Variables, operand.Line);
                 if (operand.Line.Type == ProgramLineType.OpCodeInstruction)
                 {
                     memoryMap.MemoryCellDescriptions[operand.Address] = memoryMap.MemoryCellDescriptions[operand.Address - 1];
                 }
                 memoryMap.MemoryCells[operand.Address] = uOperand;
                 break;
             // 8 bit signed operand
             case OperandType.Signed8:
                 sbyte sOperand = (sbyte)operand.Expression.GetValue(program.Variables, operand.Line);
                 if (operand.Line.Type == ProgramLineType.OpCodeInstruction)
                 {
                     memoryMap.MemoryCellDescriptions[operand.Address] = memoryMap.MemoryCellDescriptions[operand.Address - 1];
                 }
                 memoryMap.MemoryCells[operand.Address] = unchecked((byte)sOperand);
                 break;
             // 16 bit unsigned operand
             case OperandType.Unsigned16:
                 ushort lOperand = (ushort)operand.Expression.GetValue(program.Variables, operand.Line);
                 if (operand.Line.Type == ProgramLineType.OpCodeInstruction)
                 {
                     memoryMap.MemoryCellDescriptions[operand.Address] = memoryMap.MemoryCellDescriptions[operand.Address - 1];
                 }
                 memoryMap.MemoryCells[operand.Address] = (byte)(lOperand & 0xFF);
                 if (operand.Line.Type == ProgramLineType.OpCodeInstruction)
                 {
                     memoryMap.MemoryCellDescriptions[operand.Address + 1] = memoryMap.MemoryCellDescriptions[operand.Address - 1];
                 }
                 memoryMap.MemoryCells[operand.Address + 1] = (byte)(lOperand >> 8);
                 break;
         }
     }
 }
Пример #15
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 Program LoadProgramInMemory(string sourcePath, Stream inputStream, Encoding encoding, bool ignoreCase)
 {
     Program program = Assembler.ParseProgram(sourcePath, inputStream, encoding, ignoreCase);
     MemoryMap programBytes = new MemoryMap(memory.Size);
     Assembler.CompileProgram(program, 0, programBytes);
     if (program.ErrorCount > 0)
     {
         throw new Exception("Program error at line " + program.LinesWithErrors[0].LineNumber + " : " + program.LinesWithErrors[0].Text);
     }
     memory.LoadBytes(programBytes.MemoryCells);
     programInMemory = program;
     return program;
 }
        public static void GenerateIncomingCalls(IEnumerable <ProgramLine> programLines, MemoryMap memoryMap)
        {
            foreach (ProgramLine instructionLine in programLines)
            {
                if (instructionLine.OutgoingCall != null)
                {
                    // Find the memory description of the call target address
                    int targetAddress = instructionLine.OutgoingCall.Address;
                    MemoryCellDescription memDescription = memoryMap.MemoryCellDescriptions[targetAddress];

                    // This description should be a instruction line starting at the same address
                    if (memDescription == null || memDescription.Type != MemoryDescriptionType.ProgramLine)
                    {
                        throw new Exception("Outgoing call at address " + targetAddress + " points to an unknown area in memory");
                    }
                    ProgramLine targetLine = (ProgramLine)memDescription.Description;
                    if (targetLine.Type != ProgramLineType.OpCodeInstruction || targetLine.LineAddress != targetAddress)
                    {
                        throw new Exception("Outgoing call from program line " + instructionLine.Text + " at address " + instructionLine.LineAddress + " points in the middle of the instruction " + targetLine.Text + " starting at address " + targetLine.LineAddress + " in memory");
                    }
                    else
                    {
                        // Connect the source and the target
                        instructionLine.OutgoingCall.Line = targetLine;
                        if (targetLine.IncomingCalls == null)
                        {
                            targetLine.IncomingCalls = new List <CallSource>();
                        }
                        targetLine.IncomingCalls.Add(instructionLine.OutgoingCall.Source);
                    }
                }
            }
        }
 public ProgramInMemory LoadProgramInMemory(string sourcePath, Stream inputStream, Encoding encoding, bool ignoreCase, ushort baseAddress, int memorySize)
 {
     Program program = Assembler.ParseProgram(sourcePath, inputStream, encoding, ignoreCase);
     MemoryMap programBytes = new MemoryMap(memorySize);
     Assembler.CompileProgram(program, 0, programBytes);
     LoadDataInMemory(programBytes.MemoryCells, baseAddress);
     return new ProgramInMemory() { Program = program, BaseAddress = baseAddress, MemorySize = memorySize };
 }
 public void Compile(object sender, ExecutedRoutedEventArgs e)
 {
     CurrentProgram = Assembler.ParseProgram(currentFileName, new MemoryStream(Encoding.Default.GetBytes(textEditor.Document.Text)), Encoding.Default, true);
     MemoryMap memoryMap = new MemoryMap(65536);
     Assembler.CompileProgram(CurrentProgram, 0, memoryMap);
     DisplayErrors(CurrentProgram.LinesWithErrors);
 }