/// <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); }
/// <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); } }
/// <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(); } }
/// <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)); }
/// <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); } }
/// <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)); }
/// <summary> /// Catch the event of compilation /// </summary> private void OnCompilationCompleted(object sender, CompilationCompletedEventArgs e) { CompilerOutput = e.Output; }
/// <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(); }
/// <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); }
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); }
/// <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); }
/// <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); }
/// <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); }