private AddressMap.AddressRegion TryCreateRegion(AddressMap.AddressRegion delRegion, int offset, int length, int addr, out AddressMap.AddResult result) { AddressMap tmpMap = mProject.AddrMap.Clone(); if (delRegion != null && !tmpMap.RemoveEntry(delRegion.Offset, delRegion.Length)) { Debug.Assert(false, "Failed to remove existing region"); result = AddressMap.AddResult.InternalError; return(null); } result = tmpMap.AddEntry(offset, length, addr); if (result != AddressMap.AddResult.Okay) { return(null); } AddressMap.AddressRegion newRegion = tmpMap.FindRegion(offset, length); if (newRegion == null) { // Shouldn't happen. Debug.Assert(false, "Failed to find region we just created"); result = AddressMap.AddResult.InternalError; return(null); } return(newRegion); }
/// <summary> /// Constructor. /// </summary> /// <param name="owner">Parent window.</param> /// <param name="curRegion">Current region; will be null for new entries.</param> /// <param name="newEntry">Prototype entry to create.</param> /// <param name="selectionLen">Length, in bytes, of the selection.</param> /// <param name="isSingleLine">True if the selection is a single line.</param> /// <param name="project">Project reference.</param> /// <param name="formatter">Text formatter object.</param> public EditAddress(Window owner, AddressMap.AddressRegion curRegion, AddressMap.AddressMapEntry newEntry, int selectionLen, bool isSingleLine, DisasmProject project, Formatter formatter) { InitializeComponent(); Owner = owner; DataContext = this; Debug.Assert((curRegion == null) ^ (newEntry == null)); // exactly one must be true mProject = project; mMaxAddressValue = project.CpuDef.MaxAddressValue; mShowBank = !project.CpuDef.HasAddr16; mFormatter = formatter; Configure(curRegion, newEntry, selectionLen, isSingleLine); UpdateControls(); }
// IGenerator public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) { // This is similar in operation to the AsmTass64 implementation. See comments there. Debug.Assert(mPcDepth >= 0); int nextAddress = change.Address; if (nextAddress == Address.NON_ADDR) { // Start non-addressable regions at zero to ensure they don't overflow bank. nextAddress = 0; } if (change.IsStart) { if (change.Region.HasValidPreLabel) { string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel); OutputLine(labelStr, string.Empty, string.Empty, string.Empty); } if (mPcDepth == 0 && mFirstIsOpen) { mPcDepth++; // Set the "real" PC for the first address change. If we're in "loadable" // mode, just set "*=". If we're in "streaming" mode, we set "*=" to zero // and then use a pseudo-PC. if (mOutputMode == OutputMode.Loadable) { OutputLine("*", "=", SourceFormatter.FormatHexValue(nextAddress, 4), string.Empty); return; } else { // set the real PC to address zero to ensure we get a full 64KB OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty); } } AddressMap.AddressRegion region = change.Region; string addrStr; if (region.HasValidIsRelative) { int diff = nextAddress - region.PreLabelAddress; string pfxStr; if (diff >= 0) { pfxStr = "*+"; } else { pfxStr = "*-"; diff = -diff; } addrStr = pfxStr + SourceFormatter.FormatHexValue(diff, 4); } else { addrStr = SourceFormatter.FormatHexValue(nextAddress, 4); } OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), addrStr + " {", string.Empty); mPcDepth++; } else { mPcDepth--; if (mPcDepth > 0 || !mFirstIsOpen) { // close previous block OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArEndDirective), string.Empty, string.Empty); //";" + SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective)); } else { // mark initial "*=" region as closed, but don't output anything mFirstIsOpen = false; } } }
// IGenerator public GenerationResults GenerateSource(BackgroundWorker worker) { List <string> pathNames = new List <string>(1); string fileName = mFileNameBase + ASM_FILE_SUFFIX; string pathName = Path.Combine(mWorkDirectory, fileName); pathNames.Add(pathName); Formatter.FormatConfig config = new Formatter.FormatConfig(); GenCommon.ConfigureFormatterFromSettings(Settings, ref config); SetFormatConfigValues(ref config); SourceFormatter = new Formatter(config); string msg = string.Format(Res.Strings.PROGRESS_GENERATING_FMT, pathName); worker.ReportProgress(0, msg); mLocalizer = new LabelLocalizer(Project); // While '.' labels are limited to the current zone, '@' labels are visible // between global labels. (This is poorly documented.) mLocalizer.LocalPrefix = "@"; mLocalizer.QuirkNoOpcodeMnemonics = true; mLocalizer.ReservedWords = new List <string>() { "NOT" }; mLocalizer.Analyze(); mPcDepth = 0; mFirstIsOpen = true; // Use UTF-8 encoding, without a byte-order mark. using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) { mOutStream = sw; if (Settings.GetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, false)) { OutputLine(SourceFormatter.FullLineCommentDelimiter + string.Format(Res.Strings.GENERATED_FOR_VERSION_FMT, "acme", mAsmVersion, AsmAcme.OPTIONS)); } if (HasNonZeroBankCode()) { // don't try OutputLine(SourceFormatter.FullLineCommentDelimiter + "ACME can't handle 65816 code that lives outside bank zero"); int firstAddr = Project.AddrMap.OffsetToAddress(0); AddressMap.AddressRegion fakeRegion = new AddressMap.AddressRegion(0, Project.FileData.Length, firstAddr); OutputArDirective(new AddressMap.AddressChange(true, 0, firstAddr, fakeRegion, true)); OutputDenseHex(0, Project.FileData.Length, string.Empty, string.Empty); OutputArDirective(new AddressMap.AddressChange(false, 0, firstAddr, fakeRegion, true)); } else { GenCommon.Generate(this, sw, worker); } } mOutStream = null; return(new GenerationResults(pathNames, string.Empty)); }
// IGenerator public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) { // 64tass separates the "compile offset", which determines where the output fits // into the generated binary, and "program counter", which determines the code // the assembler generates. Since we need to explicitly specify every byte in // the output file, having a distinct compile offset isn't useful here. We want // to set it once before the first line of code, then leave it alone. // // Any subsequent ORG changes are made to the program counter, and take the form // of a pair of ops (".logical <addr>" to open, ".here" to end). Omitting the .here // causes an error. // // If this is a "streamable" file, meaning it won't actually load into 64K of RAM // without wrapping around, then we skip the "* = addr" (same as "* = 0") and just // start with ".logical" segments. // // The assembler's approach is best represented by having an address region that // spans the entire file, with one or more "logical" regions inside. In practice // (especially for multi-bank 65816 code) that may not be the case, but the // assembler is still expecting us to start with a "* =" and then fit everything // inside that. So we treat the first region specially, whether or not it wraps // the rest of the file. Debug.Assert(mPcDepth >= 0); int nextAddress = change.Address; if (nextAddress == Address.NON_ADDR) { // Start non-addressable regions at zero to ensure they don't overflow bank. nextAddress = 0; } if (change.IsStart) { if (change.Region.HasValidPreLabel) { string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel); OutputLine(labelStr, string.Empty, string.Empty, string.Empty); } if (mPcDepth == 0 && mFirstIsOpen) { mPcDepth++; // Set the "real" PC for the first address change. If we're in "loadable" // mode, just set "*=". If we're in "streaming" mode, we set "*=" to zero // and then use a pseudo-PC. if (mOutputMode == OutputMode.Loadable) { OutputLine("*", "=", SourceFormatter.FormatHexValue(nextAddress, 4), string.Empty); return; } else { // Set the real PC to address zero to ensure we get a full 64KB. The // assembler assumes this as a default, so it can be omitted. //OutputLine("*", "=", SourceFormatter.FormatHexValue(0, 4), string.Empty); } } AddressMap.AddressRegion region = change.Region; string addrStr; if (region.HasValidIsRelative) { int diff = nextAddress - region.PreLabelAddress; string pfxStr; if (diff >= 0) { pfxStr = "*+"; } else { pfxStr = "*-"; diff = -diff; } addrStr = pfxStr + SourceFormatter.FormatHexValue(diff, 4); } else { addrStr = SourceFormatter.FormatHexValue(nextAddress, 4); } OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), addrStr, string.Empty); mPcDepth++; } else { mPcDepth--; if (mPcDepth > 0 || !mFirstIsOpen) { // close previous block OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArEndDirective), string.Empty, string.Empty); } else { // mark initial "*=" region as closed, but don't output anything mFirstIsOpen = false; } } }
// IGenerator public void OutputArDirective(CommonUtil.AddressMap.AddressChange change) { int nextAddress = change.Address; if (nextAddress == Address.NON_ADDR) { // Start non-addressable regions at zero to ensure they don't overflow bank. nextAddress = 0; } if (change.IsStart) { AddressMap.AddressRegion region = change.Region; if (region.HasValidPreLabel || region.HasValidIsRelative) { // Need to output the previous ORG, if one is pending. if (mNextAddress >= 0) { OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), SourceFormatter.FormatHexValue(mNextAddress, 4), string.Empty); } } if (region.HasValidPreLabel) { string labelStr = mLocalizer.ConvLabel(change.Region.PreLabel); OutputLine(labelStr, string.Empty, string.Empty, string.Empty); } if (region.HasValidIsRelative) { // Found a valid IsRelative. Switch to "relative mode" if not there already. mIsInRelative = true; } if (mIsInRelative) { // Once we see a region with IsRelative set, we output regions as we // find them until the next Flush. string addrStr; if (region.HasValidIsRelative) { int diff = nextAddress - region.PreLabelAddress; string pfxStr; if (diff >= 0) { pfxStr = "*+"; } else { pfxStr = "*-"; diff = -diff; } addrStr = pfxStr + SourceFormatter.FormatHexValue(diff, 4); } else { addrStr = SourceFormatter.FormatHexValue(nextAddress, 4); } OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(sDataOpNames.ArStartDirective), addrStr, string.Empty); mNextAddress = -1; return; } } mNextAddress = nextAddress; }
private void Configure(AddressMap.AddressRegion curRegion, AddressMap.AddressMapEntry newEntry, int selectionLen, bool isSingleLine) { Debug.WriteLine("Configuring AR: reg=" + curRegion + " newEnt=" + newEntry + " selLen=" + selectionLen + " isSingle=" + isSingleLine); ShowOption1 = ShowOption2 = true; EnableOption1 = EnableOption2 = true; CheckOption1 = true; EnableAttributeControls = true; string option1Summ; string option1Msg; string option2Summ; string option2Msg; if (curRegion != null) { // Editing an existing region. CanDeleteRegion = true; ShowExistingRegion = true; mOrigPreLabel = curRegion.PreLabel; mParentNonAddr = (curRegion.PreLabelAddress == Address.NON_ADDR); if (curRegion.Address == Address.NON_ADDR) { AddressText = Address.NON_ADDR_STR; } else { AddressText = Asm65.Address.AddressToString(curRegion.Address, false); } PreLabelText = curRegion.PreLabel; UseRelativeAddressing = curRegion.IsRelative; OperationStr = (string)FindResource("str_HdrEdit"); mRegionAddress = curRegion.Address; mRegionStartOffset = curRegion.Offset; mRegionEndOffset = curRegion.Offset + curRegion.ActualLength - 1; mPreLabelAddress = curRegion.PreLabelAddress; if (isSingleLine) { // Only thing selected was arstart/arend. First action is to edit // the region properties, second action is to convert floating end // to fixed. mResultEntry1 = new AddressMap.AddressMapEntry(curRegion.Offset, curRegion.Length, curRegion.Address, curRegion.PreLabel, curRegion.IsRelative); option1Summ = (string)FindResource("str_OptEditAsIsSummary"); option1Msg = (string)FindResource("str_OptEditAsIs"); if (curRegion.IsFloating) { option2Summ = (string)FindResource("str_OptEditAndFixSummary"); option2Msg = (string)FindResource("str_OptEditAndFix"); mResultEntry2 = new AddressMap.AddressMapEntry(curRegion.Offset, curRegion.ActualLength, curRegion.Address, curRegion.PreLabel, curRegion.IsRelative); } else { option2Summ = string.Empty; option2Msg = (string)FindResource("str_EditFixedAlreadyFixed"); mResultEntry2 = null; EnableOption2 = false; // show it, but disabled } } else { // Selection started with arstart and included multiple lines. First // action is to resize region. Second action is edit without resize. // If resize is illegal (e.g. new region exactly overlaps another), // first action is disabled. mResultEntry1 = new AddressMap.AddressMapEntry(curRegion.Offset, selectionLen, curRegion.Address, curRegion.PreLabel, curRegion.IsRelative); mResultEntry2 = new AddressMap.AddressMapEntry(curRegion.Offset, curRegion.Length, curRegion.Address, curRegion.PreLabel, curRegion.IsRelative); option1Summ = (string)FindResource("str_OptResizeSummary"); string fmt = (string)FindResource("str_OptResize"); option1Msg = string.Format(fmt, mFormatter.FormatOffset24(curRegion.Offset + selectionLen - 1), FormatLength(selectionLen)); option2Summ = (string)FindResource("str_OptEditAsIsSummary"); option2Msg = (string)FindResource("str_OptEditAsIs"); Debug.Assert(selectionLen > 0); AddressMap.AddResult ares; TryCreateRegion(curRegion, curRegion.Offset, selectionLen, curRegion.Address, out ares); if (ares != AddressMap.AddResult.Okay) { // Can't resize the new region, so disable that option (still visible). option1Summ = string.Empty; string fmta = (string)FindResource("str_OptResizeFail"); option1Msg = string.Format(fmta, GetErrorString(ares)); EnableOption1 = false; CheckOption2 = true; } if (curRegion.ActualLength == selectionLen) { // The selection size matches the region's length, which means they // have the entire region selected, so "resize" and "edit" do the same // thing. No real need to disable the resize option, but we can default // to "edit only" to emphasize that there's no actual change. CheckOption2 = true; } } } else { // Creating a new region. Prototype entry specifies offset, length, and address. // First action is to create a fixed-length region, second action is to create // a floating region. Default changes for single-item selections. CanDeleteRegion = false; ShowExistingRegion = false; mOrigPreLabel = string.Empty; if (newEntry.Address == Address.NON_ADDR) { AddressText = Address.NON_ADDR_STR; } else { AddressText = Asm65.Address.AddressToString(newEntry.Address, false); } PreLabelText = string.Empty; UseRelativeAddressing = false; OperationStr = (string)FindResource("str_HdrCreate"); AddressMap.AddResult ares1; AddressMap.AddressRegion newRegion1 = TryCreateRegion(null, newEntry.Offset, newEntry.Length, newEntry.Address, out ares1); AddressMap.AddResult ares2; AddressMap.AddressRegion newRegion2 = TryCreateRegion(null, newEntry.Offset, AddressMap.FLOATING_LEN, newEntry.Address, out ares2); if (isSingleLine) { // For single-line selection, create a floating region by default. CheckOption2 = true; } // If it failed, report the error. Most common reason will be a start offset // that overlaps an existing region. You can create a fixed region inside // a fixed region with the same start offset, but can't create a float there. if (ares1 == AddressMap.AddResult.Okay) { mResultEntry1 = new AddressMap.AddressMapEntry(newEntry.Offset, newRegion1.ActualLength, newEntry.Address, string.Empty, false); option1Summ = (string)FindResource("str_CreateFixedSummary"); string fmt = (string)FindResource("str_CreateFixed"); option1Msg = string.Format(fmt, mFormatter.FormatOffset24(newEntry.Offset), FormatLength(newRegion1.ActualLength)); mPreLabelAddress = newRegion1.PreLabelAddress; mParentNonAddr = (newRegion1.PreLabelAddress == Address.NON_ADDR); } else { option1Summ = string.Empty; if (ares1 == AddressMap.AddResult.StraddleExisting) { option1Msg = (string)FindResource("str_CreateFixedFailStraddle"); } else { option1Msg = (string)FindResource("str_CreateFixedFail"); } CheckOption2 = true; EnableOption1 = false; } if (ares2 == AddressMap.AddResult.Okay) { mResultEntry2 = new AddressMap.AddressMapEntry(newEntry.Offset, AddressMap.FLOATING_LEN, newEntry.Address, string.Empty, false); option2Summ = (string)FindResource("str_CreateFloatingSummary"); string fmt = (string)FindResource("str_CreateFloating"); option2Msg = string.Format(fmt, mFormatter.FormatOffset24(newEntry.Offset), FormatLength(newRegion2.ActualLength)); mPreLabelAddress = newRegion2.PreLabelAddress; mParentNonAddr = (newRegion2.PreLabelAddress == Address.NON_ADDR); } else { option2Summ = string.Empty; option2Msg = (string)FindResource("str_CreateFloatingFail"); CheckOption1 = true; CheckOption2 = false; // required for some reason EnableOption2 = false; } if (ares1 != AddressMap.AddResult.Okay && ares2 != AddressMap.AddResult.Okay) { // Unable to create region here. Explain why not. EnableAttributeControls = false; CheckOption1 = CheckOption2 = false; mPreLabelAddress = Address.NON_ADDR; SetErrorString(ares1); } } TextBlock tb1 = option1TextBlock; tb1.Inlines.Clear(); if (!string.IsNullOrEmpty(option1Summ)) { tb1.Inlines.Add(new Run(option1Summ + " ") { FontWeight = FontWeights.Bold }); } tb1.Inlines.Add(option1Msg); TextBlock tb2 = option2TextBlock; tb2.Inlines.Clear(); if (!string.IsNullOrEmpty(option2Summ)) { tb2.Inlines.Add(new Run(option2Summ + " ") { FontWeight = FontWeights.Bold }); } tb2.Inlines.Add(option2Msg); }