| 9120e874 Andy McCall |
1 |
|
| 2 |
|
| 3 |
|
| 4 |
#include "insn_info.h" |
| 5 |
|
| 6 |
#include <stdio.h> |
| 7 |
#include <string.h> |
| 8 |
|
| 9 |
#ifdef _MSC_VER |
| 10 |
#define strcasecmp _stricmp |
| 11 |
#else |
| 12 |
#include <strings.h> |
| 13 |
#endif |
| 14 |
|
| 15 |
#define NZ (INSN_FLAG_N | INSN_FLAG_Z) |
| 16 |
#define NZC (NZ | INSN_FLAG_C) |
| 17 |
#define NZCV (NZC | INSN_FLAG_V) |
| 18 |
|
| 19 |
|
| 20 |
|
| 21 |
|
| 22 |
static const insn_info_t table[] = { |
| 23 |
|
| 24 |
{ "lda", "Load Accumulator", "A = operand", INSN_KIND_LOAD, NZ, 0, false }, |
| 25 |
{ "ldx", "Load X", "X = operand", INSN_KIND_LOAD, NZ, 0, false }, |
| 26 |
{ "ldy", "Load Y", "Y = operand", INSN_KIND_LOAD, NZ, 0, false }, |
| 27 |
{ "sta", "Store Accumulator", "operand = A", INSN_KIND_STORE, 0, 0, false }, |
| 28 |
{ "stx", "Store X", "operand = X", INSN_KIND_STORE, 0, 0, false }, |
| 29 |
{ "sty", "Store Y", "operand = Y", INSN_KIND_STORE, 0, 0, false }, |
| 30 |
{ "stz", "Store Zero", "operand = 0", INSN_KIND_STORE, 0, 0, false }, |
| 31 |
|
| 32 |
|
| 33 |
{ "tax", "Transfer A to X", "X = A", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 34 |
{ "tay", "Transfer A to Y", "Y = A", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 35 |
{ "txa", "Transfer X to A", "A = X", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 36 |
{ "tya", "Transfer Y to A", "A = Y", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 37 |
{ "tsx", "Transfer SP to X", "X = SP", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 38 |
{ "txs", "Transfer X to SP", "SP = X", INSN_KIND_TRANSFER, 0, 0, false }, |
| 39 |
{ "tcd", "Transfer C to DP", "DP = C (65C816)", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 40 |
{ "tdc", "Transfer DP to C", "C = DP (65C816)", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 41 |
{ "tcs", "Transfer C to SP", "SP = C (65C816)", INSN_KIND_TRANSFER, 0, 0, false }, |
| 42 |
{ "tsc", "Transfer SP to C", "C = SP (65C816)", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 43 |
{ "txy", "Transfer X to Y", "Y = X (65C816)", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 44 |
{ "tyx", "Transfer Y to X", "X = Y (65C816)", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 45 |
{ "xba", "Exchange B and A", "swaps the two halves of C (65C816)", INSN_KIND_TRANSFER, NZ, 0, false }, |
| 46 |
{ "xce", "Exchange Carry and E", "swaps C with the emulation flag - the only " |
| 47 |
"way to switch CPU mode (65C816)", INSN_KIND_FLAG, INSN_FLAG_C, 0, false }, |
| 48 |
|
| 49 |
|
| 50 |
{ "adc", "Add with Carry", "A = A + operand + C", INSN_KIND_ALU, NZCV, 0, false }, |
| 51 |
{ "sbc", "Subtract with Carry", "A = A - operand - (1-C)", INSN_KIND_ALU, NZCV, 0, false }, |
| 52 |
{ "and", "Logical AND", "A = A & operand", INSN_KIND_ALU, NZ, 0, false }, |
| 53 |
{ "ora", "Logical OR", "A = A | operand", INSN_KIND_ALU, NZ, 0, false }, |
| 54 |
{ "eor", "Exclusive OR", "A = A ^ operand", INSN_KIND_ALU, NZ, 0, false }, |
| 55 |
|
| 56 |
|
| 57 |
{ "cmp", "Compare Accumulator", "sets flags from A - operand (A unchanged)", INSN_KIND_COMPARE, NZC, 0, false }, |
| 58 |
{ "cpx", "Compare X", "sets flags from X - operand (X unchanged)", INSN_KIND_COMPARE, NZC, 0, false }, |
| 59 |
{ "cpy", "Compare Y", "sets flags from Y - operand (Y unchanged)", INSN_KIND_COMPARE, NZC, 0, false }, |
| 60 |
{ "bit", "Bit Test", "Z from A & operand; N and V from operand bits 7 and 6", |
| 61 |
INSN_KIND_BIT, (NZ | INSN_FLAG_V), 0, false }, |
| 62 |
|
| 63 |
|
| 64 |
{ "inc", "Increment", "operand = operand + 1 (A when no operand)", INSN_KIND_INCDEC, NZ, 0, false }, |
| 65 |
{ "dec", "Decrement", "operand = operand - 1 (A when no operand)", INSN_KIND_INCDEC, NZ, 0, false }, |
| 66 |
{ "inx", "Increment X", "X = X + 1", INSN_KIND_INCDEC, NZ, 0, false }, |
| 67 |
{ "iny", "Increment Y", "Y = Y + 1", INSN_KIND_INCDEC, NZ, 0, false }, |
| 68 |
{ "dex", "Decrement X", "X = X - 1", INSN_KIND_INCDEC, NZ, 0, false }, |
| 69 |
{ "dey", "Decrement Y", "Y = Y - 1", INSN_KIND_INCDEC, NZ, 0, false }, |
| 70 |
|
| 71 |
|
| 72 |
{ "asl", "Arithmetic Shift Left", "operand <<= 1; C = old bit 7", INSN_KIND_SHIFT, NZC, 0, false }, |
| 73 |
{ "lsr", "Logical Shift Right", "operand >>= 1; C = old bit 0", INSN_KIND_SHIFT, NZC, 0, false }, |
| 74 |
{ "rol", "Rotate Left", "operand <<= 1 through C", INSN_KIND_SHIFT, NZC, 0, false }, |
| 75 |
{ "ror", "Rotate Right", "operand >>= 1 through C", INSN_KIND_SHIFT, NZC, 0, false }, |
| 76 |
{ "trb", "Test and Reset Bits", "Z from A & operand; clears the bits of A in operand", |
| 77 |
INSN_KIND_BIT, INSN_FLAG_Z, 0, false }, |
| 78 |
{ "tsb", "Test and Set Bits", "Z from A & operand; sets the bits of A in operand", |
| 79 |
INSN_KIND_BIT, INSN_FLAG_Z, 0, false }, |
| 80 |
{ "rmb", "Reset Memory Bit", "clears one bit of the operand", INSN_KIND_BIT, 0, 0, false }, |
| 81 |
{ "smb", "Set Memory Bit", "sets one bit of the operand", INSN_KIND_BIT, 0, 0, false }, |
| 82 |
|
| 83 |
|
| 84 |
{ "bcc", "Branch if Carry Clear", "taken when C = 0", INSN_KIND_BRANCH, 0, INSN_FLAG_C, false }, |
| 85 |
{ "bcs", "Branch if Carry Set", "taken when C = 1", INSN_KIND_BRANCH, 0, INSN_FLAG_C, true }, |
| 86 |
{ "beq", "Branch if Equal", "taken when Z = 1", INSN_KIND_BRANCH, 0, INSN_FLAG_Z, true }, |
| 87 |
{ "bne", "Branch if Not Equal", "taken when Z = 0", INSN_KIND_BRANCH, 0, INSN_FLAG_Z, false }, |
| 88 |
{ "bmi", "Branch if Minus", "taken when N = 1", INSN_KIND_BRANCH, 0, INSN_FLAG_N, true }, |
| 89 |
{ "bpl", "Branch if Plus", "taken when N = 0", INSN_KIND_BRANCH, 0, INSN_FLAG_N, false }, |
| 90 |
{ "bvc", "Branch if Overflow Clear","taken when V = 0", INSN_KIND_BRANCH, 0, INSN_FLAG_V, false }, |
| 91 |
{ "bvs", "Branch if Overflow Set", "taken when V = 1", INSN_KIND_BRANCH, 0, INSN_FLAG_V, true }, |
| 92 |
{ "bra", "Branch Always", "always taken", INSN_KIND_JUMP, 0, 0, false }, |
| 93 |
{ "brl", "Branch Long", "always taken, 16-bit displacement (65C816)", |
| 94 |
INSN_KIND_JUMP, 0, 0, false }, |
| 95 |
{ "bbr", "Branch on Bit Reset", "taken when the tested bit of the operand is 0", |
| 96 |
INSN_KIND_BRANCH, 0, 0, false }, |
| 97 |
{ "bbs", "Branch on Bit Set", "taken when the tested bit of the operand is 1", |
| 98 |
INSN_KIND_BRANCH, 0, 0, false }, |
| 99 |
|
| 100 |
|
| 101 |
{ "jmp", "Jump", "PC = operand", INSN_KIND_JUMP, 0, 0, false }, |
| 102 |
{ "jml", "Jump Long", "PC and program bank = operand (65C816)", INSN_KIND_JUMP, 0, 0, false }, |
| 103 |
{ "jsr", "Jump to Subroutine", "pushes the return address, then PC = operand", INSN_KIND_JUMP, 0, 0, false }, |
| 104 |
{ "jsl", "Jump to Subroutine Long", "pushes bank and return address (65C816)", INSN_KIND_JUMP, 0, 0, false }, |
| 105 |
{ "rts", "Return from Subroutine", "pulls the return address into PC", INSN_KIND_RETURN, 0, 0, false }, |
| 106 |
{ "rtl", "Return from Subroutine Long", "pulls address and bank (65C816)", INSN_KIND_RETURN, 0, 0, false }, |
| 107 |
{ "rti", "Return from Interrupt", "pulls the status register, then the return address", |
| 108 |
INSN_KIND_RETURN, 0xFF, 0, false }, |
| 109 |
|
| 110 |
|
| 111 |
{ "pha", "Push A", "pushes the accumulator", INSN_KIND_STACK, 0, 0, false }, |
| 112 |
{ "phx", "Push X", "pushes X", INSN_KIND_STACK, 0, 0, false }, |
| 113 |
{ "phy", "Push Y", "pushes Y", INSN_KIND_STACK, 0, 0, false }, |
| 114 |
{ "php", "Push Status", "pushes the status register", INSN_KIND_STACK, 0, 0, false }, |
| 115 |
{ "phb", "Push Data Bank", "pushes DB (65C816)", INSN_KIND_STACK, 0, 0, false }, |
| 116 |
{ "phd", "Push Direct Page", "pushes DP (65C816)", INSN_KIND_STACK, 0, 0, false }, |
| 117 |
{ "phk", "Push Program Bank", "pushes K (65C816)", INSN_KIND_STACK, 0, 0, false }, |
| 118 |
{ "pla", "Pull A", "pulls into the accumulator", INSN_KIND_STACK, NZ, 0, false }, |
| 119 |
{ "plx", "Pull X", "pulls into X", INSN_KIND_STACK, NZ, 0, false }, |
| 120 |
{ "ply", "Pull Y", "pulls into Y", INSN_KIND_STACK, NZ, 0, false }, |
| 121 |
{ "plp", "Pull Status", "pulls the status register", INSN_KIND_STACK, 0xFF, 0, false }, |
| 122 |
{ "plb", "Pull Data Bank", "pulls into DB (65C816)", INSN_KIND_STACK, NZ, 0, false }, |
| 123 |
{ "pld", "Pull Direct Page", "pulls into DP (65C816)", INSN_KIND_STACK, NZ, 0, false }, |
| 124 |
{ "pea", "Push Effective Address", "pushes the 16-bit operand (65C816)", INSN_KIND_STACK, 0, 0, false }, |
| 125 |
{ "pei", "Push Effective Indirect", "pushes the word at the direct-page operand (65C816)", |
| 126 |
INSN_KIND_STACK, 0, 0, false }, |
| 127 |
{ "per", "Push Effective Relative", "pushes a PC-relative address (65C816)", INSN_KIND_STACK, 0, 0, false }, |
| 128 |
|
| 129 |
|
| 130 |
{ "clc", "Clear Carry", "C = 0", INSN_KIND_FLAG, INSN_FLAG_C, 0, false }, |
| 131 |
{ "sec", "Set Carry", "C = 1", INSN_KIND_FLAG, INSN_FLAG_C, 0, false }, |
| 132 |
{ "cld", "Clear Decimal", "D = 0 (binary arithmetic)", INSN_KIND_FLAG, INSN_FLAG_D, 0, false }, |
| 133 |
{ "sed", "Set Decimal", "D = 1 (BCD arithmetic)", INSN_KIND_FLAG, INSN_FLAG_D, 0, false }, |
| 134 |
{ "cli", "Clear Interrupt Disable", "I = 0 (IRQs allowed)", INSN_KIND_FLAG, INSN_FLAG_I, 0, false }, |
| 135 |
{ "sei", "Set Interrupt Disable", "I = 1 (IRQs masked)", INSN_KIND_FLAG, INSN_FLAG_I, 0, false }, |
| 136 |
{ "clv", "Clear Overflow", "V = 0", INSN_KIND_FLAG, INSN_FLAG_V, 0, false }, |
| 137 |
{ "rep", "Reset Status Bits", "clears the status bits set in the operand (65C816)", |
| 138 |
INSN_KIND_FLAG, 0xFF, 0, false }, |
| 139 |
{ "sep", "Set Status Bits", "sets the status bits set in the operand (65C816)", |
| 140 |
INSN_KIND_FLAG, 0xFF, 0, false }, |
| 141 |
|
| 142 |
|
| 143 |
{ "brk", "Break", "software interrupt through the IRQ/BRK vector", INSN_KIND_INTERRUPT, INSN_FLAG_I | INSN_FLAG_D, 0, false }, |
| 144 |
{ "cop", "Coprocessor", "software interrupt through the COP vector (65C816)", |
| 145 |
INSN_KIND_INTERRUPT, INSN_FLAG_I | INSN_FLAG_D, 0, false }, |
| 146 |
{ "nop", "No Operation", "does nothing", INSN_KIND_NOP, 0, 0, false }, |
| 147 |
{ "wai", "Wait for Interrupt", "halts the CPU until an interrupt arrives", INSN_KIND_OTHER, 0, 0, false }, |
| 148 |
{ "stp", "Stop", "stops the CPU until reset", INSN_KIND_OTHER, 0, 0, false }, |
| 149 |
{ "wdm", "Reserved", "reserved 65C816 opcode; acts as a 2-byte NOP", INSN_KIND_NOP, 0, 0, false }, |
| 150 |
{ "mvn", "Block Move Next", "copies a block upward (65C816)", INSN_KIND_OTHER, 0, 0, false }, |
| 151 |
{ "mvp", "Block Move Previous", "copies a block downward (65C816)", INSN_KIND_OTHER, 0, 0, false }, |
| 152 |
}; |
| 153 |
|
| 154 |
#define TABLE_COUNT ((int)(sizeof(table) / sizeof(table[0]))) |
| 155 |
|
| 156 |
const insn_info_t * |
| 157 |
insn_info_lookup(const char *mnemonic) |
| 158 |
{ |
| 159 |
if (!mnemonic || !mnemonic[0]) { |
| 160 |
return NULL; |
| 161 |
} |
| 162 |
for (int i = 0; i < TABLE_COUNT; i++) { |
| 163 |
if (strcasecmp(table[i].mnemonic, mnemonic) == 0) { |
| 164 |
return &table[i]; |
| 165 |
} |
| 166 |
} |
| 167 |
|
| 168 |
|
| 169 |
size_t len = strlen(mnemonic); |
| 170 |
if (len == 4 && mnemonic[3] >= '0' && mnemonic[3] <= '7') { |
| 171 |
char stem[4] = { mnemonic[0], mnemonic[1], mnemonic[2], '\0' }; |
| 172 |
for (int i = 0; i < TABLE_COUNT; i++) { |
| 173 |
if (strcasecmp(table[i].mnemonic, stem) == 0) { |
| 174 |
return &table[i]; |
| 175 |
} |
| 176 |
} |
| 177 |
} |
| 178 |
return NULL; |
| 179 |
} |
| 180 |
|
| 181 |
void |
| 182 |
insn_info_flag_text(uint8_t flags_set, char *out, unsigned int max) |
| 183 |
{ |
| 184 |
static const char *names[8] = { "N", "V", "M", "X", "D", "I", "Z", "C" }; |
| 185 |
unsigned int n = 0; |
| 186 |
|
| 187 |
if (!out || max == 0) { |
| 188 |
return; |
| 189 |
} |
| 190 |
out[0] = '\0'; |
| 191 |
for (int bit = 7; bit >= 0; bit--) { |
| 192 |
if (!(flags_set & (1u << bit))) { |
| 193 |
continue; |
| 194 |
} |
| 195 |
const char *nm = names[7 - bit]; |
| 196 |
unsigned int need = (unsigned int)strlen(nm) + (n ? 1u : 0u); |
| 197 |
if (n + need + 1 >= max) { |
| 198 |
break; |
| 199 |
} |
| 200 |
if (n) { |
| 201 |
out[n++] = ' '; |
| 202 |
} |
| 203 |
strcpy(out + n, nm); |
| 204 |
n += (unsigned int)strlen(nm); |
| 205 |
} |
| 206 |
out[n] = '\0'; |
| 207 |
} |