Esempio n. 1
0
 public Label EvalIdentifier(ParseTreeNode node, ref CgContext ctx)
 {
     return ctx.Lookup((String)node.Token.Value);
 }
Esempio n. 2
0
        /// <summary>
        /// The main entry point into the code generator, this assembles a
        /// complete, parsed source file.
        /// </summary>
        public CgResult CgProgram(ParseTreeNode node)
        {
            /*
             * If it weren't for labels, this would be a piece of cake.  All
             * we'd have to do is produce cpu code for each instruction and
             * join them all together.
             * 
             * But we *do* have to deal with labels, so there.  We could handle
             * this by keeping track of a list of fixups; locations that contain
             * a forward reference.  Then, once we know what that label resolves
             * to, we go back and do the fixups.
             * 
             * However, I'd like to be able to do optimisation on the generated
             * code; specifically, replacing jumps to nearby labels with
             * relative jumps using add/sub pc, X.  Doing this will cause the
             * address of all subsequent code (and labels) to change.
             * 
             * In that case, it's probably simpler to just leave all labels
             * unresolved until *after* optimisation.  But what happens when
             * we do shorten a given instruction?
             * 
             * When we shorten a given instruction, all subsequent fixups need
             * to have their target address adjusted.
             * 
             * An attractive alternative is to not keep a list of fixups;
             * instead, generated code is widened such that we can store
             * metadata for each word.  This would include storing label
             * references directly.  The advantage of this is that moving
             * code around is cheap.
             * 
             * So that's what we'll do.
             * 
             * For now, we'll just use a list to store the generated code.
             * Ideally, we'd have a linked list of pages since we won't be
             * doing a lot of cutting.
             */
            var code = new List<Code>();
            /*
             * This array stores difference literals.  We need to do this
             * since difference literals can depend on labels but won't
             * fit in a code.
             */
            var diffLits = new List<Difference>();
            /*
             * We also need something to keep track of the defined labels.
             * Since we need to be able to pack this into code entries,
             * we'll stick them in a flat array.
             */
            var labels = new List<Label>();
            /*
             * A way of mapping a label name to a label object would also
             * be handy.
             */
            var labelMap = new Dictionary<string, Label>();
            /*
             * In order to implement local labels, we need to track the
             * last non-local label we saw.
             */
            var lastGlobalLabel = InitialLabel;
            /*
             * We'll also need a function we can pass into the codegen
             * functions to turn a label name into a label object.
             */
            LabelLookup lookup = delegate(string name)
            {
                if (name.StartsWith("."))
                    name = lastGlobalLabel + name;

                if (labelMap.ContainsKey(name))
                    return labelMap[name];

                else
                {
                    var index = (ushort)labels.Count;
                    var label = new Label(index, name);
                    labels.Add(label);
                    labelMap[name] = label;
                    return label;
                }
            };
            /*
             * And while we're on the subject: a function to write to the
             * code list.
             */
            CodeWriter write = delegate(Code c)
            {
                code.Add(c);
            };
            Func<Difference, Code> diffToCode = delegate(Difference diff)
            {
                var index = (ushort)diffLits.Count;
                diffLits.Add(diff);
                return new Code(CodeType.Difference, index);
            };
            /*
             * Whack 'em in a context.
             */
            var ctx = new CgContext
            {
                Lookup = lookup,
                Write = write,
                GetCurrentAddress = () => (ushort)code.Count,
                EncodeDifference = diffToCode,
            };
            /*
             * Ok, let's process those lines.
             */
            foreach (var line in node.ChildNodes)
            {
                var labelNode = line.ChildNodes[0].ChildNodes.FirstOrDefault();
                var instrNode = line.ChildNodes[1].ChildNodes.FirstOrDefault();
                var labelName = labelNode != null ? labelNode.ChildNodes[0].Token.Text : null;

                if (labelName != null)
                {
                    if (!labelName.StartsWith("."))
                        lastGlobalLabel = labelName;

                    var label = lookup(labelName);

                    if (label.Fixed)
                        throw new CodegenException("Label '{0}' already defined at {1}.",
                            labelName, label.Span.Location);

                    if (instrNode != null && instrNode.Term.Name == "AddressFix")
                    {
                        var fix = EvalLiteralWord(instrNode.ChildNodes[0], ref ctx);
                        switch (fix.Type)
                        {
                            case CodeType.Literal:
                                label.Fix(fix.Value, labelNode.Span);
                                break;

                            case CodeType.Label:
                                label.Fix(labels[fix.Value], labelNode.Span);
                                break;

                            case CodeType.Difference:
                                throw new CodegenException("Labels cannot be fixed to difference literals");

                            default:
                                throw new UnexpectedGrammarException();
                        }

                        instrNode = null;
                    }
                    else
                    {
                        var addr = (ushort)code.Count;
                        label.Fix(addr, labelNode.Span);
                    }
                }

                if (instrNode != null)
                    CgOptInstruction(instrNode, ref ctx);
            }
            /*
             * Let's add a few extra labels for fun.  And usefulness.
             */
            ctx.Lookup("__CODE_START").Fix(0, node.Span);
            ctx.Lookup("__CODE_END").Fix((ushort)code.Count, node.Span);
            /*
             * We now have code generated for the whole program.  What we
             * we need to do now is go back and fill in the labels.
             * 
             * Before that, let's just make sure that all labels were
             * actually defined somewhere...
             */
            foreach (var label in labels)
                if (!label.Fixed)
                    throw new CodegenException("Label '{0}' never defined.", label.Name);
            /*
             * Right.  We now have enough information to generate the final, complete
             * binary.
             * 
             * First of all, we'll fill in guesses for what the labels are attached to.
             */
            foreach (var label in labels)
            {
                if (label.IsForwarded)
                    continue;

                var addr = label.Value;
                if (addr >= code.Count)
                    continue;

                switch (code[addr].Type)
                {
                    case CodeType.Instruction:
                        label.Type = LabelType.Code;
                        break;
                    case CodeType.Label:
                        if( (code[addr].Flags & CodeFlags.IsLiteral) == 0 )
                            goto default;
                        label.Type = LabelType.Data;
                        break;
                    case CodeType.Literal:
                        label.Type = LabelType.Data;
                        break;
                    default:
                        label.Type = LabelType.Unknown;
                        break;
                }
            }
            /*
             * Now we can produce the output image.
             */
            Func<Code, ushort> evalCode = null;
            evalCode = delegate(Code c)
            {
                switch (c.Type)
                {
                    case CodeType.Label:
                        return labels[c.Value].Value;

                    case CodeType.Difference:
                        {
                            var diff = diffLits[c.Value];
                            return (ushort)(evalCode(diff.Target) - evalCode(diff.Base));
                        }

                    default:
                        return c.Value;
                }
            };

            var image = new ushort[code.Count];
            {
                var i = 0;
                foreach (var c in code)
                {
                    image[i] = evalCode(c);
                    ++i;
                }
            }
            /*
             * Done.  Collect the results.
             */
            labels.Sort((a, b) => a.Value - b.Value);
            return new CgResult
            {
                Image = image,
                Labels = labels,
            };
        }