Beispiel #1
0
        private IEnumerable <AssemblyLine> LoadArgument(int index)
        {
            var parameter = MethodDefinition.Parameters[index];

            yield return(LDA(LabelGenerator.GetFromParameter(parameter)));

            yield return(PHA());
        }
Beispiel #2
0
        private IEnumerable <AssemblyLine> Ldsflda(Instruction instruction)
        {
            var fieldDefinition = (FieldDefinition)instruction.Operand;

            yield return(LDA($"#{LabelGenerator.GetFromField(fieldDefinition)}"));

            yield return(PHA());
        }
Beispiel #3
0
        private IEnumerable <AssemblyLine> StoreLocal(int index)
        {
            var local = MethodDefinition.Body.Variables[index];

            yield return(PLA());

            yield return(STA(LabelGenerator.GetFromVariable(MethodDefinition, local)));
        }
Beispiel #4
0
        private IEnumerable <AssemblyLine> StoreArgument(Instruction instruction)
        {
            // Either Starg or Starg_S.
            var parameter = (ParameterDefinition)instruction.Operand;

            yield return(PLA());

            yield return(STA(LabelGenerator.GetFromParameter(parameter)));
        }
Beispiel #5
0
        private IEnumerable <Symbol> LayoutGlobals()
        {
            var staticVariablesByType = Program.Types.SelectMany(t => t.Fields).GroupBy(f => f.FieldDefinition.DeclaringType);

            foreach (var fieldGrouping in staticVariablesByType)
            {
                foreach (var field in fieldGrouping.Where(f => f.FieldDefinition.IsStatic).OrderBy(f => f.Name))
                {
                    yield return(DefineSymbol(LabelGenerator.GetFromField(field.FieldDefinition), NextGlobal).WithComment($"{field.FieldType.Name} - {field.FieldType.TotalSize} bytes"));

                    AdvanceNextGlobal(field.FieldType.TotalSize);
                }
            }
        }
Beispiel #6
0
        private IEnumerable <Symbol> AllocateLocalAddresses(CallGraph callGraph)
        {
            foreach (var node in callGraph.AllNodes)
            {
                foreach (var parameter in node.Value.Parameters)
                {
                    yield return(DefineSymbol(LabelGenerator.GetFromParameter(parameter), NextGlobal));

                    AdvanceNextGlobal(1);
                }
                foreach (var local in node.Value.Body.Variables)
                {
                    yield return(DefineSymbol(LabelGenerator.GetFromVariable(node.Value, local), NextGlobal));

                    AdvanceNextGlobal(1);
                }
            }
        }
Beispiel #7
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());
            }
        }
Beispiel #8
0
        private IEnumerable <AssemblyLine> Stsfld(Instruction instruction)
        {
            var fieldDefinition = (FieldDefinition)instruction.Operand;

            var(_, processedField) = GetProcessedInfo(fieldDefinition);

            if (processedField.FieldType.TotalSize == 1)
            {
                yield return(PLA());

                yield return(STA(LabelGenerator.GetFromField(fieldDefinition)));

                yield break;
            }

            for (var i = processedField.FieldType.TotalSize - 1; i >= 0; i--)
            {
                yield return(PLA());

                yield return(STA(LabelGenerator.GetFromField(fieldDefinition), i));
            }
        }
Beispiel #9
0
        public static IEnumerable <AssemblyLine> CompileMethod(MethodDefinition definition, IImmutableDictionary <string, ProcessedType> types, Assembly frameworkAssembly)
        {
            var instructionCompiler = new CilInstructionCompiler(definition, types);
            var instructions        = definition.Body.Instructions;
            var compilationActions  = ProcessInstructions(instructions, types, frameworkAssembly);
            var instructionsToLabel = GetInstructionsToEmitLabelsFor(instructions).ToArray();
            var compiledBody        = new List <AssemblyLine>();
            var compilationContext  = new CompilationContext(instructionCompiler);

            foreach (var action in compilationActions)
            {
                var needLabel = action.ConsumedInstructions.Where(i => instructionsToLabel.Contains(i)).ToArray();
                foreach (var toLabel in needLabel)
                {
                    compiledBody.Add(AssemblyFactory.Label(LabelGenerator.GetFromInstruction(toLabel)));
                }

                compiledBody.AddRange(action.Execute(compilationContext));
            }

            compiledBody = OptimizeMethod(compiledBody).ToList();
            return(compiledBody);
        }
Beispiel #10
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)));
        }
Beispiel #11
0
        private IEnumerable <AssemblyLine> Brtrue(Instruction instruction)
        {
            yield return(PLA());

            yield return(BNE(LabelGenerator.GetFromInstruction((Instruction)instruction.Operand)));
        }
Beispiel #12
0
 private IEnumerable <AssemblyLine> Br(Instruction instruction)
 {
     yield return(JMP(LabelGenerator.GetFromInstruction((Instruction)instruction.Operand)));
 }