/// <summary> /// Gets the symbol associated with a symbol reference. If uniquification is enabled, /// the unique-label map for the specified offset will be used to transform the /// symbol reference. /// </summary> /// <param name="offset">Offset of start of instruction.</param> /// <param name="symRef">Reference to symbol.</param> /// <returns>Symbol, or null if no match found.</returns> public DefSymbol GetSymbol(int offset, WeakSymbolRef symRef) { AdvanceToOffset(offset); // The symRef uses the non-uniquified symbol, so we need to get the unique value at // the current offset. We may need to do this even when variables can be // redefined, because we might have a variable that's a duplicate of a user label // or project symbol. // Start by applying the de-duplication map. string label = symRef.Label; if (mDupRemap.TryGetValue(symRef.Label, out string remap)) { label = remap; } //Debug.WriteLine("GetSymbol " + symRef.Label + " -> " + label); if (mUniqueLabels != null && mUniqueLabels.TryGetValue(label, out UniqueLabel ulab)) { //Debug.WriteLine(" Unique var " + symRef.Label + " -> " + ulab.Label); label = ulab.Label; } DefSymbol defSym = mCurrentTable.GetByLabel(label); // In theory this is okay, but in practice the only things asking for symbols are // entirely convinced that the symbol exists here. So this is probably a bug. Debug.Assert(defSym != null); return(defSym); }
public void Add(DefSymbol defSym) { bool doRead = true; bool doWrite = true; if (defSym.Direction == DefSymbol.DirectionFlags.Read) { doWrite = false; } else if (defSym.Direction == DefSymbol.DirectionFlags.Write) { doRead = false; } for (int i = 0; i < defSym.DataDescriptor.Length; i++) { // See if there's already something here. If we reach the end of the // bank, wrap around. int addr = (defSym.Value & 0xff0000) + ((defSym.Value + i) & 0xffff); addr &= mMultiMask.AddressMask; // use minimal address DefSymbol curSym; if (doRead) { mByReadAddress.TryGetValue(addr, out curSym); mByReadAddress[addr] = (curSym == null) ? defSym : (DefSymbol)HighestPriority(defSym, curSym); } if (doWrite) { mByWriteAddress.TryGetValue(addr, out curSym); mByWriteAddress[addr] = (curSym == null) ? defSym : (DefSymbol)HighestPriority(defSym, curSym); } } }
/// <summary> /// Constructs a DefSymbol from an existing DefSymbol, with a different label. Use /// this to change the label while keeping everything else the same. /// </summary> /// <remarks> /// This can't be a simple Rename() function that uses a copy constructor because /// the label is in the base class. /// /// The Xrefs reference points to the actual XrefSet in the original. This is not /// ideal, but it's the easiest way to keep xrefs working across Lv de-duplication /// (you actually *want* xrefs added to copies to be held by the original). /// </remarks> /// <param name="defSym">Source DefSymbol.</param> /// <param name="label">Label to use.</param> public DefSymbol(DefSymbol defSym, string label) : this(label, defSym.Value, defSym.SymbolSource, defSym.SymbolType, defSym.LabelAnno, defSym.DataDescriptor.FormatSubType, defSym.DataDescriptor.Length, defSym.HasWidth, defSym.Comment, defSym.Direction, defSym.MultiMask, defSym.Tag) { Debug.Assert(SymbolSource == Source.Variable); Xrefs = defSym.Xrefs; }
/// <summary> /// Searches the table for symbols with matching address values. Ignores constants and /// variables. /// </summary> /// <param name="addr">Address to find.</param> /// <returns>First matching symbol found, or null if nothing matched.</returns> public Symbol FindNonVariableByAddress(int addr, OpDef.MemoryEffect effect) { bool tryRead, tryWrite; if (effect == OpDef.MemoryEffect.Read) { tryRead = true; tryWrite = false; } else if (effect == OpDef.MemoryEffect.Write) { tryRead = false; tryWrite = true; } else if (effect == OpDef.MemoryEffect.ReadModifyWrite || effect == OpDef.MemoryEffect.None) { tryRead = tryWrite = true; } else { Debug.Assert(false); return(null); } Symbol sym = null; if (tryRead) { mSymbolsByReadAddress.TryGetValue(addr, out sym); } if (tryWrite && sym == null) { mSymbolsByWriteAddress.TryGetValue(addr, out sym); } if (sym == null) { // Nothing matched, check the match groups. foreach (KeyValuePair <DefSymbol.MultiAddressMask, MaskGroup> kvp in mMaskGroups) { DefSymbol.MultiAddressMask multiMask = kvp.Key; if ((addr & multiMask.CompareMask) == multiMask.CompareValue) { MaskGroup group = kvp.Value; DefSymbol defSym = kvp.Value.Find(addr, tryRead, tryWrite); if (defSym != null) { sym = defSym; break; } } } } return(sym); }
/// <summary> /// Restores a de-duplicated symbol to original form. /// </summary> /// <remarks> /// Another kluge on the de-duplication system. This is used by the instruction /// operand editor's "edit variable" shortcut mechanism, because trying to edit the /// DefSymbol with the de-duplicated name ends badly. /// </remarks> /// <param name="sym">Symbol to un-de-duplicate.</param> /// <returns>Original or un-de-duplicated symbol.</returns> public DefSymbol GetOriginalForm(DefSymbol sym) { string orig = UnDeDuplicate(sym.Label); if (orig == sym.Label) { return(sym); } return(new DefSymbol(sym, orig)); }
/// <summary> /// Adds a symbol to the address table. All affected addresses are updated. If an /// existing symbol is already present at an address, the new or old symbol will be /// selected in priority order. /// </summary> /// <param name="sym">Symbol to add.</param> private void AddAddressTableEntry(Symbol sym) { if (sym.IsConstant) { return; } if (sym.SymbolSource == Symbol.Source.Variable) { return; } if (sym is DefSymbol && ((DefSymbol)sym).MultiMask != null) { AddMultiMaskEntry((DefSymbol)sym); return; } bool doRead = true; bool doWrite = true; int width = 1; if (sym is DefSymbol) { DefSymbol defSym = (DefSymbol)sym; width = defSym.DataDescriptor.Length; if (defSym.Direction == DefSymbol.DirectionFlags.Read) { doWrite = false; } else if (defSym.Direction == DefSymbol.DirectionFlags.Write) { doRead = false; } } for (int i = 0; i < width; i++) { // See if there's already something here. If we reach the end of the // bank, wrap around. int addr = (sym.Value & 0xff0000) + ((sym.Value + i) & 0xffff); Symbol curSym; if (doRead) { mSymbolsByReadAddress.TryGetValue(addr, out curSym); mSymbolsByReadAddress[addr] = (curSym == null) ? sym : HighestPriority(sym, curSym); } if (doWrite) { mSymbolsByWriteAddress.TryGetValue(addr, out curSym); mSymbolsByWriteAddress[addr] = (curSym == null) ? sym : HighestPriority(sym, curSym); } } }
private void AddMultiMaskEntry(DefSymbol defSym) { DefSymbol.MultiAddressMask multiMask = defSym.MultiMask; mMaskGroups.TryGetValue(multiMask, out MaskGroup group); if (group == null) { group = new MaskGroup(multiMask); mMaskGroups.Add(multiMask, group); } group.Add(defSym); }
public SerLocalVariableTable(LocalVariableTable varTab) { Variables = new List <SerDefSymbol>(varTab.Count); for (int i = 0; i < varTab.Count; i++) { DefSymbol defSym = varTab[i]; Variables.Add(new SerDefSymbol(defSym)); } ClearPrevious = varTab.ClearPrevious; }
public SerDefSymbol(DefSymbol defSym) : base(defSym) { DataDescriptor = new SerFormatDescriptor(defSym.DataDescriptor); Comment = defSym.Comment; HasWidth = defSym.HasWidth; Direction = defSym.Direction.ToString(); if (defSym.MultiMask != null) { MultiMask = new SerMultiMask(defSym.MultiMask); } }
/// <summary> /// Finds symbols that overlap with the specified value and width. If more than one /// matching symbol is found, an arbitrary match will be returned. Comparisons are /// only performed between symbols of the same type, so addresses and constants do /// not clash. /// </summary> /// <param name="value">Value to compare.</param> /// <param name="width">Width to check, useful when checking for collisions. When /// doing a simple variable lookup, this should be set to 1.</param> /// <returns>One matching symbol, or null if none matched.</returns> public DefSymbol GetByValueRange(int value, int width, Symbol.Type type) { foreach (KeyValuePair <string, DefSymbol> kvp in mVarByLabel) { if (DefSymbol.CheckOverlap(kvp.Value, value, width, type)) { return(kvp.Value); } } return(null); }
/// <summary> /// Determines whether symbol one is "wider" than symbol two. It's wider if it /// has a width, and the other symbol either doesn't have a width or has a width /// but is narrower. /// </summary> public static bool IsWider(DefSymbol a, DefSymbol b) { if (!a.HasWidth && !b.HasWidth) { return(false); } else if (a.HasWidth && !b.HasWidth) { return(true); } else if (!a.HasWidth && b.HasWidth) { return(false); } else { return(a.DataDescriptor.Length > b.DataDescriptor.Length); } }
/// <summary> /// Determines whether a symbol overlaps with a region. Useful for variables. /// </summary> /// <param name="a">Symbol to check.</param> /// <param name="value">Address.</param> /// <param name="width">Symbol width.</param> /// <param name="type">Symbol type to check against.</param> /// <returns>True if the symbols overlap.</returns> public static bool CheckOverlap(DefSymbol a, int value, int width, Type type) { if (a.DataDescriptor.Length <= 0 || width <= 0) { return(false); } if (a.Value < 0 || value < 0) { return(false); } if (a.SymbolType != type) { return(false); } int maxStart = Math.Max(a.Value, value); int minEnd = Math.Min(a.Value + a.DataDescriptor.Length - 1, value + width - 1); return(maxStart <= minEnd); }
/// <summary> /// Creates a DefSymbol from a SerDefSymbol. /// </summary> /// <param name="serDefSym">Deserialized data.</param> /// <param name="contentVersion">Serialization version.</param> /// <param name="report">Error report object.</param> /// <param name="outDefSym">Created symbol.</param> /// <returns></returns> private static bool CreateDefSymbol(SerDefSymbol serDefSym, int contentVersion, FileLoadReport report, out DefSymbol outDefSym) { outDefSym = null; if (!CreateSymbol(serDefSym, report, out Symbol sym)) { return(false); } if (!CreateFormatDescriptor(serDefSym.DataDescriptor, contentVersion, report, out FormatDescriptor dfd)) { return(false); } DefSymbol.DirectionFlags direction; if (string.IsNullOrEmpty(serDefSym.Direction)) { direction = DefSymbol.DirectionFlags.ReadWrite; } else { try { direction = (DefSymbol.DirectionFlags) Enum.Parse(typeof(DefSymbol.DirectionFlags), serDefSym.Direction); } catch (ArgumentException) { report.Add(FileLoadItem.Type.Warning, Res.Strings.ERR_BAD_DEF_SYMBOL_DIR + ": " + serDefSym.Direction); return(false); } } DefSymbol.MultiAddressMask multiMask = null; if (serDefSym.MultiMask != null) { multiMask = new DefSymbol.MultiAddressMask(serDefSym.MultiMask.CompareMask, serDefSym.MultiMask.CompareValue, serDefSym.MultiMask.AddressMask); } outDefSym = DefSymbol.Create(sym, dfd, serDefSym.HasWidth, serDefSym.Comment, direction, multiMask); return(true); }
/// <summary> /// Adds an additional annotation to an EQU directive, indicating whether the symbol /// is a constant or an address, and (if address) how many bytes it spans. /// </summary> /// <param name="formatter">Formatter object.</param> /// <param name="operand">Formatted operand string.</param> /// <param name="defSym">Project/platform/variable symbol.</param> /// <returns></returns> public static string AnnotateEquDirective(Formatter formatter, string operand, DefSymbol defSym) { string typeStr; if (defSym.IsConstant) { if (defSym.SymbolSource == Symbol.Source.Variable) { typeStr = Res.Strings.EQU_STACK_RELATIVE; } else { typeStr = Res.Strings.EQU_CONSTANT; } } else { typeStr = Res.Strings.EQU_ADDRESS; } string msgStr = null; if (defSym.HasWidth) { msgStr = typeStr + "/" + defSym.DataDescriptor.Length; } else if (defSym.IsConstant) { // not entirely convinced we want this, but there's currently no other way // to tell the difference between an address and a constant from the code list msgStr = typeStr; } if (msgStr == null) { return(operand); } else { return(operand + " {" + msgStr + "}"); } }
public override bool Equals(object obj) { if (!(obj is DefSymbol)) { return(false); } // Do base-class equality comparison and the ReferenceEquals check. if (!base.Equals(obj)) { return(false); } // All fields must be equal, except Xrefs. DefSymbol other = (DefSymbol)obj; if (DataDescriptor != other.DataDescriptor || Comment != other.Comment || Tag != other.Tag) { return(false); } return(true); }
/// <summary> /// Adds a symbol to the variable table. Existing entries with the same name or /// overlapping values will be removed. /// </summary> /// <param name="newSym">Symbol to add.</param> public void AddOrReplace(DefSymbol newSym) { if (!newSym.IsConstant && newSym.SymbolType != Symbol.Type.ExternalAddr) { Debug.Assert(false, "Unexpected symbol type " + newSym.SymbolType); return; } if (!newSym.IsVariable) { Debug.Assert(false, "Unexpected symbol source " + newSym.SymbolSource); return; } // Remove existing entries that match on label or value. The value check must // take the width into account. if (mVarByLabel.TryGetValue(newSym.Label, out DefSymbol labelSym)) { mVarByLabel.Remove(labelSym.Label); mVarByValue.Remove(labelSym); } // Inefficient, but the list should be small. DefSymbol valSym; while ((valSym = GetByValueRange(newSym.Value, newSym.DataDescriptor.Length, newSym.SymbolType)) != null) { mVarByLabel.Remove(valSym.Label); mVarByValue.Remove(valSym); } mVarByLabel.Add(newSym.Label, newSym); mVarByValue.Add(newSym); Debug.Assert(mVarByValue.Count == mVarByLabel.Count); mNeedSort = true; }
/// <summary> /// Updates internal state to reflect the state of the world at the specified offset. /// </summary> /// <remarks> /// When the offset is greater than or equal to its value on a previous call, we can /// do an incremental update. If the offset moves backward, we have to reset and walk /// forward again. /// </remarks> /// <param name="targetOffset">Target offset.</param> private void AdvanceToOffset(int targetOffset) { if (mNextLvtIndex < 0) { return; } if (targetOffset < mRecentOffset) { // We went backwards. Reset(false); } while (mNextLvtOffset <= targetOffset) { if (!mProject.GetAnattrib(mNextLvtOffset).IsStart) { // Hidden table, ignore it. Debug.WriteLine("Ignoring LvTable at +" + mNextLvtOffset.ToString("x6")); } else { // Process this table. LocalVariableTable lvt = mLvTables.Values[mNextLvtIndex]; if (lvt.ClearPrevious) { mCurrentTable.Clear(); } // Create a list for GetVariablesDefinedAtOffset mRecentSymbols = new List <DefSymbol>(); mRecentOffset = mNextLvtOffset; // Merge the new entries into the work table. This automatically // discards entries that clash by name or value. for (int i = 0; i < lvt.Count; i++) { DefSymbol defSym = lvt[i]; string newLabel = defSym.Label; if (mMaskLeadingUnderscores && newLabel[0] == '_') { newLabel = AsmGen.LabelLocalizer.NO_UNDER_PFX + newLabel; } // Look for non-variable symbols with the same label. Ordinarily the // editor prevents this from happening, but there are ways to trick // the system (e.g. add a symbol while the LvTable is hidden, or have // a non-unique local promoted to global). We deal with it here. // // TODO(someday): this is not necessary for assemblers like Merlin 32 // that put variables in a separate namespace. if (mAllNvSymbols.TryGetValue(newLabel, out Symbol unused)) { Debug.WriteLine("Detected duplicate non-var label " + newLabel + " at +" + mNextLvtOffset.ToString("x6")); newLabel = GenerateDeDupLabel(newLabel); } if (newLabel != defSym.Label) { mDupRemap[defSym.Label] = newLabel; defSym = new DefSymbol(defSym, newLabel); } if (mDoUniquify) { if (mUniqueLabels.TryGetValue(defSym.Label, out UniqueLabel ulab)) { // We've seen this label before; generate a unique version by // increasing the appended number. ulab.MakeUnique(mAllNvSymbols); defSym = new DefSymbol(defSym, ulab.Label); } else { // Haven't seen this before. Add it to the unique-labels table. mUniqueLabels.Add(defSym.Label, new UniqueLabel(defSym.Label)); } } mCurrentTable.AddOrReplace(defSym); mRecentSymbols.Add(defSym); } //mCurrentTable.DebugDump(mNextLvtOffset); } // Update state to look for next table. mNextLvtIndex++; if (mNextLvtIndex < mLvTables.Keys.Count) { mNextLvtOffset = mLvTables.Keys[mNextLvtIndex]; } else { mNextLvtOffset = mProject.FileDataLength; // never reached } } }
/// <summary> /// Constructs a DefSymbol from an existing DefSymbol, with a different label. Use /// this to change the label while keeping everything else the same. /// </summary> /// <remarks> /// This can't be a simple Rename() function that uses a copy constructor because /// the label is in the base class. /// </remarks> /// <param name="defSym">Source DefSymbol.</param> /// <param name="label">Label to use.</param> public DefSymbol(DefSymbol defSym, string label) : this(label, defSym.Value, defSym.SymbolSource, defSym.SymbolType, defSym.DataDescriptor.FormatSubType, defSym.DataDescriptor.Length, defSym.HasWidth, defSym.Comment, defSym.Direction, defSym.MultiMask, defSym.Tag) { }
/// <summary> /// Loads platform symbols. /// </summary> /// <param name="fileIdent">Relative pathname of file to open.</param> /// <param name="report">Report of warnings and errors.</param> /// <returns>True on success (no errors), false on failure.</returns> public bool LoadFromFile(string fileIdent, string projectDir, out FileLoadReport report) { // These files shouldn't be enormous. Do it the easy way. report = new FileLoadReport(fileIdent); ExternalFile ef = ExternalFile.CreateFromIdent(fileIdent); if (ef == null) { report.Add(FileLoadItem.Type.Error, CommonUtil.Properties.Resources.ERR_FILE_NOT_FOUND + ": " + fileIdent); return(false); } string pathName = ef.GetPathName(projectDir); if (pathName == null) { report.Add(FileLoadItem.Type.Error, Properties.Resources.ERR_BAD_IDENT + ": " + fileIdent); return(false); } string[] lines; try { lines = File.ReadAllLines(pathName); } catch (IOException ioe) { Debug.WriteLine("Platform symbol load failed: " + ioe); report.Add(FileLoadItem.Type.Error, CommonUtil.Properties.Resources.ERR_FILE_NOT_FOUND + ": " + pathName); return(false); } string tag = string.Empty; int lineNum = 0; foreach (string line in lines) { lineNum++; // first line is line 1, says Vim and VisualStudio if (string.IsNullOrEmpty(line) || line[0] == ';') { // ignore } else if (line[0] == '*') { if (line.StartsWith(TAG_CMD)) { tag = ParseTag(line); } else { // Do something clever with *SYNOPSIS? Debug.WriteLine("CMD: " + line); } } else { MatchCollection matches = sNameValueRegex.Matches(line); if (matches.Count == 1) { //Debug.WriteLine("GOT '" + matches[0].Groups[1] + "' " + // matches[0].Groups[2] + " '" + matches[0].Groups[3] + "'"); string label = matches[0].Groups[1].Value; bool isConst = (matches[0].Groups[2].Value[0] == '='); string badParseMsg; int value, numBase; bool parseOk; if (isConst) { // Allow various numeric options, and preserve the value. parseOk = Asm65.Number.TryParseInt(matches[0].Groups[3].Value, out value, out numBase); badParseMsg = CommonUtil.Properties.Resources.ERR_INVALID_NUMERIC_CONSTANT; } else { // Allow things like "05/1000". Always hex. numBase = 16; parseOk = Asm65.Address.ParseAddress(matches[0].Groups[3].Value, (1 << 24) - 1, out value); badParseMsg = CommonUtil.Properties.Resources.ERR_INVALID_ADDRESS; } if (!parseOk) { report.Add(lineNum, FileLoadItem.NO_COLUMN, FileLoadItem.Type.Warning, badParseMsg); } else { string comment = matches[0].Groups[4].Value; if (comment.Length > 0) { // remove ';' comment = comment.Substring(1); } FormatDescriptor.SubType subType = FormatDescriptor.GetSubTypeForBase(numBase); DefSymbol symDef = new DefSymbol(label, value, Symbol.Source.Platform, isConst ? Symbol.Type.Constant : Symbol.Type.ExternalAddr, subType, comment, tag); if (mSymbols.ContainsKey(label)) { // This is very easy to do -- just define the same symbol twice // in the same file. We don't really need to do anything about // it though. Debug.WriteLine("NOTE: stomping previous definition of " + label); } mSymbols[label] = symDef; } } else { report.Add(lineNum, FileLoadItem.NO_COLUMN, FileLoadItem.Type.Warning, CommonUtil.Properties.Resources.ERR_SYNTAX); } } } return(!report.HasErrors); }
public SerDefSymbol(DefSymbol defSym) : base(defSym) { DataDescriptor = new SerFormatDescriptor(defSym.DataDescriptor); Comment = defSym.Comment; }
/// <summary> /// Loads platform symbols. /// </summary> /// <param name="fileIdent">External file identifier of symbol file.</param> /// <param name="projectDir">Full path to project directory.</param> /// <param name="loadOrdinal">Platform file load order.</param> /// <param name="report">Report of warnings and errors.</param> /// <returns>True on success (no errors), false on failure.</returns> public bool LoadFromFile(string fileIdent, string projectDir, int loadOrdinal, out FileLoadReport report) { report = new FileLoadReport(fileIdent); ExternalFile ef = ExternalFile.CreateFromIdent(fileIdent); if (ef == null) { report.Add(FileLoadItem.Type.Error, CommonUtil.Properties.Resources.ERR_FILE_NOT_FOUND + ": " + fileIdent); return(false); } string pathName = ef.GetPathName(projectDir); if (pathName == null) { report.Add(FileLoadItem.Type.Error, Res.Strings.ERR_BAD_IDENT + ": " + fileIdent); return(false); } // These files shouldn't be enormous. Just read the entire thing into a string array. string[] lines; try { lines = File.ReadAllLines(pathName); } catch (IOException ioe) { Debug.WriteLine("Platform symbol load failed: " + ioe); report.Add(FileLoadItem.Type.Error, CommonUtil.Properties.Resources.ERR_FILE_NOT_FOUND + ": " + pathName); return(false); } string tag = string.Empty; DefSymbol.MultiAddressMask multiMask = null; int lineNum = 0; foreach (string line in lines) { lineNum++; // first line is line 1, says Vim and VisualStudio if (string.IsNullOrEmpty(line) || line[0] == ';') { // ignore } else if (line[0] == '*') { if (line.StartsWith(TAG_CMD)) { tag = ParseTag(line); } else if (line.StartsWith(MULTI_MASK_CMD)) { if (!ParseMask(line, out multiMask, out string badMaskMsg)) { report.Add(lineNum, FileLoadItem.NO_COLUMN, FileLoadItem.Type.Warning, badMaskMsg); } //Debug.WriteLine("Mask is now " + mask.ToString("x6")); } else { // Do something clever with *SYNOPSIS? Debug.WriteLine("Ignoring CMD: " + line); } } else { MatchCollection matches = sNameValueRegex.Matches(line); if (matches.Count == 1) { string label = matches[0].Groups[GROUP_NAME].Value; char typeAndDir = matches[0].Groups[GROUP_TYPE].Value[0]; bool isConst = (typeAndDir == '='); DefSymbol.DirectionFlags direction = DefSymbol.DirectionFlags.ReadWrite; if (typeAndDir == '<') { direction = DefSymbol.DirectionFlags.Read; } else if (typeAndDir == '>') { direction = DefSymbol.DirectionFlags.Write; } string badParseMsg; int value, numBase; bool parseOk; string valueStr = matches[0].Groups[GROUP_VALUE].Value; if (isConst) { // Allow various numeric options, and preserve the value. We // don't limit the value range. parseOk = Asm65.Number.TryParseInt(valueStr, out value, out numBase); badParseMsg = CommonUtil.Properties.Resources.ERR_INVALID_NUMERIC_CONSTANT; } else if (valueStr.ToUpperInvariant().Equals(ERASE_VALUE_STR)) { parseOk = true; value = ERASE_VALUE; numBase = 10; badParseMsg = CommonUtil.Properties.Resources.ERR_INVALID_ADDRESS; } else { // Allow things like "05/1000". Always hex. numBase = 16; parseOk = Asm65.Address.ParseAddress(valueStr, (1 << 24) - 1, out value); // limit to positive 24-bit values parseOk &= (value >= 0 && value < 0x01000000); badParseMsg = CommonUtil.Properties.Resources.ERR_INVALID_ADDRESS; } int width = -1; string widthStr = matches[0].Groups[GROUP_WIDTH].Value; if (parseOk && !string.IsNullOrEmpty(widthStr)) { parseOk = Asm65.Number.TryParseInt(widthStr, out width, out int ignoredBase); if (parseOk) { if (width < DefSymbol.MIN_WIDTH || width > DefSymbol.MAX_WIDTH) { parseOk = false; badParseMsg = Res.Strings.ERR_INVALID_WIDTH; } } else { badParseMsg = CommonUtil.Properties.Resources.ERR_INVALID_NUMERIC_CONSTANT; } } if (parseOk && multiMask != null && !isConst) { // We need to ensure that all possible values fit within the mask. // We don't test AddressValue here, because it's okay for the // canonical value to be outside the masked range. int testWidth = (width > 0) ? width : 1; for (int testValue = value; testValue < value + testWidth; testValue++) { if ((testValue & multiMask.CompareMask) != multiMask.CompareValue) { parseOk = false; badParseMsg = Res.Strings.ERR_VALUE_INCOMPATIBLE_WITH_MASK; Debug.WriteLine("Mask FAIL: value=" + value.ToString("x6") + " width=" + width + " testValue=" + testValue.ToString("x6") + " mask=" + multiMask); break; } } } if (!parseOk) { report.Add(lineNum, FileLoadItem.NO_COLUMN, FileLoadItem.Type.Warning, badParseMsg); } else { string comment = matches[0].Groups[GROUP_COMMENT].Value; if (comment.Length > 0) { // remove ';' comment = comment.Substring(1); } FormatDescriptor.SubType subType = FormatDescriptor.GetSubTypeForBase(numBase); DefSymbol symDef = new DefSymbol(label, value, Symbol.Source.Platform, isConst ? Symbol.Type.Constant : Symbol.Type.ExternalAddr, subType, width, width > 0, comment, direction, multiMask, tag, loadOrdinal, fileIdent); if (mSymbols.ContainsKey(label)) { // This is very easy to do -- just define the same symbol twice // in the same file. We don't really need to do anything about // it though. Debug.WriteLine("NOTE: stomping previous definition of " + label); } mSymbols[label] = symDef; } } else { report.Add(lineNum, FileLoadItem.NO_COLUMN, FileLoadItem.Type.Warning, CommonUtil.Properties.Resources.ERR_SYNTAX); } } } return(!report.HasErrors); }
private static Symbol HighestPriority(Symbol sym1, Symbol sym2) { // First determinant is symbol source. User labels have highest priority, then // project symbols, then platform symbols, then auto labels. if ((int)sym1.SymbolSource < (int)sym2.SymbolSource) { return(sym1); } else if ((int)sym1.SymbolSource > (int)sym2.SymbolSource) { return(sym2); } // Same source. Are they platform symbols? if (sym1.SymbolSource == Symbol.Source.Platform) { // Sort by file load order. Symbols from files loaded later, which will have // a higher ordinal, have priority. int lo1 = ((DefSymbol)sym1).LoadOrdinal; int lo2 = ((DefSymbol)sym2).LoadOrdinal; if (lo1 > lo2) { return(sym1); } else if (lo1 < lo2) { return(sym2); } } // Same source, so this is e.g. two project symbol definitions that overlap. We // handle this by selecting whichever one was defined closer to the target address, // i.e. whichever one has the higher value. // TODO(someday): this mishandles bank wrap... do we care? if (sym1.Value > sym2.Value) { return(sym1); } else if (sym1.Value < sym2.Value) { return(sym2); } // Check widths. Prefer the narrower definition. if (sym1 is DefSymbol && sym2 is DefSymbol) { DefSymbol dsym1 = (DefSymbol)sym1; DefSymbol dsym2 = (DefSymbol)sym2; if (DefSymbol.IsWider(dsym1, dsym2)) { return(dsym2); } else if (DefSymbol.IsWider(dsym2, dsym1)) { return(dsym1); } } // In the absence of anything better, we select them alphabetically. (If they have // the same name, value, and source, there's not much to distinguish them anyway.) if (Asm65.Label.LABEL_COMPARER.Compare(sym1.Label, sym2.Label) < 0) { return(sym1); } else { return(sym2); } }
/// <summary> /// Updates internal state to reflect the state of the world at the specified offset. /// </summary> /// <remarks> /// When the offset is greater than or equal to its value on a previous call, we can /// do an incremental update. If the offset moves backward, we have to reset and walk /// forward again. /// </remarks> /// <param name="targetOffset">Target offset.</param> private void AdvanceToOffset(int targetOffset) { if (mNextLvtIndex < 0) { return; } if (targetOffset < mRecentOffset) { // We went backwards. Reset(); } while (mNextLvtOffset <= targetOffset) { if (!mProject.GetAnattrib(mNextLvtOffset).IsStart) { // Hidden table, ignore it. Debug.WriteLine("Ignoring LvTable at +" + mNextLvtOffset.ToString("x6")); } else { // Process this table. LocalVariableTable lvt = mLvTables.Values[mNextLvtIndex]; if (lvt.ClearPrevious) { mCurrentTable.Clear(); } // Create a list for GetVariablesDefinedAtOffset mRecentSymbols = new List <DefSymbol>(); mRecentOffset = mNextLvtOffset; // Merge the new entries into the work table. This automatically // discards entries that clash by name or value. for (int i = 0; i < lvt.Count; i++) { DefSymbol defSym = lvt[i]; // Look for non-variable symbols with the same label. Ordinarily the // editor prevents this from happening, but there are ways to trick // the system (e.g. add a symbol while the LvTable is hidden). We // deal with it here. if (mSymbolTable.TryGetNonVariableValue(defSym.Label, out Symbol unused)) { Debug.WriteLine("Detected duplicate non-var label " + defSym.Label + " at +" + mNextLvtOffset.ToString("x6")); string newLabel = DeDupLabel(defSym.Label); mDupRemap[defSym.Label] = newLabel; defSym = new DefSymbol(defSym, newLabel); } if (mDoUniquify) { if (mUniqueLabels.TryGetValue(defSym.Label, out UniqueLabel ulab)) { // We've seen this label before; generate a unique version by // increasing the appended number. ulab.MakeUnique(mSymbolTable); defSym = new DefSymbol(defSym, ulab.Label); } else { // Haven't seen this before. Add it to the unique-labels table. mUniqueLabels.Add(defSym.Label, new UniqueLabel(defSym.Label)); } } mCurrentTable.AddOrReplace(defSym); mRecentSymbols.Add(defSym); } //mCurrentTable.DebugDump(mNextLvtOffset); } // Update state to look for next table. mNextLvtIndex++; if (mNextLvtIndex < mLvTables.Keys.Count) { mNextLvtOffset = mLvTables.Keys[mNextLvtIndex]; } else { mNextLvtOffset = mProject.FileDataLength; // never reached } } }
/// <summary> /// Format a numeric operand value according to the specified sub-format. /// </summary> /// <param name="formatter">Text formatter.</param> /// <param name="symbolTable">Full table of project symbols.</param> /// <param name="lvLookup">Local variable lookup object. May be null if not /// formatting an instruction.</param> /// <param name="labelMap">Symbol label remap, for local label conversion. May be /// null.</param> /// <param name="dfd">Operand format descriptor.</param> /// <param name="offset">Offset of start of instruction or data pseudo-op, for /// variable name lookup. Okay to pass -1 when not formatting an instruction.</param> /// <param name="operandValue">Operand's value. For most things this comes directly /// out of the code, for relative branches it's a 24-bit absolute address.</param> /// <param name="operandLen">Length of operand, in bytes. For an instruction, this /// does not include the opcode byte. For a relative branch, this will be 2.</param> /// <param name="flags">Special handling.</param> public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable, LocalVariableLookup lvLookup, Dictionary <string, string> labelMap, FormatDescriptor dfd, int offset, int operandValue, int operandLen, FormatNumericOpFlags flags) { Debug.Assert(operandLen > 0); int hexMinLen = operandLen * 2; switch (dfd.FormatSubType) { case FormatDescriptor.SubType.None: case FormatDescriptor.SubType.Hex: case FormatDescriptor.SubType.Address: return(formatter.FormatHexValue(operandValue, hexMinLen)); case FormatDescriptor.SubType.Decimal: return(formatter.FormatDecimalValue(operandValue)); case FormatDescriptor.SubType.Binary: return(formatter.FormatBinaryValue(operandValue, hexMinLen * 4)); case FormatDescriptor.SubType.Ascii: case FormatDescriptor.SubType.HighAscii: case FormatDescriptor.SubType.C64Petscii: case FormatDescriptor.SubType.C64Screen: CharEncoding.Encoding enc = SubTypeToEnc(dfd.FormatSubType); return(formatter.FormatCharacterValue(operandValue, enc)); case FormatDescriptor.SubType.Symbol: if (lvLookup != null && dfd.SymbolRef.IsVariable) { Debug.Assert(operandLen == 1); // only doing 8-bit stuff DefSymbol defSym = lvLookup.GetSymbol(offset, dfd.SymbolRef); if (defSym != null) { StringBuilder sb = new StringBuilder(); FormatNumericSymbolCommon(formatter, defSym, null, dfd, operandValue, operandLen, flags, sb); return(sb.ToString()); } else { Debug.WriteLine("Local variable format failed"); Debug.Assert(false); return(formatter.FormatHexValue(operandValue, hexMinLen)); } } else if (symbolTable.TryGetNonVariableValue(dfd.SymbolRef.Label, out Symbol sym)) { StringBuilder sb = new StringBuilder(); switch (formatter.ExpressionMode) { case Formatter.FormatConfig.ExpressionMode.Common: FormatNumericSymbolCommon(formatter, sym, labelMap, dfd, operandValue, operandLen, flags, sb); break; case Formatter.FormatConfig.ExpressionMode.Cc65: FormatNumericSymbolCc65(formatter, sym, labelMap, dfd, operandValue, operandLen, flags, sb); break; case Formatter.FormatConfig.ExpressionMode.Merlin: FormatNumericSymbolMerlin(formatter, sym, labelMap, dfd, operandValue, operandLen, flags, sb); break; default: Debug.Assert(false, "Unknown expression mode " + formatter.ExpressionMode); return("???"); } return(sb.ToString()); } else { return(formatter.FormatHexValue(operandValue, hexMinLen)); } default: // should not see REMOVE or ASCII_GENERIC here Debug.Assert(false); return("???"); } }