示例#1
0
        public AssemblyOutput Assemble(string assembly, string fileName = null)
        {
            Output = new AssemblyOutput();
            Output.InstructionSet = InstructionSet;
            assembly = assembly.Replace("\r", "");
            PC = 0;
            Lines = assembly.Split('\n');
            FileNames.Push(Path.GetFileName(fileName));
            LineNumbers.Push(0);
            RootLineNumber = 0;
            IfStack.Push(true);
            for (CurrentIndex = 0; CurrentIndex < Lines.Length; CurrentIndex++)
            {
                CurrentLine = Lines[CurrentIndex].Trim().TrimComments();
                if (SuspendedLines == 0)
                {
                    LineNumbers.Push(LineNumbers.Pop() + 1);
                    RootLineNumber++;
                }
                else
                    SuspendedLines--;

                if (!IfStack.Peek())
                {
                    bool match = false;
                    if (CurrentLine.StartsWith("#") || CurrentLine.StartsWith("."))
                    {
                        var directive = CurrentLine.Substring(1);
                        if (CurrentLine.Contains((' ')))
                            directive = directive.Remove(CurrentLine.IndexOf((' '))).Trim();
                        if (ifDirectives.Contains(directive.ToLower()))
                            match = true;
                    }
                    if (!match)
                        continue;
                }

                if (CurrentLine.SafeContains(".equ") && !CurrentLine.StartsWith(".equ"))
                {
                    var name = CurrentLine.Remove(CurrentLine.SafeIndexOf(".equ"));
                    var definition = CurrentLine.Substring(CurrentLine.SafeIndexOf(".equ") + 4);
                    CurrentLine = ".equ " + name.Trim() + " " + definition.Trim();
                }

                // Check for macro
                if (!CurrentLine.StartsWith(".macro") && !CurrentLine.StartsWith("#macro") && !CurrentLine.StartsWith(".undefine") && !CurrentLine.StartsWith("#undefine"))
                {
                    Macro macroMatch = null;
                    string[] parameters = null;
                    string parameterDefinition = null;
                    foreach (var macro in Macros)
                    {
                        if (CurrentLine.ToLower().SafeContains(macro.Name))
                        {
                            // Try to match
                            int startIndex = CurrentLine.ToLower().SafeIndexOf(macro.Name);
                            int endIndex = startIndex + macro.Name.Length - 1;
                            if (macro.Parameters.Length != 0)
                            {
                                if (endIndex + 1 >= CurrentLine.Length)
                                    continue;
                                if (CurrentLine.Length < endIndex + 1 || CurrentLine[endIndex + 1] != '(')
                                    continue;
                                if (macroMatch != null && macro.Name.Length < macroMatch.Name.Length)
                                    continue;
                                parameterDefinition = CurrentLine.Substring(endIndex + 2, CurrentLine.LastIndexOf(')') - (endIndex + 2));
                                parameters = parameterDefinition.SafeSplit(',');
                                if (parameters.Length != macro.Parameters.Length)
                                    continue;
                                // Matched
                                macroMatch = macro;
                            }
                            else
                                macroMatch = macro;
                        }
                    }
                    if (macroMatch != null)
                    {
                        // Add an entry to the listing
                        AddOutput(CodeType.Directive);
                        var code = macroMatch.Code;
                        int index = 0;
                        foreach (var parameter in macroMatch.Parameters)
                            code = code.Replace(parameter.Trim(), parameters[index++].Trim());
                        string newLine;
                        if (parameterDefinition != null)
                            newLine = CurrentLine.Replace(macroMatch.Name + "(" + parameterDefinition + ")", code, StringComparison.InvariantCultureIgnoreCase);
                        else
                        {
                            if (CurrentLine.Substring(CurrentLine.ToLower().IndexOf(macroMatch.Name) + macroMatch.Name.Length).StartsWith("()"))
                                newLine = CurrentLine.Replace(macroMatch.Name + "()", code, StringComparison.InvariantCultureIgnoreCase);
                            else
                                newLine = CurrentLine.Replace(macroMatch.Name, code, StringComparison.InvariantCultureIgnoreCase);
                        }
                        var newLines = newLine.Replace("\r\n", "\n").Split('\n');
                        SuspendedLines += newLines.Length;
                        // Insert macro
                        Lines = Lines.Take(CurrentIndex).Concat(newLines).Concat(Lines.Skip(CurrentIndex + 1)).ToArray();
                        CurrentIndex--;
                        continue;
                    }
                }

                // Find same-line labels
                if (CurrentLine.Contains(":"))
                {
                    int length = 0;
                    bool isLabel = true;
                    for (int j = 0; j < CurrentLine.Length; j++)
                    {
                        if (char.IsLetterOrDigit(CurrentLine[j]) || CurrentLine[j] == '_')
                            length++;
                        else if (CurrentLine[j] == ':')
                            break;
                        else
                        {
                            isLabel = false;
                            break;
                        }
                    }
                    if (isLabel)
                    {
                        var label = CurrentLine.Remove(length).ToLower();
                        label = label.ToLower();
                        if (label == "_")
                        {
                            // Relative
                            ExpressionEngine.RelativeLabels.Add(new RelativeLabel
                            {
                                Address = PC,
                                RootLineNumber = RootLineNumber
                            });
                            AddOutput(CodeType.Label);
                        }
                        else
                        {
                            bool local = label.StartsWith(".");
                            if (local)
                                label = label.Substring(1) + "@" + ExpressionEngine.LastGlobalLabel;
                            bool valid = true;
                            for (int k = 0; k < label.Length; k++) // Validate label
                            {
                                if (!char.IsLetterOrDigit(label[k]) && label[k] != '_')
                                {
                                    if (local && label[k] == '@')
                                        continue;
                                    valid = false;
                                    break;
                                }
                            }
                            if (!valid)
                                AddError(CodeType.Label, AssemblyError.InvalidLabel);
                            else if (ExpressionEngine.Symbols.ContainsKey(label.ToLower()))
                                AddError(CodeType.Label, AssemblyError.DuplicateName);
                            else
                            {
                                AddOutput(CodeType.Label);
                                ExpressionEngine.Symbols.Add(label.ToLower(), new Symbol(PC, true));
                                if (!local)
                                    ExpressionEngine.LastGlobalLabel = label.ToLower();
                            }
                        }
                        CurrentLine = CurrentLine.Substring(length + 1).Trim();
                    }
                }

                if (CurrentLine.StartsWith(":") || CurrentLine.EndsWith(":")) // Label
                {
                    string label;
                    if (CurrentLine.StartsWith(":"))
                        label = CurrentLine.Substring(1).Trim();
                    else
                        label = CurrentLine.Remove(CurrentLine.Length - 1).Trim();
                    label = label.ToLower();
                    if (label == "_")
                    {
                        // Relative
                        ExpressionEngine.RelativeLabels.Add(new RelativeLabel
                        {
                            Address = PC,
                            RootLineNumber = RootLineNumber
                        });
                        AddOutput(CodeType.Label);
                    }
                    else
                    {
                        bool local = label.StartsWith(".");
                        if (local)
                            label = label.Substring(1) + "@" + ExpressionEngine.LastGlobalLabel;
                        bool valid = true;
                        for (int k = 0; k < label.Length; k++) // Validate label
                        {
                            if (!char.IsLetterOrDigit(label[k]) && label[k] != '_')
                            {
                                if (local && label[k] == '@')
                                    continue;
                                valid = false;
                                break;
                            }
                        }
                        if (!valid)
                            AddError(CodeType.Label, AssemblyError.InvalidLabel);
                        else if (ExpressionEngine.Symbols.ContainsKey(label.ToLower()))
                            AddError(CodeType.Label, AssemblyError.DuplicateName);
                        else
                        {
                            AddOutput(CodeType.Label);
                            ExpressionEngine.Symbols.Add(label.ToLower(), new Symbol(PC, true));
                            if (!local)
                                ExpressionEngine.LastGlobalLabel = label.ToLower();
                        }
                    }
                    continue;
                }

                if (CurrentLine.SafeContains('\\'))
                {
                    // Split lines up
                    var split = CurrentLine.SafeSplit('\\');
                    Lines = Lines.Take(CurrentIndex).Concat(split).
                        Concat(Lines.Skip(CurrentIndex + 1)).ToArray();
                    SuspendedLines = split.Length;
                    CurrentIndex--;
                    continue;
                }

                if (CurrentLine.StartsWith(".") || CurrentLine.StartsWith("#")) // Directive
                {
                    // Some directives need to be handled higher up
                    var directive = CurrentLine.Substring(1).Trim();
                    string[] parameters = new string[0];
                    if (directive.SafeIndexOf(' ') != -1)
                        parameters = directive.Substring(directive.SafeIndexOf(' ')).Trim().SafeSplit(' ');
                    if (directive.ToLower().StartsWith("macro"))
                    {
                        var definitionLine = CurrentLine; // Used to update the listing later
                        if (parameters.Length == 0)
                        {
                            AddError(CodeType.Directive, AssemblyError.InvalidDirective);
                            continue;
                        }
                        string definition = directive.Substring(directive.SafeIndexOf(' ')).Trim();
                        var macro = new Macro();
                        if (definition.Contains("("))
                        {
                            var parameterDefinition = definition.Substring(definition.SafeIndexOf('(') + 1);
                            parameterDefinition = parameterDefinition.Remove(parameterDefinition.SafeIndexOf(')'));
                            // NOTE: This probably introduces the ability to use ".macro foo(bar)this_doesnt_cause_errors"
                            if (string.IsNullOrEmpty(parameterDefinition))
                                macro.Parameters = new string[0];
                            else
                                macro.Parameters = parameterDefinition.SafeSplit(',');
                            macro.Name = definition.Remove(definition.SafeIndexOf('(')).ToLower();
                        }
                        else
                            macro.Name = definition.ToLower(); // TODO: Consider enforcing character usage restrictions
                        for (CurrentIndex++; CurrentIndex < Lines.Length; CurrentIndex++)
                        {
                            CurrentLine = Lines[CurrentIndex].Trim().TrimComments();
                            LineNumbers.Push(LineNumbers.Pop() + 1);
                            RootLineNumber++;
                            if (CurrentLine == ".endmacro" || CurrentLine == "#endmacro")
                                break;
                            macro.Code += CurrentLine + Environment.NewLine;
                        }
                        macro.Code = macro.Code.Remove(macro.Code.Length - Environment.NewLine.Length);
                        macro.Name = macro.Name.ToLower();
                        if (Macros.Any(m => m.Name == macro.Name && m.Parameters.Length == macro.Parameters.Length))
                        {
                            AddError(CodeType.Label, AssemblyError.DuplicateName);
                            continue;
                        }
                        Macros.Add(macro);
                        // Add an entry to the listing
                        Output.Listing.Add(new Listing
                        {
                            Code = definitionLine,
                            CodeType = CodeType.Directive,
                            Error = AssemblyError.None,
                            Warning = AssemblyWarning.None,
                            Address = PC,
                            FileName = FileNames.Peek(),
                            LineNumber = LineNumbers.Peek(),
                            RootLineNumber = RootLineNumber
                        });
                    }
                    else
                    {
                        var result = HandleDirective(CurrentLine);
                        if (result != null)
                            Output.Listing.Add(result);
                    }
                    continue;
                }
                else
                {
                    if (string.IsNullOrEmpty(CurrentLine) || !Listing)
                        continue;
                    // Check instructions
                    var match = InstructionSet.Match(CurrentLine);
                    if (match == null)
                        AddError(CodeType.Instruction, AssemblyError.InvalidInstruction); // Unknown instruction
                    else
                    {
                        // Instruction to be fully assembled in the next pass
                        Output.Listing.Add(new Listing
                        {
                            Code = CurrentLine,
                            CodeType = CodeType.Instruction,
                            Error = AssemblyError.None,
                            Warning = AssemblyWarning.None,
                            Instruction = match,
                            Address = PC,
                            FileName = FileNames.Peek(),
                            LineNumber = LineNumbers.Peek(),
                            RootLineNumber = RootLineNumber
                        });
                        PC += match.Length;
                    }
                }
            }
            return Finish(Output);
        }
示例#2
0
 private Listing HandleDirective(string line, bool passTwo = false)
 {
     string directive = line.Substring(1).Trim();
     string[] parameters = new string[0];
     string parameter = "";
     if (directive.SafeContains(' '))
     {
         parameter = directive.Substring(directive.SafeIndexOf(' ') + 1);
         parameters = parameter.SafeSplit(' ');
         directive = directive.Remove(directive.SafeIndexOf(' '));
     }
     directive = directive.ToLower();
     var listing = new Listing
     {
         Code = line,
         CodeType = CodeType.Directive,
         Address = PC,
         Error = AssemblyError.None,
         Warning = AssemblyWarning.None,
         FileName = FileNames.Peek(),
         LineNumber = LineNumbers.Peek(),
         RootLineNumber = RootLineNumber
     };
     try
     {
         switch (directive)
         {
             case "block":
                 {
                     ulong amount = ExpressionEngine.Evaluate(parameter, PC, RootLineNumber);
                     listing.Output = new byte[amount];
                     PC += (uint)amount;
                     return listing;
                 }
             case "byte":
             case "db":
                 {
                     if (passTwo)
                     {
                         var result = new List<byte>();
                         parameters = parameter.SafeSplit(',');
                         foreach (var p in parameters)
                         {
                             if (p.Trim().StartsWith("\"") && p.Trim().EndsWith("\""))
                                 result.AddRange(
                                     Settings.Encoding.GetBytes(p.Trim().Substring(1, p.Trim().Length - 2).Unescape()));
                             else
                             {
                                 try
                                 {
                                     result.Add((byte)ExpressionEngine.Evaluate(p, PC++, RootLineNumber));
                                 }
                                 catch (KeyNotFoundException)
                                 {
                                     listing.Error = AssemblyError.UnknownSymbol;
                                 }
                             }
                         }
                         listing.Output = result.ToArray();
                         return listing;
                     }
                     else
                     {
                         parameters = parameter.SafeSplit(',');
                         int length = 0;
                         foreach (var p in parameters)
                         {
                             if (p.StartsWith("\"") && p.EndsWith("\""))
                                 length += p.Substring(1, p.Length - 2).Unescape().Length;
                             else
                                 length++;
                         }
                         listing.Output = new byte[length];
                         listing.PostponeEvalulation = true;
                         PC += (uint)listing.Output.Length;
                         return listing;
                     }
                 }
             case "word":
             case "dw":
                 {
                     if (passTwo)
                     {
                         var result = new List<byte>();
                         parameters = parameter.SafeSplit(',');
                         foreach (var item in parameters)
                             result.AddRange(TruncateWord(ExpressionEngine.Evaluate(item, PC++, RootLineNumber)));
                         listing.Output = result.ToArray();
                         return listing;
                     }
                     else
                     {
                         listing.Output = new byte[parameters.Length * (InstructionSet.WordSize / 8)];
                         listing.PostponeEvalulation = true;
                         PC += (uint)listing.Output.Length;
                         return listing;
                     }
                 }
             case "error":
             case "echo":
                 {
                     if (passTwo)
                     {
                         string output = "";
                         bool formatOutput = false;
                         List<object> formatParameters = new List<object>();
                         foreach (var item in parameters)
                         {
                             if (item.Trim().StartsWith("\"") && item.EndsWith("\""))
                             {
                                 output += item.Substring(1, item.Length - 2);
                                 formatOutput = true;
                             }
                             else
                             {
                                 if (!formatOutput)
                                     output += ExpressionEngine.Evaluate(item, PC, RootLineNumber);
                                 else
                                 {
                                     formatParameters.Add(ExpressionEngine.Evaluate(item, PC, RootLineNumber));
                                 }
                             }
                         }
                         if (formatOutput)
                             output = string.Format(output, formatParameters.ToArray());
                         Console.WriteLine((directive == "error" ? "User Error: " : "") + output);
                         return listing;
                     }
                     else
                     {
                         listing.PostponeEvalulation = true;
                         return listing;
                     }
                 }
                 break;
             case "end":
                 return listing;
             case "fill":
                 {
                     parameters = parameter.SafeSplit(',');
                     ulong amount = ExpressionEngine.Evaluate(parameters[0], PC, RootLineNumber);
                     if (parameters.Length == 1)
                     {
                         Array.Resize<string>(ref parameters, 2);
                         parameters[1] = "0";
                     }
                     listing.Output = new byte[amount];
                     for (int i = 0; i < (int)amount; i++)
                         listing.Output[i] = (byte)ExpressionEngine.Evaluate(parameters[1], PC++, RootLineNumber);
                     return listing;
                 }
             case "org":
                 PC = (uint)ExpressionEngine.Evaluate(parameter, PC, RootLineNumber);
                 return listing;
             case "include":
                 {
                     string file = GetIncludeFile(parameter);
                     if (file == null)
                     {
                         listing.Error = AssemblyError.FileNotFound;
                         return listing;
                     }
                     FileNames.Push(Path.GetFileName(parameter.Substring(1, parameter.Length - 2)));
                     LineNumbers.Push(0);
                     string includedFile = File.ReadAllText(file) + "\n.endfile";
                     string[] lines = includedFile.Replace("\r", "").Split('\n');
                     Lines =
                         Lines.Take(CurrentIndex + 1)
                              .Concat(lines)
                              .Concat(Lines.Skip(CurrentIndex + 1))
                              .ToArray();
                     return listing;
                 }
             case "endfile": // Special, undocumented directive
                 RootLineNumber--;
                 LineNumbers.Pop();
                 FileNames.Pop();
                 return null;
             case "equ":
                 if (parameters.Length == 1)
                 {
                     if (ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower()))
                     {
                         listing.Error = AssemblyError.DuplicateName;
                         return listing;
                     }
                     ExpressionEngine.Symbols.Add(parameters[0].ToLower(), new Symbol(1));
                 }
                 else
                 {
                     if (ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower()))
                     {
                         listing.Error = AssemblyError.DuplicateName;
                         return listing;
                     }
                     ExpressionEngine.Symbols.Add(parameters[0].ToLower(), new Symbol(
                                                                               (uint)
                                                                               ExpressionEngine.Evaluate(
                                                                                   parameter.Substring(
                                                                                       parameter.IndexOf(' ') + 1)
                                                                                            .Trim(), PC,
                                                                                   RootLineNumber)));
                 }
                 return listing;
             case "exec":
                 if (parameters.Length == 0 || !AllowExec)
                 {
                     listing.Error = AssemblyError.InvalidDirective;
                     return listing;
                 }
                 else
                 {
                     var process = new ProcessStartInfo(parameters[0], string.Join(" ", parameters.Skip(1).ToArray()));
                     process.UseShellExecute = false;
                     process.RedirectStandardOutput = true;
                     var p = Process.Start(process);
                     var output = p.StandardOutput.ReadToEnd().Trim('\n', '\r', ' ');
                     p.WaitForExit();
                     listing.Output = Settings.Encoding.GetBytes(output);
                     PC += (uint)listing.Output.Length;
                     return listing;
                 }
             case "define":
                 if (parameters.Length == 1)
                 {
                     if (ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower()))
                     {
                         listing.Error = AssemblyError.DuplicateName;
                         return listing;
                     }
                     ExpressionEngine.Symbols.Add(parameters[0].ToLower(), new Symbol(1));
                 }
                 else
                 {
                     var macro = new Macro();
                     if (parameter.Contains("("))
                     {
                         var parameterDefinition = parameter.Substring(parameter.SafeIndexOf('(') + 1);
                         parameterDefinition = parameterDefinition.Remove(parameterDefinition.SafeIndexOf(')'));
                         // NOTE: This probably introduces the ability to use ".macro foo(bar)this_doesnt_cause_errors"
                         macro.Parameters = parameterDefinition.SafeSplit(',');
                         for (int i = 0; i < macro.Parameters.Length; i++)
                             macro.Parameters[i] = macro.Parameters[i].Trim();
                         macro.Name = parameter.Remove(parameter.SafeIndexOf('('));
                         macro.Code = parameter.Substring(parameter.SafeIndexOf(')') + 1);
                     }
                     else
                     {
                         macro.Name = parameter.Remove(parameter.SafeIndexOf(' ') + 1);
                         // TODO: Consider enforcing character usage restrictions
                         macro.Code = parameter.Substring(parameter.SafeIndexOf(' ') + 1).Trim();
                     }
                     macro.Name = macro.Name.ToLower().Trim();
                     if (Macros.Any(m => m.Name == macro.Name && m.Parameters.Length == macro.Parameters.Length))
                     {
                         listing.Error = AssemblyError.DuplicateName;
                         return listing;
                     }
                     Macros.Add(macro);
                 }
                 return listing;
             case "if":
                 if (parameters.Length == 0)
                 {
                     listing.Error = AssemblyError.InvalidDirective;
                     return listing;
                 }
                 if (!IfStack.Peek())
                 {
                     WorkingIfStack.Push(false);
                     return listing;
                 }
                 try
                 {
                     IfStack.Push(ExpressionEngine.Evaluate(parameter, PC, RootLineNumber) != 0);
                 }
                 catch (InvalidOperationException)
                 {
                     listing.Error = AssemblyError.InvalidExpression;
                 }
                 catch (KeyNotFoundException)
                 {
                     listing.Error = AssemblyError.UnknownSymbol;
                 }
                 return listing;
             case "ifdef":
                 {
                     if (parameters.Length != 1)
                     {
                         listing.Error = AssemblyError.InvalidDirective;
                         return listing;
                     }
                     if (!IfStack.Peek())
                     {
                         WorkingIfStack.Push(false);
                         return listing;
                     }
                     var result = ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower());
                     if (!result)
                         result = Macros.Any(m => m.Name.ToLower() == parameters[0].ToLower());
                     IfStack.Push(result);
                     return listing;
                 }
             case "ifndef":
                 {
                     if (parameters.Length != 1)
                     {
                         listing.Error = AssemblyError.InvalidDirective;
                         return listing;
                     }
                     if (!IfStack.Peek())
                     {
                         WorkingIfStack.Push(false);
                         return listing;
                     }
                     var result = ExpressionEngine.Symbols.ContainsKey(parameters[0].ToLower());
                     if (!result)
                         result = Macros.Any(m => m.Name.ToLower() == parameters[0].ToLower());
                     IfStack.Push(!result);
                     return listing;
                 }
             case "endif":
                 if (parameters.Length != 0)
                 {
                     listing.Error = AssemblyError.InvalidDirective;
                     return listing;
                 }
                 if (IfStack.Count == 1)
                 {
                     listing.Error = AssemblyError.UncoupledStatement;
                     return listing;
                 }
                 if (WorkingIfStack.Any())
                 {
                     WorkingIfStack.Pop();
                     return listing;
                 }
                 IfStack.Pop();
                 return listing;
             case "else":
                 if (parameters.Length != 0)
                 {
                     listing.Error = AssemblyError.InvalidDirective;
                     return listing;
                 }
                 if (WorkingIfStack.Any())
                     return listing;
                 IfStack.Push(!IfStack.Pop());
                 return listing;
             //case "elif": // TODO: Requires major logic changes
             //case "elseif":
             //    if (IfStack.Peek())
             //    {
             //        IfStack.Pop();
             //        IfStack.Push(false);
             //        return listing;
             //    }
             //    return listing;
             case "ascii":
                 if (parameters.Length == 0)
                 {
                     listing.Error = AssemblyError.InvalidDirective;
                     return listing;
                 }
                 if (!(parameter.StartsWith("\"") && parameter.EndsWith("\"")))
                 {
                     listing.Error = AssemblyError.InvalidDirective;
                     return listing;
                 }
                 parameter = parameter.Substring(1, parameter.Length - 2);
                 listing.Output = Settings.Encoding.GetBytes(parameter.Unescape());
                 return listing;
             case "asciiz":
                 if (passTwo)
                 {
                     var result = new List<byte>();
                     parameters = parameter.SafeSplit(',');
                     foreach (var p in parameters)
                     {
                         if (p.Trim().StartsWith("\"") && p.Trim().EndsWith("\""))
                             result.AddRange(Settings.Encoding.GetBytes(p.Trim().Substring(1, p.Length - 2).Unescape()));
                         else
                             listing.Error = AssemblyError.InvalidDirective;
                     }
                     listing.Output = new byte[result.Count + 1];
                     Array.Copy(result.ToArray(), listing.Output, result.Count);
                     listing.Output[listing.Output.Length - 1] = 0;
                     return listing;
                 }
                 else
                 {
                     parameters = parameter.SafeSplit(',');
                     int length = 0;
                     foreach (var p in parameters)
                     {
                         if (p.StartsWith("\"") && p.EndsWith("\""))
                             length += p.Substring(1, p.Length - 2).Unescape().Length;
                         else
                             listing.Error = AssemblyError.InvalidDirective;
                     }
                     length++;
                     listing.Output = new byte[length];
                     listing.PostponeEvalulation = true;
                     PC += (uint)listing.Output.Length;
                     return listing;
                 }
                 break;
             case "asciip":
                 if (passTwo)
                 {
                     var result = new List<byte>();
                     parameters = parameter.SafeSplit(',');
                     foreach (var p in parameters)
                     {
                         if (p.Trim().StartsWith("\"") && p.Trim().EndsWith("\""))
                             result.AddRange(Settings.Encoding.GetBytes(p.Trim().Substring(1, p.Length - 2).Unescape()));
                         else
                             listing.Error = AssemblyError.InvalidDirective;
                     }
                     listing.Output = new byte[result.Count + 1];
                     listing.Output[0] = (byte)result.Count;
                     Array.Copy(result.ToArray(), 0, listing.Output, 1, result.Count);
                     return listing;
                 }
                 else
                 {
                     parameters = parameter.SafeSplit(',');
                     int length = 0;
                     foreach (var p in parameters)
                     {
                         if (p.StartsWith("\"") && p.EndsWith("\""))
                             length += p.Substring(1, p.Length - 2).Unescape().Length;
                         else
                             listing.Error = AssemblyError.InvalidDirective;
                     }
                     length++;
                     listing.Output = new byte[length];
                     listing.PostponeEvalulation = true;
                     PC += (uint)listing.Output.Length;
                     return listing;
                 }
                 break;
             case "nolist":
                 Listing = false;
                 return listing;
             case "list":
                 Listing = true;
                 return listing;
             case "undefine":
                 if (parameters.Length == 0)
                 {
                     listing.Error = AssemblyError.InvalidDirective;
                     return listing;
                 }
                 foreach (var item in parameters)
                 {
                     if (Macros.Any(m => m.Name == item.ToLower()))
                         Macros.Remove(Macros.FirstOrDefault(m => m.Name == item));
                     else if (ExpressionEngine.Symbols.ContainsKey(item.ToLower()))
                         ExpressionEngine.Symbols.Remove(item.ToLower());
                     else
                     {
                         listing.Error = AssemblyError.UnknownSymbol;
                         return listing;
                     }
                 }
                 return listing;
         }
     }
     catch (KeyNotFoundException)
     {
         listing.Error = AssemblyError.UnknownSymbol;
         return listing;
     }
     catch (InvalidOperationException)
     {
         listing.Error = AssemblyError.InvalidExpression;
         return listing;
     }
     return null;
 }
示例#3
0
        public AssemblyOutput Assemble(string assembly, string fileName = null)
        {
            var output = new AssemblyOutput();
            assembly = assembly.Replace("\r", "");
            PC = 0;
            Lines = assembly.Split('\n');
            FileNames.Push(fileName);
            LineNumbers.Push(0);
            RootLineNumber = 0;
            for (CurrentIndex = 0; CurrentIndex < Lines.Length; CurrentIndex++)
            {
                string line = Lines[CurrentIndex].Trim().TrimComments().ToLower();
                if (SuspendedLines == 0)
                {
                    LineNumbers.Push(LineNumbers.Pop() + 1);
                    RootLineNumber++;
                }
                else
                    SuspendedLines--;

                if (line.SafeContains('\\'))
                {
                    // Split lines up
                    var split = line.SafeSplit('\\');
                    Lines = Lines.Take(CurrentIndex).Concat(split).
                        Concat(Lines.Skip(CurrentIndex + 1)).ToArray();
                    SuspendedLines = split.Length;
                    CurrentIndex--;
                    continue;
                }

                if (line.SafeContains(".equ") && !line.StartsWith(".equ"))
                {
                    var name = line.Remove(line.SafeIndexOf(".equ"));
                    var definition = line.Substring(line.SafeIndexOf(".equ") + 4);
                    line = ".equ " + name.Trim() + ", " + definition.Trim();
                }

                if (line.StartsWith(".") || line.StartsWith("#")) // Directive
                {
                    // Some directives need to be handled higher up
                    var directive = line.Substring(1).Trim().ToLower();
                    string[] parameters = new string[0];
                    if (directive.SafeIndexOf(' ') != -1)
                        parameters = directive.Substring(directive.SafeIndexOf(' ')).Split(',');
                    if (directive.StartsWith("macro"))
                    {
                        var definitionLine = line; // Used to update the listing later
                        if (parameters.Length == 0)
                        {
                            output.Listing.Add(new Listing
                            {
                                Code = line,
                                CodeType = CodeType.Directive,
                                Error = AssemblyError.InvalidDirective,
                                Warning = AssemblyWarning.None,
                                Address = PC,
                                FileName = FileNames.Peek(),
                                LineNumber = LineNumbers.Peek(),
                                RootLineNumber = RootLineNumber
                            });
                            continue;
                        }
                        string definition = directive.Substring(directive.SafeIndexOf(' ')).Trim();
                        var macro = new Macro();
                        if (definition.Contains("("))
                        {
                            var parameterDefinition = definition.Substring(definition.SafeIndexOf('(') + 1);
                            parameterDefinition = parameterDefinition.Remove(parameterDefinition.SafeIndexOf(')'));
                            // NOTE: This probably introduces the ability to use ".macro foo(bar)this_doesnt_cause_errors"
                            macro.Parameters = parameterDefinition.SafeSplit(',');
                            macro.Name = definition.Remove(definition.SafeIndexOf('('));
                        }
                        else
                            macro.Name = definition; // TODO: Consider enforcing character usage restrictions
                        for (CurrentIndex++; CurrentIndex < Lines.Length; CurrentIndex++)
                        {
                            line = Lines[CurrentIndex].Trim().TrimComments();
                            LineNumbers.Push(LineNumbers.Pop() + 1);
                            RootLineNumber++;
                            if (line == ".endmacro" || line == "#endmacro")
                                break;
                            macro.Code += line + Environment.NewLine;
                        }
                        macro.Code = macro.Code.Remove(macro.Code.Length - Environment.NewLine.Length);
                        macro.Name = macro.Name.ToLower();
                        if (Macros.Any(m => m.Name == macro.Name))
                        {
                            output.Listing.Add(new Listing
                            {
                                Code = line,
                                CodeType = CodeType.Directive,
                                Error = AssemblyError.DuplicateName,
                                Warning = AssemblyWarning.None,
                                Address = PC,
                                FileName = FileNames.Peek(),
                                LineNumber = LineNumbers.Peek(),
                                RootLineNumber = RootLineNumber
                            });
                            continue;
                        }
                        Macros.Add(macro);
                        // Add an entry to the listing
                        output.Listing.Add(new Listing
                        {
                            Code = definitionLine,
                            CodeType = CodeType.Directive,
                            Error = AssemblyError.None,
                            Warning = AssemblyWarning.None,
                            Address = PC,
                            FileName = FileNames.Peek(),
                            LineNumber = LineNumbers.Peek(),
                            RootLineNumber = RootLineNumber
                        });
                    }
                    else if (directive.StartsWith("include"))
                    {

                    }
                    else
                    {
                        var result = HandleDirective(line);
                        if (result != null)
                            output.Listing.Add(result);
                    }
                    continue;
                }
                else if (line.StartsWith(":") || line.EndsWith(":")) // Label
                {
                    string label;
                    if (line.StartsWith(":"))
                        label = line.Substring(1).Trim();
                    else
                        label = line.Remove(line.Length - 1).Trim();
                    label = label.ToLower();
                    bool valid = true;
                    for (int k = 0; k < label.Length; k++) // Validate label
                    {
                        if (!char.IsLetterOrDigit(label[k]) && k != '_')
                        {
                            valid = false;
                            break;
                        }
                    }
                    if (!valid)
                    {
                        output.Listing.Add(new Listing
                        {
                            Code = line,
                            CodeType = CodeType.Label,
                            Error = AssemblyError.InvalidLabel,
                            Warning = AssemblyWarning.None,
                            Address = PC,
                            FileName = FileNames.Peek(),
                            LineNumber = LineNumbers.Peek(),
                            RootLineNumber = RootLineNumber
                        });
                    }
                    output.Listing.Add(new Listing
                    {
                        Code = line,
                        CodeType = CodeType.Label,
                        Error = AssemblyError.None,
                        Warning = AssemblyWarning.None,
                        Address = PC,
                        FileName = FileNames.Peek(),
                        LineNumber = LineNumbers.Peek(),
                        RootLineNumber = RootLineNumber
                    });
                    ExpressionEngine.Equates.Add(label, PC);
                }
                else
                {
                    // Check for macro
                    Macro macroMatch = null;
                    string[] parameters = null;
                    string parameterDefinition = null;
                    foreach (var macro in Macros)
                    {
                        if (line.SafeContains(macro.Name))
                        {
                            // Try to match
                            int startIndex = line.SafeIndexOf(macro.Name);
                            int endIndex = startIndex + macro.Name.Length - 1;
                            if (macro.Parameters.Length != 0)
                            {
                                if (line[endIndex + 1] != '(')
                                    continue;
                                parameterDefinition = line.Substring(endIndex + 2, line.SafeIndexOf(')') - (endIndex + 2));
                                parameters = parameterDefinition.SafeSplit(',');
                                if (parameters.Length != macro.Parameters.Length)
                                    continue;
                                // Matched
                                macroMatch = macro;
                                break;
                            }
                        }
                    }
                    if (macroMatch != null)
                    {
                        // Add an entry to the listing
                        output.Listing.Add(new Listing
                        {
                            Code = line,
                            CodeType = CodeType.Directive,
                            Error = AssemblyError.None,
                            Warning = AssemblyWarning.None,
                            Address = PC,
                            FileName = FileNames.Peek(),
                            LineNumber = LineNumbers.Peek(),
                            RootLineNumber = RootLineNumber
                        });
                        var code = macroMatch.Code;
                        int index = 0;
                        foreach (var parameter in macroMatch.Parameters)
                            code = code.Replace(parameter, parameters[index++]);
                        string newLine;
                        if (parameterDefinition != null)
                            newLine = line.Replace(macroMatch.Name + "(" + parameterDefinition + ")", code);
                        else
                            newLine = line.Replace(macroMatch.Name, code);
                        var newLines = newLine.Replace("\r\n", "\n").Split('\n');
                        SuspendedLines += newLines.Length;
                        // Insert macro
                        Lines = Lines.Take(CurrentIndex).Concat(newLines).Concat(Lines.Skip(CurrentIndex + 1)).ToArray();
                        CurrentIndex--;
                        continue;
                    }
                    if (string.IsNullOrEmpty(line))
                        continue;
                    // Check instructions
                    var match = InstructionSet.Match(line);
                    if (match == null)
                    {
                        // Unknown instruction
                        output.Listing.Add(new Listing
                        {
                            Code = line,
                            CodeType = CodeType.Instruction,
                            Error = AssemblyError.InvalidInstruction,
                            Warning = AssemblyWarning.None,
                            Instruction = match,
                            Address = PC,
                            FileName = FileNames.Peek(),
                            LineNumber = LineNumbers.Peek(),
                            RootLineNumber = RootLineNumber
                        });
                    }
                    else
                    {
                        // Instruction to be fully assembled in the next pass
                        output.Listing.Add(new Listing
                        {
                            Code = line,
                            CodeType = CodeType.Instruction,
                            Error = AssemblyError.None,
                            Warning = AssemblyWarning.None,
                            Instruction = match,
                            Address = PC,
                            FileName = FileNames.Peek(),
                            LineNumber = LineNumbers.Peek(),
                            RootLineNumber = RootLineNumber
                        });
                        PC += match.Length;
                    }
                }
            }
            return Finish(output);
        }