/// <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); }
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); }
/// <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); }
public MaskGroup(DefSymbol.MultiAddressMask multiMask) { Debug.Assert(multiMask != null); mMultiMask = multiMask; }
/// <summary> /// Parses the mask value out of a mask command line. /// </summary> /// <param name="line">Line to parse.</param> /// <param name="multiMask">Parsed mask value, or null if the line was empty.</param> /// <returns>True if the mask was parsed successfully.</returns> private bool ParseMask(string line, out DefSymbol.MultiAddressMask multiMask, out string badMaskMsg) { Debug.Assert(line.StartsWith(MULTI_MASK_CMD)); const int MIN = 0; const int MAX = 0x00ffffff; badMaskMsg = Res.Strings.ERR_INVALID_MULTI_MASK; multiMask = null; string maskStr = line.Substring(MULTI_MASK_CMD.Length).Trim(); if (string.IsNullOrEmpty(maskStr)) { // empty line, disable mask return(true); } MatchCollection matches = sMaskRegex.Matches(maskStr); if (matches.Count != 1) { return(false); } string cmpMaskStr = matches[0].Groups[1].Value; string cmpValueStr = matches[0].Groups[2].Value; string addrMaskStr = matches[0].Groups[3].Value; int cmpMask, cmpValue, addrMask, ignoredBase; if (!Asm65.Number.TryParseInt(cmpMaskStr, out cmpMask, out ignoredBase) || cmpMask < MIN || cmpMask > MAX) { Debug.WriteLine("Bad cmpMask: " + cmpMaskStr); badMaskMsg = Res.Strings.ERR_INVALID_COMPARE_MASK; return(false); } if (!Asm65.Number.TryParseInt(cmpValueStr, out cmpValue, out ignoredBase) || cmpValue < MIN || cmpValue > MAX) { Debug.WriteLine("Bad cmpValue: " + cmpValueStr); badMaskMsg = Res.Strings.ERR_INVALID_COMPARE_VALUE; return(false); } if (!Asm65.Number.TryParseInt(addrMaskStr, out addrMask, out ignoredBase) || addrMask < MIN || addrMask > MAX) { Debug.WriteLine("Bad addrMask: " + addrMaskStr); badMaskMsg = Res.Strings.ERR_INVALID_ADDRESS_MASK; return(false); } // The two masks should not overlap: one represents bits that must be in a // specific state for a match to exist, the other indicates which bits are used // to select a specific register. This should be a warning. if ((cmpMask & ~addrMask) != cmpMask) { Debug.WriteLine("Warning: cmpMask/addrMask overlap"); badMaskMsg = Res.Strings.ERR_INVALID_CMP_ADDR_OVERLAP; return(false); } // If cmpValue has bits set that aren't in cmpMask, we will never find a match. if ((cmpValue & ~cmpMask) != 0) { Debug.WriteLine("cmpValue has unexpected bits set"); badMaskMsg = Res.Strings.ERR_INVALID_CMP_EXTRA_BITS; return(false); } multiMask = new DefSymbol.MultiAddressMask(cmpMask, cmpValue, addrMask); return(true); }
/// <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); }
public SerMultiMask(DefSymbol.MultiAddressMask multiMask) { CompareMask = multiMask.CompareMask; CompareValue = multiMask.CompareValue; AddressMask = multiMask.AddressMask; }