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()); } }
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))); }