private IEnumerable <AssemblyLine> LoadArgument(int index) { var parameter = MethodDefinition.Parameters[index]; yield return(LDA(LabelGenerator.GetFromParameter(parameter))); yield return(PHA()); }
private IEnumerable <AssemblyLine> Ldsflda(Instruction instruction) { var fieldDefinition = (FieldDefinition)instruction.Operand; yield return(LDA($"#{LabelGenerator.GetFromField(fieldDefinition)}")); yield return(PHA()); }
private IEnumerable <AssemblyLine> StoreLocal(int index) { var local = MethodDefinition.Body.Variables[index]; yield return(PLA()); yield return(STA(LabelGenerator.GetFromVariable(MethodDefinition, local))); }
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))); }
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); } } }
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); } } }
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> 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)); } }
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); }
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))); }
private IEnumerable <AssemblyLine> Brtrue(Instruction instruction) { yield return(PLA()); yield return(BNE(LabelGenerator.GetFromInstruction((Instruction)instruction.Operand))); }
private IEnumerable <AssemblyLine> Br(Instruction instruction) { yield return(JMP(LabelGenerator.GetFromInstruction((Instruction)instruction.Operand))); }