Beispiel #1
0
        int WriteOutput(string disassembly)
        {
            // no errors finish up
            // save to disk
            var section    = _services.Options.OutputSection;
            var outputFile = _services.Options.OutputFile;
            var objectCode = _services.Output.GetCompilation(section);

            if (!string.IsNullOrEmpty(_services.Options.Patch))
            {
                if (!string.IsNullOrEmpty(_services.Options.OutputFile) ||
                    !string.IsNullOrEmpty(_services.Options.Format))
                {
                    _services.Log.LogEntrySimple("Output options ignored for patch mode.", false);
                }
                if (_services.Options.LabelsAddressesOnly && string.IsNullOrEmpty(_services.Options.LabelFile))
                {
                    _services.Log.LogEntrySimple("Label listing not specified; option '-labels-addresses-only' ignored.",
                                                 false);
                }
                try
                {
                    var offsetLine = new Preprocessor(_processorOptions).ProcessDefine("patch=" + _services.Options.Patch);
                    var patchExp   = offsetLine.Operands.GetIterator();
                    var offset     = _services.Evaluator.Evaluate(patchExp, 0, ushort.MaxValue);
                    if (patchExp.Current != null || _services.PassNeeded)
                    {
                        throw new Exception();
                    }
                    var filePath  = Preprocessor.GetFullPath(outputFile, _services.Options.IncludePath);
                    var fileBytes = File.ReadAllBytes(filePath);
                    Array.Copy(objectCode.ToArray(), 0, fileBytes, (int)offset, objectCode.Count);
                    File.WriteAllBytes(filePath, fileBytes);
                }
                catch
                {
                    throw new Exception($"Cannot patch file \"{outputFile}\". One or more arguments is not valid.");
                }
            }
            else
            {
                var formatProvider = _services.FormatSelector?.Invoke(_services.CPU, _services.OutputFormat);
                if (formatProvider != null)
                {
                    var startAddress = _services.Output.ProgramStart;
                    if (!string.IsNullOrEmpty(section))
                    {
                        startAddress = _services.Output.GetSectionStart(section);
                    }
                    var format = _services.Options.CaseSensitive ?
                                 _services.OutputFormat :
                                 _services.OutputFormat.ToLower();
                    var info = new FormatInfo(outputFile, format, startAddress, objectCode);
                    File.WriteAllBytes(outputFile, formatProvider.GetFormat(info).ToArray());
                }
                else
                {
                    File.WriteAllBytes(outputFile, objectCode.ToArray());
                }
            }
            // write disassembly
            if (!string.IsNullOrEmpty(disassembly) && !string.IsNullOrEmpty(_services.Options.ListingFile))
            {
                File.WriteAllText(_services.Options.ListingFile, disassembly);
            }

            // write labels
            if (!string.IsNullOrEmpty(_services.Options.LabelFile))
            {
                File.WriteAllText(_services.Options.LabelFile, _services.SymbolManager.ListLabels(!_services.Options.LabelsAddressesOnly));
            }

            if (_services.Log.HasWarnings)
            {
                _services.Log.DumpWarnings();
            }
            if (!_services.Options.NoStats)
            {
                Console.WriteLine("\n*********************************");
                Console.WriteLine($"Assembly start: ${_services.Output.ProgramStart:X4}");
                if (_services.Output.ProgramEnd > BinaryOutput.MaxAddress && _services.Options.LongAddressing)
                {
                    Console.WriteLine($"Assembly end:   ${_services.Output.ProgramEnd:X6}");
                }
                else
                {
                    Console.WriteLine($"Assembly end:   ${_services.Output.ProgramEnd & BinaryOutput.MaxAddress:X4}");
                }
                Console.WriteLine($"Passes: {_services.CurrentPass + 1}");
            }
            return(objectCode.Count);
        }
Beispiel #2
0
        /// <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.Any())
                    {
                        foreach (var section in unused)
                        {
                            _services.Log.LogEntrySimple($"Section {section} was defined but never used.", false);
                        }
                    }
                }
                if (!_services.Options.NoWarnings && _services.Log.HasWarnings)
                {
                    _services.Log.DumpWarnings();
                }
                var 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();
                }
            }
        }
Beispiel #3
0
        /// <summary>
        /// Constructs a new instance of an <see cref="AssemblyController"/>, which controls
        /// the assembly process.
        /// </summary>
        /// <param name="args">The commandline arguments.</param>
        /// <param name="cpuSetHandler">The <see cref="CpuAssembler"/> selection handler.</param>
        /// <param name="formatSelector">The format selector.</param>
        /// <exception cref="ArgumentNullException"></exception>
        public AssemblyController(IEnumerable <string> args,
                                  Func <string, AssemblyServices, CpuAssembler> cpuSetHandler,
                                  Func <string, string, IBinaryFormatProvider> formatSelector)
        {
            if (args == null || cpuSetHandler == null || formatSelector == null)
            {
                throw new ArgumentNullException();
            }
            _services                = new AssemblyServices(Options.FromArgs(args));
            _services.PassChanged   += (s, a) => _services.Output.Reset();
            _services.PassChanged   += (s, a) => _services.SymbolManager.Reset();
            _services.FormatSelector = formatSelector;
            _processorOptions        = new ProcessorOptions
            {
                CaseSensitive       = _services.Options.CaseSensitive,
                Log                 = _services.Log,
                IncludePath         = _services.Options.IncludePath,
                IgnoreCommentColons = _services.Options.IgnoreColons,
                WarnOnLabelLeft     = _services.Options.WarnLeft,
                InstructionLookup   = symbol => _services.InstructionLookupRules.Any(ilr => ilr(symbol))
            };
            CpuAssembler cpuAssembler = null;
            var          cpu          = _services.Options.CPU;

            if (!string.IsNullOrEmpty(cpu))
            {
                cpuAssembler = cpuSetHandler(cpu, _services);
            }
            if (_services.Options.InputFiles.Count > 0)
            {
                var src = new Preprocessor(_processorOptions).ProcessToFirstDirective(_services.Options.InputFiles[0]);
                if (src != null && src.Instruction != null && src.Instruction.Name.Equals(".cpu", _services.StringViewComparer))
                {
                    if (src.Operands.Count != 1 || !src.Operands[0].IsDoubleQuote())
                    {
                        _services.Log.LogEntry(src.Instruction,
                                               "Invalid expression for directive \".cpu\".");
                    }
                    else
                    {
                        cpu = src.Operands[0].Name.ToString().TrimOnce('"');
                    }
                }
            }
            _services.CPU = cpu;
            if (!string.IsNullOrEmpty(cpu) && cpuAssembler != null && !cpuAssembler.IsCpuValid(cpu))
            {
                _services.Log.LogEntrySimple($"Invalid CPU \"{cpu}\" specified.");
            }
            else
            {
                if (cpuAssembler == null)
                {
                    cpuAssembler = cpuSetHandler(cpu, _services);
                }
                _assemblers = new List <AssemblerBase>
                {
                    new AssignmentAssembler(_services),
                    new BlockAssembler(_services),
                    new EncodingAssembler(_services),
                    new PseudoAssembler(_services),
                    new MiscAssembler(_services),
                    cpuAssembler
                };
                _processorOptions.IsMacroNameValid = symbol => !_assemblers.Any(asm => asm.Assembles(symbol));
                _processorOptions.LineTerminates   = _services.LineTerminates;
            }
        }
Beispiel #4
0
        /// <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);
            }
        }