/*****************************************************************************/ /* */ /* opcodes.c */ /* */ /* Opcode and addressing mode definitions */ /* */ /* */ /* */ /* (C) 2001-2004 Ullrich von Bassewitz */ /* Römerstraße 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 "stdlib.h" #include #include /* common */ #include "check.h" #include "cpu.h" /* cc65 */ #include "codeinfo.h" #include "error.h" #include "opcodes.h" /*****************************************************************************/ /* Data */ /*****************************************************************************/ /* Opcode description table */ const OPCDesc OPCTable[OP65_COUNT] = { /* 65XX opcodes */ { OP65_ADC, /* opcode */ "adc", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_A, /* chg */ OF_SETF /* flags */ }, { OP65_AND, /* opcode */ "and", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_A, /* chg */ OF_SETF /* flags */ }, { OP65_ASL, /* opcode */ "asl", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_SETF | OF_NOIMP /* flags */ }, { OP65_BCC, /* opcode */ "bcc", /* mnemonic */ 2, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA /* flags */ }, { OP65_BCS, /* opcode */ "bcs", /* mnemonic */ 2, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA /* flags */ }, { OP65_BEQ, /* opcode */ "beq", /* mnemonic */ 2, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_ZBRA | OF_FBRA /* flags */ }, { OP65_BIT, /* opcode */ "bit", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ OF_SETF /* flags */ }, { OP65_BMI, /* opcode */ "bmi", /* mnemonic */ 2, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_FBRA /* flags */ }, { OP65_BNE, /* opcode */ "bne", /* mnemonic */ 2, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_ZBRA | OF_FBRA /* flags */ }, { OP65_BPL, /* opcode */ "bpl", /* mnemonic */ 2, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_FBRA /* flags */ }, { OP65_BRA, /* opcode */ "bra", /* mnemonic */ 2, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_UBRA /* flags */ }, { OP65_BRK, /* opcode */ "brk", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_BVC, /* opcode */ "bvc", /* mnemonic */ 2, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA /* flags */ }, { OP65_BVS, /* opcode */ "bvs", /* mnemonic */ 2, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA /* flags */ }, { OP65_CLC, /* opcode */ "clc", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_CLD, /* opcode */ "cld", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_CLI, /* opcode */ "cli", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_CLV, /* opcode */ "clv", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_CMP, /* opcode */ "cmp", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ OF_SETF | OF_CMP /* flags */ }, { OP65_CPX, /* opcode */ "cpx", /* mnemonic */ 0, /* size */ REG_X, /* use */ REG_NONE, /* chg */ OF_SETF | OF_CMP /* flags */ }, { OP65_CPY, /* opcode */ "cpy", /* mnemonic */ 0, /* size */ REG_Y, /* use */ REG_NONE, /* chg */ OF_SETF | OF_CMP /* flags */ }, { OP65_DEA, /* opcode */ "dea", /* mnemonic */ 1, /* size */ REG_A, /* use */ REG_A, /* chg */ OF_REG_INCDEC | OF_SETF /* flags */ }, { OP65_DEC, /* opcode */ "dec", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_SETF | OF_NOIMP /* flags */ }, { OP65_DEX, /* opcode */ "dex", /* mnemonic */ 1, /* size */ REG_X, /* use */ REG_X, /* chg */ OF_REG_INCDEC | OF_SETF /* flags */ }, { OP65_DEY, /* opcode */ "dey", /* mnemonic */ 1, /* size */ REG_Y, /* use */ REG_Y, /* chg */ OF_REG_INCDEC | OF_SETF /* flags */ }, { OP65_EOR, /* opcode */ "eor", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_A, /* chg */ OF_SETF /* flags */ }, { OP65_INA, /* opcode */ "ina", /* mnemonic */ 1, /* size */ REG_A, /* use */ REG_A, /* chg */ OF_REG_INCDEC | OF_SETF /* flags */ }, { OP65_INC, /* opcode */ "inc", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_SETF | OF_NOIMP /* flags */ }, { OP65_INX, /* opcode */ "inx", /* mnemonic */ 1, /* size */ REG_X, /* use */ REG_X, /* chg */ OF_REG_INCDEC | OF_SETF /* flags */ }, { OP65_INY, /* opcode */ "iny", /* mnemonic */ 1, /* size */ REG_Y, /* use */ REG_Y, /* chg */ OF_REG_INCDEC | OF_SETF /* flags */ }, { OP65_JCC, /* opcode */ "jcc", /* mnemonic */ 5, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_LBRA /* flags */ }, { OP65_JCS, /* opcode */ "jcs", /* mnemonic */ 5, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_LBRA /* flags */ }, { OP65_JEQ, /* opcode */ "jeq", /* mnemonic */ 5, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA /* flags */ }, { OP65_JMI, /* opcode */ "jmi", /* mnemonic */ 5, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_LBRA | OF_FBRA /* flags */ }, { OP65_JMP, /* opcode */ "jmp", /* mnemonic */ 3, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_UBRA | OF_LBRA /* flags */ }, { OP65_JNE, /* opcode */ "jne", /* mnemonic */ 5, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_LBRA | OF_ZBRA | OF_FBRA /* flags */ }, { OP65_JPL, /* opcode */ "jpl", /* mnemonic */ 5, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_LBRA | OF_FBRA /* flags */ }, { OP65_JSR, /* opcode */ "jsr", /* mnemonic */ 3, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CALL /* flags */ }, { OP65_JVC, /* opcode */ "jvc", /* mnemonic */ 5, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_LBRA /* flags */ }, { OP65_JVS, /* opcode */ "jvs", /* mnemonic */ 5, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_CBRA | OF_LBRA /* flags */ }, { OP65_LDA, /* opcode */ "lda", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_A, /* chg */ OF_LOAD | OF_SETF /* flags */ }, { OP65_LDX, /* opcode */ "ldx", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_X, /* chg */ OF_LOAD | OF_SETF /* flags */ }, { OP65_LDY, /* opcode */ "ldy", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_Y, /* chg */ OF_LOAD | OF_SETF /* flags */ }, { OP65_LSR, /* opcode */ "lsr", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_SETF | OF_NOIMP /* flags */ }, { OP65_NOP, /* opcode */ "nop", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_ORA, /* opcode */ "ora", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_A, /* chg */ OF_SETF /* flags */ }, { OP65_PHA, /* opcode */ "pha", /* mnemonic */ 1, /* size */ REG_A, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_PHP, /* opcode */ "php", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_PHX, /* opcode */ "phx", /* mnemonic */ 1, /* size */ REG_X, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_PHY, /* opcode */ "phy", /* mnemonic */ 1, /* size */ REG_Y, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_PLA, /* opcode */ "pla", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_A, /* chg */ OF_SETF /* flags */ }, { OP65_PLP, /* opcode */ "plp", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_PLX, /* opcode */ "plx", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_X, /* chg */ OF_SETF /* flags */ }, { OP65_PLY, /* opcode */ "ply", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_Y, /* chg */ OF_SETF /* flags */ }, { OP65_ROL, /* opcode */ "rol", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_SETF | OF_NOIMP /* flags */ }, { OP65_ROR, /* opcode */ "ror", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_SETF | OF_NOIMP /* flags */ }, /* Mark RTI as "uses all registers but doesn't change them", so the * optimizer won't remove preceeding loads. */ { OP65_RTI, /* opcode */ "rti", /* mnemonic */ 1, /* size */ REG_AXY, /* use */ REG_NONE, /* chg */ OF_RET /* flags */ }, { OP65_RTS, /* opcode */ "rts", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_RET /* flags */ }, { OP65_SBC, /* opcode */ "sbc", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_A, /* chg */ OF_SETF /* flags */ }, { OP65_SEC, /* opcode */ "sec", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_SED, /* opcode */ "sed", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_SEI, /* opcode */ "sei", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_NONE /* flags */ }, { OP65_STA, /* opcode */ "sta", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ OF_STORE /* flags */ }, { OP65_STX, /* opcode */ "stx", /* mnemonic */ 0, /* size */ REG_X, /* use */ REG_NONE, /* chg */ OF_STORE /* flags */ }, { OP65_STY, /* opcode */ "sty", /* mnemonic */ 0, /* size */ REG_Y, /* use */ REG_NONE, /* chg */ OF_STORE /* flags */ }, { OP65_STZ, /* opcode */ "stz", /* mnemonic */ 0, /* size */ REG_NONE, /* use */ REG_NONE, /* chg */ OF_STORE /* flags */ }, { OP65_TAX, /* opcode */ "tax", /* mnemonic */ 1, /* size */ REG_A, /* use */ REG_X, /* chg */ OF_XFR | OF_SETF /* flags */ }, { OP65_TAY, /* opcode */ "tay", /* mnemonic */ 1, /* size */ REG_A, /* use */ REG_Y, /* chg */ OF_XFR | OF_SETF /* flags */ }, { OP65_TRB, /* opcode */ "trb", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ OF_SETF /* flags */ }, { OP65_TSB, /* opcode */ "tsb", /* mnemonic */ 0, /* size */ REG_A, /* use */ REG_NONE, /* chg */ OF_SETF /* flags */ }, { OP65_TSX, /* opcode */ "tsx", /* mnemonic */ 1, /* size */ REG_NONE, /* use */ REG_X, /* chg */ OF_XFR | OF_SETF /* flags */ }, { OP65_TXA, /* opcode */ "txa", /* mnemonic */ 1, /* size */ REG_X, /* use */ REG_A, /* chg */ OF_XFR | OF_SETF /* flags */ }, { OP65_TXS, /* opcode */ "txs", /* mnemonic */ 1, /* size */ REG_X, /* use */ REG_NONE, /* chg */ OF_XFR /* flags */ }, { OP65_TYA, /* opcode */ "tya", /* mnemonic */ 1, /* size */ REG_Y, /* use */ REG_A, /* chg */ OF_XFR | OF_SETF /* flags */ }, }; /*****************************************************************************/ /* Code */ /*****************************************************************************/ static int FindCmp (const void* Key, const void* Desc) /* Compare function for FindOpcode */ { return strcmp (Key, ((OPCDesc*)Desc)->Mnemo); } const OPCDesc* FindOP65 (const char* M) /* Find the given opcode and return the opcode number. If the opcode was not * found, return NULL. */ { unsigned I; unsigned Len; /* Check the length of the given string, then copy it into local * storage, converting it to upper case. */ char Mnemo[sizeof (OPCTable[0].Mnemo)]; Len = strlen (M); if (Len >= sizeof (OPCTable[0].Mnemo)) { /* Invalid length means invalid opcode */ return 0; } for (I = 0; I < Len; ++I) { Mnemo[I] = tolower (M[I]); } Mnemo[I] = '\0'; /* Search for the mnemonic in the table and return the result */ return bsearch (Mnemo, OPCTable, OP65_COUNT, sizeof (OPCTable[0]), FindCmp ); } unsigned GetInsnSize (opc_t OPC, am_t AM) /* Return the size of the given instruction */ { /* Get the opcode desc and check the size given there */ const OPCDesc* D = &OPCTable[OPC]; if (D->Size != 0) { return D->Size; } /* Check the addressing mode. */ switch (AM) { case AM65_IMP: return 1; case AM65_ACC: return 1; case AM65_IMM: return 2; case AM65_ZP: return 2; case AM65_ZPX: return 2; case AM65_ABS: return 3; case AM65_ABSX: return 3; case AM65_ABSY: return 3; case AM65_ZPX_IND: return 2; case AM65_ZP_INDY: return 2; case AM65_ZP_IND: return 2; default: Internal ("Invalid addressing mode"); return 0; } } unsigned char GetAMUseInfo (am_t AM) /* Get usage info for the given addressing mode (addressing modes that use * index registers return REG_r info for these registers). */ { /* Check the addressing mode. */ switch (AM) { case AM65_ACC: return REG_A; case AM65_ZPX: return REG_X; case AM65_ABSX: return REG_X; case AM65_ABSY: return REG_Y; case AM65_ZPX_IND: return REG_X; case AM65_ZP_INDY: return REG_Y; default: return REG_NONE; } } opc_t GetInverseBranch (opc_t OPC) /* Return a branch that reverse the condition of the branch given in OPC */ { switch (OPC) { case OP65_BCC: return OP65_BCS; case OP65_BCS: return OP65_BCC; case OP65_BEQ: return OP65_BNE; case OP65_BMI: return OP65_BPL; case OP65_BNE: return OP65_BEQ; case OP65_BPL: return OP65_BMI; case OP65_BVC: return OP65_BVS; case OP65_BVS: return OP65_BVC; case OP65_JCC: return OP65_JCS; case OP65_JCS: return OP65_JCC; case OP65_JEQ: return OP65_JNE; case OP65_JMI: return OP65_JPL; case OP65_JNE: return OP65_JEQ; case OP65_JPL: return OP65_JMI; case OP65_JVC: return OP65_JVS; case OP65_JVS: return OP65_JVC; default: Internal ("GetInverseBranch: Invalid opcode: %d", OPC); return 0; } } opc_t MakeShortBranch (opc_t OPC) /* Return the short version of the given branch. If the branch is already * a short branch, return the opcode unchanged. */ { switch (OPC) { case OP65_BCC: case OP65_JCC: return OP65_BCC; case OP65_BCS: case OP65_JCS: return OP65_BCS; case OP65_BEQ: case OP65_JEQ: return OP65_BEQ; case OP65_BMI: case OP65_JMI: return OP65_BMI; case OP65_BNE: case OP65_JNE: return OP65_BNE; case OP65_BPL: case OP65_JPL: return OP65_BPL; case OP65_BVC: case OP65_JVC: return OP65_BVC; case OP65_BVS: case OP65_JVS: return OP65_BVS; case OP65_BRA: case OP65_JMP: return (CPUIsets[CPU] & CPU_ISET_65SC02)? OP65_BRA : OP65_JMP; default: Internal ("MakeShortBranch: Invalid opcode: %d", OPC); return 0; } } opc_t MakeLongBranch (opc_t OPC) /* Return the long version of the given branch. If the branch is already * a long branch, return the opcode unchanged. */ { switch (OPC) { case OP65_BCC: case OP65_JCC: return OP65_JCC; case OP65_BCS: case OP65_JCS: return OP65_JCS; case OP65_BEQ: case OP65_JEQ: return OP65_JEQ; case OP65_BMI: case OP65_JMI: return OP65_JMI; case OP65_BNE: case OP65_JNE: return OP65_JNE; case OP65_BPL: case OP65_JPL: return OP65_JPL; case OP65_BVC: case OP65_JVC: return OP65_JVC; case OP65_BVS: case OP65_JVS: return OP65_JVS; case OP65_BRA: case OP65_JMP: return OP65_JMP; default: Internal ("MakeLongBranch: Invalid opcode: %d", OPC); return 0; } } bc_t GetBranchCond (opc_t OPC) /* Get the condition for the conditional branch in OPC */ { switch (OPC) { case OP65_BCC: return BC_CC; case OP65_BCS: return BC_CS; case OP65_BEQ: return BC_EQ; case OP65_BMI: return BC_MI; case OP65_BNE: return BC_NE; case OP65_BPL: return BC_PL; case OP65_BVC: return BC_VC; case OP65_BVS: return BC_VS; case OP65_JCC: return BC_CC; case OP65_JCS: return BC_CS; case OP65_JEQ: return BC_EQ; case OP65_JMI: return BC_MI; case OP65_JNE: return BC_NE; case OP65_JPL: return BC_PL; case OP65_JVC: return BC_VC; case OP65_JVS: return BC_VS; default: Internal ("GetBranchCond: Invalid opcode: %d", OPC); return 0; } } bc_t GetInverseCond (bc_t BC) /* Return the inverse condition of the given one */ { switch (BC) { case BC_CC: return BC_CS; case BC_CS: return BC_CC; case BC_EQ: return BC_NE; case BC_MI: return BC_PL; case BC_NE: return BC_EQ; case BC_PL: return BC_MI; case BC_VC: return BC_VS; case BC_VS: return BC_VC; default: Internal ("GetInverseCond: Invalid condition: %d", BC); return 0; } }