/*
* Copyright 2019 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.Text;
using System.Diagnostics;
namespace SourceGen {
///
/// Functions for generation of "auto" labels.
///
public static class AutoLabel {
///
/// Auto-label style enumeration.
///
public enum Style {
Unknown = -1,
Simple = 0,
Annotated = 1,
FullyAnnotated = 2
}
///
/// Generates a unique address symbol. Does not add the symbol to the table.
///
/// This does not follow any Formatter rules -- labels are always entirely upper-case.
///
/// Address that label will be applied to.
/// Symbol table, for uniqueness check.
/// Prefix to use; must start with a letter.
/// Newly-created, unique symbol.
public static Symbol GenerateUniqueForAddress(int addr, SymbolTable symbols,
string prefix) {
// $1234 == L1234, $05/1234 == L51234.
string label = prefix + addr.ToString("X4"); // always upper-case
if (symbols.TryGetValue(label, out Symbol unused)) {
const int MAX_RENAME = 999;
string baseLabel = label;
StringBuilder sb = new StringBuilder(baseLabel.Length + 8);
int index = -1;
do {
// This is expected to be unlikely and infrequent, so a simple linear
// probe for uniqueness is fine. Labels are based on the address, not
// the offset, so even without user-created labels there's still an
// opportunity for duplication.
index++;
sb.Clear();
sb.Append(baseLabel);
sb.Append('_');
sb.Append(index);
label = sb.ToString();
} while (index <= MAX_RENAME && symbols.TryGetValue(label, out unused));
if (index > MAX_RENAME) {
// I give up
throw new Exception("Too many identical symbols: " + label);
}
}
Symbol sym = new Symbol(label, addr, Symbol.Source.Auto,
Symbol.Type.LocalOrGlobalAddr);
return sym;
}
///
/// Source reference type.
///
/// The enum is in priority order, i.e. the lowest-valued item "wins" in situations
/// where only one value is used.
///
[Flags]
private enum RefTypes {
None = 0,
SubCall = 1 << 0,
Branch = 1 << 1,
DataRef = 1 << 2,
Write = 1 << 3,
Read = 1 << 4,
}
private static readonly char[] TAGS = { 'S', 'B', 'D', 'W', 'R' };
///
/// Generates an auto-label with a prefix string based on the XrefSet.
///
/// Address that label will be applied to.
/// Symbol table, for uniqueness check.
/// Cross-references for this location.
/// Newly-created, unique symbol.
public static Symbol GenerateAnnotatedLabel(int addr, SymbolTable symbols,
XrefSet xset, Style style) {
Debug.Assert(xset != null);
Debug.Assert(style != Style.Simple);
RefTypes rtypes = RefTypes.None;
foreach (XrefSet.Xref xr in xset) {
switch (xr.Type) {
case XrefSet.XrefType.SubCallOp:
rtypes |= RefTypes.SubCall;
break;
case XrefSet.XrefType.BranchOp:
rtypes |= RefTypes.Branch;
break;
case XrefSet.XrefType.RefFromData:
rtypes |= RefTypes.DataRef;
break;
case XrefSet.XrefType.MemAccessOp:
switch (xr.AccType) {
case Asm65.OpDef.MemoryEffect.Read:
rtypes |= RefTypes.Read;
break;
case Asm65.OpDef.MemoryEffect.Write:
rtypes |= RefTypes.Write;
break;
case Asm65.OpDef.MemoryEffect.ReadModifyWrite:
rtypes |= RefTypes.Read;
rtypes |= RefTypes.Write;
break;
case Asm65.OpDef.MemoryEffect.None:
case Asm65.OpDef.MemoryEffect.Unknown:
break;
default:
Debug.Assert(false);
break;
}
break;
default:
Debug.Assert(false);
break;
}
}
if (rtypes == RefTypes.None) {
// unexpected
Debug.Assert(false);
return GenerateUniqueForAddress(addr, symbols, "X_");
}
StringBuilder sb = new StringBuilder(8);
for (int i = 0; i < TAGS.Length; i++) {
if (((int) rtypes & (1 << i)) != 0) {
sb.Append(TAGS[i]);
if (style == Style.Annotated) {
break;
}
}
}
sb.Append('_');
return GenerateUniqueForAddress(addr, symbols, sb.ToString());
}
}
}