/// <summary> /// Generates assembly source. /// /// This code is common to all generators. /// </summary> /// <param name="gen">Reference to generator object (presumably the caller).</param> /// <param name="sw">Text output sink.</param> /// <param name="worker">Background worker object, for progress updates and /// cancelation requests.</param> public static void Generate(IGenerator gen, StreamWriter sw, BackgroundWorker worker) { DisasmProject proj = gen.Project; Formatter formatter = gen.SourceFormatter; int offset = 0; bool doAddCycles = gen.Settings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false); LocalVariableLookup lvLookup = new LocalVariableLookup(proj.LvTables, proj, gen.Localizer.LabelMap, gen.Quirks.LeadingUnderscoreSpecial, gen.Quirks.NoRedefinableSymbols); GenerateHeader(gen, sw); // Used for M/X flag tracking. StatusFlags prevFlags = StatusFlags.AllIndeterminate; int lastProgress = 0; while (offset < proj.FileData.Length) { Anattrib attr = proj.GetAnattrib(offset); if (attr.IsInstructionStart && offset > 0 && proj.GetAnattrib(offset - 1).IsData) { // Transition from data to code. (Don't add blank line for inline data.) gen.OutputLine(string.Empty); } // Long comments come first. if (proj.LongComments.TryGetValue(offset, out MultiLineComment longComment)) { List <string> formatted = longComment.FormatText(formatter, string.Empty); foreach (string str in formatted) { gen.OutputLine(str); } } // Check for address change. int orgAddr = proj.AddrMap.Get(offset); if (orgAddr >= 0) { gen.OutputOrgDirective(offset, orgAddr); } List <DefSymbol> lvars = lvLookup.GetVariablesDefinedAtOffset(offset); if (lvars != null) { // table defined here gen.OutputLocalVariableTable(offset, lvars, lvLookup.GetMergedTableAtOffset(offset)); } if (attr.IsInstructionStart) { // Generate M/X reg width directive, if necessary. // NOTE: we can suppress the initial directive if we know what the // target assembler's default assumption is. Probably want to handle // that in the ORG output handler. if (proj.CpuDef.HasEmuFlag) { StatusFlags curFlags = attr.StatusFlags; curFlags.M = attr.StatusFlags.ShortM ? 1 : 0; curFlags.X = attr.StatusFlags.ShortX ? 1 : 0; if (curFlags.M != prevFlags.M || curFlags.X != prevFlags.X) { // changed, output directive gen.OutputRegWidthDirective(offset, prevFlags.M, prevFlags.X, curFlags.M, curFlags.X); } prevFlags = curFlags; } // Look for embedded instructions. int len; for (len = 1; len < attr.Length; len++) { if (proj.GetAnattrib(offset + len).IsInstructionStart) { break; } } // Output instruction. GenerateInstruction(gen, sw, lvLookup, offset, len, doAddCycles); if (attr.DoesNotContinue) { gen.OutputLine(string.Empty); } offset += len; } else { gen.OutputDataOp(offset); offset += attr.Length; } // Update progress meter. We don't want to spam it, so just ping it 10x. int curProgress = (offset * 10) / proj.FileData.Length; if (lastProgress != curProgress) { if (worker.CancellationPending) { Debug.WriteLine("GenCommon got cancellation request"); return; } lastProgress = curProgress; worker.ReportProgress(curProgress * 10); //System.Threading.Thread.Sleep(500); } } }
/// <summary> /// Generates assembly source. /// /// This code is common to all generators. /// </summary> /// <param name="gen">Reference to generator object (presumably the caller).</param> /// <param name="sw">Text output sink.</param> /// <param name="worker">Background worker object, for progress updates and /// cancelation requests.</param> public static void Generate(IGenerator gen, StreamWriter sw, BackgroundWorker worker) { DisasmProject proj = gen.Project; Formatter formatter = gen.SourceFormatter; int offset = gen.StartOffset; bool doAddCycles = gen.Settings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false); LocalVariableLookup lvLookup = new LocalVariableLookup(proj.LvTables, proj, gen.Localizer.LabelMap, gen.Quirks.LeadingUnderscoreSpecial, gen.Quirks.NoRedefinableSymbols); GenerateHeader(gen, sw); // Used for M/X flag tracking. StatusFlags prevFlags = StatusFlags.AllIndeterminate; int lastProgress = 0; // Create an address map iterator and advance it to match gen.StartOffset. IEnumerator <AddressMap.AddressChange> addrIter = proj.AddrMap.AddressChangeIterator; while (addrIter.MoveNext()) { if (addrIter.Current.IsStart && addrIter.Current.Offset >= offset) { break; } } bool arDirectPending = false; while (offset < proj.FileData.Length) { Anattrib attr = proj.GetAnattrib(offset); if (attr.IsInstructionStart && offset > 0 && proj.GetAnattrib(offset - 1).IsData) { // Transition from data to code. (Don't add blank line for inline data.) gen.OutputLine(string.Empty); } // Long comments come first. if (proj.LongComments.TryGetValue(offset, out MultiLineComment longComment)) { List <string> formatted = longComment.FormatText(formatter, string.Empty); foreach (string str in formatted) { gen.OutputLine(str); } } // Check for address range starts. There may be more than one at a given offset. AddressMap.AddressChange change = addrIter.Current; while (change != null && change.Offset == offset) { if (change.IsStart) { gen.OutputArDirective(change); arDirectPending = true; addrIter.MoveNext(); change = addrIter.Current; } else { break; } } // Reached end of start directives. Write the last one. if (arDirectPending) { gen.FlushArDirectives(); arDirectPending = false; } List <DefSymbol> lvars = lvLookup.GetVariablesDefinedAtOffset(offset); if (lvars != null) { // table defined here gen.OutputLocalVariableTable(offset, lvars, lvLookup.GetMergedTableAtOffset(offset)); } if (attr.IsInstructionStart) { // Generate M/X reg width directive, if necessary. // NOTE: we can suppress the initial directive if we know what the // target assembler's default assumption is. Probably want to handle // that in the ORG output handler. if (proj.CpuDef.HasEmuFlag) { StatusFlags curFlags = attr.StatusFlags; curFlags.M = attr.StatusFlags.IsShortM ? 1 : 0; curFlags.X = attr.StatusFlags.IsShortX ? 1 : 0; if (curFlags.M != prevFlags.M || curFlags.X != prevFlags.X) { // changed, output directive gen.OutputRegWidthDirective(offset, prevFlags.M, prevFlags.X, curFlags.M, curFlags.X); } prevFlags = curFlags; } // Look for embedded instructions. int len; for (len = 1; len < attr.Length; len++) { if (proj.GetAnattrib(offset + len).IsInstructionStart) { break; } } // Output instruction. GenerateInstruction(gen, sw, lvLookup, offset, len, doAddCycles); if (attr.DoesNotContinue) { gen.OutputLine(string.Empty); } offset += len; } else { gen.OutputDataOp(offset); offset += attr.Length; } // Check for address region ends. There may be more than one at a given offset. // The end-region offset will be the last byte of the instruction or data item, // so it should be one less than the updated offset. // // If we encounter a region start, we'll handle that at the top of the next // loop iteration. while (change != null && change.Offset + 1 == offset) { if (!change.IsStart) { gen.OutputArDirective(change); arDirectPending = true; addrIter.MoveNext(); change = addrIter.Current; } else { break; } } // Update progress meter. We don't want to spam it, so just ping it 10x. int curProgress = (offset * 10) / proj.FileData.Length; if (lastProgress != curProgress) { if (worker.CancellationPending) { Debug.WriteLine("GenCommon got cancellation request"); return; } lastProgress = curProgress; worker.ReportProgress(curProgress * 10); //System.Threading.Thread.Sleep(500); } } Debug.Assert(offset == proj.FileDataLength); }