/*****************************************************************************/ /* */ /* handler.c */ /* */ /* Opcode handler functions for the disassembler */ /* */ /* */ /* */ /* (C) 2000-2011, Ullrich von Bassewitz */ /* Roemerstrasse 52 */ /* D-70794 Filderstadt */ /* EMail: uz@cc65.org */ /* */ /* */ /* This software is provided 'as-is', without any expressed or implied */ /* warranty. In no event will the authors be held liable for any damages */ /* arising from the use of this software. */ /* */ /* Permission is granted to anyone to use this software for any purpose, */ /* including commercial applications, and to alter it and redistribute it */ /* freely, subject to the following restrictions: */ /* */ /* 1. The origin of this software must not be misrepresented; you must not */ /* claim that you wrote the original software. If you use this software */ /* in a product, an acknowledgment in the product documentation would be */ /* appreciated but is not required. */ /* 2. Altered source versions must be plainly marked as such, and must not */ /* be misrepresented as being the original software. */ /* 3. This notice may not be removed or altered from any source */ /* distribution. */ /* */ /*****************************************************************************/ #include /* common */ #include "xmalloc.h" #include "xsprintf.h" /* da65 */ #include "attrtab.h" #include "code.h" #include "error.h" #include "global.h" #include "handler.h" #include "labels.h" #include "opctable.h" #include "output.h" static unsigned short SubroutineParamSize[0x10000]; /*****************************************************************************/ /* Helper functions */ /*****************************************************************************/ static void Mnemonic (const char* M) /* Indent and output a mnemonic */ { Indent (MCol); Output ("%s", M); } static void OneLine (const OpcDesc* D, const char* Arg, ...) attribute ((format(printf, 2, 3))); static void OneLine (const OpcDesc* D, const char* Arg, ...) /* Output one line with the given mnemonic and argument */ { char Buf [256]; va_list ap; /* Mnemonic */ Mnemonic (D->Mnemo); /* Argument */ va_start (ap, Arg); xvsprintf (Buf, sizeof (Buf), Arg, ap); va_end (ap); Indent (ACol); Output ("%s", Buf); /* Add the code stuff as comment */ LineComment (PC, D->Size); /* End the line */ LineFeed (); } static const char* GetAbsOverride (unsigned Flags, unsigned Addr) /* If the instruction requires an abs override modifier, return the necessary ** string, otherwise return the empty string. */ { if ((Flags & flFarOverride) != 0 && Addr < 0x10000) { return "f:"; } else if ((Flags & flAbsOverride) != 0 && Addr < 0x100) { return "a:"; } else { return ""; } } static const char* GetAddrArg (unsigned Flags, unsigned Addr) /* Return an address argument - a label if we have one, or the address itself */ { const char* Label = 0; if (Flags & flUseLabel) { Label = GetLabel (Addr, PC); } if (Label) { return Label; } else { static char Buf [32]; if (Addr < 0x100) { xsprintf (Buf, sizeof (Buf), "$%02X", Addr); } else if (Addr < 0x10000) { xsprintf (Buf, sizeof (Buf), "$%04X", Addr); } else { xsprintf (Buf, sizeof (Buf), "$%06X", Addr); } return Buf; } } static void GenerateLabel (unsigned Flags, unsigned Addr) /* Generate a label in pass one if requested */ { /* Generate labels in pass #1, and only if we don't have a label already */ if (Pass == 1 && !HaveLabel (Addr) && /* Check if we must create a label */ ((Flags & flGenLabel) != 0 || ((Flags & flUseLabel) != 0 && Addr >= CodeStart && Addr <= CodeEnd))) { /* As a special case, handle ranges with tables or similar. Within ** such a range with a granularity > 1, do only generate dependent ** labels for all addresses but the first one. Be sure to generate ** a label for the start of the range, however. */ attr_t Style = GetStyleAttr (Addr); unsigned Granularity = GetGranularity (Style); if (Granularity == 1) { /* Just add the label */ AddIntLabel (Addr); } else { /* THIS CODE IS A MESS AND WILL FAIL ON SEVERAL CONDITIONS! ### */ /* Search for the start of the range or the last non dependent ** label in the range. */ unsigned Offs; attr_t LabelAttr; unsigned LabelAddr = Addr; while (LabelAddr > CodeStart) { if (Style != GetStyleAttr (LabelAddr-1)) { /* End of range reached */ break; } --LabelAddr; LabelAttr = GetLabelAttr (LabelAddr); if ((LabelAttr & (atIntLabel|atExtLabel)) != 0) { /* The address has an internal or external label */ break; } } /* If the proposed label address doesn't have a label, define one */ if ((GetLabelAttr (LabelAddr) & (atIntLabel|atExtLabel)) == 0) { AddIntLabel (LabelAddr); } /* Create the label */ Offs = Addr - LabelAddr; if (Offs == 0) { AddIntLabel (Addr); } else { AddDepLabel (Addr, atIntLabel, GetLabelName (LabelAddr), Offs); } } } } /*****************************************************************************/ /* Code */ /*****************************************************************************/ void OH_Illegal (const OpcDesc* D attribute ((unused))) { DataByteLine (1); } void OH_Accumulator (const OpcDesc* D) { OneLine (D, "a"); } void OH_Implicit (const OpcDesc* D) { Mnemonic (D->Mnemo); LineComment (PC, D->Size); LineFeed (); } void OH_Immediate (const OpcDesc* D) { OneLine (D, "#$%02X", GetCodeByte (PC+1)); } void OH_Immediate65816M (const OpcDesc* D) { if (GetAttr (PC) & atMem16) { OneLine (D, "#$%04X", GetCodeWord (PC+1)); } else { OneLine (D, "#$%02X", GetCodeByte (PC+1)); } } void OH_Immediate65816X (const OpcDesc* D) { if (GetAttr (PC) & atIdx16) { OneLine (D, "#$%04X", GetCodeWord (PC+1)); } else { OneLine (D, "#$%02X", GetCodeByte (PC+1)); } } void OH_ImmediateWord (const OpcDesc* D) { OneLine (D, "#$%04X", GetCodeWord (PC+1)); } void OH_Direct (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); } void OH_DirectX (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s,x", GetAddrArg (D->Flags, Addr)); } void OH_DirectY (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s,y", GetAddrArg (D->Flags, Addr)); } void OH_Absolute (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeWord (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s%s", GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } void OH_AbsoluteX (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeWord (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s%s,x", GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } void OH_AbsoluteY (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeWord (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s%s,y", GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } void OH_AbsoluteLong (const OpcDesc* D attribute ((unused))) { /* Get the operand */ unsigned Addr = GetCodeLongAddr (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s%s", GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } void OH_AbsoluteLongX (const OpcDesc* D attribute ((unused))) { /* Get the operand */ unsigned Addr = GetCodeLongAddr (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s%s,x", GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } void OH_Relative (const OpcDesc* D) { /* Get the operand */ signed char Offs = GetCodeByte (PC+1); /* Calculate the target address */ unsigned Addr = (((int) PC+2) + Offs) & 0xFFFF; /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ if (HaveLabel(Addr)) { OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); } else { /* No label -- make a relative address expression */ OneLine (D, "* + (%d)", (int) Offs + 2); } } void OH_RelativeLong (const OpcDesc* D attribute ((unused))) { /* Get the operand */ signed short Offs = GetCodeWord (PC+1); /* Calculate the target address */ unsigned Addr = (((int) PC+3) + Offs) & 0xFFFF; /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); } void OH_RelativeLong4510 (const OpcDesc* D attribute ((unused))) { /* Get the operand */ signed short Offs = GetCodeWord (PC+1); /* Calculate the target address */ unsigned Addr = (((int) PC+2) + Offs) & 0xFFFF; /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); } void OH_DirectIndirect (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "(%s)", GetAddrArg (D->Flags, Addr)); } void OH_DirectIndirectY (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "(%s),y", GetAddrArg (D->Flags, Addr)); } void OH_DirectIndirectZ (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "(%s),z", GetAddrArg (D->Flags, Addr)); } void OH_DirectXIndirect (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "(%s,x)", GetAddrArg (D->Flags, Addr)); } void OH_AbsoluteIndirect (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeWord (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "(%s)", GetAddrArg (D->Flags, Addr)); } void OH_BitBranch (const OpcDesc* D) { char* BranchLabel; /* Get the operands */ unsigned char TestAddr = GetCodeByte (PC+1); signed char BranchOffs = GetCodeByte (PC+2); /* Calculate the target address for the branch */ unsigned BranchAddr = (((int) PC+3) + BranchOffs) & 0xFFFF; /* Generate labels in pass 1. The bit branch codes are special in that ** they don't really match the remainder of the 6502 instruction set (they ** are a Rockwell addon), so we must pass additional flags as direct ** value to the second GenerateLabel call. */ GenerateLabel (D->Flags, TestAddr); GenerateLabel (flLabel, BranchAddr); /* Make a copy of an operand, so that ** the other operand can't overwrite it. ** [GetAddrArg() uses a statically-stored buffer.] */ BranchLabel = xstrdup (GetAddrArg (flLabel, BranchAddr)); /* Output the line */ OneLine (D, "%s,%s", GetAddrArg (D->Flags, TestAddr), BranchLabel); xfree (BranchLabel); } void OH_ImmediateDirect (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+2); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "#$%02X,%s", GetCodeByte (PC+1), GetAddrArg (D->Flags, Addr)); } void OH_ImmediateDirectX (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+2); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "#$%02X,%s,x", GetCodeByte (PC+1), GetAddrArg (D->Flags, Addr)); } void OH_ImmediateAbsolute (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeWord (PC+2); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "#$%02X,%s%s", GetCodeByte (PC+1), GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } void OH_ImmediateAbsoluteX (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeWord (PC+2); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "#$%02X,%s%s,x", GetCodeByte (PC+1), GetAbsOverride (D->Flags, Addr), GetAddrArg (D->Flags, Addr)); } void OH_StackRelative (const OpcDesc* D attribute ((unused))) { /* Output the line */ OneLine (D, "$%02X,s", GetCodeByte (PC+1)); } void OH_DirectIndirectLongX (const OpcDesc* D attribute ((unused))) { Error ("Not implemented %s", __FUNCTION__); } void OH_StackRelativeIndirectY (const OpcDesc* D attribute ((unused))) { /* Output the line */ OneLine (D, "($%02X,s),y", GetCodeByte (PC+1)); } void OH_StackRelativeIndirectY4510 (const OpcDesc* D attribute ((unused))) { /* Output the line */ OneLine (D, "($%02X,sp),y", GetCodeByte (PC+1)); } void OH_DirectIndirectLong (const OpcDesc* D attribute ((unused))) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "[%s]", GetAddrArg (D->Flags, Addr)); } void OH_DirectIndirectLongY (const OpcDesc* D attribute ((unused))) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "[%s],y", GetAddrArg (D->Flags, Addr)); } void OH_BlockMove (const OpcDesc* D) { char* DstLabel; /* Get source operand */ unsigned Src = GetCodeWord (PC+1); /* Get destination operand */ unsigned Dst = GetCodeWord (PC+3); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Src); GenerateLabel (D->Flags, Dst); /* Make a copy of an operand, so that ** the other operand can't overwrite it. ** [GetAddrArg() uses a statically-stored buffer.] */ DstLabel = xstrdup (GetAddrArg (D->Flags, Dst)); /* Output the line */ OneLine (D, "%s%s,%s%s,$%04X", GetAbsOverride (D->Flags, Src), GetAddrArg (D->Flags, Src), GetAbsOverride (D->Flags, Dst), DstLabel, GetCodeWord (PC+5)); xfree (DstLabel); } void OH_BlockMove65816 (const OpcDesc* D) { /* Get source operand */ unsigned Src = GetCodeByte (PC+2); /* Get destination operand */ unsigned Dst = GetCodeByte (PC+1); /* Output the line */ OneLine (D, "#$%02X, #$%02X", Src, Dst); } void OH_AbsoluteXIndirect (const OpcDesc* D attribute ((unused))) { /* Get the operand */ unsigned Addr = GetCodeWord (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "(%s,x)", GetAddrArg (D->Flags, Addr)); } void OH_DirectImmediate (const OpcDesc* D) { /* Get the operand */ unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%s, #$%02X", GetAddrArg (D->Flags, Addr), GetCodeByte (PC+2)); } void OH_ZeroPageBit (const OpcDesc* D) { unsigned Bit = GetCodeByte (PC) >> 5; unsigned Addr = GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* Output the line */ OneLine (D, "%01X,%s", Bit, GetAddrArg (D->Flags, Addr)); } void OH_AccumulatorBit (const OpcDesc* D) { unsigned Bit = GetCodeByte (PC) >> 5; /* Output the line */ OneLine (D, "%01X,a", Bit); } void OH_AccumulatorBitBranch (const OpcDesc* D) { unsigned Bit = GetCodeByte (PC) >> 5; signed char BranchOffs = GetCodeByte (PC+1); /* Calculate the target address for the branch */ unsigned BranchAddr = (((int) PC+3) + BranchOffs) & 0xFFFF; /* Generate labels in pass 1 */ GenerateLabel (flLabel, BranchAddr); /* Output the line */ OneLine (D, "%01X,a,%s", Bit, GetAddrArg (flLabel, BranchAddr)); } void OH_JmpDirectIndirect (const OpcDesc* D) { OH_DirectIndirect (D); if (NewlineAfterJMP) { LineFeed (); } SeparatorLine (); } void OH_SpecialPage (const OpcDesc* D) { /* Get the operand */ unsigned Addr = 0xFF00 + GetCodeByte (PC+1); /* Generate a label in pass 1 */ GenerateLabel (D->Flags, Addr); /* OneLine (D, "$FF%02X", (CodeByte (PC+1)); */ OneLine (D, "%s", GetAddrArg (D->Flags, Addr)); } void OH_Rts (const OpcDesc* D) { OH_Implicit (D); if (NewlineAfterRTS) { LineFeed (); } SeparatorLine(); } void OH_JmpAbsolute (const OpcDesc* D) { OH_Absolute (D); if (NewlineAfterJMP) { LineFeed (); } SeparatorLine (); } void OH_JmpAbsoluteIndirect (const OpcDesc* D) { OH_AbsoluteIndirect (D); if (NewlineAfterJMP) { LineFeed (); } SeparatorLine (); } void OH_JmpAbsoluteXIndirect (const OpcDesc* D) { OH_AbsoluteXIndirect (D); if (NewlineAfterJMP) { LineFeed (); } SeparatorLine (); } void OH_JsrAbsolute (const OpcDesc* D) { unsigned ParamSize = SubroutineParamSize[GetCodeWord (PC+1)]; OH_Absolute (D); if (ParamSize > 0) { unsigned RemainingBytes; unsigned BytesLeft; PC += D->Size; RemainingBytes = GetRemainingBytes (); if (RemainingBytes < ParamSize) { ParamSize = RemainingBytes; } BytesLeft = ParamSize; while (BytesLeft > 0) { unsigned Chunk = (BytesLeft > BytesPerLine) ? BytesPerLine : BytesLeft; DataByteLine (Chunk); BytesLeft -= Chunk; PC += Chunk; } PC -= D->Size; } } void SetSubroutineParamSize (unsigned Addr, unsigned Size) { SubroutineParamSize[Addr] = Size; }