/// <summary>
        /// Compiles the code.
        /// </summary>
        /// <returns>True, if compilation successful; otherwise, false</returns>
        protected virtual bool CompileCode(IVsHierarchy hierarchy, uint itemId)
        {
            if (hierarchy == null)
            {
                return(false);
            }

            var codeManager = Package.CodeManager;

            // --- Step #1: Compile
            var start = DateTime.Now;
            var pane  = OutputWindow.GetPane <Z80BuildOutputPane>();

            pane.WriteLine("Z80 Assembler");
            Output = codeManager.Compile(hierarchy, itemId, PrepareOptions());
            var duration = (DateTime.Now - start).TotalMilliseconds;

            pane.WriteLine($"Compile time: {duration}ms");

            if (Output.ErrorCount != 0)
            {
                // --- Compilation completed with errors
                return(false);
            }

            // --- Sign the compilation was successful
            Package.DebugInfoProvider.CompiledOutput = Output;
            return(true);
        }
Esempio n. 2
0
        /// <summary>
        /// Injects the code into the Spectrum virtual machine's memory
        /// </summary>
        /// <param name="output"></param>
        public void InjectCodeIntoVm(AssemblerOutput output)
        {
            // --- Do not inject faulty code
            if (output == null || output.ErrorCount > 0)
            {
                return;
            }

            // --- Do not inject code if memory is not available
            var spectrumVm = Package.MachineViewModel.SpectrumVm;

            if (Package.MachineViewModel.VmState != VmState.Paused ||
                spectrumVm?.MemoryDevice == null)
            {
                return;
            }

            if (spectrumVm is ISpectrumVmRunCodeSupport runSupport)
            {
                // --- Go through all code segments and inject them
                foreach (var segment in output.Segments)
                {
                    var addr = segment.StartAddress + (segment.Displacement ?? 0);
                    runSupport.InjectCodeToMemory((ushort)addr, segment.EmittedCode);
                }

                // --- Prepare the machine for RUN mode
                runSupport.PrepareRunMode();
                CodeInjected?.Invoke(this, EventArgs.Empty);
            }
        }
Esempio n. 3
0
        /// <summary>
        /// Injects the code into the Spectrum virtual machine's memory
        /// </summary>
        /// <param name="output"></param>
        public void InjectCodeIntoVm(AssemblerOutput output)
        {
            // --- Do not inject faulty code
            if (output == null || output.ErrorCount > 0)
            {
                return;
            }

            // --- Do not inject code if memory is not available
            var spectrumVm = Package.MachineViewModel.SpectrumVm;

            if (Package.MachineViewModel.VmState != VmState.Paused ||
                spectrumVm?.MemoryDevice == null)
            {
                return;
            }

            var memory = spectrumVm.MemoryDevice.GetMemoryBuffer();

            // --- Go through all code segments and inject them
            foreach (var segment in output.Segments)
            {
                var addr = segment.StartAddress + (segment.Displacement ?? 0);
                foreach (var codeByte in segment.EmittedCode)
                {
                    if (addr >= 0x4000 && addr < memory.Length)
                    {
                        memory[addr++] = codeByte;
                    }
                }
            }
        }
        /// <summary>
        /// Saves the output to Intel HEX file format
        /// </summary>
        /// <param name="filename">Filename</param>
        /// <param name="output">Assembly output to save</param>
        private static void SaveIntelHexFile(string filename, AssemblerOutput output)
        {
            const int rowLen = 0x10;
            var       hexOut = new StringBuilder(4096);

            foreach (var segment in output.Segments)
            {
                var offset = 0;
                while (offset + rowLen < segment.EmittedCode.Count)
                {
                    // --- Write an entire data row
                    WriteDataRecord(segment, offset, rowLen);
                    offset += rowLen;
                }

                // --- Write the left of the data row
                var leftBytes = segment.EmittedCode.Count - offset;
                WriteDataRecord(segment, offset, leftBytes);
            }

            // --- Write End-Of-File record
            hexOut.AppendLine(":00000001FF");

            // --- Save the data to a file
            var intelHexString = hexOut.ToString();

            if (filename != null)
            {
                File.WriteAllText(filename, intelHexString);
            }

            return;

            void WriteDataRecord(BinarySegment segment, int offset, int bytesCount)
            {
                if (bytesCount == 0)
                {
                    return;
                }
                var addr = (ushort)((segment.XorgValue ?? segment.StartAddress) + offset);

                hexOut.Append($":{bytesCount:X2}{addr:X4}00"); // --- Data record header
                var checksum = bytesCount + (addr >> 8) + (addr & 0xFF);

                for (var i = offset; i < offset + bytesCount; i++)
                {
                    var data = segment.EmittedCode[i];
                    checksum += data;
                    hexOut.Append($"{data:X2}");
                }

                var chk = (byte)(256 - (checksum & 0xff));

                hexOut.Append($"{chk:X2}");
                hexOut.AppendLine();
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Compiles the specified Visua Studio document.
        /// </summary>
        /// <param name="itemPath">VS document item path</param>
        /// <param name="options">Assembler options to use</param>
        /// <param name="output">Assembler output</param>
        /// <returns>True, if compilation is successful; otherwise, false</returns>
        public async Task <AssemblerOutput> CompileDocument(string itemPath,
                                                            AssemblerOptions options)
        {
            var zxbOptions = PrepareZxbOptions(itemPath);

            MergeOptionsFromSource(zxbOptions);
            var output = new AssemblerOutput(new SourceFileItem(itemPath));
            var runner = new ZxbRunner(SpectNetPackage.Default.Options.ZxbPath);
            var result = await runner.RunAsync(zxbOptions);

            if (result.ExitCode != 0)
            {
                // --- Compile error - stop here
                output.Errors.Clear();
                output.Errors.AddRange(result.Errors);
                return(output);
            }

            // --- HACK: Take care that "ZXBASIC_HEAP_SIZE EQU" is added to the assembly file
            var asmContents      = File.ReadAllText(zxbOptions.OutputFilename);
            var hasHeapSizeLabel = Regex.Match(asmContents, "ZXBASIC_HEAP_SIZE\\s+EQU");

            if (!hasHeapSizeLabel.Success)
            {
                asmContents = Regex.Replace(asmContents, "ZXBASIC_USER_DATA_END\\s+EQU\\s+ZXBASIC_MEM_HEAP",
                                            "ZXBASIC_USER_DATA_END EQU ZXBASIC_USER_DATA");
                File.WriteAllText(zxbOptions.OutputFilename, asmContents);
            }

            // --- Second pass, compile the assembly file
            var compiler = new Z80Assembler();

            options.ProcExplicitLocalsOnly = true;
            if (_traceMessageHandler != null)
            {
                compiler.AssemblerMessageCreated += _traceMessageHandler;
            }
            compiler.AssemblerMessageCreated += OnAssemblerMessage;
            try
            {
                output           = compiler.CompileFile(zxbOptions.OutputFilename, options);
                output.ModelType = SpectrumModelType.Spectrum48;
            }
            finally
            {
                if (_traceMessageHandler != null)
                {
                    compiler.AssemblerMessageCreated -= _traceMessageHandler;
                }
                compiler.AssemblerMessageCreated -= OnAssemblerMessage;
            }
            return(output);
        }
        public bool Build(IProject project)
        {
            AssemblerOutput output;

            using (IAssembler assembler = _assemblerFactory.CreateAssembler())
            {
                AssemblerHelper.SetupAssembler(assembler, _inputFile, _outputFile, project.ProjectDirectory,
                                               project.IncludeDirs);

                string outputString;
                switch (_stepType)
                {
                case BuildStepType.All:
                    outputString =
                        assembler.Assemble(AssemblyFlags.Normal | AssemblyFlags.SymbolTable | AssemblyFlags.List);
                    output = new AssemblerOutput(outputString,
                                                 !outputString.Contains("error") && !outputString.Contains("Couldn't"));
                    project.BuildSystem.ProjectOutput = _outputFile;
                    project.BuildSystem.ListOutput    = _outputFile.ChangeExtension("lst");
                    project.BuildSystem.LabelOutput   = _outputFile.ChangeExtension("lab");
                    break;

                case BuildStepType.Assemble:
                    outputString = assembler.Assemble(AssemblyFlags.Normal);
                    output       = new AssemblerOutput(outputString,
                                                       !outputString.Contains("error") && !outputString.Contains("Couldn't"));
                    project.BuildSystem.ProjectOutput = _outputFile;
                    break;

                case BuildStepType.Listing:
                    outputString = assembler.Assemble(AssemblyFlags.Normal | AssemblyFlags.List);
                    output       = new AssemblerOutput(outputString,
                                                       !outputString.Contains("error") && !outputString.Contains("Couldn't"));
                    project.BuildSystem.ListOutput = _outputFile.ChangeExtension("lst");
                    break;

                case BuildStepType.SymbolTable:
                    outputString = assembler.Assemble(AssemblyFlags.Normal | AssemblyFlags.SymbolTable);
                    output       = new AssemblerOutput(outputString,
                                                       !outputString.Contains("error") && !outputString.Contains("Couldn't"));
                    project.BuildSystem.LabelOutput = _outputFile.ChangeExtension("lab");
                    break;

                default:
                    throw new InvalidOperationException("Unknown step type");
                }
            }
            _outputText = output.OutputText;
            return(output.Succeeded);
        }
 /// <summary>
 /// Creates auto start block (header+data) to save
 /// </summary>
 /// <param name="output">Assembler output</param>
 /// <param name="name">Program name</param>
 /// <param name="useScreenFile">Indicates if a screen file is used</param>
 /// <param name="addPause0">Indicates if a "PAUSE 0" should be added</param>
 /// <param name="borderColor">Border color ("0"-"7")</param>
 /// <param name="startAddr">Auto start address</param>
 /// <param name="clearAddr">Optional CLEAR address</param>
 /// <returns>Block contents</returns>
 private static List <byte[]> CreateAutoStartBlock(AssemblerOutput output, string name,
                                                   bool useScreenFile,
                                                   bool addPause0,
                                                   string borderColor,
                                                   ushort startAddr,
                                                   ushort?clearAddr = null)
 {
     return(output.ModelType == SpectrumModelType.Spectrum48 ||
            output.Segments.Count(s => s.Bank != null) == 0
            // --- No banks to emit, use the ZX Spectrum 48 auto-loader format
         ? CreateSpectrum48StartBlock(output, name, useScreenFile, addPause0,
                                      borderColor, startAddr, clearAddr)
            // --- There are banks to emit, use the ZX Spectrum 128 auto-loader format
         : CreateSpectrum128StartBlock(output, name, useScreenFile, addPause0,
                                       borderColor, startAddr, clearAddr));
 }
Esempio n. 8
0
        /// <summary>
        /// Injects the code into the Spectrum virtual machine's memory
        /// </summary>
        /// <param name="output"></param>
        public void InjectCodeIntoVm(AssemblerOutput output)
        {
            // --- Do not inject faulty code
            if (output == null || output.ErrorCount > 0)
            {
                return;
            }

            // --- Do not inject code if memory is not available
            var vm         = SpectNetPackage.Default.EmulatorViewModel;
            var spectrumVm = vm.Machine.SpectrumVm;

            if (vm.MachineState != VmState.Paused || spectrumVm?.MemoryDevice == null)
            {
                return;
            }

            if (spectrumVm is ISpectrumVmRunCodeSupport runSupport)
            {
                // --- Clear the screen unless required else
                if (!output.InjectOptions.Contains("nocls"))
                {
                    runSupport.ClearScreen();
                }

                // --- Go through all code segments and inject them
                foreach (var segment in output.Segments)
                {
                    if (segment.Bank != null)
                    {
                        runSupport.InjectCodeToBank(segment.Bank.Value, segment.BankOffset, segment.EmittedCode);
                    }
                    else
                    {
                        var addr = segment.StartAddress;
                        runSupport.InjectCodeToMemory((ushort)addr, segment.EmittedCode);
                    }
                }

                // --- Prepare the machine for RUN mode
                runSupport.PrepareRunMode(output.InjectOptions);
                CodeInjected?.Invoke(this, EventArgs.Empty);
            }
        }
Esempio n. 9
0
 /// <summary>
 /// Signs that the code compilation completed.
 /// </summary>
 /// <param name="output">Assembler output</param>
 public void RaiseCompilationCompleted(AssemblerOutput output)
 {
     CompilationCompleted?.Invoke(this, new CompilationCompletedEventArgs(output));
 }
Esempio n. 10
0
 /// <summary>
 /// Catch the event of compilation
 /// </summary>
 private void OnCompilationCompleted(object sender, CompilationCompletedEventArgs e)
 {
     CompilerOutput = e.Output;
 }
Esempio n. 11
0
        /// <summary>
        /// Compiles the Z80 code file
        /// </summary>
        protected override async Task ExecuteAsync()
        {
            GetItem(out var hierarchy, out var itemId);
            if (hierarchy == null)
            {
                return;
            }

            var codeManager = Package.CodeManager;
            var options     = Package.Options;

            // --- Step #1: Compile
            var start = DateTime.Now;
            var pane  = OutputWindow.GetPane <Z80OutputPane>();

            pane.WriteLine("Z80 Assembler");
            _output = codeManager.Compile(hierarchy, itemId);
            var duration = (DateTime.Now - start).TotalMilliseconds;

            pane.WriteLine($"Compile time: {duration}ms");

            if (_output.ErrorCount != 0)
            {
                // --- Compilation completed with errors
                return;
            }
            if (_output.Segments.Sum(s => s.EmittedCode.Count) == 0)
            {
                VsxDialogs.Show("The lenght of the compiled code is 0, " +
                                "so there is no code to inject into the virtual machine and run.",
                                "No code to run.");
                return;
            }

            // --- Step #2: Check non-zero displacements
            if (_output.Segments.Any(s => (s.Displacement ?? 0) != 0) && options.ConfirmNonZeroDisplacement)
            {
                var answer = VsxDialogs.Show("The compiled code contains non-zero displacement" +
                                             "value, so the displaced code may fail. Are you sure you want to run the code?",
                                             "Non-zero displacement found",
                                             MessageBoxButton.YesNo, VsxMessageBoxIcon.Question, 1);
                if (answer == VsxDialogResult.No)
                {
                    return;
                }
            }

            // --- Step #3: Stop the virtual machine if required
            await SwitchToMainThreadAsync();

            Package.ShowToolWindow <SpectrumEmulatorToolWindow>();
            var vm           = Package.MachineViewModel;
            var machineState = vm.VmState;

            if ((machineState == VmState.Running || machineState == VmState.Paused))
            {
                if (options.ConfirmMachineRestart)
                {
                    var answer = VsxDialogs.Show("Are you sure, you want to restart " +
                                                 "the ZX Spectrum virtual machine?",
                                                 "The ZX Spectum virtual machine is running",
                                                 MessageBoxButton.YesNo, VsxMessageBoxIcon.Question, 1);
                    if (answer == VsxDialogResult.No)
                    {
                        return;
                    }
                }

                // --- Stop the machine and allow 3 frames' time to stop.
                Package.MachineViewModel.StopVmCommand.Execute(null);
                await Task.Delay(60);

                if (vm.VmState != VmState.Stopped)
                {
                    VsxDialogs.Show("The ZX Spectrum virtual machine did not stop.",
                                    "Unexpected issue", MessageBoxButton.OK, VsxMessageBoxIcon.Error);
                    return;
                }
            }

            // --- Step #4: Start the virtual machine so that later we can load the program
            vm.StartVmWithCodeCommand.Execute(null);

            const int timeOutInSeconds = 5;
            var       counter          = 0;

            while (vm.VmState != VmState.Paused && counter < timeOutInSeconds * 10)
            {
                await Task.Delay(100);

                counter++;
            }
            if (vm.VmState != VmState.Paused)
            {
                VsxDialogs.Show($"The ZX Spectrum virtual machine did not start within {timeOutInSeconds} seconds.",
                                "Unexpected issue", MessageBoxButton.OK, VsxMessageBoxIcon.Error);
                vm.StopVmCommand.Execute(null);
            }

            // --- Step #5: Inject the code into the memory
            codeManager.InjectCodeIntoVm(_output);

            // --- Step #6: Jump to execute the code
            vm.SpectrumVm.Cpu.Registers.PC = _output.EntryAddress ?? _output.Segments[0].StartAddress;
            ResumeVm();
        }
Esempio n. 12
0
        /// <summary>
        /// Creates an auto start block for Spectrum 128K
        /// </summary>
        /// <param name="output">Assembler output</param>
        /// <param name="name">Program name</param>
        /// <param name="useScreenFile">Indicates if a screen file is used</param>
        /// <param name="addPause0">Indicates if a "PAUSE 0" should be added</param>
        /// <param name="borderColor">Border color ("0"-"7")</param>
        /// <param name="startAddr">Auto start address</param>
        /// <param name="clearAddr">Optional CLEAR address</param>
        /// <returns>Block contents</returns>
        private static List <byte[]> CreateSpectrum128StartBlock(AssemblerOutput output, string name,
                                                                 bool useScreenFile,
                                                                 bool addPause0,
                                                                 string borderColor,
                                                                 ushort startAddr,
                                                                 ushort?clearAddr = null)
        {
            var result = new List <byte[]>();

            // --- We keep the code lines here
            var lines = new List <List <byte> >();

            // --- Create placeholder for the paging code (line 10)
            var codeLine = new List <byte>(100)
            {
                REM_TKN
            };

            WriteString(codeLine, "012345678901234567890");
            codeLine.Add(NEW_LINE);
            lines.Add(codeLine);

            // --- Create code for CLEAR/PEEK program address (line 20)
            codeLine = new List <byte>(100);
            if (clearAddr.HasValue && clearAddr.Value >= 0x6000)
            {
                // --- Add clear statement
                codeLine.Add(CLEAR_TKN);
                WriteNumber(codeLine, (ushort)(clearAddr.Value - 1));
                codeLine.Add(COLON);
            }

            // --- Add "LET c=(PEEK 23635 + 256*PEEK 23636)+5
            codeLine.Add(LET_TKN);
            WriteString(codeLine, "c=(");
            codeLine.Add(PEEK_TKN);
            WriteNumber(codeLine, 23635);
            WriteString(codeLine, "+");
            WriteNumber(codeLine, 256);
            WriteString(codeLine, "*");
            codeLine.Add(PEEK_TKN);
            WriteNumber(codeLine, 23636);
            WriteString(codeLine, ")+");
            WriteNumber(codeLine, 5);
            codeLine.Add(NEW_LINE);
            lines.Add(codeLine);

            // --- Setup the machine code
            codeLine = new List <byte>(100)
            {
                FOR_TKN
            };
            WriteString(codeLine, "i=");
            WriteNumber(codeLine, 0);
            codeLine.Add(TO_TKN);
            WriteNumber(codeLine, 20);
            codeLine.Add(COLON);
            codeLine.Add(READ_TKN);
            WriteString(codeLine, "d:");
            codeLine.Add(POKE_TKN);
            WriteString(codeLine, "c+i,d:");
            codeLine.Add(NEXT_TKN);
            WriteString(codeLine, "i");
            codeLine.Add(NEW_LINE);
            lines.Add(codeLine);

            // --- Create code for BORDER/SCREEN and loading normal code blocks (line 30)
            codeLine = new List <byte>(100);
            if (borderColor != null)
            {
                var border = int.Parse(borderColor);
                codeLine.Add(BORDER_TKN);
                WriteNumber(codeLine, (ushort)border);
                codeLine.Add(COLON);
            }

            // --- Add optional screen loader, LET o = PEEK 23739:LOAD "" SCREEN$ : POKE 23739,111
            if (useScreenFile)
            {
                codeLine.Add(LET_TKN);
                WriteString(codeLine, "o=");
                codeLine.Add(PEEK_TKN);
                WriteNumber(codeLine, 23739);
                codeLine.Add(COLON);
                codeLine.Add(LOAD_TKN);
                codeLine.Add(DQUOTE);
                codeLine.Add(DQUOTE);
                codeLine.Add(SCREEN_TKN);
                codeLine.Add(COLON);
                codeLine.Add(POKE_TKN);
                WriteNumber(codeLine, 23739);
                codeLine.Add(COMMA);
                WriteNumber(codeLine, 111);
                codeLine.Add(COLON);
            }

            // --- Add 'LOAD "" CODE' for each block
            for (var i = 0; i < output.Segments.Count(s => s.Bank == null); i++)
            {
                if (i > 0)
                {
                    codeLine.Add(COLON);
                }
                codeLine.Add(LOAD_TKN);
                codeLine.Add(DQUOTE);
                codeLine.Add(DQUOTE);
                codeLine.Add(CODE_TKN);
            }

            codeLine.Add(NEW_LINE);
            lines.Add(codeLine);

            // --- Code for reading banks
            codeLine = new List <byte>(100)
            {
                READ_TKN
            };
            WriteString(codeLine, "b");
            codeLine.Add(NEW_LINE);
            lines.Add(codeLine);

            // --- "IF b = 8 THEN GO TO 80";
            codeLine = new List <byte>(100)
            {
                IF_TKN
            };
            WriteString(codeLine, "b=");
            WriteNumber(codeLine, 8);
            codeLine.Add(THEN_TKN);
            codeLine.Add(GOTO_TKN);
            WriteNumber(codeLine, 80);
            codeLine.Add(NEW_LINE);
            lines.Add(codeLine);

            // --- "POKE 23608,b: RANDOMIZE USR c: LOAD "" CODE: GO TO 50"
            codeLine = new List <byte>(100)
            {
                POKE_TKN
            };
            WriteNumber(codeLine, 23608);
            WriteString(codeLine, ",b:");
            codeLine.Add(RAND_TKN);
            codeLine.Add(USR_TKN);
            WriteString(codeLine, "c:");
            codeLine.Add(LOAD_TKN);
            codeLine.Add(DQUOTE);
            codeLine.Add(DQUOTE);
            codeLine.Add(CODE_TKN);
            codeLine.Add(COLON);
            codeLine.Add(GOTO_TKN);
            WriteNumber(codeLine, 50);
            codeLine.Add(NEW_LINE);
            lines.Add(codeLine);

            // --- PAUSE and START
            codeLine = new List <byte>(100);
            if (addPause0)
            {
                codeLine.Add(PAUSE_TKN);
                WriteNumber(codeLine, 0);
                codeLine.Add(COLON);
            }
            if (useScreenFile)
            {
                codeLine.Add(POKE_TKN);
                WriteNumber(codeLine, 23739);
                WriteString(codeLine, ",o:");
            }

            // --- Add 'RANDOMIZE USR address: STOP'
            codeLine.Add(RAND_TKN);
            codeLine.Add(USR_TKN);
            WriteNumber(codeLine, startAddr);
            codeLine.Add(COLON);
            codeLine.Add(STOP_TKN);
            codeLine.Add(NEW_LINE);
            lines.Add(codeLine);

            // --- Add data lines with the machine code subroutine
            codeLine = new List <byte>(100);
            WriteDataStatement(codeLine, new ushort[]
            {
                243, 58, 92, 91, 230, 248, 71,
                58, 56, 92, 176, 50, 92, 91,
                1, 253, 127, 237, 121, 251, 201
            });
            lines.Add(codeLine);

            // --- Add data lines with used banks and terminating 8
            codeLine = new List <byte>(100);
            var banks = output
                        .Segments
                        .Where(s => s.Bank != null)
                        .Select(s => (ushort)s.Bank)
                        .OrderBy(n => n)
                        .ToList();

            banks.Add(8);
            WriteDataStatement(codeLine, banks.ToArray());
            lines.Add(codeLine);

            // --- All code lines are set up, create the file blocks
            var dataBlock = CreateDataBlockForCodeLines(lines);
            var header    = new SpectrumTapeHeader
            {
                // --- Program block
                Type       = 0,
                Name       = name,
                DataLength = (ushort)(dataBlock.Length - 2),

                // --- Auto-start at Line 10
                Parameter1 = 10,

                // --- Variable area offset
                Parameter2 = (ushort)(dataBlock.Length - 2)
            };

            // --- Step #4: Retrieve the auto start header and data block for save
            result.Add(header.HeaderBytes);
            result.Add(dataBlock);
            return(result);
        }
Esempio n. 13
0
 public void ToVcxprojProperties(XmlWriter writer)
 {
     if (AdditionalIncludeDirectories != null && AdditionalIncludeDirectories.Length > 0)
     {
         writer.WriteElementString("AdditionalIncludeDirectories", string.Join(";", AdditionalIncludeDirectories));
     }
     if (AdditionalOptions != null && AdditionalOptions.Length > 0)
     {
         writer.WriteElementString("AdditionalOptions", string.Join(";", AdditionalOptions));
     }
     if (AdditionalUsingDirectories != null && AdditionalUsingDirectories.Length > 0)
     {
         writer.WriteElementString("AdditionalUsingDirectories", string.Join(";", AdditionalUsingDirectories));
     }
     if (!string.IsNullOrWhiteSpace(AssemblerListingLocation))
     {
         writer.WriteElementString("AssemblerListingLocation", AssemblerListingLocation);
     }
     writer.WriteElementString("AssemblerOutput", AssemblerOutput.ToString());
     if (BasicRuntimeChecks != RuntimeCheckType.Default)
     {
         writer.WriteElementString("BasicRuntimeChecks", BasicRuntimeChecks.ToString());
     }
     if (!string.IsNullOrWhiteSpace("BrowseInformationFile"))
     {
         writer.WriteElementString("BrowseInformationFile", BrowseInformationFile);
     }
     writer.WriteElementString("BufferSecurityCheck", XmlConvert.ToString(BufferSecurityCheck));
     writer.WriteElementString("CallingConvention", CallingConvention.ToString());
     if (CompileAs != CLanguage.Default)
     {
         writer.WriteElementString("CompileAs", CompileAs.ToString());
     }
     writer.WriteElementString("CompileAsManaged", CompileAsManagedToString(CompileAsManaged));
     writer.WriteElementString("CreateHotpatchableImage", XmlConvert.ToString(CreateHotpatchableImage));
     if (DebugInformationFormat != DebugInformationFormat.None)
     {
         writer.WriteElementString("DebugInformationFormat", DebugInformationFormat.ToString());
     }
     writer.WriteElementString("DisableLanguageExtensions", XmlConvert.ToString(DisableLanguageExtensions));
     WriteStringArray(writer, "DisableSpecificWarnings", SuppressedWarnings.Select(warn => warn.ToString(CultureInfo.InvariantCulture)).ToArray());
     if (EnableEnhancedInstructionSet != EnhancedInstructionSet.None)
     {
         writer.WriteElementString("EnhancedInstructionSet", EnableEnhancedInstructionSet.ToString());
     }
     writer.WriteElementString("EnableFiberSafeOptimizations", XmlConvert.ToString(EnableFiberSafeOptimizations));
     writer.WriteElementString("CodeAnalysis", XmlConvert.ToString(CodeAnalysis));
     if (ExceptionHandling != ExceptionHandlingType.NotSpecified)
     {
         writer.WriteElementString("ExceptionHandling", ExceptionHandling.ToString());
     }
     writer.WriteElementString("ExpandAttributedSource", XmlConvert.ToString(ExpandAttributedSource));
     writer.WriteElementString("FavorSizeOrSpeed", Favor.ToString());
     writer.WriteElementString("FloatingPointExceptions", XmlConvert.ToString(FloatingPointExceptions));
     writer.WriteElementString("FloatingPointModel", FloatingPointModel.ToString());
     writer.WriteElementString("ForceConformanceInForLoopScope", XmlConvert.ToString(ForceConformanceInForLoopScope));
     WriteStringArray(writer, "ForcedUsingFiles", ForcedUsingFiles);
     writer.WriteElementString("FunctionLevelLinking", XmlConvert.ToString(FunctionLevelLinking));
     writer.WriteElementString("GenerateXMLDocumentationFiles", XmlConvert.ToString(GenerateXMLDocumentationFiles));
     writer.WriteElementString("IgnoreStandardIncludePath", XmlConvert.ToString(IgnoreStandardIncludePath));
     if (InlineFunctionExpansion != InlineExpansion.Default)
     {
         writer.WriteElementString("InlineFunctionExpansion", InlineFunctionExpansion.ToString());
     }
     writer.WriteElementString("IntrinsicFunctions", XmlConvert.ToString(IntrinsicFunctions));
     writer.WriteElementString("MinimalRebuild", XmlConvert.ToString(MinimalRebuild));
     writer.WriteElementString("MultiProcessorCompilation", XmlConvert.ToString(MultiProcessorCompilation));
     writer.WriteElementString("OmitDefaultLibName", XmlConvert.ToString(OmitDefaultLibName));
     writer.WriteElementString("OmitFramePointers", XmlConvert.ToString(OmitFramePointers));
     writer.WriteElementString("OpenMPSupport", XmlConvert.ToString(OpenMPSupport));
     writer.WriteElementString("Optimization", Optimization.ToString());
     WriteStringArray(writer, "PreprocessorDefinitions", Defines);
     if (ProcessorNumber.HasValue)
     {
         writer.WriteElementString("ProcessorNumber", ProcessorNumber.Value.ToString(CultureInfo.InvariantCulture));
     }
     writer.WriteElementString("RuntimeLibrary", RuntimeLibrary.ToString());
     writer.WriteElementString("RuntimeTypeInfo", XmlConvert.ToString(RuntimeTypeInfo));
     writer.WriteElementString("SmallerTypeCheck", XmlConvert.ToString(SmallerTypeCheck));
     writer.WriteElementString("StringPooling", XmlConvert.ToString(StringPooling));
     if (StructMemberAlignment.HasValue)
     {
         writer.WriteElementString("StructMemberAlignment", StructMemberAlignment.Value.ToString(CultureInfo.InvariantCulture));
     }
     writer.WriteElementString("AllWarningsAsError", XmlConvert.ToString(AllWarningsAsError));
     WriteStringArray(writer, "SpecificWarningsAsError", SpecificWarningsAsError.Select(warn => warn.ToString(CultureInfo.InvariantCulture)).ToArray());
     writer.WriteElementString("TreatWCharTAsBuiltInType", XmlConvert.ToString(TreatWCharTAsBuiltInType));
     writer.WriteElementString("UndefineAllPreprocessorDefinitions", XmlConvert.ToString(UndefineAllPreprocessorDefinitions));
     WriteStringArray(writer, "UndefinePreprocessorDefinitions", UndefinePreprocessorDefinitions);
     writer.WriteElementString("WarningLevel", WarningLevelToString(WarningLevel));
     writer.WriteElementString("WholeProgramOptimization", XmlConvert.ToString(WholeProgramOptimization));
     writer.WriteElementString("ProgramDataBaseFileName", PDBFileName);
 }
Esempio n. 14
0
        /// <summary>
        /// Exports the specified compiler output
        /// </summary>
        /// <param name="output">Compiler output</param>
        /// <param name="programName">Name of the program</param>
        /// <param name="vm">Export options</param>
        /// <returns></returns>
        public static int ExportCompiledCode(AssemblerOutput output, ExportZ80ProgramViewModel vm)
        {
            var oldExt = vm.Format;

            vm.Format = ExportFormat.Unknown;
            vm.Format = oldExt;

            if (vm.Format == ExportFormat.IntelHex)
            {
                SaveIntelHexFile(vm.Filename, output);
                return(0);
            }

            // --- Check for screen file error
            var useScreenFile = !string.IsNullOrEmpty(vm.ScreenFile) && vm.ScreenFile.Trim().Length > 0;

            if (useScreenFile && !CommonTapeFilePlayer.CheckScreenFile(vm.ScreenFile))
            {
                return(1);
            }

            // --- Step #6: Create code segments
            var           codeBlocks   = CreateTapeBlocks(output, vm.Name, vm.SingleBlock);
            List <byte[]> screenBlocks = null;

            if (useScreenFile)
            {
                screenBlocks = CreateScreenBlocks(vm.ScreenFile);
            }

            // --- Step #7: Create Auto Start header block, if required
            var blocksToSave = new List <byte[]>();

            if (!ushort.TryParse(vm.StartAddress, out var startAddress))
            {
                startAddress = (ushort)(output == null
                    ? -1
                    : output.ExportEntryAddress
                                        ?? output.EntryAddress
                                        ?? output.Segments[0].StartAddress);
            }

            if (vm.AutoStartEnabled)
            {
                var autoStartBlocks = CreateAutoStartBlock(
                    output,
                    vm.Name,
                    useScreenFile,
                    vm.AddPause0,
                    vm.Border,
                    startAddress,
                    vm.ApplyClear
                        ? output.Segments.Min(s => s.StartAddress)
                        : (ushort?)null);
                blocksToSave.AddRange(autoStartBlocks);
            }

            // --- Step #8: Save all the blocks
            if (screenBlocks != null)
            {
                blocksToSave.AddRange(screenBlocks);
            }

            blocksToSave.AddRange(codeBlocks);
            SaveDataBlocks(vm, blocksToSave);
            return(0);
        }
 /// <summary>Initializes a new instance of the <see cref="T:System.EventArgs" /> class.</summary>
 public CompilationCompletedEventArgs(AssemblerOutput output)
 {
     Output = output;
 }
        /// <summary>
        /// Compiles the specified Visua Studio document.
        /// </summary>
        /// <param name="itemPath">VS document item path</param>
        /// <param name="options">Assembler options to use</param>
        /// <returns>True, if compilation is successful; otherwise, false</returns>
        public async Task <AssemblerOutput> CompileDocument(string itemPath,
                                                            AssemblerOptions options)
        {
            var       addToProject = SpectNetPackage.Default.Options.StoreGeneratedZ80Files;
            var       zxbOptions   = PrepareZxbOptions(itemPath, addToProject);
            var       output       = new AssemblerOutput(new SourceFileItem(itemPath), options?.UseCaseSensitiveSymbols ?? false);
            var       runner       = new ZxbRunner(SpectNetPackage.Default.Options.ZxbPath);
            ZxbResult result;

            try
            {
                result = await runner.RunAsync(zxbOptions, true);
            }
            catch (Exception ex)
            {
                output.Errors.Clear();
                output.Errors.Add(new AssemblerErrorInfo("ZXB", "", 1, 0, ex.Message));
                return(output);
            }
            if (result.ExitCode != 0)
            {
                // --- Compile error - stop here
                output.Errors.Clear();
                output.Errors.AddRange(result.Errors);
                return(output);
            }

            // --- Add the generated file to the project
            if (addToProject)
            {
                var zxBasItem =
                    SpectNetPackage.Default.ActiveProject.ZxBasicProjectItems.FirstOrDefault(pi =>
                                                                                             pi.Filename == itemPath)?.DteProjectItem;
                if (SpectNetPackage.Default.Options.NestGeneratedZ80Files && zxBasItem != null)
                {
                    var newItem = zxBasItem.ProjectItems.AddFromFile(zxbOptions.OutputFilename);
                    newItem.Properties.Item("DependentUpon").Value = zxBasItem.Name;
                }
                else
                {
                    SpectNetPackage.Default.ActiveRoot.ProjectItems.AddFromFile(zxbOptions.OutputFilename);
                }
            }

            var asmContents = File.ReadAllText(zxbOptions.OutputFilename);

            ZxBasicNamespacePreprocessor preProc = new ZxBasicNamespacePreprocessor(asmContents);

            asmContents = "\t.zxbasic\r\n" + preProc.ProcessContent();
            var hasHeapSizeLabel = Regex.Match(asmContents, "ZXBASIC_HEAP_SIZE\\s+EQU");

            if (!hasHeapSizeLabel.Success)
            {
                // --- HACK: Take care that "ZXBASIC_HEAP_SIZE EQU" is added to the assembly file
                asmContents = Regex.Replace(asmContents, "ZXBASIC_USER_DATA_END\\s+EQU\\s+ZXBASIC_MEM_HEAP",
                                            "ZXBASIC_USER_DATA_END EQU ZXBASIC_USER_DATA");
            }
            File.WriteAllText(zxbOptions.OutputFilename, asmContents);

            // --- Second pass, compile the assembly file
            var compiler = new Z80Assembler();

            if (_traceMessageHandler != null)
            {
                compiler.AssemblerMessageCreated += _traceMessageHandler;
            }
            compiler.AssemblerMessageCreated += OnAssemblerMessage;
            try
            {
                output           = compiler.CompileFile(zxbOptions.OutputFilename, options);
                output.ModelType = SpectrumModelType.Spectrum48;
            }
            finally
            {
                if (_traceMessageHandler != null)
                {
                    compiler.AssemblerMessageCreated -= _traceMessageHandler;
                }
                compiler.AssemblerMessageCreated -= OnAssemblerMessage;
            }
            return(output);
        }
Esempio n. 17
0
        /// <summary>
        /// Creates tape blocks from the assembler output.
        /// </summary>
        /// <param name="name">Program name</param>
        /// <param name="output">Assembler output</param>
        /// <param name="singleBlock">
        /// Indicates if a single block should be created from all segments
        /// </param>
        /// <returns>The list that contains headers and data blocks to save</returns>
        public List <byte[]> CreateTapeBlocks(string name, AssemblerOutput output, bool singleBlock)
        {
            var result = new List <byte[]>();

            if (output.Segments.Sum(s => s.EmittedCode.Count) == 0)
            {
                // --- No code to return
                return(null);
            }

            if (singleBlock)
            {
                // --- Merge all blocks together
                var startAddr = output.Segments.Min(s => s.StartAddress);
                var endAddr   = output.Segments.Max(s => s.StartAddress + s.EmittedCode.Count - 1);

                var mergedSegment = new byte[endAddr - startAddr + 3];
                foreach (var segment in output.Segments)
                {
                    segment.EmittedCode.CopyTo(mergedSegment, segment.StartAddress - startAddr + 1);
                }

                // --- The first byte of the merged segment is 0xFF (Data block)
                mergedSegment[0] = 0xff;
                SetTapeCheckSum(mergedSegment);

                // --- Create the single header
                var singleHeader = new SpectrumTapeHeader
                {
                    Type       = 3, // --- Code block
                    Name       = name,
                    DataLength = (ushort)(mergedSegment.Length - 2),
                    Parameter1 = startAddr,
                    Parameter2 = 0x8000
                };

                // --- Create the two tape blocks (header + data)
                result.Add(singleHeader.HeaderBytes);
                result.Add(mergedSegment);
            }
            else
            {
                // --- Create separate block for each segment
                var segmentIdx = 0;
                foreach (var segment in output.Segments)
                {
                    segmentIdx++;
                    var startAddr = segment.StartAddress;
                    var endAddr   = segment.StartAddress + segment.EmittedCode.Count - 1;

                    var codeSegment = new byte[endAddr - startAddr + 3];
                    segment.EmittedCode.CopyTo(codeSegment, segment.StartAddress - startAddr + 1);

                    // --- The first byte of the code segment is 0xFF (Data block)
                    codeSegment[0] = 0xff;
                    SetTapeCheckSum(codeSegment);

                    // --- Create the single header
                    var header = new SpectrumTapeHeader
                    {
                        Type       = 3, // --- Code block
                        Name       = $"{segmentIdx}_{name}",
                        DataLength = (ushort)(codeSegment.Length - 2),
                        Parameter1 = startAddr,
                        Parameter2 = 0x8000
                    };

                    // --- Create the two tape blocks (header + data)
                    result.Add(header.HeaderBytes);
                    result.Add(codeSegment);
                }
            }
            return(result);
        }
Esempio n. 18
0
        /// <summary>
        /// Creates an auto start block for Spectrum 48K
        /// </summary>
        /// <param name="output">Assembler output</param>
        /// <param name="name">Program name</param>
        /// <param name="useScreenFile">Indicates if a screen file is used</param>
        /// <param name="addPause0">Indicates if a "PAUSE 0" should be added</param>
        /// <param name="borderColor">Border color ("0"-"7")</param>
        /// <param name="startAddr">Auto start address</param>
        /// <param name="clearAddr">Optional CLEAR address</param>
        /// <returns>Block contents</returns>
        private static List <byte[]> CreateSpectrum48StartBlock(AssemblerOutput output, string name,
                                                                bool useScreenFile,
                                                                bool addPause0,
                                                                string borderColor,
                                                                ushort startAddr,
                                                                ushort?clearAddr = null)
        {
            var result = new List <byte[]>();

            // --- Step #1: Create the code line for auto start
            var codeLine = new List <byte>(100);

            if (clearAddr.HasValue && clearAddr.Value >= 0x6000)
            {
                // --- Add clear statement
                codeLine.Add(CLEAR_TKN);
                WriteNumber(codeLine, (ushort)(clearAddr.Value - 1));
                codeLine.Add(COLON);
            }

            // --- Add optional border color
            if (borderColor != null)
            {
                var border = int.Parse(borderColor);
                codeLine.Add(BORDER_TKN);
                WriteNumber(codeLine, (ushort)border);
                codeLine.Add(COLON);
            }

            // --- Add optional screen loader, LET o = PEEK 23739 : LOAD "" SCREEN$ : POKE 23739,111
            if (useScreenFile)
            {
                codeLine.Add(LET_TKN);
                WriteString(codeLine, "o=");
                codeLine.Add(PEEK_TKN);
                WriteNumber(codeLine, 23739);
                codeLine.Add(COLON);
                codeLine.Add(LOAD_TKN);
                codeLine.Add(DQUOTE);
                codeLine.Add(DQUOTE);
                codeLine.Add(SCREEN_TKN);
                codeLine.Add(COLON);
                codeLine.Add(POKE_TKN);
                WriteNumber(codeLine, 23739);
                codeLine.Add(COMMA);
                WriteNumber(codeLine, 111);
                codeLine.Add(COLON);
            }

            // --- Add 'LOAD "" CODE' for each block
            for (var i = 0; i < output.Segments.Count; i++)
            {
                codeLine.Add(LOAD_TKN);
                codeLine.Add(DQUOTE);
                codeLine.Add(DQUOTE);
                codeLine.Add(CODE_TKN);
                codeLine.Add(COLON);
            }

            // --- Add 'PAUSE 0'
            if (addPause0)
            {
                codeLine.Add(PAUSE_TKN);
                WriteNumber(codeLine, 0);
                codeLine.Add(COLON);
            }

            // --- Some SCREEN$ related poking
            if (useScreenFile)
            {
                codeLine.Add(POKE_TKN);
                WriteNumber(codeLine, 23739);
                WriteString(codeLine, ",o:");
            }

            // --- Add 'RANDOMIZE USR address'
            codeLine.Add(RAND_TKN);
            codeLine.Add(USR_TKN);
            WriteNumber(codeLine, startAddr);

            // --- Complete the line
            codeLine.Add(NEW_LINE);

            // --- Step #2: Now, complete the data block
            // --- Allocate extra 6 bytes: 1 byte - header, 2 byte - line number
            // --- 2 byte - line length, 1 byte - checksum
            var dataBlock = new byte[codeLine.Count + 6];

            codeLine.CopyTo(dataBlock, 5);
            dataBlock[0] = 0xff;
            // --- Set line number to 10. Line number uses MSB/LSB order
            dataBlock[1] = 0x00;
            dataBlock[2] = 10;
            // --- Set line length
            dataBlock[3] = (byte)codeLine.Count;
            dataBlock[4] = (byte)(codeLine.Count >> 8);
            SetTapeCheckSum(dataBlock);

            // --- Step #3: Create the header
            var header = new SpectrumTapeHeader
            {
                // --- Program block
                Type       = 0,
                Name       = name,
                DataLength = (ushort)(dataBlock.Length - 2),

                // --- Auto-start at Line 10
                Parameter1 = 10,
                // --- Variable area offset
                Parameter2 = (ushort)(dataBlock.Length - 2)
            };

            // --- Step #4: Retrieve the auto start header and data block for save
            result.Add(header.HeaderBytes);
            result.Add(dataBlock);
            return(result);
        }