OXYGENHUB
# 2980b7b6d7cd enhanced-x16-emulator / src / insn_info.c
14.1 KiB · C · last changed chore(): initial commit 26 days ago source blame history raw
1
// Commander X16 Emulator
2
// Instruction reference data for the debugger. See insn_info.h.
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
// Keyed by mnemonic, covering the 65C02 set plus the 65C816 additions. Several
20
// mnemonics cover many opcodes/addressing modes; the description is the same for
21
// all of them, and the addressing mode is already visible in the operand text.
22
static const insn_info_t table[] = {
23
	// ---- loads / stores ----
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
	// ---- transfers ----
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
	// ---- arithmetic / logic ----
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
	// ---- compares ----
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
	// ---- increment / decrement ----
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
	// ---- shifts / rotates ----
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
	// ---- branches ----
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
	// ---- jumps / calls / returns ----
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
	// ---- stack ----
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
	// ---- flags ----
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
	// ---- misc ----
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
	// The bit-addressed 65C02 forms are emitted with the bit number appended
168
	// (rmb0..rmb7, smb0..smb7, bbr0..bbr7, bbs0..bbs7); match on the stem.
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
}