/// <summary> /// Creates one or more FormatDescriptor entries for the specified range, adding them /// to the Results list. /// </summary> /// <param name="low">Offset of first byte in range.</param> /// <param name="high">Offset of last byte in range.</param> /// <param name="subType">String sub-type.</param> private void CreateLengthStringEntries(int low, int high, FormatDescriptor.SubType subType) { int i; for (i = low; i <= high;) { int length = mFileData[i]; if (subType == FormatDescriptor.SubType.L16String) { length |= mFileData[i + 1] << 8; length += 2; } else { length++; } // Zero-length strings are allowed. FormatDescriptor dfd = FormatDescriptor.Create(length, FormatDescriptor.Type.String, subType); Results.Add(i, dfd); i += length; } Debug.Assert(i == high + 1); }
/// <summary> /// Creates one or more FormatDescriptor entries for the specified range, adding them /// to the Results list. /// /// This will either create one entry that spans the entire range (for e.g. strings /// and bulk data), or create equal-sized chunks. /// </summary> /// <param name="type">Region data type.</param> /// <param name="subType">Region data sub-type.</param> /// <param name="chunkLength">Length of a chunk, or -1 for full buffer.</param> /// <param name="symbolRef">Symbol reference, or null if not applicable.</param> /// <param name="low">Offset of first byte in range.</param> /// <param name="high">Offset of last byte in range.</param> private void CreateSimpleEntries(FormatDescriptor.Type type, FormatDescriptor.SubType subType, int chunkLength, WeakSymbolRef symbolRef, int low, int high) { if (chunkLength == -1) { chunkLength = (high - low) + 1; } Debug.Assert(((high - low + 1) / chunkLength) * chunkLength == high - low + 1); // Either we have one chunk, or we have multiple chunks with the same type and // length. Either way, we only need to create the descriptor once. (This is // safe because FormatDescriptor instances are immutable.) // // Because certain details, like the fill byte and high-vs-low ASCII, are pulled // out of the data stream at format time, we don't have to dig for them now. FormatDescriptor dfd; if (subType == FormatDescriptor.SubType.Symbol) { dfd = FormatDescriptor.Create(chunkLength, symbolRef, type == FormatDescriptor.Type.NumericBE); } else { dfd = FormatDescriptor.Create(chunkLength, type, subType); } while (low <= high) { Results.Add(low, dfd); low += chunkLength; } }
/// <summary> /// Creates a format descriptor for a single-byte numeric value. /// </summary> /// <param name="offset">File offset.</param> /// <param name="subType">How to format the item.</param> private void CreateByteFD(int offset, FormatDescriptor.SubType subType) { FormatDescriptor dfd = FormatDescriptor.Create(1, FormatDescriptor.Type.NumericLE, subType); Results.Add(offset, dfd); }
private void okButton_Click(object sender, EventArgs e) { bool isConstant = constantRadioButton.Checked; ParseValue(out int value, out int numBase); FormatDescriptor.SubType subType = FormatDescriptor.GetSubTypeForBase(numBase); DefSym = new DefSymbol(labelTextBox.Text, value, Symbol.Source.Project, isConstant ? Symbol.Type.Constant : Symbol.Type.ExternalAddr, subType, commentTextBox.Text, string.Empty); }
/// <summary> /// Constructor. Used for platform symbol files. /// </summary> /// <param name="loadOrdinal">Indicates the order in which the defining platform /// symbol file was loaded. Higher numbers indicate later loading, which translates /// to higher priority.</param> /// <param name="fileIdent">Platform symbol file identifier, for the Info panel.</param> public DefSymbol(string label, int value, Source source, Type type, FormatDescriptor.SubType formatSubType, int width, bool widthSpecified, string comment, DirectionFlags direction, MultiAddressMask multiMask, string tag, int loadOrdinal, string fileIdent) : this(label, value, source, type, formatSubType, width, widthSpecified, comment, direction, multiMask, tag) { LoadOrdinal = loadOrdinal; FileIdentifier = fileIdent; }
/// <summary> /// Creates one or more FormatDescriptor entries for the specified range, adding them /// to the Results list. /// </summary> /// <param name="low">Offset of first byte in range.</param> /// <param name="high">Offset of last byte in range.</param> /// <param name="subType">String sub-type.</param> private void CreateMixedStringEntries(int low, int high, FormatDescriptor.SubType subType) { int stringStart = -1; int highBit = 0; int cur; for (cur = low; cur <= high; cur++) { byte val = mFileData[cur]; if (CommonUtil.TextUtil.IsHiLoAscii(val)) { // is ASCII if (stringStart >= 0) { // was in a string if (highBit != (val & 0x80)) { // end of string due to high bit flip, output CreateStringOrByte(stringStart, cur - stringStart, subType); // start a new string stringStart = cur; } else { // still in string, keep going } } else { // wasn't in a string, start one stringStart = cur; } highBit = val & 0x80; } else { // not ASCII if (stringStart >= 0) { // was in a string, output it CreateStringOrByte(stringStart, cur - stringStart, subType); stringStart = -1; } // output as single byte CreateByteFD(cur, FormatDescriptor.SubType.Hex); } } if (stringStart >= 0) { // close out the string CreateStringOrByte(stringStart, cur - stringStart, subType); } }
/// <summary> /// Constructor. /// </summary> /// <param name="label">Symbol's label.</param> /// <param name="value">Symbol's value.</param> /// <param name="source">Symbol source (general point of origin).</param> /// <param name="type">Symbol type.</param> /// <param name="formatSubType">Format descriptor sub-type, so we know how the /// user wants the value to be displayed.</param> /// <param name="comment">End-of-line comment.</param> /// <param name="tag">Symbol tag, used for grouping platform symbols.</param> public DefSymbol(string label, int value, Source source, Type type, FormatDescriptor.SubType formatSubType, string comment, string tag) : this(label, value, source, type) { Debug.Assert(comment != null); Debug.Assert(tag != null); // Length doesn't matter; use 1 to get prefab object. DataDescriptor = FormatDescriptor.Create(1, FormatDescriptor.Type.NumericLE, formatSubType); Comment = comment; Tag = tag; }
private void OkButton_Click(object sender, RoutedEventArgs e) { ParseValue(out int value, out int numBase); FormatDescriptor.SubType subType = FormatDescriptor.GetSubTypeForBase(numBase); int width = -1; if (IsConstant && !IsVariable) { // width field is ignored, don't bother parsing } else if (!string.IsNullOrEmpty(VarWidth)) { bool ok = Asm65.Number.TryParseInt(VarWidth, out width, out int unusedNumBase); Debug.Assert(ok); } DefSymbol.DirectionFlags direction; if (IsReadChecked && IsWriteChecked) { direction = DefSymbol.DirectionFlags.ReadWrite; } else if (IsReadChecked) { direction = DefSymbol.DirectionFlags.Read; } else if (IsWriteChecked) { direction = DefSymbol.DirectionFlags.Write; } else { Debug.Assert(false); direction = DefSymbol.DirectionFlags.None; } // Parse and strip the annotation. string trimLabel = Symbol.TrimAndValidateLabel(Label, string.Empty, out bool unused1, out bool unused2, out bool unused3, out bool unused4, out Symbol.LabelAnnotation anno); NewSym = new DefSymbol(trimLabel, value, IsVariable ? Symbol.Source.Variable : Symbol.Source.Project, IsConstant ? Symbol.Type.Constant : Symbol.Type.ExternalAddr, anno, subType, width, width > 0, Comment, direction, null, string.Empty); DialogResult = true; }
/// <summary> /// Creates a format descriptor for ASCII data. If the data is only one byte long, /// a single-byte ASCII char item is emitted instead. /// </summary> /// <param name="offset">Offset of first byte.</param> /// <param name="length">Length of string.</param> /// <param name="subType">String sub-type.</param> private void CreateStringOrByte(int offset, int length, FormatDescriptor.SubType subType) { Debug.Assert(length > 0); if (length == 1) { // single byte, output as single ASCII char rather than 1-byte string CreateByteFD(offset, FormatDescriptor.SubType.Ascii); } else { FormatDescriptor dfd; dfd = FormatDescriptor.Create(length, FormatDescriptor.Type.String, subType); Results.Add(offset, dfd); } }
/// <summary> /// Creates one or more FormatDescriptor entries for the specified range, adding them /// to the Results list. /// </summary> /// <param name="low">Offset of first byte in range.</param> /// <param name="high">Offset of last byte in range.</param> /// <param name="subType">String sub-type.</param> private void CreateDciStringEntries(int low, int high, FormatDescriptor.SubType subType) { int start, end, adj, endMask; if (subType == FormatDescriptor.SubType.Dci) { start = low; end = high + 1; adj = 1; } else if (subType == FormatDescriptor.SubType.DciReverse) { start = high; end = low - 1; adj = -1; } else { Debug.Assert(false); return; } // Zero-length strings aren't a thing for DCI. The analyzer requires that all // strings in a region have the same polarity, so just grab the last byte. endMask = mFileData[end - 1] & 0x80; int stringStart = start; for (int i = start; i != end; i += adj) { byte val = mFileData[i]; if ((val & 0x80) == endMask) { // found the end of a string int length = (i - stringStart) * adj + 1; FormatDescriptor dfd = FormatDescriptor.Create(length, FormatDescriptor.Type.String, subType); Results.Add(stringStart < i ? stringStart : i, dfd); stringStart = i + adj; } } Debug.Assert(stringStart == end); }
/// <summary> /// Converts a FormatDescriptor SubType to a CharEncoding.Encoding value. /// </summary> /// <param name="subType">FormatDescriptor sub-type.</param> /// <returns>The corresponding CharEncoding.Encoding value, or Encoding.Unknown /// if the sub-type isn't a character encoding.</returns> public static CharEncoding.Encoding SubTypeToEnc(FormatDescriptor.SubType subType) { switch (subType) { case FormatDescriptor.SubType.Ascii: return(CharEncoding.Encoding.Ascii); case FormatDescriptor.SubType.HighAscii: return(CharEncoding.Encoding.HighAscii); case FormatDescriptor.SubType.C64Petscii: return(CharEncoding.Encoding.C64Petscii); case FormatDescriptor.SubType.C64Screen: return(CharEncoding.Encoding.C64ScreenCode); default: return(CharEncoding.Encoding.Unknown); } }
/// <summary> /// Constructor. General form. /// </summary> /// <param name="label">Symbol's label.</param> /// <param name="value">Symbol's value.</param> /// <param name="source">Symbol source (general point of origin).</param> /// <param name="type">Symbol type.</param> /// <param name="formatSubType">Format descriptor sub-type, so we know how the /// user wants the value to be displayed.</param> /// <param name="width">Variable width.</param> /// <param name="widthSpecified">True if width was explicitly specified. If this is /// <param name="comment">End-of-line comment.</param> /// <param name="direction">I/O direction.</param> /// <param name="multiMask">Bit mask to apply before comparisons.</param> /// <param name="tag">Symbol tag, used for grouping platform symbols.</param> /// false, the value of the "width" argument is ignored.</param> public DefSymbol(string label, int value, Source source, Type type, LabelAnnotation labelAnno, FormatDescriptor.SubType formatSubType, int width, bool widthSpecified, string comment, DirectionFlags direction, MultiAddressMask multiMask, string tag) : this(label, value, source, type, labelAnno) { Debug.Assert(comment != null); Debug.Assert(tag != null); if (widthSpecified && type == Type.Constant && source != Source.Variable) { // non-variable constants don't have a width; override arg Debug.WriteLine("Overriding constant DefSymbol width"); widthSpecified = false; } HasWidth = widthSpecified; if (!widthSpecified) { width = DEFAULT_WIDTH; } Debug.Assert(width >= MIN_WIDTH && width <= MAX_WIDTH); DataDescriptor = FormatDescriptor.Create(width, FormatDescriptor.Type.NumericLE, formatSubType); Comment = comment; Debug.Assert(((int)direction & ~(int)DirectionFlags.ReadWrite) == 0); Direction = direction; // constants don't have masks if (type != Type.Constant) { MultiMask = multiMask; } Tag = tag; }
/// <summary> /// Creates one or more FormatDescriptor entries for the specified range, adding them /// to the Results list. /// </summary> /// <param name="low">Offset of first byte in range.</param> /// <param name="high">Offset of last byte in range.</param> /// <param name="subType">String sub-type.</param> private void CreateCStringEntries(int low, int high, FormatDescriptor.SubType subType) { int startOffset = low; for (int i = low; i <= high; i++) { if (mFileData[i] == 0x00) { // End of string. Zero-length strings are allowed. FormatDescriptor dfd = FormatDescriptor.Create( i - startOffset + 1, FormatDescriptor.Type.String, subType); Results.Add(startOffset, dfd); startOffset = i + 1; } else { // keep going } } // Earlier analysis guaranteed that the last byte in the buffer is 0x00. Debug.Assert(startOffset == high + 1); }
/// <summary> /// Converts a collection of bytes that represent a string into an array of characters, /// stripping the high bit. Framing data, such as leading lengths and trailing nulls, /// are not shown. /// </summary> /// <param name="formatter">Formatter object.</param> /// <param name="subType">String sub-type.</param> /// <param name="data">File data.</param> /// <param name="offset">Offset, within data, of start of string.</param> /// <param name="length">Number of bytes to convert.</param> /// <param name="popcode">Pseudo-opcode string.</param> /// <param name="showHexZeroes">If nonzero, show 1+ zeroes (representing a leading /// length or null-termination) instead of an empty string.</param> /// <returns>Array of characters with string data.</returns> private static char[] BytesToChars(Formatter formatter, PseudoOpNames opNames, FormatDescriptor.SubType subType, byte[] data, int offset, int length, out string popcode, out int showHexZeroes) { Debug.Assert(length > 0); // See also GenMerlin32.OutputString(). int strOffset = offset; int strLen = length; bool highAscii = false; bool reverse = false; showHexZeroes = 0; switch (subType) { case FormatDescriptor.SubType.None: // High or low ASCII, full width specified by formatter. highAscii = (data[offset] & 0x80) != 0; popcode = highAscii ? opNames.StrGenericHi : opNames.StrGeneric; break; case FormatDescriptor.SubType.Dci: // High or low ASCII, full width specified by formatter. highAscii = (data[offset] & 0x80) != 0; popcode = highAscii ? opNames.StrDciHi : opNames.StrDci; break; case FormatDescriptor.SubType.Reverse: // High or low ASCII, full width specified by formatter. Show characters // in reverse order. highAscii = (data[offset + strLen - 1] & 0x80) != 0; popcode = highAscii ? opNames.StrReverseHi : opNames.StrReverse; reverse = true; break; case FormatDescriptor.SubType.DciReverse: // High or low ASCII, full width specified by formatter. Show characters // in reverse order. highAscii = (data[offset + strLen - 1] & 0x80) != 0; popcode = highAscii ? opNames.StrDciReverseHi : opNames.StrDciReverse; reverse = true; break; case FormatDescriptor.SubType.CString: // High or low ASCII, with a terminating null. Don't show the null. If // it's an empty string, just show the null byte as hex. highAscii = (data[offset] & 0x80) != 0; popcode = highAscii ? opNames.StrNullTermHi : opNames.StrNullTerm; strLen--; if (strLen == 0) { showHexZeroes = 1; } break; case FormatDescriptor.SubType.L8String: // High or low ASCII, with a leading length byte. Don't show the null. // If it's an empty string, just show the length byte as hex. strOffset++; strLen--; if (strLen == 0) { showHexZeroes = 1; } else { highAscii = (data[strOffset] & 0x80) != 0; } popcode = highAscii ? opNames.StrLen8Hi : opNames.StrLen8; break; case FormatDescriptor.SubType.L16String: // High or low ASCII, with a leading length word. Don't show the null. // If it's an empty string, just show the length word as hex. Debug.Assert(strLen > 1); strOffset += 2; strLen -= 2; if (strLen == 0) { showHexZeroes = 2; } else { highAscii = (data[strOffset] & 0x80) != 0; } popcode = highAscii ? opNames.StrLen16Hi : opNames.StrLen16; break; default: Debug.Assert(false); popcode = ".!!!"; break; } char[] text = new char[strLen]; if (!reverse) { for (int i = 0; i < strLen; i++) { text[i] = (char)(data[i + strOffset] & 0x7f); } } else { for (int i = 0; i < strLen; i++) { text[i] = (char)(data[strOffset + (strLen - i - 1)] & 0x7f); } } return(text); }
/// <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); }
/// <summary> /// Creates a list of FormatDescriptors, based on the current control configuration. /// /// The entries in the list are guaranteed to be sorted by start address and not /// overlap. /// /// We assume that whatever the control gives us is correct, e.g. it's not going /// to tell us to put a buffer full of zeroes into a DCI string. /// </summary> /// <returns>Result list.</returns> private void CreateDescriptorListFromControls() { FormatDescriptor.Type type = FormatDescriptor.Type.Default; FormatDescriptor.SubType subType = FormatDescriptor.SubType.None; WeakSymbolRef symbolRef = null; int chunkLength = -1; // Decode the "display as" panel, if it's relevant. if (radioSimpleDataHex.Enabled) { if (radioSimpleDataHex.Checked) { subType = FormatDescriptor.SubType.Hex; } else if (radioSimpleDataDecimal.Checked) { subType = FormatDescriptor.SubType.Decimal; } else if (radioSimpleDataBinary.Checked) { subType = FormatDescriptor.SubType.Binary; } else if (radioSimpleDataAscii.Checked) { subType = FormatDescriptor.SubType.Ascii; } else if (radioSimpleDataAddress.Checked) { subType = FormatDescriptor.SubType.Address; } else if (radioSimpleDataSymbolic.Checked) { WeakSymbolRef.Part part; if (radioSymbolPartLow.Checked) { part = WeakSymbolRef.Part.Low; } else if (radioSymbolPartHigh.Checked) { part = WeakSymbolRef.Part.High; } else if (radioSymbolPartBank.Checked) { part = WeakSymbolRef.Part.Bank; } else { Debug.Assert(false); part = WeakSymbolRef.Part.Low; } subType = FormatDescriptor.SubType.Symbol; symbolRef = new WeakSymbolRef(symbolEntryTextBox.Text, part); } else { Debug.Assert(false); } } else { subType = 0; // set later, or doesn't matter } // Decode the main format. if (radioDefaultFormat.Checked) { // Default/None; note this would create a multi-byte Default format, which isn't // really allowed. What we actually want to do is remove the explicit formatting // from all spanned offsets, so we use a dedicated type for that. type = FormatDescriptor.Type.REMOVE; } else if (radioSingleBytes.Checked) { type = FormatDescriptor.Type.NumericLE; chunkLength = 1; } else if (radio16BitLittle.Checked) { type = FormatDescriptor.Type.NumericLE; chunkLength = 2; } else if (radio16BitBig.Checked) { type = FormatDescriptor.Type.NumericBE; chunkLength = 2; } else if (radio24BitLittle.Checked) { type = FormatDescriptor.Type.NumericLE; chunkLength = 3; } else if (radio32BitLittle.Checked) { type = FormatDescriptor.Type.NumericLE; chunkLength = 4; } else if (radioDenseHex.Checked) { type = FormatDescriptor.Type.Dense; } else if (radioFill.Checked) { type = FormatDescriptor.Type.Fill; } else if (radioStringMixed.Checked) { type = FormatDescriptor.Type.String; } else if (radioStringMixedReverse.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.Reverse; } else if (radioStringNullTerm.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.CString; } else if (radioStringLen8.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.L8String; } else if (radioStringLen16.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.L16String; } else if (radioStringDci.Checked) { type = FormatDescriptor.Type.String; subType = FormatDescriptor.SubType.Dci; //} else if (radioStringDciReverse.Checked) { // type = FormatDescriptor.Type.String; // subType = FormatDescriptor.SubType.DciReverse; } else { Debug.Assert(false); // default/none } Results = new SortedList <int, FormatDescriptor>(); IEnumerator <TypedRangeSet.TypedRange> iter = Selection.RangeListIterator; while (iter.MoveNext()) { TypedRangeSet.TypedRange rng = iter.Current; if (type == FormatDescriptor.Type.String) { // We want to create one FormatDescriptor object per string. That way // each string gets its own line. if ((subType == FormatDescriptor.SubType.None || subType == FormatDescriptor.SubType.Reverse)) { CreateMixedStringEntries(rng.Low, rng.High, subType); } else if (subType == FormatDescriptor.SubType.CString) { CreateCStringEntries(rng.Low, rng.High, subType); } else if (subType == FormatDescriptor.SubType.L8String || subType == FormatDescriptor.SubType.L16String) { CreateLengthStringEntries(rng.Low, rng.High, subType); } else if (subType == FormatDescriptor.SubType.Dci || subType == FormatDescriptor.SubType.DciReverse) { CreateDciStringEntries(rng.Low, rng.High, subType); } else { Debug.Assert(false); CreateMixedStringEntries(rng.Low, rng.High, subType); // shrug } } else { CreateSimpleEntries(type, subType, chunkLength, symbolRef, rng.Low, rng.High); } } }
/// <summary> /// Constructor. Limited form, used in a couple of places, e.g. when we need to start /// with a default value. The symbol will have unspecified width, ReadWrite direction, /// and no mask. /// </summary> /// <param name="label">Symbol's label.</param> /// <param name="value">Symbol's value.</param> /// <param name="source">Symbol source (general point of origin).</param> /// <param name="type">Symbol type.</param> /// <param name="formatSubType">Format descriptor sub-type, so we know how the /// user wants the value to be displayed.</param> public DefSymbol(string label, int value, Source source, Type type, FormatDescriptor.SubType formatSubType) : this(label, value, source, type, formatSubType, -1, false, string.Empty, DirectionFlags.ReadWrite, null, string.Empty) { }
/// <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); }