private static void GenerateHeader(IGenerator gen, StreamWriter sw) { DisasmProject proj = gen.Project; Formatter formatter = gen.SourceFormatter; // Check for header comment. if (proj.LongComments.TryGetValue(DisplayList.Line.HEADER_COMMENT_OFFSET, out MultiLineComment headerComment)) { List <string> formatted = headerComment.FormatText(formatter, string.Empty); foreach (string str in formatted) { gen.OutputLine(str); } } gen.OutputAsmConfig(); // Format symbols. foreach (DefSymbol defSym in proj.ActiveDefSymbolList) { // Use an operand length of 1 so things are shown as concisely as possible. string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, defSym.DataDescriptor, defSym.Value, 1, false); gen.OutputEquDirective(defSym.Label, valueStr, defSym.Comment); } // If there was at least one symbol, output a blank line. if (proj.ActiveDefSymbolList.Count != 0) { gen.OutputLine(string.Empty); } }
//- instructions ------------------------------------------------------ public PseudoOp handlePseudoOp(Token token) { PseudoOp pseudo = null; switch (token.strval) { case "DB": case "DW": case "DD": case "DQ": case "DT": char ch = token.strval[1]; int size = (ch == 'B') ? 1 : (ch == 'W') ? 2 : (ch == 'D') ? 4 : (ch == 'Q') ? 8 : 10; Operand val = getOperand(); pseudo = new DataDefinition(size, val); break; } //skip any trailing junk & goto eoln while (token.type != TokenType.EOLN) { token = prep.getToken(); } return(pseudo); }
// IGenerator public void OutputLocalVariableTable(int offset, List <DefSymbol> newDefs, LocalVariableTable allDefs) { foreach (DefSymbol defSym in newDefs) { string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter, Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); OutputLine(SourceFormatter.FormatVariableLabel(defSym.Label), SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective), valueStr, SourceFormatter.FormatEolComment(defSym.Comment)); } }
// IGenerator public void OutputLocalVariableTable(int offset, List <DefSymbol> newDefs, LocalVariableTable allDefs) { foreach (DefSymbol defSym in newDefs) { // Use an operand length of 1 so values are shown as concisely as possible. string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter, Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, PseudoOp.FormatNumericOpFlags.None); OutputLine(SourceFormatter.FormatVariableLabel(defSym.Label), SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective), valueStr, SourceFormatter.FormatEolComment(defSym.Comment)); } }
// IGenerator public void OutputLocalVariableTable(int offset, List <DefSymbol> newDefs, LocalVariableTable allDefs) { OutputLine(string.Empty, "!zone", "Z" + offset.ToString("x6"), string.Empty); for (int i = 0; i < allDefs.Count; i++) { DefSymbol defSym = allDefs[i]; string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter, Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, PseudoOp.FormatNumericOpFlags.None); OutputEquDirective(SourceFormatter.FormatVariableLabel(defSym.Label), valueStr, defSym.Comment); } }
// 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; } }
private static void GenerateHeader(IGenerator gen, StreamWriter sw) { DisasmProject proj = gen.Project; Formatter formatter = gen.SourceFormatter; // Check for header comment. if (proj.LongComments.TryGetValue(LineListGen.Line.HEADER_COMMENT_OFFSET, out MultiLineComment headerComment)) { List <string> formatted = headerComment.FormatText(formatter, string.Empty); foreach (string str in formatted) { gen.OutputLine(str); } } gen.OutputAsmConfig(); // Format symbols. bool prevConst = false; foreach (DefSymbol defSym in proj.ActiveDefSymbolList) { if (prevConst && !defSym.IsConstant) { // Output a blank line between the constants and the address equates. gen.OutputLine(string.Empty); } // Use an operand length of 1 so values are shown as concisely as possible. string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, defSym.DataDescriptor, defSym.Value, 1, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); string labelStr = gen.Localizer.ConvLabel(defSym.Label); gen.OutputEquDirective(labelStr, valueStr, defSym.Comment); prevConst = defSym.IsConstant; } // If there was at least one symbol, output a blank line. if (proj.ActiveDefSymbolList.Count != 0) { gen.OutputLine(string.Empty); } }
// IGenerator public void OutputLocalVariableTable(int offset, List <DefSymbol> newDefs, LocalVariableTable allDefs) { // We can do better here, but it requires knowing whether anything in "newDefs" // overwrote a previous entry. If everything is new, we don't need to start // a new zone, and can just output newDefs. (We don't need to start a new zone // on a "clear previous".) OutputLine(string.Empty, "!zone", "Z" + offset.ToString("x6"), string.Empty); for (int i = 0; i < allDefs.Count; i++) { DefSymbol defSym = allDefs[i]; string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter, Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); OutputEquDirective(SourceFormatter.FormatVariableLabel(defSym.Label), valueStr, defSym.Comment); } }
// IGenerator public void OutputDataOp(int offset) { Formatter formatter = SourceFormatter; byte[] data = Project.FileData; Anattrib attr = Project.GetAnattrib(offset); string labelStr = string.Empty; if (attr.Symbol != null) { labelStr = mLocalizer.ConvLabel(attr.Symbol.Label); } string commentStr = SourceFormatter.FormatEolComment(Project.Comments[offset]); string opcodeStr, operandStr; FormatDescriptor dfd = attr.DataDescriptor; Debug.Assert(dfd != null); int length = dfd.Length; Debug.Assert(length > 0); bool multiLine = false; switch (dfd.FormatType) { case FormatDescriptor.Type.Default: if (length != 1) { Debug.Assert(false); length = 1; } opcodeStr = sDataOpNames.DefineData1; int operand = RawData.GetWord(data, offset, length, false); operandStr = formatter.FormatHexValue(operand, length * 2); break; case FormatDescriptor.Type.NumericLE: opcodeStr = sDataOpNames.GetDefineData(length); operand = RawData.GetWord(data, offset, length, false); UpdateCharacterEncoding(dfd); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); break; case FormatDescriptor.Type.NumericBE: opcodeStr = sDataOpNames.GetDefineBigData(length); if ((string.IsNullOrEmpty(opcodeStr))) { // Nothing defined, output as comma-separated single-byte values. GenerateShortSequence(offset, length, out opcodeStr, out operandStr); } else { UpdateCharacterEncoding(dfd); operand = RawData.GetWord(data, offset, length, true); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); } break; case FormatDescriptor.Type.Fill: opcodeStr = sDataOpNames.Fill; operandStr = length + "," + formatter.FormatHexValue(data[offset], 2); break; case FormatDescriptor.Type.Dense: multiLine = true; opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); break; case FormatDescriptor.Type.Junk: int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length); if (fillVal >= 0 && GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap)) { // .align <expression>[, <fill>] opcodeStr = sDataOpNames.Align; int alignVal = 1 << FormatDescriptor.AlignmentToPower(dfd.FormatSubType); operandStr = alignVal.ToString() + "," + formatter.FormatHexValue(fillVal, 2); } else if (fillVal >= 0) { // treat same as Fill opcodeStr = sDataOpNames.Fill; operandStr = length + "," + formatter.FormatHexValue(fillVal, 2); } else { // treat same as Dense multiLine = true; opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); } break; case FormatDescriptor.Type.StringGeneric: case FormatDescriptor.Type.StringReverse: case FormatDescriptor.Type.StringNullTerm: case FormatDescriptor.Type.StringL8: case FormatDescriptor.Type.StringL16: case FormatDescriptor.Type.StringDci: multiLine = true; opcodeStr = operandStr = null; OutputString(offset, labelStr, commentStr); break; default: opcodeStr = "???"; operandStr = "***"; break; } if (!multiLine) { opcodeStr = formatter.FormatPseudoOp(opcodeStr); OutputLine(labelStr, opcodeStr, operandStr, commentStr); } }
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); } } }
// IGenerator public void OutputDataOp(int offset) { Formatter formatter = SourceFormatter; byte[] data = Project.FileData; Anattrib attr = Project.GetAnattrib(offset); string labelStr = string.Empty; if (attr.Symbol != null) { labelStr = mLocalizer.ConvLabel(attr.Symbol.Label); } string commentStr = SourceFormatter.FormatEolComment(Project.Comments[offset]); string opcodeStr, operandStr; FormatDescriptor dfd = attr.DataDescriptor; Debug.Assert(dfd != null); int length = dfd.Length; Debug.Assert(length > 0); bool multiLine = false; switch (dfd.FormatType) { case FormatDescriptor.Type.Default: if (length != 1) { Debug.Assert(false); length = 1; } opcodeStr = sDataOpNames.DefineData1; int operand = RawData.GetWord(data, offset, length, false); operandStr = formatter.FormatHexValue(operand, length * 2); break; case FormatDescriptor.Type.NumericLE: opcodeStr = sDataOpNames.GetDefineData(length); operand = RawData.GetWord(data, offset, length, false); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, false); break; case FormatDescriptor.Type.NumericBE: opcodeStr = sDataOpNames.GetDefineBigData(length); if (opcodeStr == null) { // Nothing defined, output as comma-separated single-byte values. GenerateShortSequence(offset, length, out opcodeStr, out operandStr); } else { operand = RawData.GetWord(data, offset, length, true); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, false); } break; case FormatDescriptor.Type.Fill: opcodeStr = sDataOpNames.Fill; operandStr = length + "," + formatter.FormatHexValue(data[offset], 2); break; case FormatDescriptor.Type.Dense: multiLine = true; opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); break; case FormatDescriptor.Type.String: multiLine = true; opcodeStr = operandStr = null; OutputString(offset, labelStr, commentStr); break; default: opcodeStr = "???"; operandStr = "***"; break; } if (!multiLine) { opcodeStr = formatter.FormatPseudoOp(opcodeStr); OutputLine(labelStr, opcodeStr, operandStr, commentStr); } }
// IGenerator public void OutputDataOp(int offset) { Formatter formatter = SourceFormatter; byte[] data = Project.FileData; Anattrib attr = Project.GetAnattrib(offset); string labelStr = string.Empty; if (attr.Symbol != null) { labelStr = mLocalizer.ConvLabel(attr.Symbol.Label); } string commentStr = SourceFormatter.FormatEolComment(Project.Comments[offset]); string opcodeStr, operandStr; FormatDescriptor dfd = attr.DataDescriptor; Debug.Assert(dfd != null); int length = dfd.Length; Debug.Assert(length > 0); bool multiLine = false; switch (dfd.FormatType) { case FormatDescriptor.Type.Default: if (length != 1) { Debug.Assert(false); length = 1; } opcodeStr = sDataOpNames.DefineData1; int operand = RawData.GetWord(data, offset, length, false); operandStr = formatter.FormatHexValue(operand, length * 2); break; case FormatDescriptor.Type.NumericLE: opcodeStr = sDataOpNames.GetDefineData(length); operand = RawData.GetWord(data, offset, length, false); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); break; case FormatDescriptor.Type.NumericBE: opcodeStr = sDataOpNames.GetDefineBigData(length); if ((string.IsNullOrEmpty(opcodeStr))) { // Nothing defined, output as comma-separated single-byte values. GenerateShortSequence(offset, length, out opcodeStr, out operandStr); } else { operand = RawData.GetWord(data, offset, length, true); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); } break; case FormatDescriptor.Type.Fill: opcodeStr = sDataOpNames.Fill; operandStr = length + "," + formatter.FormatHexValue(data[offset], 2); break; case FormatDescriptor.Type.Dense: multiLine = true; opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); break; case FormatDescriptor.Type.Junk: // The ca65 .align directive has a dependency on the alignment of the // segment as a whole. We're not currently declaring multiple segments, // so we can't use .align without generating complaints. int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length); if (fillVal >= 0) { // treat same as Fill opcodeStr = sDataOpNames.Fill; operandStr = length + "," + formatter.FormatHexValue(fillVal, 2); } else { // treat same as Dense multiLine = true; opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); } break; case FormatDescriptor.Type.StringGeneric: case FormatDescriptor.Type.StringReverse: case FormatDescriptor.Type.StringNullTerm: case FormatDescriptor.Type.StringL8: case FormatDescriptor.Type.StringL16: case FormatDescriptor.Type.StringDci: multiLine = true; opcodeStr = operandStr = null; OutputString(offset, labelStr, commentStr); break; default: opcodeStr = "???"; operandStr = "***"; break; } if (!multiLine) { opcodeStr = formatter.FormatPseudoOp(opcodeStr); OutputLine(labelStr, opcodeStr, operandStr, commentStr); } }
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); } } }
/// <summary> /// Sets the text displayed in the "preview" text box. /// </summary> private void SetPreviewText() { //symbolValueLabel.Text = string.Empty; mIsSymbolAuto = false; FormatDescriptor dfd = CreateDescriptorFromControls(); if (dfd == null) { // Default format. We can't actually know what this look like, so just // clear the box. previewTextBox.Text = string.Empty; return; } if (dfd.FormatSubType == FormatDescriptor.SubType.Symbol && string.IsNullOrEmpty(dfd.SymbolRef.Label)) { // no label yet, nothing to show previewTextBox.Text = string.Empty; return; } StringBuilder preview = new StringBuilder(); if (mShowHashPrefix) { preview.Append('#'); } switch (dfd.FormatSubType) { case FormatDescriptor.SubType.Hex: preview.Append(mFormatter.FormatHexValue(mOperandValue, mPreviewHexDigits)); break; case FormatDescriptor.SubType.Decimal: preview.Append(mFormatter.FormatDecimalValue(mOperandValue)); break; case FormatDescriptor.SubType.Binary: preview.Append(mFormatter.FormatBinaryValue(mOperandValue, 8)); break; case FormatDescriptor.SubType.Ascii: preview.Append(mFormatter.FormatAsciiOrHex(mOperandValue)); break; case FormatDescriptor.SubType.Symbol: if (mProject.SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) { if (mIsBlockMove) { // For a 24-bit symbol, we grab the high byte. This is the // expected behavior, according to Eyes & Lichty; see the // explanation of the MVP instruction. For an 8-bit symbol // the assembler just takes the value. // TODO(someday): allow a different symbol for each part of the // operand. if (sym.Value > 0xff) { radioButtonBank.Checked = true; } else { radioButtonLow.Checked = true; } dfd = CreateDescriptorFromControls(); } // Hack to make relative branches look right in the preview window. // Otherwise they show up like "<LABEL" because they appear to be // only 8 bits. int operandLen = dfd.Length - 1; if (operandLen == 1 && mIsPcRelative) { operandLen = 2; } PseudoOp.FormatNumericOpFlags flags; if (mIsPcRelative) { flags = PseudoOp.FormatNumericOpFlags.IsPcRel; } else if (mShowHashPrefix) { flags = PseudoOp.FormatNumericOpFlags.HasHashPrefix; } else { flags = PseudoOp.FormatNumericOpFlags.None; } string str = PseudoOp.FormatNumericOperand(mFormatter, mProject.SymbolTable, null, dfd, mOperandValue, operandLen, flags); preview.Append(str); if (sym.SymbolSource == Symbol.Source.Auto) { mIsSymbolAuto = true; } } else { preview.Append(dfd.SymbolRef.Label + " (?)"); Debug.Assert(!string.IsNullOrEmpty(dfd.SymbolRef.Label)); //symbolValueLabel.Text = Properties.Resources.MSG_SYMBOL_NOT_FOUND; } break; default: Debug.Assert(false); preview.Append("BUG"); break; } previewTextBox.Text = preview.ToString(); }
// IGenerator public void OutputDataOp(int offset) { Formatter formatter = SourceFormatter; byte[] data = Project.FileData; Anattrib attr = Project.GetAnattrib(offset); string labelStr = string.Empty; if (attr.Symbol != null) { labelStr = mLocalizer.ConvLabel(attr.Symbol.Label); } string commentStr = SourceFormatter.FormatEolComment(Project.Comments[offset]); string opcodeStr, operandStr; FormatDescriptor dfd = attr.DataDescriptor; Debug.Assert(dfd != null); int length = dfd.Length; Debug.Assert(length > 0); bool multiLine = false; switch (dfd.FormatType) { case FormatDescriptor.Type.Default: if (length != 1) { Debug.Assert(false); length = 1; } opcodeStr = sDataOpNames.DefineData1; int operand = RawData.GetWord(data, offset, length, false); operandStr = formatter.FormatHexValue(operand, length * 2); break; case FormatDescriptor.Type.NumericLE: opcodeStr = sDataOpNames.GetDefineData(length); operand = RawData.GetWord(data, offset, length, false); if (length == 1 && dfd.IsStringOrCharacter && ((operand & 0x7f) == '{' || (operand & 0x7f) == '}')) { // Merlin32 can't handle "DFB '{'", so just output hex. operandStr = formatter.FormatHexValue(operand, length * 2); } else { operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); } break; case FormatDescriptor.Type.NumericBE: opcodeStr = sDataOpNames.GetDefineBigData(length); if ((string.IsNullOrEmpty(opcodeStr))) { // Nothing defined, output as comma-separated single-byte values. GenerateShortSequence(offset, length, out opcodeStr, out operandStr); } else { operand = RawData.GetWord(data, offset, length, true); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, PseudoOp.FormatNumericOpFlags.OmitLabelPrefixSuffix); } break; case FormatDescriptor.Type.Fill: opcodeStr = sDataOpNames.Fill; if (data[offset] == 0) { operandStr = length.ToString(); } else { operandStr = length + "," + formatter.FormatHexValue(data[offset], 2); } break; case FormatDescriptor.Type.Dense: multiLine = true; opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); break; case FormatDescriptor.Type.Uninit: case FormatDescriptor.Type.Junk: int fillVal = Helper.CheckRangeHoldsSingleValue(data, offset, length); if (fillVal >= 0) { opcodeStr = sDataOpNames.Fill; if (dfd.FormatSubType == FormatDescriptor.SubType.Align256 && GenCommon.CheckJunkAlign(offset, dfd, Project.AddrMap)) { // special syntax for page alignment if (fillVal == 0) { operandStr = "\\"; } else { operandStr = "\\," + formatter.FormatHexValue(fillVal, 2); } } else if (length == 1 && fillVal != 0x00) { // Single-byte HEX looks better than "ds 1,$xx", and will match up // with adjacent multi-byte junk/uninit. multiLine = true; opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); } else { if (fillVal == 0) { operandStr = length.ToString(); } else { operandStr = length + "," + formatter.FormatHexValue(fillVal, 2); } } } else { // treat same as Dense multiLine = true; opcodeStr = operandStr = null; OutputDenseHex(offset, length, labelStr, commentStr); } break; case FormatDescriptor.Type.StringGeneric: case FormatDescriptor.Type.StringReverse: case FormatDescriptor.Type.StringNullTerm: case FormatDescriptor.Type.StringL8: case FormatDescriptor.Type.StringL16: case FormatDescriptor.Type.StringDci: multiLine = true; opcodeStr = operandStr = null; OutputString(offset, labelStr, commentStr); break; default: opcodeStr = "???"; operandStr = "***"; break; } if (!multiLine) { opcodeStr = formatter.FormatPseudoOp(opcodeStr); OutputLine(labelStr, opcodeStr, operandStr, commentStr); } }