/// <summary> /// Begin the assembly process. /// </summary> /// <returns>The time in seconds for the assembly to complete.</returns> /// <exception cref="Exception"></exception> public double Assemble() { if (_services.Log.HasErrors) { _services.Log.DumpErrors(); return(double.NaN); } if (_services.Options.InputFiles.Count == 0) { throw new Exception("One or more required input files was not specified."); } var stopWatch = new Stopwatch(); stopWatch.Start(); // process cmd line args if (_services.Options.Quiet) { Console.SetOut(TextWriter.Null); } var preprocessor = new Preprocessor(_processorOptions); var processed = new List <SourceLine>(); try { // preprocess all passed option defines and sections foreach (var define in _services.Options.LabelDefines) { processed.Add(preprocessor.ProcessDefine(define)); } foreach (var section in _services.Options.Sections) { processed.AddRange(preprocessor.Process(string.Empty, processed.Count, $".dsection {section}")); } // preprocess all input files foreach (var inputFile in _services.Options.InputFiles) { processed.AddRange(preprocessor.Process(inputFile)); } if (!_services.Options.NoStats) { Console.WriteLine($"{Assembler.AssemblerName}"); Console.WriteLine($"{Assembler.AssemblerVersion}"); } var multiLineAssembler = new MultiLineAssembler() .WithAssemblers(_assemblers) .WithOptions(new MultiLineAssembler.Options { AllowReturn = false, DisassembleAll = _services.Options.VerboseList, ErrorHandler = AssemblyErrorHandler, StopDisassembly = () => _services.PrintOff, Evaluator = _services.Evaluator }); var disassembly = string.Empty; // while passes are needed while (_services.PassNeeded && !_services.Log.HasErrors) { if (_services.DoNewPass() == 4) { throw new Exception("Too many passes attempted."); } _ = multiLineAssembler.AssembleLines(processed.GetIterator(), out disassembly); if (!_services.PassNeeded && _services.CurrentPass == 0) { _services.PassNeeded = _services.SymbolManager.SearchedNotFound; } } if (!_services.Options.WarnNotUnusedSections && !_services.Log.HasErrors) { var unused = _services.Output.UnusedSections; if (unused.Count() > 0) { foreach (var section in unused) { _services.Log.LogEntry(null, 1, 1, $"Section {section} was defined but never used.", false); } } } if (!_services.Options.NoWarnings && _services.Log.HasWarnings) { _services.Log.DumpWarnings(); } int byteCount = 0; if (_services.Log.HasErrors) { _services.Log.DumpErrors(); } else { var passedArgs = _services.Options.GetPassedArgs(); var exec = Process.GetCurrentProcess().MainModule.ModuleName; var inputFiles = string.Join("\n// ", preprocessor.GetInputFiles()); var fullDisasm = $"// {Assembler.AssemblerNameSimple}\n" + $"// {exec} {string.Join(' ', passedArgs)}\n" + $"// {DateTime.Now:f}\n\n// Input files:\n\n" + $"// {inputFiles}\n\n" + disassembly; byteCount = WriteOutput(fullDisasm); } if (!_services.Options.NoStats) { Console.WriteLine($"Number of errors: {_services.Log.ErrorCount}"); Console.WriteLine($"Number of warnings: {_services.Log.WarningCount}"); } stopWatch.Stop(); var ts = stopWatch.Elapsed.TotalSeconds; if (!_services.Log.HasErrors) { var section = _services.Options.OutputSection; if (!_services.Options.NoStats) { if (!string.IsNullOrEmpty(section)) { Console.Write($"[{section}] "); } if (!string.IsNullOrEmpty(_services.Options.Patch)) { Console.WriteLine($"{byteCount} (Offs:{_services.Options.Patch}), {ts} sec."); } else { Console.WriteLine($"{byteCount} bytes, {ts} sec."); } if (_services.Options.ShowChecksums) { Console.WriteLine($"Checksum: {_services.Output.GetOutputHash(section)}"); } Console.WriteLine("*********************************"); Console.Write("Assembly completed successfully."); } } else { _services.Log.ClearAll(); } return(ts); } catch (Exception ex) { _services.Log.LogEntrySimple(ex.Message); return(double.NaN); } finally { if (_services.Log.HasErrors) { _services.Log.DumpErrors(); } } }
/// <summary> /// Begin the assembly process. /// </summary> /// <exception cref="Exception"></exception> public void Assemble() { var stopWatch = new Stopwatch(); stopWatch.Start(); try { // process cmd line args if (Assembler.Options.Quiet) { Console.SetOut(TextWriter.Null); } Console.WriteLine(Assembler.AssemblerName); Console.WriteLine(Assembler.AssemblerVersion); // init all line assemblers var multiLineAssembler = new MultiLineAssembler(); _assemblers.Add(multiLineAssembler); _assemblers.Add(new AssignmentAssembler()); _assemblers.Add(new EncodingAssembler()); _assemblers.Add(new PseudoAssembler()); _assemblers.Add(new MiscAssembler()); // preprocess all input files var preprocessor = new Preprocessor(); var processed = new List <SourceLine>(); // define all passed option defines if (Assembler.Options.LabelDefines.Count > 0) { foreach (var define in Assembler.Options.LabelDefines) { processed.AddRange(preprocessor.PreprocessSource(define)); } } foreach (var path in Assembler.Options.InputFiles) { processed.AddRange(preprocessor.PreprocessFile(path)); } // set the iterator Assembler.LineIterator = processed.GetIterator(); var exec = Process.GetCurrentProcess().MainModule.ModuleName; var argsPassed = string.Join(' ', Assembler.Options.Arguments); var inputFiles = string.Join("\n// ", preprocessor.GetInputFiles()); var disasmHeader = $"// {Assembler.AssemblerNameSimple}\n// {exec} {argsPassed}\n// {DateTime.Now:f}\n\n// Input files:" + $"\n\n// {inputFiles}\n\n"; StringBuilder disassembly = null; // while passes are needed while (Assembler.PassNeeded && !Assembler.Log.HasErrors) { if (Assembler.CurrentPass++ == 4) { throw new Exception("Too many passes attempted."); } disassembly = new StringBuilder(disasmHeader); foreach (var line in Assembler.LineIterator) { try { if (line.Label != null || line.Instruction != null) { var asm = _assemblers.FirstOrDefault(a => a.AssemblesLine(line)); if (asm != null) { var disasm = asm.AssembleLine(line); if (!string.IsNullOrWhiteSpace(disasm) && !Assembler.PrintOff) { disassembly.AppendLine(disasm); } } else if (line.Instruction != null) { Assembler.Log.LogEntry(line, line.Instruction.Position, $"Unknown instruction \"{line.InstructionName}\"."); } } else if (Assembler.Options.VerboseList) { disassembly.AppendLine(line.UnparsedSource.PadLeft(50, ' ')); } } catch (Exception ex) { if (ex is SymbolException symbEx) { Assembler.Log.LogEntry(line, symbEx.Position, symbEx.Message, true); } else if (ex is FormatException fmtEx) { Assembler.Log.LogEntry(line, line.Operand, $"There was a problem with the format string:\n{fmtEx.Message}.", true); } else if (Assembler.CurrentPass > 0 && !Assembler.PassNeeded) { if (ex is ExpressionException expEx) { if (ex is IllegalQuantityException) { Assembler.Log.LogEntry(line, expEx.Position, $"Illegal quantity for \"{line.Instruction}\" in expression \"{line.Operand}\"."); } else { Assembler.Log.LogEntry(line, expEx.Position, ex.Message); } } else if (ex is OverflowException) { Assembler.Log.LogEntry(line, line.Operand.Position, $"Illegal quantity for \"{line.Instruction}\" in expression \"{line.Operand}\"."); } else if (ex is InvalidPCAssignmentException pcEx) { Assembler.Log.LogEntry(line, line.Instruction, $"Invalid Program Counter assignment in expression \"{line.Operand}\"."); } else if (ex is ProgramOverflowException prgEx) { Assembler.Log.LogEntry(line, line.Instruction, "Program Overflow."); } else { Assembler.Log.LogEntry(line, line.Operand.Position, ex.Message); } } else { Assembler.PassNeeded = true; } } } if (multiLineAssembler.InAnActiveBlock) { SourceLine activeLine = multiLineAssembler.ActiveBlockLine; Assembler.Log.LogEntry(activeLine, activeLine.Instruction, $"End of source file reached before finding block closure for \"{activeLine.InstructionName}\"."); } Assembler.LineIterator.Reset(); } if (!Assembler.Options.NoWarnings && Assembler.Log.HasWarnings) { Assembler.Log.DumpWarnings(); } if (Assembler.Log.HasErrors) { Assembler.Log.DumpErrors(); } else { WriteOutput(disassembly?.ToString()); } Console.WriteLine($"Number of errors: {Assembler.Log.ErrorCount}"); Console.WriteLine($"Number of warnings: {Assembler.Log.WarningCount}"); if (!Assembler.Log.HasErrors) { stopWatch.Stop(); var ts = stopWatch.Elapsed.TotalSeconds; Console.WriteLine($"{Assembler.Output.GetCompilation().Count} bytes, {ts} sec."); if (Assembler.Options.ShowChecksums) { Console.WriteLine($"Checksum: {Assembler.Output.GetOutputHash()}"); } Console.WriteLine("*********************************"); Console.WriteLine("Assembly completed successfully."); } } catch (Exception ex) { Console.Error.WriteLine(ex.Message); } }