Esempio n. 1
0
 void assembler_HandleCodeLine(object sender, HandleCodeEventArgs e)
 {
     // These things aren't really relevant to a DCPU-16 program, so
     // we just discard them so it doesn't throw an error.
     if (e.Code == ".text")
     {
         e.Handled = true;
         e.Output.CodeType = CodeType.Directive;
         e.Output.Output = new ushort[0];
     }
     if (e.Code.StartsWith(".globl"))
     {
         e.Handled = true;
         e.Output.CodeType = CodeType.Directive;
         e.Output.Output = new ushort[0];
     }
     if (e.Code == ".data")
     {
         e.Handled = true;
         e.Output.CodeType = CodeType.Directive;
         e.Output.Output = new ushort[0];
     }
     if (e.Code.StartsWith(".short "))
     {
         var expression = assembler.ParseExpression(e.Code.Substring(7)); // TODO: Postpone evalulation?
         e.Output.Output = new[] { expression.Value };
         e.Output.CodeType = CodeType.Directive;
         e.Handled = true;
     }
     if (e.Code.StartsWith(".byte "))
     {
         var expression = assembler.ParseExpression(e.Code.Substring(5));
         e.Output.Output = new[] { expression.Value };
         e.Output.CodeType = CodeType.Directive;
         e.Handled = true;
     }
     if (e.Code.StartsWith(".comm "))
     {
         e.Code = e.Code.Replace(".comm", ".equ").Replace(",", " ");
     }
 }
Esempio n. 2
0
        /// <summary>
        /// Assembles the provided code.
        /// This will use the current directory to fetch include files and such.
        /// </summary>
        /// <returns>A listing for the code</returns>
        public List<ListEntry> Assemble(string code, string FileName)
        {
            FileNames = new Stack<string>();
            LineNumbers = new Stack<int>();
            FileNames.Push(FileName);
            LineNumbers.Push(0);
            RootLineNumber = 0;
            IfStack.Push(true);
            bool isFromExpanded;

            // Pass one
            string[] lines = code.Replace("\r", "").Split('\n');
            List<ListEntry> output = new List<ListEntry>();
            for (int i = 0; i < lines.Length; i++)
            {
                isFromExpanded = false;
                if (SuspendedLineCounts.Count == 0)
                {
                    LineNumbers.Push(LineNumbers.Pop() + 1);
                    RootLineNumber++;
                }
                else
                {
                    isFromExpanded = true;
                    int count = SuspendedLineCounts.Pop();
                    count--;
                    if (count > 0)
                        SuspendedLineCounts.Push(count);
                }

                string line = lines[i].TrimComments().TrimExcessWhitespace();
                if (string.IsNullOrEmpty(line))
                    continue;
                string[] sublines = line.SafeSplit('\\');
                if (sublines.Length > 1)
                {
                    string[] newLines = new string[lines.Length + sublines.Length - 1];
                    Array.Copy(lines, 0, newLines, 0, i);
                    Array.Copy(sublines, 0, newLines, i, sublines.Length);
                    if (lines.Length > i + 1)
                        Array.Copy(lines, i + 1, newLines, i + sublines.Length, lines.Length - i - 1);
                    lines = newLines;
                    i--;
                    SuspendedLineCounts.Push(sublines.Length);
                    continue;
                }
                ListEntry listEntry = new ListEntry(line, FileNames.Peek(), LineNumbers.Peek(), currentAddress, !noList, isFromExpanded);
                listEntry.RootLineNumber = RootLineNumber;
                if (HandleCodeLine != null)
                {
                    HandleCodeEventArgs args = new HandleCodeEventArgs();
                    args.Code = line;
                    args.Handled = false;
                    args.Output = listEntry;
                    HandleCodeLine(this, args);
                    if (args.Handled)
                    {
                        output.Add(args.Output);
                        continue;
                    }
                    listEntry = args.Output;
                    line = args.Code;
                }
                if (line.SafeContains(':') && !noList)
                {
                    if (!IfStack.Peek())
                        continue;
                    listEntry.CodeType = CodeType.Directive;
                    // Parse labels
                    string label = line;
                    if (line.StartsWith(":"))
                    {
                        label = label.Substring(1);
                        if (line.Contains(' '))
                            line = line.Substring(line.IndexOf(' ') + 1).Trim();
                        else
                            line = "";
                    }
                    else
                    {
                        label = label.Remove(label.IndexOf(':'));
                        line = line.Substring(line.IndexOf(':') + 1);
                    }
                    line = line.Trim();
                    if (label.Contains(" "))
                        label = label.Remove(label.IndexOf(' '));
                    if (label == "$")
                    {
                        RelativeLabels.Add(GetRootNumber(LineNumbers), currentAddress);
                        output.Add(listEntry);
                        continue;
                    }
                    if (label.Contains(' ') || label.Contains('\t') || !(char.IsLetter(label[0]) || label[0] == '.' || label[0] == '_'))
                    {
                        listEntry.ErrorCode = ErrorCode.InvalidLabel;
                        output.Add(listEntry);
                        continue;
                    }
                    bool invalid = false;
                    if (label.StartsWith("_"))
                    {
                        listEntry.ErrorCode = ErrorCode.InvalidLabel;
                        output.Add(listEntry);
                        continue;
                    }
                    foreach (char c in label)
                    {
                        if (!char.IsLetterOrDigit(c) && c != '_' && c != '.')
                        {
                            listEntry.ErrorCode = ErrorCode.InvalidLabel;
                            output.Add(listEntry);
                            invalid = true;
                            break;
                        }
                    }
                    if (invalid)
                        continue;
                    if (Values.ContainsKey(label) || LabelValues.ContainsKey(label))
                    {
                        listEntry.ErrorCode = ErrorCode.DuplicateName;
                        output.Add(listEntry);
                        continue;
                    }
                    if (label.StartsWith("."))
                        label = PriorGlobalLabel + "_" + label.Substring(1);
                    else
                        PriorGlobalLabel = label;
                    LabelValues.Add(new Label()
                    {
                        LineNumber = LineNumbers.Peek(),
                        Name = label,
                        RootLineNumber = listEntry.RootLineNumber,
                        Address = currentAddress,
                    });
                    if (!IsRelocating)
                        LabelValues[LabelValues.Count - 1].RelocationGroup = -1;
                    else
                        LabelValues[LabelValues.Count - 1].RelocationGroup = RelocationGroup;
                    listEntry.CodeType = CodeType.Label;
                    output.Add(listEntry);
                }
                if (string.IsNullOrEmpty(line))
                    continue;
                if (line.Contains(".equ") && !line.StartsWith(".equ")) // TASM compatibility
                {
                    line = ".equ " + line.Replace(".equ", "").TrimExcessWhitespace();
                }
                if (line.StartsWith("dat "))
                {
                    line = "." + line;
                }
                if (line.StartsWith(".") || line.StartsWith("#"))
                {
                    // #include has to be handled in this method
                    if (line.StartsWith("#include") || line.StartsWith(".include"))
                    {
                        if (!IfStack.Peek())
                            continue;
                        string includedFileName = line.Substring(line.IndexOf(" ") + 1);
                        includedFileName = includedFileName.Trim('"', '\'');
                        if (includedFileName.StartsWith("<") && includedFileName.EndsWith(">"))
                        {
                            // Find included file
                            includedFileName = includedFileName.Trim('<', '>');
                            string[] paths = IncludePath.Split(new []{';'}, StringSplitOptions.RemoveEmptyEntries );
                            foreach (var path in paths)
                            {
                                if (File.Exists(Path.Combine(path, includedFileName)))
                                {
                                    includedFileName = Path.Combine(path, includedFileName);
                                    break;
                                }
                            }
                        }
                        if (!File.Exists(includedFileName))
                        {
                            listEntry.ErrorCode = ErrorCode.FileNotFound;
                            output.Add(listEntry);
                        }
                        else if (IncludedFiles.Contains(includedFileName))
                        {

                        }
                        else
                        {
                            using (Stream includedFile = File.Open(includedFileName, FileMode.Open))
                            {
                                StreamReader sr = new StreamReader(includedFile);
                                string contents = sr.ReadToEnd();
                                sr.Close();

                                string[] newSource = contents.Replace("\r", "").Split('\n');
                                string[] newLines = new string[newSource.Length + lines.Length];
                                Array.Copy(lines, newLines, i);
                                Array.Copy(newSource, 0, newLines, i, newSource.Length);
                                newLines[i + newSource.Length] = "#endfile";
                                if (lines.Length > i + 1)
                                    Array.Copy(lines, i + 1, newLines, i + newSource.Length + 1, lines.Length - i - 1);
                                lines = newLines;
                            }
                            WorkingDirectories.Push(Directory.GetCurrentDirectory());
                            if (Path.IsPathRooted(includedFileName))
                                Directory.SetCurrentDirectory(GetDirectory(includedFileName));
                            else
                                Directory.SetCurrentDirectory(Path.Combine(Directory.GetCurrentDirectory(),
                                    GetDirectory(includedFileName)));
                            FileNames.Push(includedFileName);
                            LineNumbers.Push(0);
                            IncludedFiles.Add(includedFileName);
                            i--;
                            continue;
                        }
                    }
                    else if ((line.StartsWith("#incbin") || line.StartsWith(".incbin")) && !noList)
                    {
                        if (!IfStack.Peek())
                            continue;
                        string includedFileName = line.Substring(line.IndexOf(" ") + 1);
                        includedFileName = includedFileName.Trim('"', '\'');
                        if (includedFileName.StartsWith("<") && includedFileName.EndsWith(">"))
                        {
                            // Find included file
                            includedFileName = includedFileName.Trim('<', '>');
                            string[] paths = IncludePath.Split(';');
                            foreach (var path in paths)
                            {
                                if (File.Exists(Path.Combine(path, includedFileName)))
                                {
                                    includedFileName = Path.Combine(path, includedFileName);
                                    break;
                                }
                            }
                        }
                        if (!File.Exists(includedFileName))
                        {
                            listEntry.ErrorCode = ErrorCode.FileNotFound;
                            output.Add(listEntry);
                        }
                        else
                        {
                            using (Stream includedFile = File.Open(includedFileName, FileMode.Open))
                            {
                                byte[] rawData = new byte[includedFile.Length];
                                includedFile.Read(rawData, 0, (int)includedFile.Length);

                                List<ushort> binOutput = new List<ushort>();
                                foreach (byte b in rawData)
                                    binOutput.Add(b);
                                listEntry.Output = binOutput.ToArray();
                                output.Add(listEntry);
                                output[output.Count - 1].CodeType = CodeType.Directive;
                                if (!noList)
                                    currentAddress += (ushort)binOutput.Count;
                            }
                        }
                    }
                    else if ((line.StartsWith("#incpack") || line.StartsWith(".incpack")) && !noList)
                    {
                        if (!IfStack.Peek())
                            continue;
                        string includedFileName = line.Substring(line.IndexOf(" ") + 1);
                        includedFileName = includedFileName.Trim('"', '\'');
                        if (includedFileName.StartsWith("<") && includedFileName.EndsWith(">"))
                        {
                            // Find included file
                            includedFileName = includedFileName.Trim('<', '>');
                            string[] paths = IncludePath.Split(';');
                            foreach (var path in paths)
                            {
                                if (File.Exists(Path.Combine(path, includedFileName)))
                                {
                                    includedFileName = Path.Combine(path, includedFileName);
                                    break;
                                }
                            }
                        }
                        if (!File.Exists(includedFileName))
                        {
                            listEntry.ErrorCode = ErrorCode.FileNotFound;
                            output.Add(listEntry);
                        }
                        else
                        {
                            using (Stream includedFile = File.Open(includedFileName, FileMode.Open))
                            {
                                byte[] rawData = new byte[includedFile.Length];
                                includedFile.Read(rawData, 0, (int)includedFile.Length);

                                List<ushort> binOutput = new List<ushort>();
                                for (int j = 0; j < rawData.Length; j += 2)
                                {
                                    binOutput.Add((ushort)(
                                        rawData[j + 1] |
                                        (rawData[j] << 8)
                                        ));
                                }
                                listEntry.Output = binOutput.ToArray();
                                output.Add(listEntry);
                                output[output.Count - 1].CodeType = CodeType.Directive;
                                if (!noList)
                                    currentAddress += (ushort)binOutput.Count;
                            }
                        }
                    }
                    else if (line == "#endfile" || line == ".endfile")
                    {
                        if (!IfStack.Peek())
                            continue;
                        FileNames.Pop();
                        LineNumbers.Pop();
                        RootLineNumber--;
                        Directory.SetCurrentDirectory(WorkingDirectories.Pop());
                    }
                    else if (line.StartsWith(".macro") && !noList)
                    {
                        if (!IfStack.Peek())
                            continue;
                        string macroDefinition = line.Substring(7).Trim();
                        Macro macro = new Macro();
                        macro.Args = new string[0];
                        if (macroDefinition.EndsWith("{"))
                            macroDefinition = macroDefinition.Remove(macroDefinition.Length - 1).Trim();
                        if (macroDefinition.Contains("("))
                        {
                            string paramDefinition = macroDefinition.Substring(macroDefinition.IndexOf("(") + 1);
                            macro.Name = macroDefinition.Remove(macroDefinition.IndexOf("(")).Trim();
                            if (!paramDefinition.EndsWith(")"))
                            {
                                listEntry.ErrorCode = ErrorCode.InvalidMacroDefintion;
                                output.Add(listEntry);
                            }
                            else
                            {
                                paramDefinition = paramDefinition.Remove(paramDefinition.Length - 1);
                                if (paramDefinition.Length > 0)
                                {
                                    string[] parameters = paramDefinition.Split(',');
                                    bool continueEvaluation = true;
                                    for (int j = 0; j < parameters.Length; j++)
                                    {
                                        string parameter = parameters[j].Trim();
                                        if (!char.IsLetter(parameter[0]))
                                        {
                                            continueEvaluation = false;
                                            break;
                                        }
                                        foreach (char c in parameter)
                                        {
                                            if (!char.IsLetterOrDigit(c) && c != '_')
                                            {
                                                continueEvaluation = false;
                                                break;
                                            }
                                        }
                                        if (!continueEvaluation)
                                            break;
                                        macro.Args = macro.Args.Concat(new string[] { parameter }).ToArray();
                                    }
                                    if (!continueEvaluation)
                                        continue;
                                }
                            }
                        }
                        else
                            macro.Name = macroDefinition;
                        // Isolate macro code
                        macro.Code = "";
                        bool foundEndmacro = false;
                        string macroLine = line;
                        i++;
                        for (; i < lines.Length; i++)
                        {
                            line = lines[i].TrimComments().TrimExcessWhitespace();
                            LineNumbers.Push(LineNumbers.Pop() + 1);
                            if (line == ".endmacro" || line == "#endmacro" || line == "}")
                            {
                                foundEndmacro = true;
                                break;
                            }
                            if (line != "{")
                                macro.Code += "\n" + line;
                        }
                        if (!foundEndmacro)
                        {
                            listEntry.ErrorCode = ErrorCode.UncoupledStatement;
                            output.Add(listEntry);
                            continue;
                        }
                        macro.Code = macro.Code.Trim('\n');
                        Macros.Add(macro);
                        output.Add(new ListEntry(".macro " + macroDefinition, FileNames.Peek(), LineNumbers.Peek(), currentAddress, isFromExpanded));
                        output[output.Count - 1].CodeType = CodeType.Directive;
                        foreach (var codeLine in macro.Code.Split('\n'))
                        {
                            output.Add( new ListEntry( codeLine, FileNames.Peek(), LineNumbers.Peek(), currentAddress, isFromExpanded ) );
                            output[output.Count - 1].CodeType = CodeType.Directive;
                        }
                        output.Add( new ListEntry( ".endmacro", FileNames.Peek(), LineNumbers.Peek(), currentAddress, isFromExpanded ) );
                        output[output.Count - 1].CodeType = CodeType.Directive;
                    }
                    else
                    {
                        // Parse preprocessor directives
                        ParseDirectives( output, line, isFromExpanded );
                    }
                }
                else
                {
                    if (!IfStack.Peek())
                        continue;
                    // Search through macros
                    bool mayHaveMacro = false;
                    foreach (Macro macro in Macros)
                    {
                        if (line.StartsWith(macro.Name))
                        {
                            mayHaveMacro = true;
                            break;
                        }
                    }
                    if (line.SafeContains('(') && line.SafeContains(')') && mayHaveMacro)
                    {
                        Macro userMacro = new Macro();
                        userMacro.Args = new string[0];
                        string macroDefinition = line;
                        string paramDefinition = macroDefinition.Substring(macroDefinition.IndexOf("(") + 1);
                        userMacro.Name = macroDefinition.Remove(macroDefinition.IndexOf("(")).Trim();
                        if (!paramDefinition.EndsWith(")"))
                        {
                            listEntry.ErrorCode = ErrorCode.InvalidMacroDefintion;
                            output.Add(listEntry);
                        }
                        else
                        {
                            paramDefinition = paramDefinition.Remove(paramDefinition.Length - 1);
                            if (paramDefinition.Length > 0)
                            {
                                string[] parameters = paramDefinition.SafeSplit(',');
                                for (int j = 0; j < parameters.Length; j++)
                                {
                                    string parameter = parameters[j].Trim();
                                    userMacro.Args = userMacro.Args.Concat(new[] { parameter }).ToArray();
                                }
                            }
                        }
                        bool macroMatched = false;
                        foreach (Macro macro in Macros)
                        {
                            if (macro.Name == userMacro.Name &&
                                macro.Args.Length == userMacro.Args.Length)
                            {
                                // Expand the macro
                                userMacro.Code = macro.Code;
                                for (int j = 0; j < macro.Args.Length; j++)
                                    userMacro.Code = userMacro.Code.Replace(macro.Args[j], userMacro.Args[j]);
                                string[] macroCode = userMacro.Code.Replace("\r", "\n").Split('\n');
                                string[] newLines = new string[lines.Length + macroCode.Length - 1];
                                Array.Copy(lines, 0, newLines, 0, i);
                                Array.Copy(macroCode, 0, newLines, i, macroCode.Length);
                                if (lines.Length > i + 1)
                                    Array.Copy(lines, i + 1, newLines, i + macroCode.Length, lines.Length - i - 1);
                                lines = newLines;
                                output.Add(listEntry);
                                output[output.Count - 1].CodeType = CodeType.Directive;
                                line = lines[i].TrimComments().TrimExcessWhitespace();
                                macroMatched = true;
                                SuspendedLineCounts.Push(macroCode.Length); // Suspend the line counts for the expanded macro
                            }
                        }
                        if (macroMatched)
                        {
                            i--;
                            continue;
                        }
                        // We'll just let the opcode matcher yell at them if it isn't found
                    }

                    // Check for OPCodes
                    var opcode = MatchString(line, OpcodeTable);
                    bool nonBasic = false;
                    if (opcode == null)
                    {
                        opcode = MatchString(line, NonBasicOpcodeTable);
                        nonBasic = true;
                    }
                    if (opcode == null)
                    {
                        listEntry.ErrorCode = ErrorCode.InvalidOpcode;
                        output.Add(listEntry);
                        continue;
                    }
                    else
                    {
                        listEntry.Opcode = opcode;
                        StringMatch valueA = null, valueB = null;
                        listEntry.Output = new ushort[1];
                        if (!nonBasic)
                        {
                            listEntry.CodeType = CodeType.BasicInstruction;
                            if (opcode.valueA != null)
                                valueA = MatchString(opcode.valueA, ValueTable);
                            if (opcode.valueB != null)
                                valueB = MatchString(opcode.valueB, ValueTable);
                            if (nonBasic == false && (opcode.valueA == null || opcode.valueB == null))
                            {
                                listEntry.ErrorCode = ErrorCode.InvalidOpcode;
                                output.Add(listEntry);
                                continue;
                            }
                            if (valueA.value == valueB.value && valueA.value != 0x1E && valueB.value != 0x1E && opcode.value == 0x1)
                                listEntry.WarningCode = WarningCode.RedundantStatement;
                            if (valueB.value == 0x1F && !opcode.match.Contains("IF"))
                                listEntry.WarningCode = WarningCode.AssignToLiteral;
                            listEntry.ValueA = valueA;
                            listEntry.ValueB = valueB;
                            // De-localize labels
                            if (listEntry.ValueA.isLiteral)
                            {
                                listEntry.Output = listEntry.Output.Concat(new ushort[1]).ToArray();
                                var result = ParseExpression(listEntry.ValueA.literal);
                                foreach (var reference in result.References)
                                {
                                    if (reference.StartsWith("."))
                                        listEntry.ValueA.literal = listEntry.ValueA.literal.Replace(reference,
                                            PriorGlobalLabel + "_" + reference.Substring(1));
                                }
                            }
                            if (listEntry.ValueB.isLiteral)
                            {
                                listEntry.Output = listEntry.Output.Concat(new ushort[1]).ToArray();
                                var result = ParseExpression(listEntry.ValueB.literal);
                                foreach (var reference in result.References)
                                {
                                    if (reference.StartsWith("."))
                                        listEntry.ValueB.literal = listEntry.ValueB.literal.Replace(reference,
                                            PriorGlobalLabel + "_" + reference.Substring(1));
                                }
                            }
                        }
                        else
                        {
                            listEntry.CodeType = CodeType.NonBasicInstruction;
                            if (opcode.valueA != null)
                                valueA = MatchString(opcode.valueA, ValueTable);
                            listEntry.ValueA = valueA;
                            // De-localize labels
                            if (listEntry.ValueA.isLiteral)
                            {
                                listEntry.Output = listEntry.Output.Concat(new ushort[1]).ToArray();
                                var result = ParseExpression(listEntry.ValueA.literal);
                                foreach (var reference in result.References)
                                {
                                    if (reference.StartsWith(".") || reference.StartsWith("_"))
                                        listEntry.ValueA.literal = listEntry.ValueA.literal.Replace(reference,
                                            PriorGlobalLabel + "_" + reference.Substring(1));
                                }
                            }
                        }
                        output.Add(listEntry);
                        currentAddress++;
                        if (valueA != null)
                            if (valueA.isLiteral)
                                currentAddress++;
                        if (valueB != null)
                            if (valueB.isLiteral)
                                currentAddress++;
                    }
                }
            }
            return EvaluateAssembly(output);
        }
        void assembler_HandleCodeLine(object sender, HandleCodeEventArgs e)
        {
            // Handle custom directives
            if (e.Code.ToLower().StartsWith(".prereq"))
            {
                CurrentSection = e.Code.Substring(7).Trim();
                e.Handled = true;
            }
            else if (e.Code.ToLower().StartsWith(".test"))
            {
                if (IsInTest)
                {
                    e.Handled = true;
                    // Error
                }
                string code = e.Code;
                if (TestFile == null)
                    Assembler.noList = true;
                else
                    e.Code = "SET PC, end_test_" + e.Code.Substring(5).Trim(); // change the code to jump past the test under normal conditions
                Tests.Add(new UnitTest(code.Substring(5).Trim(), Assembler.currentAddress));
                IsInTest = true;
            }
            else if (e.Code.ToLower().StartsWith(".endtest"))
            {
                if (!IsInTest)
                    e.Output.ErrorCode = ErrorCode.UncoupledStatement;
                else
                    Tests[Tests.Count - 1].EndAddress = Assembler.currentAddress;
                e.Code = "end_test_" + Tests[Tests.Count - 1].Name + ":";
                IsInTest = false;
            }
            else if (e.Code.ToLower().StartsWith(".assert"))
            {
                Tests[Tests.Count - 1].Assersions.Add(new Assertion()
                {
                    Address = Assembler.currentAddress,
                    Expression = e.Code.Substring(7).Trim()
                });
                e.Handled = true;
            }
            else if (e.Code.ToLower().StartsWith(".dump"))
            {
                // TODO
            }
            else if (e.Code.ToLower().StartsWith(".log"))
            {
                // TODO
            }

            e.Output.Tags["dunit-section"] = CurrentSection;
        }