void SetupArguments (OpDef def) { // Attributes related to the InputArg's type are inferred automatically // and are not exposed to the client. var inferred_input_args = new Dictionary<string, bool> (); required_attrs = new List<OpDef.AttrDef> (); optional_attrs = new List<OpDef.AttrDef> (); foreach (var argdef in def.input_arg) { if (argdef.type_attr != "") inferred_input_args [argdef.type_attr] = true; else if (argdef.type_list_attr != "") inferred_input_args [argdef.type_list_attr] = true; if (argdef.number_attr != "") inferred_input_args [argdef.number_attr] = true; } foreach (var attr in def.attr) { if (inferred_input_args.ContainsKey (attr.name)) continue; if (attr.default_value == null) required_attrs.Add (attr); else optional_attrs.Add (attr); } have_return_value = def.output_arg.Count > 0; }
// Generates arguments: // * Input arguments (TFOutput or TFOutput []) // * All required attributes // * variadic optional arguments string FillArguments(OpDef def) { var sb = new StringBuilder(); string comma = ""; foreach (var inarg in def.input_arg) { string type = "TF_Output" + (IsListArg(inarg) ? "[]" : ""); sb.AppendFormat($"{comma}{type} {ParamMap(inarg.name)}"); comma = ", "; } foreach (var attr in required_attrs) { sb.AppendFormat($"{comma}{CSharpType(attr.type)} {ParamMap(attr.name)}"); comma = ", "; } foreach (var attr in optional_attrs) { bool reftype = IsReferenceType(attr.type); var cstype = CSharpType(attr.type); var cstypesuffix = reftype ? "" : "?"; sb.AppendFormat($"{comma}{cstype}{cstypesuffix} {attr.name} = null"); comma = ", "; } if (sb.Length != 0) { sb.Append(", "); } return(sb.ToString()); }
// IGenerator public string ReplaceMnemonic(OpDef op) { if ((op == OpDef.OpWDM_WDM || op == OpDef.OpBRK_StackInt) && mAsmVersion <= V2_17) { // cc65 v2.17 doesn't support WDM, and assembles BRK <arg> to opcode $05. // https://github.com/cc65/cc65/issues/715 // https://github.com/cc65/cc65/issues/716 return(null); } else if (op.IsUndocumented) { if (sUndocMap.TryGetValue(op.Mnemonic, out string newValue)) { if ((op.Mnemonic == OpName.ANC && op.Opcode != 0x0b) || (op.Mnemonic == OpName.HLT && op.Opcode != 0x02)) { // There are multiple opcodes for the same thing. cc65 outputs // one specific thing, so we need to match that, and just do a hex // dump for the others. return(null); } return(newValue); } return(null); } else { return(string.Empty); } }
// IGenerator public string ModifyOpcode(int offset, OpDef op) { if (op.IsUndocumented) { return(null); } // The assembler works correctly if the symbol is defined as a two-digit hex // value (e.g. "foo equ $80") but fails if it's four (e.g. "foo equ $0080"). We // output symbols with minimal digits, but we have no control over labels when // the code has a zero-page EQU. So if the operand is a reference to a user // label, we need to output the instruction as hex. if (op == OpDef.OpPEI_StackDPInd || op == OpDef.OpSTY_DPIndexX || op == OpDef.OpSTX_DPIndexY || op.AddrMode == OpDef.AddressMode.DPIndLong || op.AddrMode == OpDef.AddressMode.DPInd || op.AddrMode == OpDef.AddressMode.DPIndexXInd) { FormatDescriptor dfd = Project.GetAnattrib(offset).DataDescriptor; if (dfd != null && dfd.HasSymbol) { // It has a symbol. See if the symbol target is a label (auto or user). if (Project.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) { if (sym.IsInternalLabel) { return(null); } } } } return(string.Empty); }
// Produces the C# inline documentation void GenDocs (OpDef oper) { p ("/// <summary>"); Comment (oper.summary); p ("/// </summary>"); foreach (var input in oper.input_arg) { p ($"/// <param name=\"{ParamMap (input.name)}\">"); Comment (input.description); p ($"/// </param>"); } #if DOCS if (!return_is_tfoutput) { foreach (var attr in oper.output_arg) { if (String.IsNullOrEmpty (attr.description)) continue; p ($"/// <param name=\"{ParamMap (attr.name)}\">"); Comment (attr.description); p ($"/// </param>"); } } #endif p ("/// <param name=\"operName\">"); p ($"/// If specified, the created operation in the graph will be this one, otherwise it will be named '{oper.name}'."); p ("/// </param>"); foreach (var attr in optional_attrs) { p ($"/// <param name=\"{ParamMap (attr.name)}\">"); Comment ("Optional argument"); Comment (attr.description); p ($"/// </param>"); } foreach (var attr in required_attrs) { p ($"/// <param name=\"{ParamMap (attr.name)}\">"); Comment (attr.description); p ($"/// </param>"); } p ($"/// <returns>"); if (have_return_value) { if (oper.output_arg.Count == 1) { Comment (oper.output_arg.First ().description); Comment ("The TFOperation can be fetched from the resulting TFOutput, by fethching the Operation property from the result."); } else { Comment ("Returns a tuple with multiple values, as follows:"); foreach (var arg in oper.output_arg) { Comment (ParamMap (arg.name) + ": " + arg.description); } Comment ("The TFOperation can be fetched from any of the TFOutputs returned in the tuple values, by fethching the Operation property."); } } else { Comment ("Returns the description of the operation"); } p ($"/// </returns>"); if (!String.IsNullOrEmpty (oper.description)) { p ("/// <remarks>"); Comment (oper.description); p ("/// </remarks>"); } }
// Produces the C# inline documentation void GenDocs(OpDef oper) { p("/// <summary>"); Comment(oper.summary); p("/// </summary>"); foreach (var input in oper.input_arg) { if (String.IsNullOrEmpty(input.description)) { continue; } p($"/// <param name=\"{ParamMap(input.name)}\">"); Comment(input.description); p($"/// </param>"); } if (!return_is_tfoutput) { foreach (var attr in oper.output_arg) { if (String.IsNullOrEmpty(attr.description)) { continue; } p($"/// <param name=\"{ParamMap (attr.name)}\">"); Comment(attr.description); p($"/// </param>"); } } p("/// <param name=\"operName\">"); p($"/// If specified, the created operation in the graph will be this one, otherwise it will be named '{oper.name}'."); p("/// </param>"); foreach (var attr in optional_attrs) { if (String.IsNullOrEmpty(attr.description)) { continue; } p($"/// <param name=\"{ParamMap (attr.name)}\">"); Comment("Optional argument"); Comment(attr.description); p($"/// </param>"); } if (return_is_tfoutput) { p($"/// <returns>"); Comment(oper.output_arg.First().description); p($"/// </returns>"); } if (!String.IsNullOrEmpty(oper.description)) { p("/// <remarks>"); Comment(oper.description); p("/// </remarks>"); } }
void SetupArguments(OpDef def) { // Attributes related to the InputArg's type are inferred automatically // and are not exposed to the client. var inferred_input_args = new Dictionary <string, bool> (); required_attrs = new List <OpDef.AttrDef> (); optional_attrs = new List <OpDef.AttrDef> (); foreach (var argdef in def.input_arg) { if (argdef.type_attr != "") { inferred_input_args [argdef.type_attr] = true; } else if (argdef.type_list_attr != "") { inferred_input_args [argdef.type_list_attr] = true; } if (argdef.number_attr != "") { inferred_input_args [argdef.number_attr] = true; } } foreach (var attr in def.attr) { if (inferred_input_args.ContainsKey(attr.name)) { continue; } if (attr.default_value == null) { required_attrs.Add(attr); } else { optional_attrs.Add(attr); } } // API: currently, if we have a single ref TFOutput result, we make the signature of the // function return that TFOutput instead of the TFOperation (as you can get the TFOperation // from the TFOutput anyways. // // When we move to tuples, we could probably put everything in a Tuple result, but for now // mult-return functions will just return all outputs on ref variables, instead of the first // as a ref, and the rest as TFOutputs. // // This means that we generate methods like this: // TFOutput Constant (....) // when there is a single output // // TFOperation Foo (..) // When there is no result or more than one result. return_is_tfoutput = def.output_arg.Count == 1; }
// IGenerator public string ReplaceMnemonic(OpDef op) { if (op.IsUndocumented) { return(null); } else { return(string.Empty); } }
/// <summary> /// Formats the instruction operand. /// </summary> /// <param name="op">Opcode definition (needed for address mode).</param> /// <param name="contents">Label or numeric operand value.</param> /// <param name="wdis">Width disambiguation value.</param> /// <returns>Formatted string.</returns> public string FormatOperand(OpDef op, string contents, OpDef.WidthDisambiguation wdis) { Debug.Assert(((int)op.AddrMode & 0xff) == (int)op.AddrMode); int key = (int)op.AddrMode | ((int)wdis << 8); if (!mOperandFormats.TryGetValue(key, out string format)) { format = mOperandFormats[key] = GenerateOperandFormat(op.AddrMode, wdis); } return(string.Format(format, contents)); }
// IGenerator public string ModifyOpcode(int offset, OpDef op) { if (op == OpDef.OpBRK_StackInt) { if (mAsmVersion < V2_18) { // cc65 v2.17 assembles BRK <arg> to opcode $05 // https://github.com/cc65/cc65/issues/716 return(null); } else if (Project.CpuDef.Type != CpuDef.CpuType.Cpu65816) { // cc65 v2.18 only supports BRK <arg> on 65816 (?!) return(null); } else { return(string.Empty); } } else if (op == OpDef.OpWDM_WDM && mAsmVersion < V2_18) { // cc65 v2.17 doesn't support WDM // https://github.com/cc65/cc65/issues/715 return(null); } else if (op.IsUndocumented) { if (sUndocMap.TryGetValue(op.Mnemonic, out string newValue)) { if ((op.Mnemonic == OpName.ANC && op.Opcode != 0x0b) || (op.Mnemonic == OpName.JAM && op.Opcode != 0x02)) { // There are multiple opcodes for the same thing. cc65 outputs // one specific thing, so we need to match that, and just do a hex // dump for the others. return(null); } return(newValue); } // Unmapped values include DOP, TOP, and the alternate SBC. Output hex. return(null); } else { return(string.Empty); } }
public EditOperand(int offset, DisasmProject project, Asm65.Formatter formatter) { InitializeComponent(); mProject = project; mFormatter = formatter; // Configure the appearance. mAttr = mProject.GetAnattrib(offset); OpDef op = mProject.CpuDef.GetOpDef(mProject.FileData[offset]); mInstructionLength = mAttr.Length; mPreviewHexDigits = (mAttr.Length - 1) * 2; if (mAttr.OperandAddress >= 0) { // Use this as the operand value when available. This lets us present // relative branch instructions in the expected form. mOperandValue = mAttr.OperandAddress; if (op.AddrMode == OpDef.AddressMode.PCRel) { mPreviewHexDigits = 4; mIsPcRelative = true; } else if (op.AddrMode == OpDef.AddressMode.PCRelLong || op.AddrMode == OpDef.AddressMode.StackPCRelLong) { mIsPcRelative = true; } } else { int opVal = op.GetOperand(mProject.FileData, offset, mAttr.StatusFlags); mOperandValue = opVal; if (op.AddrMode == OpDef.AddressMode.BlockMove) { // MVN and MVP screw things up by having two operands in one instruction. // We deal with this by passing in the value from the second byte // (source bank) as the value, and applying the chosen format to both bytes. mIsBlockMove = true; mOperandValue = opVal >> 8; mPreviewHexDigits = 2; } } mIsExtendedImmediate = op.IsExtendedImmediate; // Imm, PEA, MVN/MVP mShowHashPrefix = op.IsImmediate; // just Imm }
// IGenerator public string ModifyOpcode(int offset, OpDef op) { if (op.IsUndocumented) { if (Project.CpuDef.Type == CpuDef.CpuType.Cpu65C02 || Project.CpuDef.Type == CpuDef.CpuType.CpuW65C02) { // none of the "LDD" stuff is handled return(null); } if ((op.Mnemonic == OpName.ANC && op.Opcode != 0x0b) || (op.Mnemonic == OpName.JAM && op.Opcode != 0x02)) { // There are multiple opcodes that match the mnemonic. Output the // mnemonic for the first one and hex for the rest. return(null); } else if (op.Mnemonic == OpName.NOP || op.Mnemonic == OpName.DOP || op.Mnemonic == OpName.TOP) { // the various undocumented no-ops aren't handled return(null); } else if (op.Mnemonic == OpName.SBC) { // this is the alternate reference to SBC return(null); } else if (op == OpDef.OpSHA_DPIndIndexY) { // not recognized ($93) if (mAsmVersion < V1_55) { return(null); } } } if (op == OpDef.OpWDM_WDM) { // 64tass v1.53 doesn't know what this is. // 64tass v1.55 doesn't like this to have an operand. // Output as hex. return(null); } return(string.Empty); // indicate original is fine }
private static void InternalValidate(CpuDef cdef) { for (int i = 0; i < 256; i++) { OpDef op = cdef.mOpDefs[i]; if (op.Opcode != i && op.AddrMode != OpDef.AddressMode.Unknown) { throw new Exception("CpuDef for " + cdef.Type + ": entry 0x" + i.ToString("x") + " has value " + op.Opcode.ToString("x")); } if (op.AddrMode != OpDef.AddressMode.Unknown && op.Cycles == 0) { throw new Exception("Instruction 0x" + i.ToString("x2") + ": " + op + " missing cycles"); } } }
// IGenerator public string ModifyOpcode(int offset, OpDef op) { if (op.IsUndocumented) { if (Project.CpuDef.Type == CpuDef.CpuType.Cpu65C02 || Project.CpuDef.Type == CpuDef.CpuType.CpuW65C02) { // none of the "LDD" stuff is handled return(null); } if ((op.Mnemonic == OpName.ANC && op.Opcode != 0x0b) || (op.Mnemonic == OpName.JAM && op.Opcode != 0x02)) { // There are multiple opcodes that match the mnemonic. Output the // mnemonic for the first one and hex for the rest. return(null); } else if (op.Mnemonic == OpName.NOP || op.Mnemonic == OpName.DOP || op.Mnemonic == OpName.TOP) { // the various undocumented no-ops aren't handled return(null); } else if (op.Mnemonic == OpName.SBC) { // this is the alternate reference to SBC return(null); } else if (op == OpDef.OpALR_Imm) { // ACME wants "ASR" instead for $4b return("asr"); } else if (op == OpDef.OpLAX_Imm) { // ACME spits out an error on $ab return(null); } } if (op == OpDef.OpWDM_WDM || op == OpDef.OpBRK_StackInt) { // ACME doesn't like these to have an operand. Output as hex. return(null); } return(string.Empty); // indicate original is fine }
/// <summary> /// Formats the instruction opcode mnemonic, and caches the result. /// /// It may be necessary to modify the mnemonic for some assemblers, e.g. LDA from a /// 24-bit address might need to be LDAL, even if the high byte is nonzero. /// </summary> /// <param name="op">Opcode to format</param> /// <param name="wdis">Width disambiguation specifier.</param> /// <returns>Formatted string.</returns> public string FormatOpcode(OpDef op, OpDef.WidthDisambiguation wdis) { // TODO(someday): using op.Opcode as the key is a bad idea, as the operation may // not be the same on different CPUs. We currently rely on the caller to discard // the Formatter when the CPU definition changes. We'd be better off keying off of // the OpDef object and factoring wdis in some other way. int key = op.Opcode | ((int)wdis << 8); if (!mOpcodeStrings.TryGetValue(key, out string opcodeStr)) { // Not found, generate value. opcodeStr = FormatMnemonic(op.Mnemonic, wdis); // Memoize. mOpcodeStrings[key] = opcodeStr; } return(opcodeStr); }
// IGenerator public string ModifyOpcode(int offset, OpDef op) { if (op.IsUndocumented) { return(null); } if (Project.CpuDef.Type == CpuDef.CpuType.CpuW65C02) { if ((op.Opcode & 0x0f) == 0x07 || (op.Opcode & 0x0f) == 0x0f) { // BBR, BBS, RMB, SMB not supported return(null); } } // The assembler works correctly if the symbol is defined as a two-digit hex // value (e.g. "foo equ $80") but fails if it's four (e.g. "foo equ $0080"). We // output symbols with minimal digits, but this doesn't help if the code itself // lives on zero page. If the operand is a reference to a zero-page user label, // we need to output the instruction as hex. // More info: https://github.com/apple2accumulator/merlin32/issues/8 if (op == OpDef.OpPEI_StackDPInd || op == OpDef.OpSTY_DPIndexX || op == OpDef.OpSTX_DPIndexY || op.AddrMode == OpDef.AddressMode.DPIndLong || op.AddrMode == OpDef.AddressMode.DPInd || op.AddrMode == OpDef.AddressMode.DPIndexXInd) { FormatDescriptor dfd = Project.GetAnattrib(offset).DataDescriptor; if (dfd != null && dfd.HasSymbol) { // It has a symbol. See if the symbol target is a label (auto or user). if (Project.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) { if (sym.IsInternalLabel) { return(null); } } } } return(string.Empty); }
private void SetupArguments(OpDef opDef) { // Attributes related to the InputArg's type are not exposed. var inferredInputArgs = new List <string>(); _requiredAttrs = new List <OpDef.AttrDef>(); _optionalAttrs = new List <OpDef.AttrDef>(); foreach (var argdef in opDef.InputArgs) { if (argdef.TypeAttr != "") { inferredInputArgs.Add(argdef.TypeAttr); } if (argdef.TypeListAttr != "") { inferredInputArgs.Add(argdef.TypeListAttr); } if (argdef.NumberAttr != "") { inferredInputArgs.Add(argdef.NumberAttr); } } foreach (var attr in opDef.Attrs) { if (!inferredInputArgs.Contains(attr.Name)) { if (attr.DefaultValue == null) { _requiredAttrs.Add(attr); } else { _optionalAttrs.Add(attr); } } } _haveReturnValue = opDef.OutputArgs.Count > 0; }
private string FillArguments(OpDef opDef) { var sb = new StringBuilder(); string comma = ""; foreach (var inArg in opDef.InputArgs) { string type = "Output!" + (IsListArg(inArg) ? "[]" : ""); sb.AppendFormat($"{comma}{type} {ParamMap(inArg.Name)}"); comma = ", "; } foreach (var attr in _requiredAttrs) { bool isRefType = IsReferenceType(attr.Type); var cstype = CSharpType(attr.Type); var cstypeSuffix = isRefType ? "!" : ""; sb.AppendFormat($"{comma}{cstype}{cstypeSuffix} {ParamMap(attr.Name)}"); comma = ", "; } foreach (var attr in _optionalAttrs) { bool isRefType = IsReferenceType(attr.Type); var cstype = CSharpType(attr.Type); var cstypeSuffix = isRefType ? "" : "?"; sb.AppendFormat($"{comma}{cstype}{cstypeSuffix} {ParamMap(attr.Name)} = null"); comma = ", "; } if (sb.Length != 0) { sb.Append(", "); } return(sb.ToString()); }
private static String CodeGen(OpDef op) { String defaultStr = String.Format("{1}// Skipped function {0}", op.Name, _sixSpaces); if (op.Name.StartsWith("_")) { return(defaultStr); } //if (op.name.Equals("PaddingFIFOQueueV2")) { StringBuilder document = new StringBuilder( String.Format("{0}{2}///<summary>{0}{2}///{1}{0}{2}///</summary>{0}", Environment.NewLine, CleanUpDescription(op.Summary), _sixSpaces)); var outputs = op.OutputArg; String[] returnDescs = new string[outputs.Count]; for (int i = 0; i < outputs.Count; i++) { returnDescs[i] = String.Format("{4}///{3} {0}(type: {1}){2}", outputs[i].Name, outputs[i].Type, String.IsNullOrEmpty(CleanUpDescription(outputs[i].Description)) ? "." : ": " + CleanUpDescription(outputs[i].Description), outputs.Count > 0 ? String.Format("[{0}]", i) : String.Empty, _sixSpaces); } String returnDoc = String.Join(Environment.NewLine, returnDescs); returnDoc = String.Format("{2}///<return>{0}{1}{0}{2}///</return>{0}", Environment.NewLine, returnDoc, _sixSpaces); if (outputs.Count == 0) { returnDoc = String.Empty; } String returnStr = "Operation"; var inputs = op.InputArg; List <String> inputsStrList = new List <string>(); foreach (var input in inputs) { string inputDescription = CleanUpDescription(input.Description); document.AppendFormat("{4}///<param name=\"{0}\">Input to the operation{3} {1}</param>{2}", FixParamName(input.Name), inputDescription, Environment.NewLine, String.IsNullOrEmpty(inputDescription) ? "." : ":", _sixSpaces); inputsStrList.Add(String.Format(" {0} {1} ", "Output", FixParamName(input.Name))); } String inputsStr = String.Join(",", inputsStrList); var attr = op.Attr; List <OpDef.Types.AttrDef> unknownAttr = new List <OpDef.Types.AttrDef>(); List <OpDef.Types.AttrDef> knownAttr = new List <OpDef.Types.AttrDef>(); foreach (var a in attr) { if (IsTypeKnown(a.Type)) { knownAttr.Add(a); } else { unknownAttr.Add(a); } } var requiredAttr = knownAttr.FindAll(a => a.DefaultValue == null); var optionalAttr = knownAttr.FindAll(a => a.DefaultValue != null); foreach (var iarg in op.InputArg) { if (iarg.TypeAttr != "") { requiredAttr.RemoveAll(a => a.Name.Equals(iarg.TypeAttr)); optionalAttr.RemoveAll(a => a.Name.Equals(iarg.TypeAttr)); } if (iarg.TypeListAttr != "") { requiredAttr.RemoveAll(a => a.Name.Equals(iarg.TypeListAttr)); optionalAttr.RemoveAll(a => a.Name.Equals(iarg.TypeListAttr)); } if (iarg.NumberAttr != "") { requiredAttr.RemoveAll(a => a.Name.Equals(iarg.NumberAttr)); optionalAttr.RemoveAll(a => a.Name.Equals(iarg.NumberAttr)); } } foreach (var required in requiredAttr) { document.AppendFormat("{3}///<param name=\"{0}\">{1}</param>{2}", FixParamName(required.Name), CleanUpDescription(required.Description), Environment.NewLine, _sixSpaces); } foreach (var optional in optionalAttr) { document.AppendFormat("{3}///<param name=\"{0}\">{1}</param>{2}", FixParamName(optional.Name), CleanUpDescription(optional.Description), Environment.NewLine, _sixSpaces); } document.AppendFormat("{0}///<param name=\"opName\">The name of the operation</param>{1}", _sixSpaces, Environment.NewLine); document.Append(returnDoc); if (unknownAttr.Count > 0) { document.AppendFormat("{2}//The following attributes are not known: {1}{0}", Environment.NewLine, String.Join("; ", unknownAttr.ConvertAll(a => String.Format("{0}: {1}", a.Name, a.Type))), _sixSpaces); } String requiredStr = String.Join(",", requiredAttr.ConvertAll <String>( a => { String isTypeList = IsTypeList(a.Type); if (String.IsNullOrEmpty(isTypeList)) { return(String.Format(" {0} {1}", _typeMap[a.Type], a.Name)); } else { return(String.Format(" {0}[] {1}", _typeMap[isTypeList], a.Name)); } })); String optionalStr = String.Join(",", optionalAttr.ConvertAll <String>( a => { String isTypeList = IsTypeList(a.Type); if (String.IsNullOrEmpty(isTypeList)) { if (_typeIsStruct[a.Type]) { if (a.Type.Equals("int")) { return(String.Format(" {0} {1} = {2} ", _typeMap[a.Type], a.Name, a.DefaultValue.I)); } else if (a.Type.Equals("bool")) { return(String.Format(" {0} {1} = {2} ", _typeMap[a.Type], a.Name, a.DefaultValue.B ? "true" : "false")); } else if (a.Type.Equals("float")) { String valueString = a.DefaultValue.F.ToString(); if (Single.IsPositiveInfinity(a.DefaultValue.F)) { valueString = "Single.PositiveInfinity"; } else if (Single.IsNegativeInfinity(a.DefaultValue.F)) { valueString = "Single.NegativeInfinity"; } else { valueString = valueString + "f"; } String res = String.Format(" {0} {1} = {2} ", _typeMap[a.Type], a.Name, valueString); return(res); } else { return(String.Format(" {0}? {1} = null ", _typeMap[a.Type], a.Name)); } } else { return(String.Format(" {0} {1} = null ", _typeMap[a.Type], a.Name)); } } else { return(String.Format(" {0}[] {1} = null ", _typeMap[isTypeList], a.Name)); } })); List <string> paramList = new List <string>(); if (!String.IsNullOrEmpty(inputsStr)) { paramList.Add(inputsStr); } if (!String.IsNullOrEmpty(requiredStr)) { paramList.Add(requiredStr); } if (!String.IsNullOrEmpty(optionalStr)) { paramList.Add(optionalStr); } paramList.Add(String.Format("String opName= \"{0}\"", op.Name)); String paramStr = String.Join(",", paramList); StringBuilder body = new StringBuilder(String.Format( "{2}OperationDescription desc = NewOperation(\"{0}\", opName);{1}", op.Name, Environment.NewLine, _nineSpaces)); List <String> addInputStringList = new List <string>(); foreach (var i in inputs) { addInputStringList.Add(String.Format("{1}desc.AddInput({0});", FixParamName(i.Name), _nineSpaces)); } body.Append(String.Join(Environment.NewLine, addInputStringList)); body.Append(Environment.NewLine); body.Append(String.Join(Environment.NewLine, requiredAttr.ConvertAll(RequiredAttrToString))); body.Append(Environment.NewLine); body.Append(String.Join(Environment.NewLine, optionalAttr.ConvertAll(OptionalAttrToString))); body.Append(Environment.NewLine); body.Append(String.Format("{0}return desc.FinishOperation();", _nineSpaces)); String code = String.Format("{5}public {0} {1} ( {2} ) {3}{5}{{{3}{4}{3}{5}}} ", returnStr, op.Name, paramStr, Environment.NewLine, body.ToString(), _sixSpaces); return(String.Format("{0}{1}", document, code)); } }
/// <summary> /// Generate the specified oper. /// </summary> /// <param name="oper">Oper.</param> void Generate(OpDef oper) { SetupArguments(oper); GenDocs(oper); var name = oper.name; string retType; if (have_return_value) { if (oper.output_arg.Count > 1) { var rb = new StringBuilder("("); foreach (var arg in oper.output_arg) { rb.AppendFormat("TFOutput{0} {1}, ", IsListArg(arg) ? "[]" : "", ParamMap(arg.name)); } rb.Remove(rb.Length - 2, 2); rb.Append(")"); retType = rb.ToString(); } else { retType = "TFOutput" + (IsListArg(oper.output_arg.First()) ? "[]" : ""); } } else { retType = "TFOperation"; } p($"public {retType} {name} ({FillArguments(oper)}string operName = null)"); pi("{"); bool needStatus = required_attrs.Concat(optional_attrs).Any(attr => attr.type.Contains("TFTensor")); p($"var desc = new TFOperationDesc (this, \"{oper.name}\", MakeName (\"{oper.name}\", operName));"); foreach (var arg in oper.input_arg) { if (IsListArg(arg)) { p($"desc.AddInputs ({ParamMap (arg.name)});"); } else { p($"desc.AddInput ({ParamMap (arg.name)});"); } } pi("foreach ( TFOperation control in CurrentDependencies )"); p("desc.AddControlInput (control);"); pd(""); // If we have attributes if (required_attrs.Count > 0 || optional_attrs.Count > 0) { foreach (var attr in required_attrs) { SetAttribute(attr.type, attr.name, ParamMap(attr.name)); } foreach (var attr in optional_attrs) { var reftype = IsReferenceType(attr.type); var csattr = ParamMap(attr.name); if (reftype) { pi($"if ({csattr} != null)"); } else { pi($"if ({csattr}.HasValue)"); } SetAttribute(attr.type, attr.name, csattr + (reftype ? "" : ".Value")); pd(""); } } p("var op = desc.FinishOperation ();"); if (oper.output_arg.Count() > 0) { p("int _idx = 0;"); } if (oper.output_arg.Any(x => IsListArg(x))) { p("int _n = 0;"); } foreach (var arg in oper.output_arg) { if (IsListArg(arg)) { var outputs = new StringBuilder(); p($"_n = op.OutputListLength (\"{ParamMap (arg.name)}\");"); p($"var {ParamMap (arg.name)} = new TFOutput [_n];"); pi("for (int i = 0; i < _n; i++)"); p($"{ParamMap (arg.name)} [i] = new TFOutput (op, _idx++);"); pd(""); } else { p($"var {ParamMap (arg.name)} = new TFOutput (op, _idx++);"); } } if (have_return_value) { if (oper.output_arg.Count == 1) { p($"return {ParamMap (oper.output_arg.First ().name)};"); } else { ; p("return (" + oper.output_arg.Select(x => ParamMap(x.name)).Aggregate((i, j) => (i + ", " + j)) + ");"); } } else { p("return op;"); } pd("}\n"); }
private static void GenerateInstruction(IGenerator gen, StreamWriter sw, LocalVariableLookup lvLookup, int offset, int instrBytes, bool doAddCycles) { DisasmProject proj = gen.Project; Formatter formatter = gen.SourceFormatter; byte[] data = proj.FileData; Anattrib attr = proj.GetAnattrib(offset); string labelStr = string.Empty; if (attr.Symbol != null) { labelStr = gen.Localizer.ConvLabel(attr.Symbol.Label); } OpDef op = proj.CpuDef.GetOpDef(data[offset]); int operand = op.GetOperand(data, offset, attr.StatusFlags); int instrLen = op.GetLength(attr.StatusFlags); OpDef.WidthDisambiguation wdis = OpDef.WidthDisambiguation.None; if (op.IsWidthPotentiallyAmbiguous) { wdis = OpDef.GetWidthDisambiguation(instrLen, operand); } if (gen.Quirks.SinglePassAssembler && wdis == OpDef.WidthDisambiguation.None && (op.AddrMode == OpDef.AddressMode.DP || op.AddrMode == OpDef.AddressMode.DPIndexX) || op.AddrMode == OpDef.AddressMode.DPIndexY) { // Could be a forward reference to a direct-page label. For ACME, we don't // care if it's forward or not. if ((gen.Quirks.SinglePassNoLabelCorrection && IsLabelReference(gen, offset)) || IsForwardLabelReference(gen, offset)) { wdis = OpDef.WidthDisambiguation.ForceDirect; } } string opcodeStr = formatter.FormatOpcode(op, wdis); string formattedOperand = null; int operandLen = instrLen - 1; PseudoOp.FormatNumericOpFlags opFlags = PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix; bool isPcRelBankWrap = false; // Tweak branch instructions. We want to show the absolute address rather // than the relative offset (which happens with the OperandAddress assignment // below), and 1-byte branches should always appear as a 4-byte hex value. if (op.AddrMode == OpDef.AddressMode.PCRel) { Debug.Assert(attr.OperandAddress >= 0); operandLen = 2; opFlags |= PseudoOp.FormatNumericOpFlags.IsPcRel; } else if (op.AddrMode == OpDef.AddressMode.PCRelLong || op.AddrMode == OpDef.AddressMode.StackPCRelLong) { opFlags |= PseudoOp.FormatNumericOpFlags.IsPcRel; } else if (op.AddrMode == OpDef.AddressMode.Imm || op.AddrMode == OpDef.AddressMode.ImmLongA || op.AddrMode == OpDef.AddressMode.ImmLongXY) { opFlags |= PseudoOp.FormatNumericOpFlags.HasHashPrefix; } if ((opFlags & PseudoOp.FormatNumericOpFlags.IsPcRel) != 0) { int branchDist = attr.Address - attr.OperandAddress; isPcRelBankWrap = branchDist > 32767 || branchDist < -32768; } // 16-bit operands outside bank 0 need to include the bank when computing // symbol adjustment. int operandForSymbol = operand; if (attr.OperandAddress >= 0) { operandForSymbol = attr.OperandAddress; } // Check Length to watch for bogus descriptors. (ApplyFormatDescriptors() should // now be screening bad descriptors out, so we may not need the Length test.) if (attr.DataDescriptor != null && attr.Length == attr.DataDescriptor.Length) { FormatDescriptor dfd = gen.ModifyInstructionOperandFormat(offset, attr.DataDescriptor, operand); // Format operand as directed. if (op.AddrMode == OpDef.AddressMode.BlockMove) { // Special handling for the double-operand block move. string opstr1 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, dfd, operand >> 8, 1, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); string opstr2 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, dfd, operand & 0xff, 1, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); if (gen.Quirks.BlockMoveArgsReversed) { string tmp = opstr1; opstr1 = opstr2; opstr2 = tmp; } string hash = gen.Quirks.BlockMoveArgsNoHash ? "" : "#"; formattedOperand = hash + opstr1 + "," + hash + opstr2; } else { if (attr.DataDescriptor.IsStringOrCharacter) { gen.UpdateCharacterEncoding(dfd); } formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, lvLookup, gen.Localizer.LabelMap, dfd, offset, operandForSymbol, operandLen, opFlags); } } else { // Show operand value in hex. if (op.AddrMode == OpDef.AddressMode.BlockMove) { int arg1, arg2; if (gen.Quirks.BlockMoveArgsReversed) { arg1 = operand & 0xff; arg2 = operand >> 8; } else { arg1 = operand >> 8; arg2 = operand & 0xff; } string hash = gen.Quirks.BlockMoveArgsNoHash ? "" : "#"; formattedOperand = hash + formatter.FormatHexValue(arg1, 2) + "," + hash + formatter.FormatHexValue(arg2, 2); } else { if (operandLen == 2) { // This is necessary for 16-bit operands, like "LDA abs" and "PEA val", // when outside bank zero. The bank is included in the operand address, // but we don't want to show it here. operandForSymbol &= 0xffff; } formattedOperand = formatter.FormatHexValue(operandForSymbol, operandLen * 2); } } string operandStr = formatter.FormatOperand(op, formattedOperand, wdis); if (gen.Quirks.StackIntOperandIsImmediate && op.AddrMode == OpDef.AddressMode.StackInt) { // COP $02 is standard, but some require COP #$02 operandStr = '#' + operandStr; } string eolComment = proj.Comments[offset]; if (doAddCycles) { bool branchCross = (attr.Address & 0xff00) != (operandForSymbol & 0xff00); int cycles = proj.CpuDef.GetCycles(op.Opcode, attr.StatusFlags, attr.BranchTaken, branchCross); if (cycles > 0) { if (!string.IsNullOrEmpty(eolComment)) { eolComment = cycles.ToString() + " " + eolComment; } else { eolComment = cycles.ToString(); } } else { if (!string.IsNullOrEmpty(eolComment)) { eolComment = (-cycles).ToString() + "+ " + eolComment; } else { eolComment = (-cycles).ToString() + "+"; } } } string commentStr = formatter.FormatEolComment(eolComment); string replMnemonic = gen.ModifyOpcode(offset, op); if (attr.Length != instrBytes) { // This instruction has another instruction inside it. Throw out what we // computed and just output as bytes. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (isPcRelBankWrap && gen.Quirks.NoPcRelBankWrap) { // Some assemblers have trouble generating PC-relative operands that wrap // around the bank. Output as raw hex. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (op.AddrMode == OpDef.AddressMode.BlockMove && gen.Quirks.BlockMoveArgsReversed) { // On second thought, just don't even output the wrong thing. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (replMnemonic == null) { // No mnemonic exists for this opcode. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (replMnemonic != string.Empty) { // A replacement mnemonic has been provided. opcodeStr = formatter.FormatMnemonic(replMnemonic, wdis); } gen.OutputLine(labelStr, opcodeStr, operandStr, commentStr); // Assemblers like Merlin32 try to be helpful and track SEP/REP, but they do the // wrong thing if we're in emulation mode. Force flags back to short. if (proj.CpuDef.HasEmuFlag && gen.Quirks.TracksSepRepNotEmu && op == OpDef.OpREP_Imm) { if ((operand & 0x30) != 0 && attr.StatusFlags.E == 1) { gen.OutputRegWidthDirective(offset, 0, 0, 1, 1); } } }
private static void GenerateInstruction(IGenerator gen, StreamWriter sw, int offset, int instrBytes, bool doAddCycles) { DisasmProject proj = gen.Project; Formatter formatter = gen.SourceFormatter; byte[] data = proj.FileData; Anattrib attr = proj.GetAnattrib(offset); string labelStr = string.Empty; if (attr.Symbol != null) { labelStr = gen.Localizer.ConvLabel(attr.Symbol.Label); } OpDef op = proj.CpuDef.GetOpDef(data[offset]); int operand = op.GetOperand(data, offset, attr.StatusFlags); int instrLen = op.GetLength(attr.StatusFlags); OpDef.WidthDisambiguation wdis = OpDef.WidthDisambiguation.None; if (op.IsWidthPotentiallyAmbiguous) { wdis = OpDef.GetWidthDisambiguation(instrLen, operand); } string replMnemonic = gen.ReplaceMnemonic(op); string opcodeStr = formatter.FormatOpcode(op, wdis); string formattedOperand = null; int operandLen = instrLen - 1; bool isPcRel = false; bool isPcRelBankWrap = false; // Tweak branch instructions. We want to show the absolute address rather // than the relative offset (which happens with the OperandAddress assignment // below), and 1-byte branches should always appear as a 4-byte hex value. if (op.AddrMode == OpDef.AddressMode.PCRel) { Debug.Assert(attr.OperandAddress >= 0); operandLen = 2; isPcRel = true; } else if (op.AddrMode == OpDef.AddressMode.PCRelLong || op.AddrMode == OpDef.AddressMode.StackPCRelLong) { isPcRel = true; } if (isPcRel) { int branchDist = attr.Address - attr.OperandAddress; isPcRelBankWrap = branchDist > 32767 || branchDist < -32768; } // 16-bit operands outside bank 0 need to include the bank when computing // symbol adjustment. int operandForSymbol = operand; if (attr.OperandAddress >= 0) { operandForSymbol = attr.OperandAddress; } // Check Length to watch for bogus descriptors (?) if (attr.DataDescriptor != null && attr.Length == attr.DataDescriptor.Length) { // Format operand as directed. if (op.AddrMode == OpDef.AddressMode.BlockMove) { // Special handling for the double-operand block move. string opstr1 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, attr.DataDescriptor, operand >> 8, 1, false); string opstr2 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, attr.DataDescriptor, operand & 0xff, 1, false); if (gen.Quirks.BlockMoveArgsReversed) { string tmp = opstr1; opstr1 = opstr2; opstr2 = tmp; } formattedOperand = opstr1 + "," + opstr2; } else { formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, attr.DataDescriptor, operandForSymbol, operandLen, isPcRel); } } else { // Show operand value in hex. if (op.AddrMode == OpDef.AddressMode.BlockMove) { int arg1, arg2; if (gen.Quirks.BlockMoveArgsReversed) { arg1 = operand & 0xff; arg2 = operand >> 8; } else { arg1 = operand >> 8; arg2 = operand & 0xff; } formattedOperand = formatter.FormatHexValue(arg1, 2) + "," + formatter.FormatHexValue(arg2, 2); } else { if (operandLen == 2) { // This is necessary for 16-bit operands, like "LDA abs" and "PEA val", // when outside bank zero. The bank is included in the operand address, // but we don't want to show it here. operandForSymbol &= 0xffff; } formattedOperand = formatter.FormatHexValue(operandForSymbol, operandLen * 2); } } string operandStr = formatter.FormatOperand(op, formattedOperand, wdis); string eolComment = proj.Comments[offset]; if (doAddCycles) { bool branchCross = (attr.Address & 0xff00) != (operandForSymbol & 0xff00); int cycles = proj.CpuDef.GetCycles(op.Opcode, attr.StatusFlags, attr.BranchTaken, branchCross); if (cycles > 0) { eolComment = cycles.ToString() + " " + eolComment; } else { eolComment = (-cycles).ToString() + "+ " + eolComment; } } string commentStr = formatter.FormatEolComment(eolComment); if (attr.Length != instrBytes) { // This instruction has another instruction inside it. Throw out what we // computed and just output as bytes. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (isPcRelBankWrap && gen.Quirks.NoPcRelBankWrap) { // Some assemblers have trouble generating PC-relative operands that wrap // around the bank. Output as raw hex. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (op.AddrMode == OpDef.AddressMode.BlockMove && gen.Quirks.BlockMoveArgsReversed) { // On second thought, just don't even output the wrong thing. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (replMnemonic == null) { // No mnemonic exists for this opcode. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (replMnemonic != string.Empty) { // A replacement mnemonic has been provided. opcodeStr = formatter.FormatMnemonic(replMnemonic, wdis); } gen.OutputLine(labelStr, opcodeStr, operandStr, commentStr); // Assemblers like Merlin32 try to be helpful and track SEP/REP, but they do the // wrong thing if we're in emulation mode. Force flags back to short. if (proj.CpuDef.HasEmuFlag && gen.Quirks.TracksSepRepNotEmu && op == OpDef.OpREP_Imm) { if ((operand & 0x30) != 0 && attr.StatusFlags.E == 1) { gen.OutputRegWidthDirective(offset, 0, 0, 1, 1); } } }
public OpGenException(OpDef op, string msg) : base($"Error generating code for op {op.name}: {msg}.") { }
private static void GenerateInstruction(IGenerator gen, StreamWriter sw, LocalVariableLookup lvLookup, int offset, int instrBytes, bool doAddCycles) { DisasmProject proj = gen.Project; Formatter formatter = gen.SourceFormatter; byte[] data = proj.FileData; Anattrib attr = proj.GetAnattrib(offset); string labelStr = string.Empty; if (attr.Symbol != null) { labelStr = gen.Localizer.ConvLabel(attr.Symbol.Label); } OpDef op = proj.CpuDef.GetOpDef(data[offset]); int operand = op.GetOperand(data, offset, attr.StatusFlags); int instrLen = op.GetLength(attr.StatusFlags); OpDef.WidthDisambiguation wdis = OpDef.WidthDisambiguation.None; if (op.IsWidthPotentiallyAmbiguous) { wdis = OpDef.GetWidthDisambiguation(instrLen, operand); } if (gen.Quirks.SinglePassAssembler && wdis == OpDef.WidthDisambiguation.None && (op.AddrMode == OpDef.AddressMode.DP || op.AddrMode == OpDef.AddressMode.DPIndexX) || op.AddrMode == OpDef.AddressMode.DPIndexY) { // Could be a forward reference to a direct-page label. For ACME, we don't // care if it's forward or not. if ((gen.Quirks.SinglePassNoLabelCorrection && IsLabelReference(gen, offset)) || IsForwardLabelReference(gen, offset)) { wdis = OpDef.WidthDisambiguation.ForceDirect; } } if (wdis == OpDef.WidthDisambiguation.ForceLongMaybe && gen.Quirks.SinglePassAssembler && IsForwardLabelReference(gen, offset)) { // Assemblers like cc65 can't tell if a symbol reference is Absolute or // Long if they haven't seen the symbol yet. Irrelevant for ACME, which // doesn't currently handle 65816 outside bank 0. wdis = OpDef.WidthDisambiguation.ForceLong; } string opcodeStr = formatter.FormatOpcode(op, wdis); string formattedOperand = null; int operandLen = instrLen - 1; PseudoOp.FormatNumericOpFlags opFlags = PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix; bool isPcRelBankWrap = false; // Tweak branch instructions. We want to show the absolute address rather // than the relative offset (which happens with the OperandAddress assignment // below), and 1-byte branches should always appear as a 4-byte hex value. // Unless we're outside bank 0 on 65816, in which case most assemblers require // them to be 6-byte hex values. if (op.AddrMode == OpDef.AddressMode.PCRel || op.AddrMode == OpDef.AddressMode.DPPCRel) { Debug.Assert(attr.OperandAddress >= 0); operandLen = 2; opFlags |= PseudoOp.FormatNumericOpFlags.IsPcRel; } else if (op.AddrMode == OpDef.AddressMode.PCRelLong || op.AddrMode == OpDef.AddressMode.StackPCRelLong) { opFlags |= PseudoOp.FormatNumericOpFlags.IsPcRel; } else if (op.AddrMode == OpDef.AddressMode.Imm || op.AddrMode == OpDef.AddressMode.ImmLongA || op.AddrMode == OpDef.AddressMode.ImmLongXY) { opFlags |= PseudoOp.FormatNumericOpFlags.HasHashPrefix; } if ((opFlags & PseudoOp.FormatNumericOpFlags.IsPcRel) != 0) { int branchDist = attr.Address - attr.OperandAddress; isPcRelBankWrap = branchDist > 32767 || branchDist < -32768; } if (op.IsAbsolutePBR) { opFlags |= PseudoOp.FormatNumericOpFlags.IsAbsolutePBR; } if (gen.Quirks.BankZeroAbsPBRRestrict) { // Hack to avoid having to define a new FormatConfig.ExpressionMode for 64tass. // Get rid of this 64tass gets its own exp mode. opFlags |= PseudoOp.FormatNumericOpFlags.Is64Tass; } // 16-bit operands outside bank 0 need to include the bank when computing // symbol adjustment. int operandForSymbol = operand; if (attr.OperandAddress >= 0) { operandForSymbol = attr.OperandAddress; } // Check Length to watch for bogus descriptors. (ApplyFormatDescriptors() should // now be screening bad descriptors out, so we may not need the Length test.) if (attr.DataDescriptor != null && attr.Length == attr.DataDescriptor.Length) { FormatDescriptor dfd = gen.ModifyInstructionOperandFormat(offset, attr.DataDescriptor, operand); // Format operand as directed. if (op.AddrMode == OpDef.AddressMode.BlockMove) { // Special handling for the double-operand block move. string opstr1 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, dfd, operand >> 8, 1, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); string opstr2 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, dfd, operand & 0xff, 1, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); if (gen.Quirks.BlockMoveArgsReversed) { string tmp = opstr1; opstr1 = opstr2; opstr2 = tmp; } string hash = gen.Quirks.BlockMoveArgsNoHash ? "" : "#"; formattedOperand = hash + opstr1 + "," + hash + opstr2; } else if (op.AddrMode == OpDef.AddressMode.DPPCRel) { // Special handling for double-operand BBR/BBS. The instruction generally // behaves like a branch, so format that first. string branchStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, dfd, operandForSymbol, operandLen, opFlags); string dpStr = formatter.FormatHexValue(operand & 0xff, 2); formattedOperand = dpStr + "," + branchStr; } else { if (attr.DataDescriptor.IsStringOrCharacter) { gen.UpdateCharacterEncoding(dfd); } formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, lvLookup, gen.Localizer.LabelMap, dfd, offset, operandForSymbol, operandLen, opFlags); } } else { // Show operand value in hex. if (op.AddrMode == OpDef.AddressMode.BlockMove) { int arg1, arg2; if (gen.Quirks.BlockMoveArgsReversed) { arg1 = operand & 0xff; arg2 = operand >> 8; } else { arg1 = operand >> 8; arg2 = operand & 0xff; } string hash = gen.Quirks.BlockMoveArgsNoHash ? "" : "#"; formattedOperand = hash + formatter.FormatHexValue(arg1, 2) + "," + hash + formatter.FormatHexValue(arg2, 2); } else if (op.AddrMode == OpDef.AddressMode.DPPCRel) { formattedOperand = formatter.FormatHexValue(operand & 0xff, 2) + "," + formatter.FormatHexValue(operandForSymbol, operandLen * 2); } else { if (operandLen == 2 && !(op.IsAbsolutePBR && gen.Quirks.Need24BitsForAbsPBR) && (opFlags & PseudoOp.FormatNumericOpFlags.IsPcRel) == 0) { // This is necessary for 16-bit operands, like "LDA abs" and "PEA val", // when outside bank zero. The bank is included in the operand address, // but we don't want to show it here. We may need it for JSR/JMP though, // and the bank is required for relative branch instructions. operandForSymbol &= 0xffff; } formattedOperand = formatter.FormatHexValue(operandForSymbol, operandLen * 2); } } string operandStr = formatter.FormatOperand(op, formattedOperand, wdis); if (gen.Quirks.StackIntOperandIsImmediate && op.AddrMode == OpDef.AddressMode.StackInt) { // COP $02 is standard, but some require COP #$02 operandStr = '#' + operandStr; } // The BBR/BBS/RMB/SMB instructions include a bit index (0-7). The standard way is // to make it part of the mnemonic, but some assemblers make it an argument. if (gen.Quirks.BitNumberIsArg && op.IsNumberedBitOp) { // Easy way: do some string manipulation. char bitIndex = opcodeStr[opcodeStr.Length - 1]; opcodeStr = opcodeStr.Substring(0, opcodeStr.Length - 1); operandStr = bitIndex.ToString() + "," + operandStr; } string eolComment = proj.Comments[offset]; if (doAddCycles) { bool branchCross = (attr.Address & 0xff00) != (operandForSymbol & 0xff00); int cycles = proj.CpuDef.GetCycles(op.Opcode, attr.StatusFlags, attr.BranchTaken, branchCross); if (cycles > 0) { if (!string.IsNullOrEmpty(eolComment)) { eolComment = cycles.ToString() + " " + eolComment; } else { eolComment = cycles.ToString(); } } else { if (!string.IsNullOrEmpty(eolComment)) { eolComment = (-cycles).ToString() + "+ " + eolComment; } else { eolComment = (-cycles).ToString() + "+"; } } } string commentStr = formatter.FormatEolComment(eolComment); string replMnemonic = gen.ModifyOpcode(offset, op); if (attr.Length != instrBytes) { // This instruction has another instruction inside it. Throw out what we // computed and just output as bytes. // TODO: in some odd situations we can split something that doesn't need // to be split (see note at end of #107). Working around the problem at // this stage is a little awkward because I think we need to check for the // presence of labels on one or more later lines. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (isPcRelBankWrap && gen.Quirks.NoPcRelBankWrap) { // Some assemblers have trouble generating PC-relative operands that wrap // around the bank. Output as raw hex. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (op.AddrMode == OpDef.AddressMode.BlockMove && gen.Quirks.BlockMoveArgsReversed) { // On second thought, just don't even output the wrong thing. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (replMnemonic == null) { // No mnemonic exists for this opcode. gen.GenerateShortSequence(offset, instrBytes, out opcodeStr, out operandStr); } else if (replMnemonic != string.Empty) { // A replacement mnemonic has been provided. opcodeStr = formatter.FormatMnemonic(replMnemonic, wdis); } gen.OutputLine(labelStr, opcodeStr, operandStr, commentStr); // Assemblers like Merlin32 try to be helpful and track SEP/REP, but they do the // wrong thing if we're in emulation mode. Force flags back to short. if (proj.CpuDef.HasEmuFlag && gen.Quirks.TracksSepRepNotEmu && op == OpDef.OpREP_Imm) { if ((operand & 0x30) != 0 && attr.StatusFlags.IsEmulationMode) { gen.OutputRegWidthDirective(offset, 0, 0, 1, 1); } } }
private void GenDocs(OpDef opDef) { var api = _apimap.Get(opDef.Name); P("/// <summary>"); Comment(api.Summary); P("/// </summary>"); foreach (var input in api.InArgs) { P($"/// <param name=\"{ParamMap(input.Name)}\">"); Comment(input.Description); P($"/// </param>"); } P("/// <param name=\"opName\">"); P($"/// If specified, the created operation in the graph will be this one, otherwise it will be named '{opDef.Name}'."); P("/// </param>"); foreach (var attr in _optionalAttrs) { P($"/// <param name=\"{ParamMap(attr.Name)}\">"); Comment("Optional argument"); Comment(api.Attrs.Where(x => x.Name == attr.Name).FirstOrDefault().Description); P($"/// </param>"); } foreach (var attr in _requiredAttrs) { P($"/// <param name=\"{ParamMap(attr.Name)}\">"); Comment(api.Attrs.Where(x => x.Name == attr.Name).FirstOrDefault().Description); P($"/// </param>"); } P($"/// <returns>"); if (_haveReturnValue) { if (opDef.OutputArgs.Count == 1) { Comment(api.OutArgs.First().Description); Comment("The Operation can be fetched from the resulting Output, by fetching the Operation property from the result."); } else { Comment("Returns a tuple with multiple values, as follows:"); foreach (var arg in opDef.OutputArgs) { var oapi = api.OutArgs.Where(x => x.Name == arg.Name).FirstOrDefault(); Comment(ParamMap(arg.Name) + ": " + oapi.Description); } Comment("The Operation can be fetched from any of the Outputs returned in the tuple values, by fetching the Operation property."); } } else { Comment("Returns the description of the operation"); } P($"/// </returns>"); if (!String.IsNullOrEmpty(api.Description)) { P("/// <remarks>"); Comment(api.Description); P("/// </remarks>"); } }
private void Generate(OpDef opDef) { SetupArguments(opDef); GenDocs(opDef); var name = opDef.Name; string retType; if (_haveReturnValue) { if (opDef.OutputArgs.Count > 1) { var sb = new StringBuilder("("); foreach (var arg in opDef.OutputArgs) { sb.AppendFormat("Output{0} {1}, ", IsListArg(arg) ? "[]" : "", ParamMap(arg.Name)); } sb.Remove(sb.Length - 2, 2); sb.Append(")"); retType = sb.ToString(); } else { retType = "Output" + (IsListArg(opDef.OutputArgs.First()) ? "[]" : ""); } } else { retType = "Operation"; } P($"public {retType} {name}({FillArguments(opDef)}string opName = null)"); PI("{"); bool needStatus = _requiredAttrs.Concat(_optionalAttrs).Any(attr => attr.Type.Contains("Tensor")); P($"var desc = new OperationDescription withGraph(this) OpType(\"{opDef.Name}\") OpName(MakeName(\"{opDef.Name}\", opName));"); foreach (var arg in opDef.InputArgs) { if (IsListArg(arg)) { P($"desc.AddInputs({ParamMap(arg.Name)});"); } else { P($"desc.AddInput({ParamMap(arg.Name)});"); } } P(" "); PI("foreach (Operation control in CurrentDependencies) {"); P("desc.AddControlInput(control);"); PD("}\n"); // If we have attributes if (_requiredAttrs.Count > 0 || _optionalAttrs.Count > 0) { foreach (var attr in _requiredAttrs) { SetAttribute(attr.Type, attr.Name, ParamMap(attr.Name)); } if (_requiredAttrs.Count > 0) { P(""); } foreach (var attr in _optionalAttrs) { var reftype = IsReferenceType(attr.Type); var csattr = ParamMap(attr.Name); if (reftype) { PI($"if ({csattr} != null) {{"); } else { PI($"if ({csattr}.HasValue) {{"); } //SetAttribute(attr.Type, attr.Name, csattr + (reftype ? "" : ".Value")); SetAttribute(attr.Type, attr.Name, csattr); PD("}\n"); } } PI("using (var status = new Status()) {"); P("var (success, op) = desc.FinishOperation(status);"); PI("if(!success) {"); P($"throw new OpCreateException withOpType(\"{opDef.Name}\") Error(status.Message);"); PD("}"); if (opDef.OutputArgs.Count() > 0) { P("int _idx = 0;"); } if (opDef.OutputArgs.Any(x => IsListArg(x))) { P("int _n = 0;"); } foreach (var arg in opDef.OutputArgs) { if (IsListArg(arg)) { var outputs = new StringBuilder(); P($"_n = op.GetOutputListLength(\"{ParamMap(arg.Name)}\");"); P($"var {ParamMap(arg.Name)} = new Output[_n];"); PI("for (int i = 0; i < _n; i++) {"); P($"{ParamMap(arg.Name)} [i] = new Output withOp(op) Index(_idx++);"); PD("}\n"); } else { P($"var {ParamMap(arg.Name)} = new Output withOp(op) Index(_idx++);"); } } if (_haveReturnValue) { if (opDef.OutputArgs.Count == 1) { P($"return {ParamMap(opDef.OutputArgs.First().Name)};"); } else { ; P("return (" + opDef.OutputArgs.Select(x => ParamMap(x.Name)).Aggregate((i, j) => (i + ", " + j)) + ");"); } } else { P("return op;"); } PD("}"); PD("}\n"); }
/// <summary> /// Generates the mCycleCounts and mCycleMods arrays. /// </summary> private void GenerateCycleCounts() { if (mCycleCounts != null) { return; } mCycleCounts = new int[256]; mCycleMods = new OpDef.CycleMod[256]; // Figure out which mods apply for this CPU. OpDef.CycleMod ignoreMask = 0; switch (Type) { case CpuType.Cpu6502: ignoreMask = OpDef.CycleMod.OneIfM0 | OpDef.CycleMod.TwoIfM0 | OpDef.CycleMod.OneIfX0 | OpDef.CycleMod.OneIfDpNonzero | OpDef.CycleMod.OneIfD1 | OpDef.CycleMod.OneIfE0 | OpDef.CycleMod.OneIf65C02 | OpDef.CycleMod.MinusOneIfNoPage | OpDef.CycleMod.BlockMove; break; case CpuType.Cpu65C02: ignoreMask = OpDef.CycleMod.OneIfM0 | OpDef.CycleMod.TwoIfM0 | OpDef.CycleMod.OneIfX0 | OpDef.CycleMod.OneIfDpNonzero | OpDef.CycleMod.OneIfE0 | OpDef.CycleMod.BlockMove; break; case CpuType.Cpu65816: ignoreMask = OpDef.CycleMod.OneIfD1 | OpDef.CycleMod.OneIf65C02 | OpDef.CycleMod.MinusOneIfNoPage; break; default: Debug.Assert(false, "unsupported cpu type " + Type); return; } // If an instruction has one or more applicable mods, declare it as variable. for (int i = 0; i < 256; i++) { OpDef op = mOpDefs[i]; int baseCycles = op.Cycles; OpDef.CycleMod mods = op.CycleMods & ~ignoreMask; if ((mods & OpDef.CycleMod.OneIf65C02) != 0) { // This isn't variable -- the instruction always takes one cycle longer // on the 65C02. (Applies to $6C, JMP (addr).) Debug.Assert(Type == CpuType.Cpu65C02); baseCycles++; mods &= ~OpDef.CycleMod.OneIf65C02; } mCycleCounts[i] = baseCycles; mCycleMods[i] = mods; } }
/// <summary> /// Generate the specified oper. /// </summary> /// <param name="oper">Oper.</param> void Generate(OpDef oper) { SetupArguments(oper); GenDocs(oper); var name = oper.name; string retType; if (return_is_tfoutput) { if (oper.output_arg.Any(x => IsListArg(x))) { retType = "TFOutput []"; } else { retType = "TFOutput"; } } else { retType = "TFOperation"; } p($"public {retType} {name} ({FillArguments(oper)}string operName = null)"); pi("{"); bool needStatus = required_attrs.Concat(optional_attrs).Any(attr => attr.type.Contains("TFTensor")); p($"var desc = new TFOperationDesc (this, \"{oper.name}\", MakeName (\"{oper.name}\", operName));"); foreach (var arg in oper.input_arg) { if (IsListArg(arg)) { p($"desc.AddInputs ({ParamMap (arg.name)});"); } else { p($"desc.AddInput ({ParamMap (arg.name)});"); } } // If we have attributes if (required_attrs.Count > 0 || optional_attrs.Count > 0) { foreach (var attr in required_attrs) { SetAttribute(attr.type, attr.name, ParamMap(attr.name)); } foreach (var attr in optional_attrs) { var reftype = IsReferenceType(attr.type); var csattr = ParamMap(attr.name); if (reftype) { pi($"if ({csattr} != null)"); } else { pi($"if ({csattr}.HasValue)"); } SetAttribute(attr.type, attr.name, csattr + (reftype ? "" : ".Value")); pd(""); } } p("var op = desc.FinishOperation ();"); if (oper.output_arg.Any(x => IsListArg(x))) { p("int _idx = 0, _n = 0;"); foreach (var arg in oper.output_arg) { string retDecl = "", retOutput; if (return_is_tfoutput) { retDecl = "var "; retOutput = "_ret"; } else { retOutput = ParamMap(arg.name); } if (IsListArg(arg)) { var outputs = new StringBuilder(); p($"_n = op.OutputListLength (\"{arg.name}\");"); p($"{retDecl}{retOutput} = new TFOutput [_n];"); pi("for (int i = 0; i < _n; i++)"); p($"{retOutput} [i] = new TFOutput (op, _idx++);"); pd(""); if (return_is_tfoutput) { p($"return {retOutput};"); } } else { if (return_is_tfoutput) { p($"return new TFOutput (op, _idx++);"); } else { p($"{retOutput} = new TFOutput (op, _idx++);"); } } } } else { int idx = 0; foreach (var arg in oper.output_arg) { if (return_is_tfoutput) { p($"return new TFOutput (op, 0);"); } else { p($"{ParamMap (arg.name)} = new TFOutput (op, {idx++});"); } } } if (!return_is_tfoutput) { p("return op;"); } pd("}\n"); }
private void UpdateControls() { CpuItem item = (CpuItem)cpuSelectionComboBox.SelectedItem; if (item == null) { // initializing return; } // Push current choice to settings. AppSettings.Global.SetEnum(AppSettings.INSTCH_MODE, typeof(CpuDef.CpuType), (int)item.Type); AppSettings.Global.SetBool(AppSettings.INSTCH_SHOW_UNDOC, mShowUndocumented); // Populate the items source. InstructionItems.Clear(); CpuDef cpuDef = CpuDef.GetBestMatch(item.Type, true, false); for (int opc = 0; opc < 256; opc++) { OpDef op = cpuDef[opc]; if (!mShowUndocumented && op.IsUndocumented) { continue; } int opLen = op.GetLength(StatusFlags.AllIndeterminate); string sampleValue = "$12"; if (op.AddrMode == OpDef.AddressMode.BlockMove) { sampleValue = "#$12,#$34"; } else if (opLen == 3) { sampleValue = "$1234"; } else if (opLen == 4) { sampleValue = "$123456"; } string instrSample = mFormatter.FormatMnemonic(op.Mnemonic, OpDef.WidthDisambiguation.None) + " " + mFormatter.FormatOperand(op, sampleValue, OpDef.WidthDisambiguation.None); StringBuilder flags = new StringBuilder(8); const string FLAGS = "NVMXDIZC"; Asm65.StatusFlags affectedFlags = op.FlagsAffected; for (int fl = 0; fl < 8; fl++) { if (affectedFlags.GetBit((StatusFlags.FlagBits)(7 - fl)) >= 0) { flags.Append(FLAGS[fl]); } else { flags.Append("-"); } } string cycles = op.Cycles.ToString(); OpDef.CycleMod mods = cpuDef.GetOpCycleMod(opc); if (mods != 0) { cycles += '+'; } InstructionItems.Add(new InstructionItem(mFormatter.FormatHexValue(opc, 2), instrSample, flags.ToString(), cycles, mOpDesc.GetShortDescription(op.Mnemonic), mOpDesc.GetAddressModeDescription(op.AddrMode), op.IsUndocumented)); } }
private int FindAlternateTarget(int srcOffset, int targetOffset) { int origTargetOffset = targetOffset; // Is the target outside the instruction stream? If it's just referencing data, // do a simple check and move on. if (!mAnattribs[targetOffset].IsInstruction) { // We want to use user-defined labels whenever possible. If they're accessing // memory within a few bytes, use that. We don't want to do this for // code references, though, or our branches will get all weird. // TODO(someday): make MAX user-configurable? Seek forward as well as backward? const int MAX = 4; for (int probeOffset = targetOffset - 1; probeOffset >= 0 && probeOffset != targetOffset - MAX; probeOffset--) { Symbol sym = mAnattribs[probeOffset].Symbol; if (sym != null && sym.SymbolSource == Symbol.Source.User) { // Found a nearby user label. Make sure it's actually nearby. int addrDiff = mAnattribs[targetOffset].Address - mAnattribs[probeOffset].Address; if (addrDiff == targetOffset - probeOffset) { targetOffset = probeOffset; } else { Debug.WriteLine("NOT probing past address boundary change"); } break; } } return(targetOffset); } // Target is an instruction. Is the source an instruction or data element // (e.g. ".dd2 <addr>"). if (!mAnattribs[srcOffset].IsInstructionStart) { // Might be address-1 to set up an RTS. If the target address isn't // an instruction start, check to see if the following byte is. if (!mAnattribs[targetOffset].IsInstructionStart && targetOffset + 1 < mAnattribs.Length && mAnattribs[targetOffset + 1].IsInstructionStart) { LogD(srcOffset, "Offsetting address reference"); targetOffset++; } return(targetOffset); } // Source is an instruction, so we have an instruction referencing an instruction. // Could be a branch, an address push, or self-modifying code. OpDef op = mProject.CpuDef.GetOpDef(mProject.FileData[srcOffset]); if (op.IsBranch) { // Don't mess with jumps and branches -- always go directly to the // target address. } else if (op == OpDef.OpPEA_StackAbs || op == OpDef.OpPER_StackPCRelLong) { // They might be pushing address-1 to set up an RTS. If the target address isn't // an instruction start, check to see if the following byte is. if (!mAnattribs[targetOffset].IsInstructionStart && targetOffset + 1 < mAnattribs.Length && mAnattribs[targetOffset + 1].IsInstructionStart) { LogD(srcOffset, "Offsetting PEA/PER"); targetOffset++; } } else { // Data operation (LDA, STA, etc). This could be self-modifying code, or // an indexed access with an offset base address (LDA addr-1,Y) to an // adjacent data area. Check to see if there's data right after this. bool nearbyData = false; for (int i = targetOffset + 1; i <= targetOffset + 2; i++) { if (i < mAnattribs.Length && !mAnattribs[i].IsInstruction) { targetOffset = i; nearbyData = true; break; } } if (!nearbyData && !mAnattribs[targetOffset].IsInstructionStart) { // There's no data nearby, and the target is not the start of the // instruction, so this is probably self-modifying code. We want // the label to be on the opcode, so back up to the instruction start. while (!mAnattribs[--targetOffset].IsInstructionStart) { // Should not be possible to move past the start of the file, // since we know we're in the middle of an instruction. Debug.Assert(targetOffset > 0); } } } if (targetOffset != origTargetOffset) { LogV(srcOffset, "Creating instruction ref adj=" + (origTargetOffset - targetOffset)); } return(targetOffset); }