/*
* Copyright 2021 faddenSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using Asm65;
using CommonUtil;
using AddressChange = CommonUtil.AddressMap.AddressChange;
namespace SourceGen {
///
/// Functions for generating a human-readable form of the address map.
///
/// A graphical / interactive visualization would be nicer, but this can be pasted
/// into bug reports.
///
public static class RenderAddressMap {
private const string CRLF = "\r\n";
///
/// Formats the address map for viewing.
///
public static string GenerateString(DisasmProject project, Formatter formatter) {
AddressMap addrMap = project.AddrMap;
bool showBank = !project.CpuDef.HasAddr16;
StringBuilder sb = new StringBuilder();
int depth = 0;
int prevOffset = -1;
int prevAddr = 0;
int lastEndOffset = -1;
sb.AppendLine("Address region map for \"" + project.DataFileName + "\"");
sb.Append(CRLF);
IEnumerator iter = addrMap.AddressChangeIterator;
while (iter.MoveNext()) {
AddressChange change = iter.Current;
if (change.IsStart) {
//if (change.Offset == lastEndOffset) {
// // Extra vertical space for a START following an END at the same offset.
// PrintDepthLines(sb, depth, true);
// sb.Append(CRLF);
// lastEndOffset = -1;
//}
if (prevOffset >= 0 && change.Offset != prevOffset) {
// Start of region at new offset. Output address info for space
// between previous start or end.
PrintAddressInfo(sb, formatter, depth, prevAddr,
change.Offset - prevOffset, showBank);
}
// Start following end, or start following start after a gap.
sb.Append(formatter.FormatOffset24(change.Offset));
PrintDepthLines(sb, depth, false);
sb.Append("+- " + "start");
if (change.IsSynthetic) {
sb.Append(" (auto-generated)");
} else {
// If there's a label here, show it.
Anattrib attr = project.GetAnattrib(change.Offset);
if (attr.Symbol != null && !string.IsNullOrEmpty(attr.Symbol.Label)) {
sb.Append(" '");
sb.Append(attr.Symbol.GenerateDisplayLabel(formatter));
sb.Append("'");
}
}
if (change.Region.HasValidPreLabel) {
sb.Append(" pre='");
sb.Append(change.Region.PreLabel);
sb.Append("'");
}
if (change.Region.DisallowInward) {
sb.Append(" [!in]");
}
if (change.Region.DisallowOutward) {
sb.Append(" [!out]");
}
sb.Append(CRLF);
prevOffset = change.Offset;
prevAddr = change.Address;
depth++;
} else {
Debug.Assert(prevOffset >= 0);
depth--;
if (change.Offset + 1 != prevOffset) {
// End of region at new offset. Output address info for space
// between previous start or end.
PrintAddressInfo(sb, formatter, depth + 1, prevAddr,
change.Offset + 1 - prevOffset, showBank);
}
sb.Append(formatter.FormatOffset24(change.Offset));
PrintDepthLines(sb, depth, false);
sb.Append("+- " + "end");
if (change.Region.IsFloating) {
sb.Append(" (floating)");
}
//PrintAddress(sb, formatter, change.Address, showBank);
//sb.Append(")");
sb.Append(CRLF);
// Add a blank line, but with the depth lines.
if (depth > 0) {
PrintDepthLines(sb, depth, true);
}
sb.Append(CRLF);
// Use offset+1 here so it lines up with start records.
prevOffset = lastEndOffset = change.Offset + 1;
prevAddr = change.Address;
}
}
Debug.Assert(depth == 0);
return sb.ToString();
}
private static void PrintDepthLines(StringBuilder sb, int depth, bool doIndent) {
if (doIndent) {
sb.Append(" ");
}
sb.Append(" ");
while (depth-- > 0) {
sb.Append("| ");
}
}
private static void PrintAddressInfo(StringBuilder sb, Formatter formatter, int depth,
int startAddr, int length, bool showBank) {
//PrintDepthLines(sb, depth);
//sb.Append(CRLF);
PrintDepthLines(sb, depth, true);
sb.Append(' ');
if (startAddr == Address.NON_ADDR) {
sb.Append("-" + Address.NON_ADDR_STR + "-");
} else {
PrintAddress(sb, formatter, startAddr, showBank);
sb.Append(" - ");
PrintAddress(sb, formatter, startAddr + length - 1, showBank);
}
sb.Append(" length=" + length + " ($" + length.ToString("x4") + ")");
sb.Append(CRLF);
//PrintDepthLines(sb, depth, true);
//sb.Append(CRLF);
}
private static void PrintAddress(StringBuilder sb, Formatter formatter, int addr,
bool showBank) {
if (addr == Address.NON_ADDR) {
sb.Append("-" + Address.NON_ADDR_STR + "-");
} else {
sb.Append("$");
sb.Append(formatter.FormatAddress(addr, showBank));
}
}
}
}