/// <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 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 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); }
// IGenerator public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd, int operand) { if (dfd.FormatType == FormatDescriptor.Type.NumericLE && dfd.IsStringOrCharacter && (operand & 0x7f) == (byte)',') { // Merlin throws an error on comma operands, e.g. LDA #',' dfd = FormatDescriptor.Create(dfd.Length, FormatDescriptor.Type.NumericLE, FormatDescriptor.SubType.None); } return(dfd); }
// IGenerator public FormatDescriptor ModifyInstructionOperandFormat(int offset, FormatDescriptor dfd, int operand) { string badChars = ",{}"; if (dfd.FormatType == FormatDescriptor.Type.NumericLE && dfd.IsStringOrCharacter && badChars.IndexOf((char)(operand & 0x7f)) >= 0) { // Merlin throws an error on certain ASCII operands, e.g. LDA #',' dfd = FormatDescriptor.Create(dfd.Length, FormatDescriptor.Type.NumericLE, FormatDescriptor.SubType.None); } return(dfd); }
/// <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> /// 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); }
private void GenerateFormats(int div, int highConst, int bankConst) { SortedList <int, FormatDescriptor> newDfds = new SortedList <int, FormatDescriptor>(); Dictionary <int, Symbol> newLabels = new Dictionary <int, Symbol>(); List <int> targetOffsets = new List <int>(); bool isBigEndian; // Identify the offset where each set of data starts. int span = mSelection.Count / div; int lowOff, highOff, bankOff; int stride; if (lowFirstPartRadio.IsChecked == true) { lowOff = 0; isBigEndian = false; } else if (lowSecondPartRadio.IsChecked == true) { lowOff = 1; isBigEndian = true; } else if (lowThirdPartRadio.IsChecked == true) { lowOff = 2; isBigEndian = true; } else { Debug.Assert(false); lowOff = -1; isBigEndian = false; } if (highFirstPartRadio.IsChecked == true) { highOff = 0; } else if (highSecondPartRadio.IsChecked == true) { highOff = 1; } else if (highThirdPartRadio.IsChecked == true) { highOff = 2; } else { highOff = -1; // use constant } if (width24Radio.IsChecked == true) { if (bankNthPartRadio.IsChecked == true) { // Use whichever part isn't being used by the other two. if (lowOff != 0 && highOff != 0) { bankOff = 0; } else if (lowOff != 1 && highOff != 1) { bankOff = 1; } else { Debug.Assert(lowOff != 2 && highOff != 2); bankOff = 2; } } else { bankOff = -1; // use constant } } else { bankOff = -1; // use constant bankConst = 0; // always bank 0 } if (IsSplitTable) { // Split table, so stride is 1 and each section start is determined by the span. stride = 1; lowOff *= span; highOff *= span; bankOff *= span; } else { // For non-split table, the stride is the width of each entry. stride = 1; if (highOff >= 0) { stride++; } if (bankOff >= 0) { stride++; } } Debug.WriteLine("FormatAddressTable: stride=" + stride + " span=" + span + " count=" + mSelection.Count); Debug.WriteLine(" low=" + lowOff + " high=" + highOff + " bank=" + bankOff); // The TypedRangeSet doesn't have an index operation, so copy the values into // an array. int[] offsets = new int[mSelection.Count]; int index = 0; foreach (TypedRangeSet.Tuple tup in mSelection) { offsets[index++] = tup.Value; } int adj = 0; if (IsAdjustedForReturn) { adj = 1; } // Walk through the file data, generating addresses as we go. byte[] fileData = mProject.FileData; for (int i = 0; i < span; i++) { byte low, high, bank; low = fileData[offsets[lowOff + i * stride]]; if (highOff >= 0) { high = fileData[offsets[highOff + i * stride]]; } else { high = (byte)highConst; } if (bankOff >= 0) { bank = fileData[offsets[bankOff + i * stride]]; } else { bank = (byte)bankConst; } int addr = ((bank << 16) | (high << 8) | low) + adj; int targetOffset = mProject.AddrMap.AddressToOffset(offsets[0], addr); if (targetOffset < 0) { // Address not within file bounds. // TODO(maybe): look for matching platform/project symbols AddPreviewItem(addr, -1, Res.Strings.INVALID_ADDRESS); } else { // Note the same target offset may appear more than once. targetOffsets.Add(targetOffset); // If there's a user-defined label there already, use it. Otherwise, we'll // need to generate one. string targetLabel; if (mProject.UserLabels.TryGetValue(targetOffset, out Symbol sym)) { targetLabel = sym.Label; AddPreviewItem(addr, targetOffset, targetLabel); } else { // Generate a symbol that's unique vs. the symbol table. We don't need // it to be unique vs. the labels we're generating here, because we // won't generate identical labels for different addresses, and we do // want to generate a single label if more than one table entry refers // to the same target. Symbol tmpSym = AutoLabel.GenerateUniqueForAddress(addr, mProject.SymbolTable, "T"); // tmpSym was returned as an auto-label, make it a user label instead // (with global scope) tmpSym = new Symbol(tmpSym.Label, tmpSym.Value, Symbol.Source.User, Symbol.Type.GlobalAddr, Symbol.LabelAnnotation.Generated); newLabels[targetOffset] = tmpSym; // overwrites previous targetLabel = tmpSym.Label; AddPreviewItem(addr, targetOffset, "(+) " + targetLabel); } if (IsSplitTable) { // Now we need to create format descriptors for the addresses where we // extracted the low, high, and bank values. newDfds.Add(offsets[lowOff + i * stride], FormatDescriptor.Create(1, new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.Low), false)); if (highOff >= 0) { newDfds.Add(offsets[highOff + i * stride], FormatDescriptor.Create(1, new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.High), false)); } if (bankOff >= 0) { newDfds.Add(offsets[bankOff + i * stride], FormatDescriptor.Create(1, new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.Bank), false)); } } else { // Create a single format descriptor that spans all bytes. Note we // don't want to use lowOff here -- we want to put the format on // whichever byte came first. // TODO(maybe): we don't correctly deal with a "scrambled" non-split // 24-bit table, i.e. low then bank then high. This is not really // a thing, but we should either prevent it or punt to single-byte // like we do for split tables. Debug.Assert(stride >= 1 && stride <= 3); newDfds.Add(offsets[0 + i * stride], FormatDescriptor.Create(stride, new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.Low), isBigEndian)); } } } NewFormatDescriptors = newDfds; NewUserLabels = newLabels; AllTargetOffsets = targetOffsets; // Don't show ready if all addresses are invalid. It's okay if some work and // some don't. mOutputReady = (AllTargetOffsets.Count > 0); }
private void GenerateFormats(int div, int highConst, int bankConst) { SortedList <int, FormatDescriptor> newDfds = new SortedList <int, FormatDescriptor>(); Dictionary <int, Symbol> newLabels = new Dictionary <int, Symbol>(); List <int> targetOffsets = new List <int>(); // Identify the offset where each set of data starts. int span = mSelection.Count / div; int lowOff, highOff, bankOff; if (lowFirstPartRadio.Checked) { lowOff = 0; } else if (lowSecondPartRadio.Checked) { lowOff = span; } else if (lowThirdPartRadio.Checked) { lowOff = span * 2; } else { Debug.Assert(false); lowOff = -1; } if (highFirstPartRadio.Checked) { highOff = 0; } else if (highSecondPartRadio.Checked) { highOff = span; } else if (highThirdPartRadio.Checked) { highOff = span * 2; } else { highOff = -1; // use constant } if (width24Radio.Checked) { if (bankNthPartRadio.Checked) { // Use whichever part isn't being used by the other two. if (lowOff != 0 && highOff != 0) { bankOff = 0; } else if (lowOff != span && highOff != span) { bankOff = span; } else { Debug.Assert(lowOff != span * 2 && highOff != span * 2); bankOff = span * 2; } } else { bankOff = -1; // use constant } } else { bankOff = -1; // use constant bankConst = 0; // always bank 0 } Debug.WriteLine("Extract from low=" + lowOff + " high=" + highOff + " bank=" + bankOff); // The TypedRangeSet doesn't have an index operation, so copy the values into // an array. int[] offsets = new int[mSelection.Count]; int index = 0; foreach (TypedRangeSet.Tuple tup in mSelection) { offsets[index++] = tup.Value; } int adj = 0; if (pushRtsCheckBox.Checked) { adj = 1; } // Walk through the file data, generating addresses as we go. byte[] fileData = mProject.FileData; for (int i = 0; i < span; i++) { byte low, high, bank; low = fileData[offsets[lowOff + i]]; if (highOff >= 0) { high = fileData[offsets[highOff + i]]; } else { high = (byte)highConst; } if (bankOff >= 0) { bank = fileData[offsets[bankOff + i]]; } else { bank = (byte)bankConst; } int addr = ((bank << 16) | (high << 8) | low) + adj; int targetOffset = mProject.AddrMap.AddressToOffset(offsets[0], addr); if (targetOffset < 0) { // Address not within file bounds. // TODO(maybe): look for matching platform/project symbols AddPreviewItem(addr, -1, Properties.Resources.INVALID_ADDRESS); } else { // Note the same target offset may appear more than once. targetOffsets.Add(targetOffset); // If there's a user-defined label there already, use it. Otherwise, we'll // need to generate one. string targetLabel; if (mProject.UserLabels.TryGetValue(targetOffset, out Symbol sym)) { targetLabel = sym.Label; AddPreviewItem(addr, targetOffset, targetLabel); } else { // Generate a symbol that's unique vs. the symbol table. We don't need // it to be unique vs. the labels we're generating here, because we // won't generate identical labels for different addresses, and we do // want to generate a single label if more than one table entry refers // to the same target. Symbol tmpSym = SymbolTable.GenerateUniqueForAddress(addr, mProject.SymbolTable, "T"); // tmpSym was returned as an auto-label, make it a user label instead tmpSym = new Symbol(tmpSym.Label, tmpSym.Value, Symbol.Source.User, Symbol.Type.LocalOrGlobalAddr); newLabels[targetOffset] = tmpSym; // overwrites previous targetLabel = tmpSym.Label; AddPreviewItem(addr, targetOffset, "(+) " + targetLabel); } // Now we need to create format descriptors for the addresses where we // extracted the low, high, and bank values. newDfds.Add(offsets[lowOff + i], FormatDescriptor.Create(1, new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.Low), false)); if (highOff >= 0) { newDfds.Add(offsets[highOff + i], FormatDescriptor.Create(1, new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.High), false)); } if (bankOff >= 0) { newDfds.Add(offsets[bankOff + i], FormatDescriptor.Create(1, new WeakSymbolRef(targetLabel, WeakSymbolRef.Part.Bank), false)); } } } NewFormatDescriptors = newDfds; NewUserLabels = newLabels; AllTargetOffsets = targetOffsets; // Don't show ready if all addresses are invalid. mOutputReady = (AllTargetOffsets.Count > 0); }
/// <summary> /// Creates a FormatDescriptor from the current state of the dialog controls. /// </summary> /// <returns>New FormatDescriptor.</returns> private FormatDescriptor CreateDescriptorFromControls() { if (radioButtonSymbol.Checked) { if (string.IsNullOrEmpty(symbolTextBox.Text)) { // empty symbol --> default format (intuitive way to delete label reference) return(null); } WeakSymbolRef.Part part; if (radioButtonLow.Checked) { part = WeakSymbolRef.Part.Low; } else if (radioButtonHigh.Checked) { part = WeakSymbolRef.Part.High; } else if (radioButtonBank.Checked) { part = WeakSymbolRef.Part.Bank; } else { Debug.Assert(false); part = WeakSymbolRef.Part.Low; } return(FormatDescriptor.Create(mInstructionLength, new WeakSymbolRef(symbolTextBox.Text, part), false)); } FormatDescriptor.SubType subType; if (radioButtonDefault.Checked) { return(null); } else if (radioButtonHex.Checked) { subType = FormatDescriptor.SubType.Hex; } else if (radioButtonDecimal.Checked) { subType = FormatDescriptor.SubType.Decimal; } else if (radioButtonBinary.Checked) { subType = FormatDescriptor.SubType.Binary; } else if (radioButtonAscii.Checked) { subType = FormatDescriptor.SubType.Ascii; } else if (radioButtonSymbol.Checked) { subType = FormatDescriptor.SubType.Symbol; } else { Debug.Assert(false); subType = FormatDescriptor.SubType.None; } return(FormatDescriptor.Create(mInstructionLength, FormatDescriptor.Type.NumericLE, subType)); }