Example #1
0
        private void ParsePlusOrMinusLabel(ref StringSection line, char labelChar, int iInstruction, int iSourceLine)
        {
            int charCount = 1; // Number of times the label char (+ or -) appears

            while (charCount < line.Length && line[charCount] == labelChar)
            {
                charCount++;
            }


            if (labelChar == '+')
            {
                assembly.AnonymousLabels.AddPlusLabel(charCount, iSourceLine);
            }
            else if (labelChar == '-')
            {
                assembly.AnonymousLabels.AddMinusLabel(charCount, iSourceLine);
            }
            else
            {
                throw new ArgumentException("Invalid label character for +/- label.", "labelChar");
            }
            assembly.TagAnonLabel(iInstruction, iSourceLine);

            line = line.Substring(charCount).Trim();
            // Remove colon if present
            if (line.Length > 0 && line[0] == ':')
            {
                line = line.Substring(1).TrimLeft();
            }
        }
Example #2
0
        public void Process(StringSection source, List <StringSection> output, IFileSystem files)
        {
            bool moreLines = !source.IsNullOrEmpty;

            while (moreLines)
            {
                int           iLineBreak = source.IndexOfAny(lineBreaks);
                StringSection line;

                if (iLineBreak == -1)
                {
                    // Last line
                    line      = source;
                    source    = StringSection.Empty;
                    moreLines = false;
                }
                else
                {
                    line = source.Substring(0, iLineBreak);
                    bool isCRLF = (source[iLineBreak] == '\r') && (source.Length > iLineBreak + 1) && (source[iLineBreak + 1] == '\n');
                    if (isCRLF)
                    {
                        source = source.Substring(iLineBreak + 2);
                    }
                    else
                    {
                        source = source.Substring(iLineBreak + 1);
                    }
                }

                if (IsIncludeLine(line))
                {
                    string includeFile = line.Substring(includeDirective.Length).Trim().ToString();
                    // Need to remove optional quotes
                    if (includeFile.Length >= 2 && includeFile[0] == '\"' && includeFile[includeFile.Length - 1] == '\"')
                    {
                        includeFile = includeFile.Substring(1, includeFile.Length - 2).Trim();
                    }
                    if (assembler.FileSystem.FileExists(includeFile))
                    {
                        ProcessInclude(output, files, includeFile);
                    }
                    else
                    {
                        assembler.AddError(new Error(ErrorCode.File_Error, string.Format(Error.Msg_FileNotFound_name, includeFile), output.Count));
                    }
                }
                else
                {
                    output.Add(line);
                }
            }
        }
Example #3
0
        private bool ParseNamedLabels(ref StringSection line, int iParsedLine, int iSourceLine)
        {
            int iColon = line.IndexOf(':');

            if (iColon < 0)
            {
                return(false);
            }

            if ((line.Length - 1 > iColon) && (line[iColon + 1] == '='))
            {
                // := is not a label
                return(false);
            }

            var labelName = line.Substring(0, iColon).Trim();

            // Todo: should be able to validate via a 'parse symbol' func

            // Check for nonzero length and that label starts with letter or @
            if (labelName.Length == 0)
            {
                return(false);
            }
            if (!char.IsLetter(labelName[0]) && labelName[0] != '@')
            {
                return(false);
            }

            for (int i = 1; i < labelName.Length; i++)   // i = 1 because we've already checked zero
            {
                if (!char.IsLetter(labelName[i]) && !char.IsDigit(labelName[i]) && labelName[i] != '_')
                {
                    return(false);
                }
            }

            if (labelName[0] == '@')                                                 // Local label
            {
                labelName = labelName.Substring(1);                                  // Remove @
                string fullName = mostRecentNamedLabel + "." + labelName.ToString(); // example: SomeFunction.LoopTop
                assembly.Labels.Add(new Label(fullName, iParsedLine, iSourceLine, true));
            }
            else     // Normal label
            {
                var sLabelName = labelName.ToString();
                mostRecentNamedLabel = sLabelName;
                assembly.Labels.Add(new Label(sLabelName, iParsedLine, iSourceLine, false));
            }
            line = line.Substring(iColon + 1).TrimLeft();

            return(true);
        }
Example #4
0
        /// <summary>
        /// Returns a char array containing all the characters from a string. Escapes are processed. The specified string
        /// should not include the opening quote. The parsed string will be removed from the string passed in. The closing
        /// quote will not be removed, so that the caller can examine it and verify that there was a closing quote.
        /// </summary>
        /// <param name="section"></param>
        /// <returns></returns>
        public static char[] ParseString(ref StringSection str, out ErrorCode error)
        {
            error = ErrorCode.None;

            // Only one thread can run this method at a time
            lock (ParseLock) {
                charBuilder.Clear();

                while (str.Length > 0)
                {
                    char c = str[0];
                    if (c == '\"')
                    {
                        return(charBuilder.ToArray());
                    }
                    else if (c == '\\')
                    {
                        str = str.Substring(1);
                        if (str.Length > 0)
                        {
                            c = str[0];

                            int escapeIndex = Array.IndexOf(escapeCodes, c);
                            if (escapeIndex < 0)
                            {
                                error = ErrorCode.Invalid_Escape;
                                return(null);
                            }
                            else
                            {
                                charBuilder.Add(escapeValues[escapeIndex]);
                            }
                        }
                        else
                        {
                            error = ErrorCode.Invalid_Escape;
                            return(null);
                        }
                    }
                    else
                    {
                        charBuilder.Add(c);
                    }
                    str = str.Substring(1);
                }

                return(charBuilder.ToArray());
            }
        }
Example #5
0
        /// <summary>
        /// Returns a symbol name, or a zero-length string of no symbol was found.
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private StringSection GetSymbol(StringSection exp)
        {
            // Check for special '$' variable
            if (exp.Length > 0 && exp[0] == '$' && !char.IsLetter(exp[0]) && !char.IsDigit(exp[0]))
            {
                return("$");
            }

            if (exp.Length == 0)
            {
                return(StringSection.Empty);
            }
            if (!char.IsLetter(exp[0]) && exp[0] != '@')
            {
                return(StringSection.Empty);
            }

            int i = 1;

            while (i < exp.Length)
            {
                char c = exp[i];
                if (char.IsLetter(c) | char.IsDigit(c) | c == '_')
                {
                    i++;
                }
                else
                {
                    var result = exp.Substring(0, i);
                    return(result);
                }
            }
            return(exp);
        }
Example #6
0
        private void RemoveComments(ref StringSection line)
        {
            // ; denotes a comment, except within a string
            bool inString = false;

            for (int i = 0; i < line.Length; i++)
            {
                if (inString)
                {
                    if (line[i] == '\"')   // End of string
                    // unless it is preceeded by a backslash (then it's as escaped quote)
                    {
                        if (i == 0 || line[i - 1] != '\\')
                        {
                            inString = false;
                        }
                    }
                }
                else
                {
                    if (line[i] == ';')   // Comment
                    {
                        line = line.Substring(0, i);
                        return;
                    }
                    else if (line[i] == '\"')     // Start of string
                    {
                        inString = true;
                    }
                }
            }
        }
Example #7
0
        private static void StripComments(ref StringSection line)
        {
            int iComment = line.IndexOf(';');

            if (iComment >= 0)
            {
                line = line.Substring(0, iComment);
            }
            line = line.Trim();
        }
Example #8
0
        private StringSection ParseDirectiveName(StringSection text)
        {
            int i = 0;

            while (i < text.Length && Char.IsLetter(text[i]))
            {
                i++;
            }
            return(text.Substring(0, i));
        }
Example #9
0
        public IncBinDirective(int instructionIndex, int sourceLine, StringSection file)
            : base(instructionIndex, sourceLine)
        {
            file = file.Trim();
            if (file.Length > 1 && file[0] == '\"' && file[file.Length - 1] == '\"')
            {
                file = file.Substring(1, file.Length - 2);
            }

            this.Filename = file.ToString();
        }
Example #10
0
        /// <summary>
        /// Parses one anonymous label (both * and +/- types), if found, and removes it from the expression.
        /// </summary>
        /// <param name="line">The text to parse a label from. This text will be modified to remove the label.</param>
        /// <param name="lineNumber">The index of the first instruction that follows the label.</param>
        /// <param name="iSourceLine">The index of the source line the label occurs on.</param>
        /// <returns>True if a label was parsed.</returns>
        private bool ParseAnonymousLabel(ref StringSection line, int iInstruction, int iSourceLine)
        {
            // Todo: support named +/-/* labels. This means that the anon label collection will need to be able to store names.

            if (line.Length > 0)
            {
                if (line[0] == '*')
                {
                    assembly.AnonymousLabels.AddStarLabel(iSourceLine);
                    assembly.TagAnonLabel(iInstruction, iSourceLine);

                    line = line.Substring(1).TrimLeft();
                    // Remove colon if present
                    if (line.Length > 0 && line[0] == ':')
                    {
                        line = line.Substring(1).TrimLeft();
                    }
                    return(true);
                }
                else if (line[0] == '+' | line[0] == '-')
                {
                    ParsePlusOrMinusLabel(ref line, line[0], iInstruction, iSourceLine);
                    return(true);
                }
                else if (line[0] == '{')
                {
                    assembly.AnonymousLabels.AddLeftBraceLabel(iSourceLine);
                    assembly.TagAnonLabel(iInstruction, iSourceLine);
                    line = line.Substring(1).TrimLeft();
                }
                else if (line[0] == '}')
                {
                    assembly.AnonymousLabels.AddRightBraceLabel(iSourceLine);
                    assembly.TagAnonLabel(iInstruction, iSourceLine);
                    line = line.Substring(1).TrimLeft();
                }
            }
            return(false);
        }
Example #11
0
        private bool IsIncludeLine(StringSection line)
        {
            if (line.Length < includeDirective.Length)
            {
                return(false);
            }
            var lineStart = line.Substring(0, includeDirective.Length);

            if (StringSection.Compare(lineStart, includeDirective, true) == 0)
            {
                return(true);
            }
            return(false);
        }
Example #12
0
        /// <summary>
        /// Returns the addressing mode for the operand. Zero-page addressing modes are not considered (this can be addressed when omitting opcodes).
        /// 'Accumulator' addressing is considered implied. The 'operand'
        /// paremeter is updated to remove any addressing characters.
        /// </summary>
        /// <param name="operand">The operand string. Must be trimmed.</param>
        /// <param name="addressing"></param>
        /// <returns></returns>
        private Opcode.addressing ParseAddressing(ref StringSection operand)
        {
            int opLen = operand.Length;

            // Accumulator or implied
            if (opLen == 0 || (opLen == 1 && Char.ToUpper(operand[0]) == 'A'))
            {
                return(Opcode.addressing.implied);
            }

            // Immediate
            if (operand[0] == '#')
            {
                operand = operand.Substring(1).Trim();
                return(Opcode.addressing.immediate);
            }

            // ,X
            if (char.ToUpper(operand[opLen - 1]) == 'X')
            {
                var sansX = operand.Substring(0, opLen - 1).TrimRight();
                if (sansX.Length > 0 && sansX[sansX.Length - 1] == ',')
                {
                    sansX = sansX.Substring(0, sansX.Length - 1).Trim();

                    // Update operand to remove addressing
                    operand = sansX;
                    return(Opcode.addressing.absoluteIndexedX);
                }
            }
            // (),Y
            // ,Y
            if (char.ToUpper(operand[opLen - 1]) == 'Y')
            {
                var sansY = operand.Substring(0, opLen - 1).TrimRight();
                if (sansY.Length > 0 && sansY[sansY.Length - 1] == ',')
                {
                    sansY = sansY.Substring(0, sansY.Length - 1).Trim();

                    //(),Y
                    if (sansY.Length > 0 && sansY[0] == '(' && sansY[sansY.Length - 1] == ')')
                    {
                        sansY = sansY.Substring(1, sansY.Length - 2).Trim();

                        operand = sansY;
                        return(Opcode.addressing.indirectY);
                    }

                    operand = sansY;
                    return(Opcode.addressing.absoluteIndexedY);
                }
            }

            // ()
            // (,X)
            if (operand[0] == '(' && operand[opLen - 1] == ')')
            {
                operand = operand.Substring(1, opLen - 2).Trim();
                opLen   = operand.Length;

                // (,X)
                if (opLen > 0 && char.ToUpper(operand[opLen - 1]) == 'X')
                {
                    var sansX = operand.Substring(0, opLen - 1).TrimRight();
                    if (sansX.Length > 0 && sansX[sansX.Length - 1] == ',')
                    {
                        sansX = sansX.Substring(0, sansX.Length - 1).Trim();

                        // Update operand to remove addressing
                        operand = sansX;
                        return(Opcode.addressing.indirectX);
                    }
                }

                return(Opcode.addressing.indirect);
            }

            ////// ,x ,y () all require at least 3 chars
            ////if (opLen > 2) {
            ////    bool isIndexed = operand[operand.Length - 2] == ',';

            ////    char indexRegister = Char.ToUpper(operand[opLen - 1]);
            ////    if (isIndexed) {
            ////        if (indexRegister == 'X') {
            ////            operand = operand.Substring(0, opLen - 2);
            ////            return Opcode.addressing.absoluteIndexedX;
            ////        } else if (indexRegister == 'Y') {
            ////            bool isIndirect = operand[0] == '(' && operand.Length > 3 && operand[opLen - 3] == ')';
            ////            if (isIndirect) {
            ////                operand = operand.Substring(1, opLen - 4);
            ////                return Opcode.addressing.indirectY;
            ////            } else {
            ////                operand = operand.Substring(0, opLen - 2);
            ////                return Opcode.addressing.absoluteIndexedY;
            ////            }
            ////        }
            ////    }

            ////    bool is_Indirect = operand[0] == '(' && operand[opLen - 1] == ')';
            ////    if (is_Indirect) {
            ////        bool isXIndexed = opLen > 4 && operand[opLen - 3] == ',' && char.ToUpper(operand[opLen - 2]) == 'X';
            ////        if (isXIndexed) {
            ////            operand = operand.Substring(1, opLen - 4);
            ////            return Opcode.addressing.indirectX;

            ////        } else {
            ////            operand = operand.Substring(1, opLen - 2);
            ////            return Opcode.addressing.indirect;
            ////        }
            ////    }

            ////}

            // If there are no addressing chars, it must be absolute (absolute vs. zero page is determined later)
            return(Opcode.addressing.absolute);
        }
Example #13
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="line">The line of code to parse.</param>
        /// <param name="sourceLine">The line number of the code file.</param>
        void ParseLine(StringSection line, int iSourceLine, out Error error)
        {
            error = Error.None;

            line = line.TrimLeft();
            RemoveComments(ref line);

            int newInstructionIndex = assembly.ParsedInstructions.Count;

            bool loopLabel;

            do   // This loop allows us to parse alternating named and anonymous labels (* label: +++ -- anotherLabel:)
            {
                loopLabel  = false;
                loopLabel |= ParseNamedLabels(ref line, newInstructionIndex, iSourceLine);
                loopLabel |= ParseAnonymousLabel(ref line, newInstructionIndex, iSourceLine);
            } while (loopLabel);

            if (line.IsNullOrEmpty || line[0] == ';')
            {
                // Nothing on this line
                return;
            }

            // Dot-prefixed directive
            if (line[0] == '.')
            {
                line = line.Substring(1);
                var directiveName = GetSymbol(line);
                line = line.Substring(directiveName.Length).TrimLeft();
                if (!ParseDirective(directiveName, line, iSourceLine, out error))
                {
                    error = new Error(ErrorCode.Directive_Not_Defined, string.Format(Error.Msg_DirectiveUndefined_name, directiveName), iSourceLine);
                    return;
                }
                return;
            }



            bool parsedUncolonedLabel = false; // Set to true when an uncoloned label is found so we don't look for one again.
            bool loopAgain            = false; // Set to true when an uncoloned label is found to loop back and try to parse the remaining text

            do
            {
                loopAgain = false;

                var symbol = GetSymbol(line);
                if (symbol.Length == 0)
                {
                    break;
                }
                line = line.Substring(symbol.Length).Trim();

                if (ParseDirective(symbol, line, iSourceLine, out error))
                {
                    return;
                }
                else if (ParseInstruction(symbol, line, iSourceLine, out error))
                {
                    return;
                }
                else if ((line.Length > 0 && line[0] == '=') || (line.Length > 1 && line[0] == ':' && line[1] == '='))     // Assignments and label assignments
                // := is a cross between a label and assignment: It declares a label with an explicit value.
                {
                    bool isLabel = (line[0] == ':');

                    var expression = line.Substring(1).TrimLeft();
                    if (isLabel)
                    {
                        expression = expression.Substring(1).TrimLeft();
                    }

                    if (expression.IsNullOrEmpty)
                    {
                        error = new Error(ErrorCode.Expected_Expression, Error.Msg_ExpectedValue, iSourceLine);
                    }
                    else
                    {
                        ParseAssignment(symbol, isLabel, expression, iSourceLine);
                    }
                }
                else
                {
                    // Todo: ensure 'symbol' is a valid label name
                    if (!Assembler.RequireColonOnLabels && !parsedUncolonedLabel && symbol.Length > 0)
                    {
                        if (symbol[0] == '@')
                        {
                            string labelName = mostRecentNamedLabel + "." + symbol.Substring(1).ToString();
                            assembly.Labels.Add(new Label(labelName, newInstructionIndex, iSourceLine, true));
                        }
                        else
                        {
                            mostRecentNamedLabel = symbol.ToString();
                            assembly.Labels.Add(new Label(symbol.ToString(), newInstructionIndex, iSourceLine, false));
                        }
                        parsedUncolonedLabel = true;
                        loopAgain            = true;
                    }
                    else
                    {
                        error = new Error(ErrorCode.Unexpected_Text, Error.Msg_BadLine, iSourceLine);
                    }
                }
            } while (parsedUncolonedLabel && loopAgain);
        }
Example #14
0
        /// <summary>
        /// Returns true of a directive was parsed, even if it was not parsed successfully due to an error
        /// </summary>
        /// <param name="directiveName"></param>
        /// <param name="line"></param>
        /// <param name="sourceLine"></param>
        /// <param name="error"></param>
        /// <returns></returns>
        private bool ParseDirective(StringSection directiveName, StringSection line, int sourceLine, out Error error)
        {
            error = Error.None;

            if (StringEquals(directiveName, "org", true))
            {
                assembly.Directives.Add(new OrgDirective(NextInstructionIndex, sourceLine, new AsmValue(line.ToString())));
            }
            else if (StringEquals(directiveName, "base", true))
            {
                assembly.Directives.Add(new BaseDirective(NextInstructionIndex, sourceLine, new AsmValue(line.ToString())));
            }
            else if (StringEquals(directiveName, "incbin", true))
            {
                assembly.Directives.Add(new IncBinDirective(NextInstructionIndex, sourceLine, line));
            }
            else if (StringEquals(directiveName, "error", true))
            {
                assembly.Directives.Add(new ErrorDirective(NextInstructionIndex, sourceLine, line));
            }
            else if (StringEquals(directiveName, "patch", true))
            {
                assembly.Directives.Add(new PatchDirective(NextInstructionIndex, sourceLine, line.ToString()));
            }
            else if (StringEquals(directiveName, "define", true))
            {
                line = line.Trim();
                if (GetSymbol(line).Length == line.Length)   // line should contain a only a symbol
                {
                    assembly.Directives.Add(new DefineDirective(NextInstructionIndex, sourceLine, line));
                }
                else
                {
                    error = new Error(ErrorCode.Expected_LValue, string.Format(Error.Msg_InvalidSymbolName_name, line.ToString()), sourceLine);
                }
            }
            else if (StringEquals(directiveName, "hex", true))
            {
                assembly.Directives.Add(new HexDirective(NextInstructionIndex, sourceLine, line));
            }
            else if (StringEquals(directiveName, "db", true))
            {
                assembly.Directives.Add(new DataDirective(NextInstructionIndex, sourceLine, line, DataDirective.DataType.Bytes));
            }
            else if (StringEquals(directiveName, "byte", true))
            {
                assembly.Directives.Add(new DataDirective(NextInstructionIndex, sourceLine, line, DataDirective.DataType.Bytes));
            }
            else if (StringEquals(directiveName, "dw", true))
            {
                assembly.Directives.Add(new DataDirective(NextInstructionIndex, sourceLine, line, DataDirective.DataType.Words));
            }
            else if (StringEquals(directiveName, "word", true))
            {
                assembly.Directives.Add(new DataDirective(NextInstructionIndex, sourceLine, line, DataDirective.DataType.Words));
            }
            else if (StringEquals(directiveName, "data", true))
            {
                assembly.Directives.Add(new DataDirective(NextInstructionIndex, sourceLine, line, DataDirective.DataType.Implicit));
            }
            else if (StringEquals(directiveName, "dsb", true))
            {
                assembly.Directives.Add(new StorageDirective(NextInstructionIndex, sourceLine, line, StorageDirective.DataType.Bytes));
            }
            else if (StringEquals(directiveName, "dsw", true))
            {
                assembly.Directives.Add(new StorageDirective(NextInstructionIndex, sourceLine, line, StorageDirective.DataType.Words));
            }
            else if (StringEquals(directiveName, "overflow", true))
            {
                assembly.Directives.Add(new OptionDirective(NextInstructionIndex, sourceLine, directiveName.ToString(), line.Trim().ToString()));
            }
            else if (StringEquals(directiveName, "if", true) ||
                     StringEquals(directiveName, "else", true) ||
                     StringEquals(directiveName, "ifdef", true) ||
                     StringEquals(directiveName, "ifndef", true) ||
                     StringEquals(directiveName, "endif", true))
            {
                assembly.Directives.Add(new ConditionalDirective(NextInstructionIndex, sourceLine, directiveName, line.Trim(), out error));
            }
            else if (StringEquals(directiveName, "ENUM", true))
            {
                assembly.Directives.Add(new EnumDirective(NextInstructionIndex, sourceLine, line));
            }
            else if (StringEquals(directiveName, "ENDE", true) || StringEquals(directiveName, "ENDENUM", true))
            {
                if (line.IsNullOrEmpty)
                {
                    assembly.Directives.Add(new EndEnumDirective(NextInstructionIndex, sourceLine));
                }
                else
                {
                    error = new Error(ErrorCode.Unexpected_Text, Error.Msg_NoTextExpected, sourceLine);
                }
            }
            else if (StringEquals(directiveName, "signed", true))
            {
                assembly.Directives.Add(new OptionDirective(NextInstructionIndex, sourceLine, directiveName.ToString(), line.Trim().ToString()));
            }
            else if (StringEquals(directiveName, "needdot", true))
            {
                assembly.Directives.Add(new OptionDirective(NextInstructionIndex, sourceLine, directiveName.ToString(), line.Trim().ToString()));
            }
            else if (StringEquals(directiveName, "needcolon", true))
            {
                assembly.Directives.Add(new OptionDirective(NextInstructionIndex, sourceLine, directiveName.ToString(), line.Trim().ToString()));
            }
            else if (StringEquals(directiveName, "alias", true))
            {
                var varName = GetSymbol(line);
                if (varName.IsNullOrEmpty)
                {
                    error = new Error(ErrorCode.Syntax_Error, Error.Msg_ExpectedText, sourceLine);
                    return(true);
                }
                line = line.Substring(varName.Length).Trim();

                assembly.Directives.Add(new Assignment(NextInstructionIndex, sourceLine, varName, true, new AsmValue(line.ToString())));
            }
            else
            {
                return(false);
            }

            return(true);
        }