Пример #1
0
        private static void AddCommentsInTestsParametersTable(int address, SpectrumMemoryMap memory, CharDecoder decoder, Program program)
        {
            bool   isFlagTests   = address == 0x822B;
            string testType      = isFlagTests ? "FLAGS" : "MEMPTR";
            int    testCounter   = 0;
            int    paramAddress  = address;
            int    targetAddress = 0;

            while ((targetAddress = memory.MemoryCells[address++] + memory.MemoryCells[address++] * 256) > 0)
            {
                program.DefineWordData(address - 2);
                program.CommentStringChars(address, decoder.DecodeChar, Program.StringTerminationType.SpecialChar, 0, 0xFF);
                StringBuilder testName = new StringBuilder();
                for (; memory.MemoryCells[address] != 0xFF; address++)
                {
                    testName.Append(SpectrumCharSet.GetSpectrumChar(memory.MemoryCells[address]).Text);
                }

                testCounter++;
                string testDescription = testType + "  test " + testCounter.ToString("D2") + " : " + testName.ToString();
                string testLabel       = testType + "_TEST_" + testCounter.ToString("D2");
                program.InsertCommentAboveProgramLine(paramAddress, "Params for " + testDescription);
                program.InsertCommentAboveProgramLine(targetAddress, "Launcher for " + testDescription);
                program.PrefixLabelToProgramLine(targetAddress, testLabel + "_LAUNCH", true);
                if ((isFlagTests && (testCounter <= 29)) || !isFlagTests)
                {
                    program.InsertCommentAboveProgramLine(targetAddress + 9, "Code executed for " + testDescription);
                    program.PrefixLabelToProgramLine(targetAddress + 9, testLabel + "_EXEC", true);
                }
                address++;
                paramAddress = address;
            }
        }
        private static void AddCommentsInTestsParametersTable(int address, SpectrumMemoryMap memory, CharDecoder decoder, Program program)
        {
            bool isFlagTests = address == 0x822B;
            string testType = isFlagTests ? "FLAGS" : "MEMPTR";
            int testCounter = 0;
            int paramAddress = address;
            int targetAddress = 0;
            while ((targetAddress = memory.MemoryCells[address++] + memory.MemoryCells[address++] * 256) > 0)
            {
                program.DefineWordData(address - 2);
                program.CommentStringChars(address, decoder.DecodeChar, Program.StringTerminationType.SpecialChar, 0, 0xFF);
                StringBuilder testName = new StringBuilder();
                for (; memory.MemoryCells[address] != 0xFF; address++)
                {
                    testName.Append(SpectrumCharSet.GetSpectrumChar(memory.MemoryCells[address]).Text);
                }

                testCounter++;
                string testDescription = testType + "  test " + testCounter.ToString("D2") + " : " + testName.ToString();
                string testLabel = testType + "_TEST_" + testCounter.ToString("D2");
                program.InsertCommentAboveProgramLine(paramAddress, "Params for " + testDescription);
                program.InsertCommentAboveProgramLine(targetAddress, "Launcher for " + testDescription);
                program.PrefixLabelToProgramLine(targetAddress, testLabel + "_LAUNCH", true);
                if ((isFlagTests && (testCounter <= 29)) || !isFlagTests)
                {
                    program.InsertCommentAboveProgramLine(targetAddress + 9, "Code executed for " + testDescription);
                    program.PrefixLabelToProgramLine(targetAddress + 9, testLabel + "_EXEC", true);
                }
                address++;
                paramAddress = address;
            }
        }
Пример #3
0
        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 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);
        }