示例#1
0
        private static IEnumerable <AssemblyLine> CreateMethods(CompiledProgram program)
        {
            var nodes   = program.CallGraph.AllNodes;
            var methods = program.Types.SelectMany(t => t.Subroutines)
                          .Where(s => s != program.EntryPoint)
                          .Where(s => s.Body.Any())                                   // Don't emit empty methods.
                          .Where(s => nodes.Any(n => n.Value == s.MethodDefinition)); // Don't emit methods that are never called.

            yield return(Comment("Begin subroutine emit.", 0));

            yield return(BlankLine());

            foreach (var method in methods)
            {
                // Do not emit subroutines that will never be JSR'd.
                if (method.TryGetFrameworkAttribute <AlwaysInlineAttribute>(out _))
                {
                    continue;
                }
                foreach (var line in CreateMethod(method))
                {
                    yield return(line);
                }
            }
            yield return(Comment("End subroutine emit.", 0));

            yield return(BlankLine());

            IEnumerable <AssemblyLine> CreateMethod(CompiledSubroutine subroutine)
            {
                yield return(Comment(subroutine.MethodDefinition.ToString(), 0));

                yield return(Label(LabelGenerator.GetFromMethod(subroutine.MethodDefinition)));

                foreach (var line in subroutine.Body)
                {
                    yield return(line);
                }
                yield return(BlankLine());
            }
        }
示例#2
0
        private IEnumerable <AssemblyLine> Call(Instruction instruction)
        {
            // Could be either a MethodDefinition or MethodReference.
            MethodReference method = (MethodReference)instruction.Operand;

            var methodDeclaringType = method.DeclaringType.FullName;
            var processedSubroutine = Types[methodDeclaringType].Subroutines.Single(s => s.FullName == method.FullName);

            // Check if this method should be replaced with a direct store to a symbol (generally a TIA register).
            // Don't directly compare types since we may have received a different Framework assembly than what this library was built against.
            if (processedSubroutine.TryGetFrameworkAttribute <OverrideWithStoreToSymbolAttribute>(out var overrideStore))
            {
                if (!overrideStore.Strobe)
                {
                    //TODO - We assume this is a 1-arg void method. Actually enforce this at the processing stage.
                    if (method.Parameters.Count != 1)
                    {
                        throw new NotImplementedException($"{method.Name}, marked with {nameof(OverrideWithStoreToSymbolAttribute)}, must take 1 parameter for now.");
                    }
                    yield return(PLA());
                }
                yield return(STA(overrideStore.Symbol));

                yield break;
            }

            if (processedSubroutine.TryGetFrameworkAttribute <OverrideWithLoadToRegisterAttribute>(out var overrideRegisterLoad))
            {
                //TODO - We assume this is a 1-arg void method. Actually enforce this at the processing stage.
                if (method.Parameters.Count != 1)
                {
                    throw new NotImplementedException($"{method.Name}, marked with {nameof(OverrideWithLoadToRegisterAttribute)} must take 1 parameter.");
                }
                yield return(PLA());

                switch (overrideRegisterLoad.Register)
                {
                case "A":
                    break;

                case "X":
                    yield return(TAX());

                    break;

                case "Y":
                    yield return(TAY());

                    break;

                default:
                    throw new FatalCompilationException($"Attempted load to unknown register: {overrideRegisterLoad.Register}");
                }
                yield break;
            }

            if (processedSubroutine.TryGetFrameworkAttribute <OverrideWithLoadFromSymbolAttribute>(out var overrideLoad))
            {
                if (method.Parameters.Count != 0)
                {
                    throw new NotImplementedException($"{method.Name}, marked with {nameof(OverrideWithLoadFromSymbolAttribute)}, must take 0 parameters.");
                }
                yield return(LDA(overrideLoad.Symbol));

                yield return(PHA());

                yield break;
            }

            var parameters = ((MethodReference)method).Parameters.ToImmutableArray();

            if (parameters.Any())
            {
                // PLA arguments in reverse off stack and assign to parameters.
                foreach (var parameter in parameters.Reverse())
                {
                    yield return(PLA());

                    yield return(STA(LabelGenerator.GetFromParameter(parameter)));
                }
            }

            if (processedSubroutine.TryGetFrameworkAttribute <AlwaysInlineAttribute>(out _))
            {
                var compiledSubroutine = Types[method.DeclaringType.FullName].Subroutines.Single(s => s.FullName == method.FullName) as CompiledSubroutine;
                if (compiledSubroutine == null)
                {
                    throw new FatalCompilationException($"Attempted to inline method '{processedSubroutine.Name}' that hasn't been compiled yet. This suggests a bug in determining method compilation order.");
                }

                foreach (var assemblyLine in compiledSubroutine.Body)
                {
                    //TODO - If the subroutine contains labels you can end up emitting duplicates if the inline subroutine is called more than once. Make them unique.
                    //TODO - Once we have branching and multiple return statements this will explode.
                    // In reality we probably want to replace RTS with JMP to a label inserted after this method body.
                    if (!assemblyLine.Text.Contains("RTS"))
                    {
                        yield return(assemblyLine);
                    }
                }
                yield break;
            }

            yield return(JSR(LabelGenerator.GetFromMethod(method)));
        }