/// <summary> /// Formats a single-character operand. Output will be a delimited printable character /// when possible, a hex value when the converted character is unprintable. /// </summary> /// <param name="value">Value to format. Could be a 16-bit immediate value.</param> /// <param name="enc">Character encoding to use for value.</param> /// <returns>Formatted string.</returns> public string FormatCharacterValue(int value, CharEncoding.Encoding enc) { if (value < 0 || value > 0xff) { return(FormatHexValue(value, 2)); } DelimiterDef delimDef = mFormatConfig.mCharDelimiters.Get(enc); if (delimDef == null) { return(FormatHexValue(value, 2)); } string fmt = delimDef.FormatStr; Debug.Assert(fmt != null); CharEncoding.Convert conv; switch (enc) { case CharEncoding.Encoding.Ascii: conv = CharEncoding.ConvertAscii; break; case CharEncoding.Encoding.HighAscii: conv = CharEncoding.ConvertHighAscii; break; case CharEncoding.Encoding.C64Petscii: conv = CharEncoding.ConvertC64Petscii; break; case CharEncoding.Encoding.C64ScreenCode: conv = CharEncoding.ConvertC64ScreenCode; break; default: return(FormatHexValue(value, 2)); } char ch = conv((byte)value); if (ch == CharEncoding.UNPRINTABLE_CHAR || ch == delimDef.OpenDelim || ch == delimDef.CloseDelim) { // We might be able to do better with delimiter clashes, e.g. '\'', but // that's assembler-specific. return(FormatHexValue(value, 2)); } else { // Possible optimization: replace fmt with a prefix/suffix pair, and just concat return(string.Format(fmt, ch)); } }
// IGenerator public void UpdateCharacterEncoding(FormatDescriptor dfd) { CharEncoding.Encoding newEnc = PseudoOp.SubTypeToEnc(dfd.FormatSubType); if (newEnc == CharEncoding.Encoding.Unknown) { // probably not a character operand return; } if (newEnc != mCurrentEncoding) { switch (newEnc) { case CharEncoding.Encoding.Ascii: OutputLine(string.Empty, ".enc", '"' + ASCII_ENC_NAME + '"', string.Empty); break; case CharEncoding.Encoding.HighAscii: // If this is a numeric operand (not string), and we're currently in // ASCII mode, the "| $80" in the delimiter will handle this without // the need for a .enc. Much less clutter for sources that have plain // ASCII strings but test high ASCII constants. if (mCurrentEncoding == CharEncoding.Encoding.Ascii && !dfd.IsString) { newEnc = mCurrentEncoding; } else { OutputLine(string.Empty, ".enc", '"' + HIGH_ASCII_ENC_NAME + '"', string.Empty); } break; case CharEncoding.Encoding.C64Petscii: OutputLine(string.Empty, ".enc", "\"none\"", string.Empty); break; case CharEncoding.Encoding.C64ScreenCode: OutputLine(string.Empty, ".enc", "\"screen\"", string.Empty); break; default: Debug.Assert(false); break; } mCurrentEncoding = newEnc; } }
// IGenerator public void OutputAsmConfig() { CpuDef cpuDef = Project.CpuDef; string cpuStr; if (cpuDef.Type == CpuDef.CpuType.Cpu65816) { cpuStr = "65816"; } else if (cpuDef.Type == CpuDef.CpuType.Cpu65C02) { cpuStr = "65c02"; } else if (cpuDef.Type == CpuDef.CpuType.CpuW65C02) { cpuStr = "w65c02"; } else if (cpuDef.Type == CpuDef.CpuType.Cpu6502 && cpuDef.HasUndocumented) { cpuStr = "6502i"; } else { cpuStr = "6502"; } OutputLine(string.Empty, SourceFormatter.FormatPseudoOp(".cpu"), '\"' + cpuStr + '\"', string.Empty); // C64 PETSCII and C64 screen codes are built in. Define ASCII if we also // need that. mCurrentEncoding = CharEncoding.Encoding.C64Petscii; CheckAsciiFormats(out bool hasAscii, out bool hasHighAscii); if (hasHighAscii) { OutputLine(string.Empty, ".enc", '"' + HIGH_ASCII_ENC_NAME + '"', string.Empty); OutputLine(string.Empty, ".cdef", "$20,$7e,$a0", string.Empty); mCurrentEncoding = CharEncoding.Encoding.HighAscii; } if (hasAscii) { OutputLine(string.Empty, ".enc", '"' + ASCII_ENC_NAME + '"', string.Empty); OutputLine(string.Empty, ".cdef", "$20,$7e,$20", string.Empty); mCurrentEncoding = CharEncoding.Encoding.Ascii; } }
public void Set(CharEncoding.Encoding enc, DelimiterDef def) { mDelimiters[enc] = def; }
/// <summary> /// Returns the specified DelimiterDef, or null if not found. /// </summary> public DelimiterDef Get(CharEncoding.Encoding enc) { mDelimiters.TryGetValue(enc, out DelimiterDef def); return(def); }
/// <summary> /// Format a numeric operand value according to the specified sub-format. /// </summary> /// <param name="formatter">Text formatter.</param> /// <param name="symbolTable">Full table of project symbols.</param> /// <param name="lvLookup">Local variable lookup object. May be null if not /// formatting an instruction.</param> /// <param name="labelMap">Symbol label remap, for local label conversion. May be /// null.</param> /// <param name="dfd">Operand format descriptor.</param> /// <param name="offset">Offset of start of instruction or data pseudo-op, for /// variable name lookup. Okay to pass -1 when not formatting an instruction.</param> /// <param name="operandValue">Operand's value. For most things this comes directly /// out of the code, for relative branches it's a 24-bit absolute address.</param> /// <param name="operandLen">Length of operand, in bytes. For an instruction, this /// does not include the opcode byte. For a relative branch, this will be 2.</param> /// <param name="flags">Special handling.</param> public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable, LocalVariableLookup lvLookup, Dictionary <string, string> labelMap, FormatDescriptor dfd, int offset, int operandValue, int operandLen, FormatNumericOpFlags flags) { Debug.Assert(operandLen > 0); int hexMinLen = operandLen * 2; switch (dfd.FormatSubType) { case FormatDescriptor.SubType.None: case FormatDescriptor.SubType.Hex: case FormatDescriptor.SubType.Address: return(formatter.FormatHexValue(operandValue, hexMinLen)); case FormatDescriptor.SubType.Decimal: return(formatter.FormatDecimalValue(operandValue)); case FormatDescriptor.SubType.Binary: return(formatter.FormatBinaryValue(operandValue, hexMinLen * 4)); case FormatDescriptor.SubType.Ascii: case FormatDescriptor.SubType.HighAscii: case FormatDescriptor.SubType.C64Petscii: case FormatDescriptor.SubType.C64Screen: CharEncoding.Encoding enc = SubTypeToEnc(dfd.FormatSubType); return(formatter.FormatCharacterValue(operandValue, enc)); case FormatDescriptor.SubType.Symbol: if (lvLookup != null && dfd.SymbolRef.IsVariable) { Debug.Assert(operandLen == 1); // only doing 8-bit stuff DefSymbol defSym = lvLookup.GetSymbol(offset, dfd.SymbolRef); if (defSym != null) { StringBuilder sb = new StringBuilder(); FormatNumericSymbolCommon(formatter, defSym, null, dfd, operandValue, operandLen, flags, sb); return(sb.ToString()); } else { Debug.WriteLine("Local variable format failed"); Debug.Assert(false); return(formatter.FormatHexValue(operandValue, hexMinLen)); } } else if (symbolTable.TryGetNonVariableValue(dfd.SymbolRef.Label, out Symbol sym)) { StringBuilder sb = new StringBuilder(); switch (formatter.ExpressionMode) { case Formatter.FormatConfig.ExpressionMode.Common: FormatNumericSymbolCommon(formatter, sym, labelMap, dfd, operandValue, operandLen, flags, sb); break; case Formatter.FormatConfig.ExpressionMode.Cc65: FormatNumericSymbolCc65(formatter, sym, labelMap, dfd, operandValue, operandLen, flags, sb); break; case Formatter.FormatConfig.ExpressionMode.Merlin: FormatNumericSymbolMerlin(formatter, sym, labelMap, dfd, operandValue, operandLen, flags, sb); break; default: Debug.Assert(false, "Unknown expression mode " + formatter.ExpressionMode); return("???"); } return(sb.ToString()); } else { return(formatter.FormatHexValue(operandValue, hexMinLen)); } default: // should not see REMOVE or ASCII_GENERIC here Debug.Assert(false); return("???"); } }