/// <summary> /// Creates a new instance of a page block processor. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> public PageBlockProcessor(AssemblyServices services, int index) : base(services, index, false) { Reserved.DefineType("Directives", ".page", ".endpage"); _page = GetPage(); }
/// <summary> /// Creates a new instance of a switch block processor. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> public SwitchBlock(AssemblyServices services, int index) : base(services, index) { Reserved.DefineType("Directives", ".switch", ".case", ".default", ".endswitch"); Reserved.DefineType("BreakContReturn", ".break", ".continue", ".return"); }
/// <summary> /// Gets a string from the tokenized expression. /// </summary> /// <param name="iterator">The iterator to the tokenized expression.</param> /// <param name="services">The shared assembly services.</param> /// <returns></returns> public static string GetString(RandomAccessIterator <Token> iterator, AssemblyServices services) { if (iterator.Current == null && !iterator.MoveNext()) { return(string.Empty); } var token = iterator.Current; if (IsStringLiteral(iterator)) { iterator.MoveNext(); return(Regex.Unescape(token.Name.ToString()).TrimOnce('"')); } else if (token.Type == TokenType.Function && token.Name.Equals("format", services.StringComparison)) { var str = GetFormatted(iterator, services); if (!string.IsNullOrEmpty(str) && Token.IsEnd(iterator.Current)) { return(str); } } else if (token.Type == TokenType.Function && token.Name.Equals("char", services.StringComparison)) { var code = (int)services.Evaluator.Evaluate(iterator, 0, 0x10FFFF); return(char.ConvertFromUtf32(services.Encoding.GetCodePoint(code))); } else if (token.Type == TokenType.Operand && (char.IsLetter(token.Name[0]) || token.Name[0] == '_') && !services.Evaluator.IsReserved(token.Name)) { var sym = services.SymbolManager.GetSymbol(token, services.CurrentPass > 0); if (sym == null) { return(string.Empty); } if (sym.DataType == DataType.String) { if ((!iterator.MoveNext() || Token.IsEnd(iterator.Current)) && sym.StorageType == StorageType.Scalar) { return(sym.StringValue.TrimOnce('"').ToString()); } else if (sym.StorageType == StorageType.Vector && iterator.Current.Name.Equals("[")) { var current = iterator.Current; var subscript = (int)services.Evaluator.Evaluate(iterator); if (Token.IsEnd(iterator.Current)) { if (subscript >= 0 && subscript < sym.StringVector.Count) { return(sym.StringVector[subscript].ToString()); } throw new SyntaxException(current, "Index out of range."); } } } } throw new SyntaxException(token, "Type mismatch."); }
/// <summary> /// Constructs an instance of the class implementing the base class. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> protected AssemblerBase(AssemblyServices services) { Services = services; ExcludedInstructionsForLabelDefines = new HashSet <StringView>(services.StringViewComparer); Reserved = new ReservedWords(services.StringViewComparer); services.IsReserved.Add(Reserved.IsReserved); services.SymbolManager.AddValidSymbolNameCriterion(s => !Reserved.IsReserved(s)); services.InstructionLookupRules.Add(s => Assembles(s)); }
/// <summary> /// Creates a new instance of a block processor. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> /// <param name="createScope">Automatically create a scope when initialized.</param> protected BlockProcessorBase(AssemblyServices services, int index, bool createScope) : base(services) { Index = index; _createScope = createScope; if (_createScope) { services.SymbolManager.PushScope(Index.ToString()); } }
static CpuAssembler SetCpu(string cpu, AssemblyServices services) { return(cpu switch { "m6800" => new M6809Asm(services), "m6809" => new M6809Asm(services), "i8080" => new Z80Asm(services), "z80" => new Z80Asm(services), _ => new Asm6502(services) });
/// <summary> /// Gets the formatted string from the tokenized expression. /// </summary> /// <param name="iterator">The iterator to the tokenized expression.</param> /// <param name="services">The shared assembly services.</param> /// <returns></returns> public static string GetFormatted(RandomAccessIterator <Token> iterator, AssemblyServices services) { iterator.MoveNext(); var format = iterator.GetNext(); if (Token.IsEnd(format)) { return(null); } string fmt; if (!format.IsDoubleQuote()) { if (format.Type != TokenType.Function && !format.Name.Equals("format", services.StringComparison)) { return(null); } fmt = GetFormatted(iterator, services); } else { fmt = Regex.Unescape(format.Name.TrimOnce('"').ToString()); } var parms = new List <object>(); if (iterator.MoveNext()) { while (!Token.IsEnd(iterator.GetNext())) { if (ExpressionIsAString(iterator, services)) { parms.Add(GetString(iterator, services)); } else { var parmVal = services.Evaluator.Evaluate(iterator, false); if (Regex.IsMatch(fmt, $"\\{{{parms.Count}(,-?\\d+)?:(d|D|x|X)\\d*\\}}")) { parms.Add((int)parmVal); } else { parms.Add(parmVal); } } } } if (parms.Count == 0) { return(fmt); } return(string.Format(fmt, parms.ToArray())); }
/// <summary> /// Constructs a DotNetAsm.MiscAssembler class. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> public MiscAssembler(AssemblyServices services) : base(services) { Reserved.DefineType("Directives", ".assert", ".bank", ".end", ".eor", ".echo", ".forcepass", ".format", ".invoke", ".initmem", ".target", ".error", ".errorif", ".pron", ".proff", ".warnif", ".warn", ".dsection", ".section" ); ExcludedInstructionsForLabelDefines.Add(".section"); Services.PassChanged += (s, a) => Services.PrintOff = false; }
/// <summary> /// Constructs an instance of a <see cref="PseudoAssembler"/> line assembler. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> public PseudoAssembler(AssemblyServices services) : base(services) { Reserved.DefineType("Types", ".addr", ".align", ".binary", ".bstring", ".byte", ".sbyte", ".char", ".dint", ".dword", ".fill", ".hstring", ".lint", ".long", ".rta", ".short", ".sint", ".word", ".cstring", ".lstring", ".nstring", ".pstring", ".string", ".cbmflt", ".cbmfltp" ); Reserved.DefineType("Functions", "cbmflt", "cbmfltp", "char", "format", "peek", "poke", "section" ); services.Evaluator.AddFunctionEvaluator(this); _includedBinaries = new Dictionary <StringView, BinaryFile>(services.StringViewComparer); }
/// <summary> /// Creates a new instance of the Function class. /// </summary> /// <param name="name">The function's name.</param> /// <param name="parameterList">The list of parameters for the function.</param> /// <param name="iterator">The <see cref="SourceLine"/> iterator to traverse to define the function block.</param> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="caseSensitive">Determines whether to compare the passed parameters /// to the source block's own defined parameters should be case-sensitive.</param> /// <exception cref="SyntaxException"></exception> public Function(StringView name, List <Token> parameterList, RandomAccessIterator <SourceLine> iterator, AssemblyServices services, bool caseSensitive) : base(parameterList, caseSensitive) { Name = name; _services = services; _definedLines = new List <SourceLine>(); SourceLine line; while ((line = iterator.GetNext()) != null) { if (line.Label != null && line.Label.Name.Equals("+")) { _services.Log.LogEntry(line.Label, "Anonymous labels are not supported inside functions.", false); } if (line.Instruction != null) { if (line.Instruction.Name.Equals(".global", _services.StringViewComparer)) { throw new SyntaxException(line.Instruction, $"Directive \".global\" not allowed inside a function block."); } if (line.Instruction.Name.Equals(".endfunction", _services.StringViewComparer)) { if (line.Operands.Count > 0) { throw new SyntaxException(line.Operands[0], "Unexpected expression found after \".endfunction\" directive."); } break; } } _definedLines.Add(line); } if (line == null) { throw new SyntaxException(iterator.Current.Instruction, "Function definition does not have a closing \".endfunction\" directive."); } }
/// <summary> /// Creates a new instance of a conditional block processor. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> public ConditionalBlock(AssemblyServices services, int index) : base(services, index, false) { _opens = new HashSet <string>(services.StringComparer) { ".if", ".ifdef", ".ifndef" }; Reserved.DefineType("Keywords", ".if", ".ifdef", ".ifndef", ".else", ".elseif", ".elseif", ".elseifdef", ".elseifndef", ".endif"); Reserved.DefineType("Defs", ".ifdef", ".ifndef"); _ifDefEvaluations = new Dictionary <(string, int), bool>(); _ifTrue = _elseEvaluated = false; }
/// <summary> /// Constructs a new instance of a block assembler. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> public BlockAssembler(AssemblyServices services) : base(services) { _blocks = new Stack <BlockProcessorBase>(); _functionDefs = new Dictionary <StringView, Function>(services.StringViewComparer); _currentBlock = null; _openClosures = new Dictionary <StringView, StringView>(services.StringViewComparer) { { ".block", ".endblock" }, { ".for", ".next" }, { ".foreach", ".next" }, { ".function", ".endfunction" }, { ".if", ".endif" }, { ".ifdef", ".endif" }, { ".ifndef", ".endif" }, { ".namespace", ".endnamespace" }, { ".page", ".endpage" }, { ".repeat", ".endrepeat" }, { ".switch", ".endswitch" }, { ".while", ".endwhile" } }; Reserved.DefineType("Functional", ".function", ".endfunction"); Reserved.DefineType("NonOpens", ".break", ".case", ".continue", ".default", ".endblock", ".endif", ".endfunction", ".endpage", ".endnamespace", ".endrepeat", ".endswitch", ".endwhile", ".else", ".elseif", ".elseifdef", ".elseifdef", ".elseifndef", ".next"); Reserved.DefineType("BreakContinue", ".break", ".continue"); Reserved.DefineType("Goto", ".goto"); ExcludedInstructionsForLabelDefines.Add(".function"); ExcludedInstructionsForLabelDefines.Add(".block"); Services.Evaluator.AddFunctionEvaluator(this); Services.IsReserved.Add(s => _functionDefs.ContainsKey(s)); }
/// <summary> /// Constructs a new instance of the assignment assembler class. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> public AssignmentAssembler(AssemblyServices services) : base(services) { Reserved.DefineType("Assignments", ".equ", ".global", "="); Reserved.DefineType("Pseudo", ".relocate", ".pseudopc", ".endrelocate", ".realpc"); Reserved.DefineType("Directives", ".let", ".org"); Reserved.DefineType("Functions", "len"); ExcludedInstructionsForLabelDefines.Add(".org"); ExcludedInstructionsForLabelDefines.Add(".equ"); ExcludedInstructionsForLabelDefines.Add("="); ExcludedInstructionsForLabelDefines.Add(".global"); Services.Evaluator.AddFunctionEvaluator(this); }
/// <summary> /// Constructs a new instance of a CPU assembler. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> protected CpuAssembler(AssemblyServices services) : base(services) { Reserved.DefineType("CPU", ".cpu"); if (!string.IsNullOrEmpty(Services.CPU)) { CPU = Services.CPU; } else { _cpu = string.Empty; } _initCpu = _cpu; OnSetCpu(); Evaluations = new double[] { double.NaN, double.NaN, double.NaN }; }
/// <summary> /// Creates a new instance of a while block processor. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> public WhileBlock(AssemblyServices services, int index) : base(services, index) => Reserved.DefineType("Directives", ".while", ".endwhile");
/// <summary> /// Determines whether the tokenized expression is a string. /// </summary> /// <param name="iterator">The iterator to the tokenized expression.</param> /// <param name="services">The shared assembly services.</param> /// <returns></returns> public static bool ExpressionIsAString(RandomAccessIterator <Token> iterator, AssemblyServices services) { var token = iterator.Current; if (token.IsDoubleQuote()) { return(token.Name.Length > 2 && Token.IsEnd(iterator.PeekNext())); } var ix = iterator.Index; var result = false; if (token.Type == TokenType.Function && (token.Name.Equals("format", services.StringComparison) || token.Name.Equals("char", services.StringComparison))) { iterator.MoveNext(); var parms = Token.GetGroup(iterator); var last = iterator.Current; result = Token.IsEnd(last); if (token.Name.Equals("char", services.StringComparison)) { result &= services.Evaluator.Evaluate(parms.GetIterator(), 0, 0x10FFFF).IsInteger(); } } else if (token.Type == TokenType.Operand && (char.IsLetter(token.Name[0]) || token.Name[0] == '_') && !services.Evaluator.IsReserved(token.Name)) { var sym = services.SymbolManager.GetSymbol(token, false); if (sym != null) { if (iterator.MoveNext() && iterator.Current.Name.Equals("[")) { var subscript = (int)services.Evaluator.Evaluate(iterator); result = Token.IsEnd(iterator.Current) && subscript >= 0 && subscript < sym.StringVector.Count; } else { result = Token.IsEnd(iterator.Current) && sym.StorageType == StorageType.Scalar && sym.DataType == DataType.String; } } } iterator.SetIndex(ix); return(result); }
/// <summary> /// Creates a new instance of a scoped block processor. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> public ScopeBlock(AssemblyServices services, int index) : base(services, index, false) => Reserved.DefineType("Directives", ".block", ".endblock");
/// <summary> /// Creates a new instance of a repeat block processor. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> public RepeatBlock(AssemblyServices services, int index) : base(services, index) => Reserved.DefineType("Directives", ".repeat", ".endrepeat");
/// <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.Filename, src.LineNumber, src.Instruction.Position, "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; } }
/// <summary> /// Creates a new instance of the for next block processor. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> public ForNextBlock(AssemblyServices services, int index) : base(services, index) => Reserved.DefineType("Directives", ".for", ".next");
/// <summary> /// Creates a new instance of a function block processor placeholder. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> public FunctionBlock(AssemblyServices services, int index) : base(services, index) { }
/// <summary> /// Creates a new instance of a block processor. /// </summary> /// <param name="services">The shared <see cref="AssemblyServices"/> object.</param> /// <param name="index">The index at which the block is defined.</param> public BlockProcessorBase(AssemblyServices services, int index) : this(services, index, true) { }