/// <summary> /// Determines whether the project appears to have a PRG header. /// </summary> /// <param name="project">Project to check.</param> /// <returns>True if we think we found a PRG header.</returns> public static bool HasPrgHeader(DisasmProject project) { if (project.FileDataLength < 3 || project.FileDataLength > 65536 + 2) { // Must fit in 64KB of memory. A 65538-byte file will work if the // first two bytes are the PRG header (and it starts at address zero). //Debug.WriteLine("PRG test: incompatible file length"); return(false); } Anattrib attr0 = project.GetAnattrib(0); Anattrib attr1 = project.GetAnattrib(1); if (!(attr0.IsDataStart && attr1.IsData)) { //Debug.WriteLine("PRG test: +0/1 not data"); return(false); } if (attr0.Length != 2) { //Debug.WriteLine("PRG test: +0/1 not 16-bit value"); return(false); } if (attr0.Symbol != null || attr1.Symbol != null) { //Debug.WriteLine("PRG test: +0/1 has label"); return(false); } // The first part of the address map should be a two-byte region, either added // explicitly or a hole left at the start of the file. Address doesn't matter. IEnumerator <AddressMap.AddressChange> iter = project.AddrMap.AddressChangeIterator; if (!iter.MoveNext()) { Debug.Assert(false); return(false); } AddressMap.AddressChange change = iter.Current; if (change.Region.ActualLength != 2) { Debug.WriteLine("PRG test: first entry is not a two-byte region"); } // Confirm there's a single address map entry at offset 2. If there's more than // one we likely have a situation where the first one is a "full-file" region, and // the second determines the address. This weird scenario causes problems with // code generation, so we just don't support it. if (project.AddrMap.GetEntries(0x000002).Count != 1) { //Debug.WriteLine("PRG test: wrong #of entries at +000002"); return(false); } // See if the address at offset 2 matches the value at 0/1. int value01 = project.FileData[0] | (project.FileData[1] << 8); int addr2 = project.AddrMap.OffsetToAddress(0x000002); if (value01 != addr2) { //Debug.WriteLine("PRG test: +0/1 value is " + value01.ToString("x4") + // ", address at +2 is " + addr2); return(false); } // TODO? confirm project fits in 64K of memory return(true); }
/// <summary> /// Gathers a list of symbols from the project's symbol table. /// </summary> /// <remarks> /// Remember that we need to set this up before code analysis runs, so many of the /// secondary data structures (like Anattribs) won't be available. /// </remarks> private List <PlSymbol> GeneratePlSymbolList() { List <PlSymbol> plSymbols = new List <PlSymbol>(); SymbolTable symTab = mProject.SymbolTable; // UserLabels maps offset to Symbol. Create the reverse mapping. Dictionary <Symbol, int> symbolOffsets = new Dictionary <Symbol, int>(mProject.UserLabels.Count); foreach (KeyValuePair <int, Symbol> kvp in mProject.UserLabels) { symbolOffsets[kvp.Value] = kvp.Key; } // Add in the address region pre-labels. IEnumerator <AddressMap.AddressChange> addrIter = mProject.AddrMap.AddressChangeIterator; while (addrIter.MoveNext()) { AddressMap.AddressChange change = addrIter.Current; if (!change.IsStart) { continue; } if (change.Region.HasValidPreLabel) { Symbol newSym = new Symbol(change.Region.PreLabel, change.Region.PreLabelAddress, Symbol.Source.AddrPreLabel, Symbol.Type.ExternalAddr, Symbol.LabelAnnotation.None); symbolOffsets[newSym] = change.Region.Offset; } } foreach (Symbol sym in symTab) { PlSymbol.Source plsSource; int symOff, offset = -1; switch (sym.SymbolSource) { case Symbol.Source.User: plsSource = PlSymbol.Source.User; if (symbolOffsets.TryGetValue(sym, out symOff)) { offset = symOff; } break; case Symbol.Source.AddrPreLabel: plsSource = PlSymbol.Source.AddrPreLabel; if (symbolOffsets.TryGetValue(sym, out symOff)) { offset = symOff; } break; case Symbol.Source.Project: plsSource = PlSymbol.Source.Project; break; case Symbol.Source.Platform: plsSource = PlSymbol.Source.Platform; break; case Symbol.Source.Auto: case Symbol.Source.Variable: // don't forward these to plugins continue; default: Debug.Assert(false); continue; } PlSymbol.Type plsType; switch (sym.SymbolType) { case Symbol.Type.NonUniqueLocalAddr: // don't forward these to plugins continue; case Symbol.Type.LocalOrGlobalAddr: case Symbol.Type.GlobalAddr: case Symbol.Type.GlobalAddrExport: case Symbol.Type.ExternalAddr: plsType = PlSymbol.Type.Address; break; case Symbol.Type.Constant: plsType = PlSymbol.Type.Constant; break; default: Debug.Assert(false); continue; } int width = -1; string tag = string.Empty; if (sym is DefSymbol) { DefSymbol defSym = sym as DefSymbol; width = defSym.DataDescriptor.Length; tag = defSym.Tag; } plSymbols.Add(new PlSymbol(sym.Label, sym.Value, width, plsSource, plsType, tag, offset)); } return(plSymbols); }
/// <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); }