OXYGENHUB
◆ main x16-emulator / src / disasm.c
10.8 KiB · C · last changed chore(): initial commit 15 days ago source blame history raw
1
// Commander X16 Emulator
2
// Copyright (c) 2019 Michael Steil
3
// All rights reserved. License: 2-clause BSD
4
5
#include <stdlib.h>
6
#include <stdio.h>
7
#include <inttypes.h>
8
#include <string.h>
9
#include "memory.h"
10
#include "glue.h"
11
12
#include "cpu/mnemonics.h"				// Automatically generated mnemonic table.
13
14
// *******************************************************************************************
15
//
16
//		Disassemble a single 65C02/65C816 instruction into buffer. Returns the length of the
17
//		instruction in total in bytes.
18
//
19
// *******************************************************************************************
20
21
int disasm(uint16_t pc, uint8_t bank, uint8_t *RAM, char *line, unsigned int max_line, int16_t x16Bank, uint8_t implied_status, int32_t *eff_addr) {
22
	uint8_t opcode = debug_read6502(pc, bank, x16Bank);
23
	const char *mnemonic = regs.is65c816 ? mnemonics_c816[opcode] : mnemonics_c02[opcode];
24
25
	*eff_addr = -1;
26
27
	int isZprel = 0;
28
	int isRel16 = 0;
29
	int isYrel = 0;
30
	int isXrel = 0;
31
	int isIndirect = 0;
32
	int isBlockMove = 0;
33
	int isImmediate = 0;
34
	int isStackRel = 0;
35
36
	if (regs.is65c816) {
37
		// Immediate opcodes (65C816)
38
		switch (opcode) {
39
			case 0xC2: // rep #imm8
40
			case 0xE2: // sep #imm8
41
				isImmediate = 1;
42
				;;
43
			default:
44
				;;
45
		}
46
47
		// X relative opcodes (65C816)
48
		switch (opcode) {
49
			case 0x1F: // ora long,x
50
			case 0x3F: // and long,x
51
			case 0x5F: // eor long,x
52
			case 0x7F: // adc long,x
53
			case 0x9F: // sta long,x
54
			case 0xBF: // lda long,x
55
			case 0xDF: // cmp long,x
56
			case 0xFC: // jsr (abs,x)
57
			case 0xFF: // sbc long,x
58
				isXrel = 1;
59
				;;
60
			default:
61
				;;
62
		}
63
64
		// PER and BRL
65
		switch (opcode) {
66
			case 0x62: // per rel16
67
			case 0x82: // brl rel16
68
				isRel16 = 1;
69
			default:
70
				;;
71
		}
72
73
		// Y relative opcodes (65C816)
74
		switch (opcode) {
75
			case 0x13: // ora (zp,S),Y
76
			case 0x17: // ora [zp],y
77
			case 0x33: // and (zp,S),Y
78
			case 0x37: // and [zp],y
79
			case 0x53: // eor (zp,S),Y
80
			case 0x57: // eor [zp],y
81
			case 0x73: // adc (zp,S),Y
82
			case 0x77: // adc [zp],y
83
			case 0x93: // sta (zp,S),Y
84
			case 0x97: // sta [zp],y
85
			case 0xB3: // lda (zp,S),Y
86
			case 0xB7: // lda [zp],y
87
			case 0xD3: // cmp (zp,S),Y
88
			case 0xD7: // cmp [zp],y
89
			case 0xF3: // sbc (zp,S),Y
90
			case 0xF7: // sbc [zp],y
91
				isYrel = 1;
92
				;;
93
			default:
94
				;;
95
		}
96
97
		// indirect opcodes (65C816)
98
		switch (opcode) {
99
			case 0x07: // ora [zp]
100
			case 0x13: // ora (zp,S),Y
101
			case 0x17: // ora [zp],y
102
			case 0x27: // and [zp]
103
			case 0x33: // and (zp,S),Y
104
			case 0x37: // and [zp],y
105
			case 0x47: // eor [zp]
106
			case 0x53: // eor (zp,S),Y
107
			case 0x57: // eor [zp],y
108
			case 0x67: // adc [zp]
109
			case 0x73: // adc (zp,S),Y
110
			case 0x77: // adc [zp],y
111
			case 0x87: // sta [zp]
112
			case 0x93: // sta (zp,S),Y
113
			case 0x97: // sta [zp],y
114
			case 0xA7: // lda [zp]
115
			case 0xB3: // lda (zp,S),Y
116
			case 0xB7: // lda [zp],y
117
			case 0xC7: // cmp [zp]
118
			case 0xD3: // cmp (zp,S),Y
119
			case 0xD4: // pei (zp)
120
			case 0xD7: // cmp [zp],y
121
			case 0xDC: // jmp [abs]
122
			case 0xE7: // sbc [zp]
123
			case 0xF3: // sbc (zp,S),Y
124
			case 0xF7: // sbc [zp],y
125
			case 0xFC: // jsr (abs,x)
126
				isIndirect = 1;
127
				;;
128
			default:
129
				;;
130
		}
131
132
		// stack-relative opcodes (65C816)
133
		isStackRel = (opcode & 0x0F) == 0x03;
134
135
		// block move (MVN and MVP)
136
		isBlockMove = opcode == 0x44 || opcode == 0x54;
137
138
	} else {
139
		// BBRx and BBSx
140
		isZprel = (opcode & 0x0F) == 0x0F;
141
142
	}
143
144
	//
145
	//		The following cases are common to both 65C816 and 65C02
146
	//
147
148
	//
149
	//      X relative opcodes (including indirect/indexed)
150
	//
151
	switch (opcode) {
152
		case 0x01: // ora (zp,x)
153
		case 0x15: // ora zp,x
154
		case 0x16: // asl zp,x
155
		case 0x1D: // ora abs,x
156
		case 0x1E: // asl abs,x
157
		case 0x21: // and (zp,x)
158
		case 0x34: // bit zp,x
159
		case 0x35: // and zp,x
160
		case 0x36: // rol zp,x
161
		case 0x3C: // bit abs,x
162
		case 0x3D: // rol abs,x
163
		case 0x41: // eor (zp,x)
164
		case 0x55: // eor zp,x
165
		case 0x56: // lsr zp,x
166
		case 0x5d: // eor abs,x
167
		case 0x5e: // lsr abs,x
168
		case 0x61: // adc (zp,x)
169
		case 0x74: // stz zp,x
170
		case 0x75: // adc zp,x
171
		case 0x76: // ror zp,x
172
		case 0x7C: // jmp (abs,x)
173
		case 0x7D: // adc abs,x
174
		case 0x7e: // ror abs,x
175
		case 0x81: // sta (zp,x)
176
		case 0x94: // sty zp,x
177
		case 0x95: // sta zp,x
178
		case 0x9D: // sta abs,x
179
		case 0x9E: // stz abs,x
180
		case 0xA1: // lda (zp,x)
181
		case 0xB4: // ldy zp,x
182
		case 0xB5: // lda zp,x
183
		case 0xBC: // ldy abs,x
184
		case 0xBD: // lda abs,x
185
		case 0xC1: // cmp (zp,x)
186
		case 0xD5: // cmp zp,x
187
		case 0xD6: // dec zp,x
188
		case 0xDD: // cmp abs,x
189
		case 0xDE: // dec abs,x
190
		case 0xD1: // sbc (zp,x)
191
		case 0xF5: // sbc zp,x
192
		case 0xF6: // inc zp,x
193
		case 0xFC: // jsr (abs,x)
194
		case 0xFD: // sbc abs,x
195
		case 0xFE: // inc abs,x
196
			isXrel = 1;
197
			;;
198
		default:
199
			;;
200
	}
201
202
	//
203
	//		Test for branches, relative address. These are BRA ($80) and
204
	//		$10,$30,$50,$70,$90,$B0,$D0,$F0.
205
	//
206
	int isRel8 = (opcode == 0x80) || ((opcode & 0x1F) == 0x10);
207
208
	// is opcode immediate?
209
	switch (opcode) {
210
		case 0x09: // ora #imm
211
		case 0x29: // and #imm
212
		case 0x49: // eor #imm
213
		case 0x69: // adc #imm
214
		case 0x89: // bit #imm
215
		case 0xA0: // ldy #imm
216
		case 0xA2: // ldx #imm
217
		case 0xA9: // lda #imm
218
		case 0xC0: // cpy #imm
219
		case 0xC9: // cmp #imm
220
		case 0xE0: // cpx #imm
221
		case 0xE9: // sbc #imm
222
			isImmediate = 1;
223
			;;
224
		default:
225
			;;
226
	}
227
228
	//      Y relative opcodes (including indirect/indexed)
229
	switch (opcode) {
230
		case 0x11: // ora (zp),y
231
		case 0x19: // ora abs,y
232
		case 0x31: // and (zp),y
233
		case 0x39: // and abs,y
234
		case 0x51: // eor (zp),y
235
		case 0x59: // eor abs,y
236
		case 0x71: // adc (zp),y
237
		case 0x79: // adc abs,y
238
		case 0x91: // sta (zp),y
239
		case 0x96: // stx zp,y
240
		case 0x99: // sta abs,y
241
		case 0xB1: // lda (zp),y
242
		case 0xB6: // ldx zp,y
243
		case 0xB9: // lda abs,y
244
		case 0xBE: // ldx abs,y
245
		case 0xD1: // cmp (zp),y
246
		case 0xD9: // cmp abs,y
247
		case 0xF1: // sbc (zp),y
248
		case 0xF9: // sbc abs,y
249
			isYrel = 1;
250
			;;
251
		default:
252
			;;
253
	}
254
255
	//	All indirect opcodes
256
	
257
	switch (opcode) {
258
		case 0x01: // ora (zp,x)
259
		case 0x11: // ora (zp),y
260
		case 0x12: // ora (zp)
261
		case 0x21: // and (zp,x)
262
		case 0x31: // and (zp),y
263
		case 0x32: // and (zp)
264
		case 0x41: // eor (zp,x)
265
		case 0x51: // eor (zp),y
266
		case 0x52: // eor (zp)
267
		case 0x61: // adc (zp,x)
268
		case 0x6C: // jmp (abs)
269
		case 0x71: // adc (zp),y
270
		case 0x72: // adc (zp)
271
		case 0x7C: // jmp (abs,x)
272
		case 0x81: // sta (zp,x)
273
		case 0x91: // sta (zp),y
274
		case 0x92: // sta (zp)
275
		case 0xA1: // lda (zp,x)
276
		case 0xB1: // lda (zp),y
277
		case 0xB2: // lda (zp)
278
		case 0xC1: // cmp (zp,x)
279
		case 0xD1: // cmp (zp),y
280
		case 0xD2: // cmp (zp)
281
		case 0xE1: // sbc (zp,x)
282
		case 0xF1: // sbc (zp),y
283
		case 0xF2: // sbc (zp)
284
			isIndirect = 1;
285
			;;
286
		default:
287
			;;
288
	}
289
290
	// BRK and COP (works with 65C02 as well since it's a nop #imm8)
291
	int isBrkOrCop = opcode == 0x00 || opcode == 0x02;
292
293
	int length = 1;
294
295
	char *where = strstr(mnemonic, "%%0%hhux");
296
	if (where) {
297
		int isImmediateIndex = (opcode == 0xa0) || (opcode == 0xa2) || (opcode == 0xc0) || (opcode == 0xe0);
298
		int len = snprintf(line, max_line, mnemonic, (implied_status & (isImmediateIndex ? FLAG_INDEX_WIDTH : FLAG_MEMORY_WIDTH)) ? 2 : 4);
299
		if (len == -1) {
300
			return 0;
301
		}
302
303
		mnemonic = malloc(len + 1);
304
		memcpy((char *) mnemonic, line, len + 1);
305
	}
306
	else {
307
		strncpy(line,mnemonic,max_line);
308
	}
309
310
	if (isBlockMove) {
311
		snprintf(line, max_line, mnemonic, debug_read6502(pc + 2, bank, x16Bank), debug_read6502(pc + 1, bank, x16Bank));
312
		length = 3;
313
		if (regs.c != 0xFFFF) *eff_addr = regs.y; // We can have only one effective address, so we're choosing the destination
314
	}
315
	else if (isZprel) {
316
		snprintf(line, max_line, mnemonic, debug_read6502(pc + 1, bank, x16Bank), pc + 3 + (int8_t)debug_read6502(pc + 2, bank, x16Bank));
317
		length = 3;
318
	}
319
	else if (isRel16) {
320
		snprintf(line, max_line, mnemonic, (pc + 3 + (int16_t)(debug_read6502(pc + 1, bank, x16Bank) | (debug_read6502(pc + 2, bank, x16Bank) << 8))) & 0xffff);
321
		length = 3;
322
	}
323
	else if (isBrkOrCop) {
324
		snprintf(line, max_line, mnemonic, debug_read6502(pc + 1, bank, x16Bank));
325
		length = 2;
326
	} else {
327
		if (strstr(line, "%02x")) {
328
			length = 2;
329
			if (isRel8) {
330
				snprintf(line, max_line, mnemonic, pc + 2 + (int8_t)debug_read6502(pc + 1, bank, x16Bank));
331
			} else {
332
				snprintf(line, max_line, mnemonic, debug_read6502(pc + 1, bank, x16Bank));
333
				if (isStackRel) {
334
					uint16_t ptr = regs.sp + debug_read6502(pc + 1, bank, x16Bank);
335
					uint8_t ind_bank = ptr < 0xc000 ? memory_get_ram_bank() : memory_get_rom_bank();
336
					if (isIndirect && isYrel) {
337
						*eff_addr = (debug_read6502(ptr, bank, ind_bank) | (debug_read6502(ptr + 1, bank, ind_bank) << 8)) + regs.y;
338
					} else {
339
						*eff_addr = ptr;
340
					}
341
				} else if (isIndirect) {
342
					uint16_t ptr = debug_read6502(pc + 1, bank, x16Bank);
343
					if (isXrel)
344
						ptr += regs.x;
345
					*eff_addr = debug_read6502(ptr, bank, x16Bank) | (debug_read6502(ptr + 1, bank, x16Bank) << 8);
346
					if (isYrel)
347
						*eff_addr += regs.y;
348
				} else if (!isImmediate) {
349
					*eff_addr = debug_read6502(pc + 1, bank, x16Bank);
350
					if (isXrel)
351
						*eff_addr += regs.x;
352
					if (isYrel)
353
						*eff_addr += regs.y;
354
				}
355
			}
356
		}
357
		if (strstr(line, "%04x")) {
358
			length = 3;
359
			snprintf(line, max_line, mnemonic, debug_read6502(pc + 1, bank, x16Bank) | debug_read6502(pc + 2, bank, x16Bank) << 8);
360
			if (isIndirect) {
361
				uint16_t ptr = debug_read6502(pc + 1, bank, x16Bank) | (debug_read6502(pc + 2, bank, x16Bank) << 8);
362
				if (isXrel)
363
					ptr += regs.x;
364
				uint8_t ind_bank = ptr < 0xc000 ? memory_get_ram_bank() : memory_get_rom_bank();
365
				*eff_addr = debug_read6502(ptr, bank, ind_bank) | (debug_read6502(ptr + 1, bank, ind_bank) << 8);
366
				if (isYrel)
367
					*eff_addr += regs.y;
368
			} else if (!isImmediate) {
369
				*eff_addr = debug_read6502(pc + 1, bank, x16Bank) | (debug_read6502(pc + 2, bank, x16Bank) << 8);
370
				if (isXrel)
371
					*eff_addr += regs.x;
372
				if (isYrel)
373
					*eff_addr += regs.y;
374
			}
375
		}
376
		if (strstr(line, "%06x")) {
377
			length = 4;
378
			snprintf(line, max_line, mnemonic, debug_read6502(pc + 1, bank, x16Bank) | debug_read6502(pc + 2, bank, x16Bank) << 8 | debug_read6502(pc + 3, bank, x16Bank) << 16);
379
			if (isIndirect) {
380
				uint16_t ptr = debug_read6502(pc + 1, bank, x16Bank) | (debug_read6502(pc + 2, bank, x16Bank) << 8);
381
				if (isXrel)
382
					ptr += regs.x;
383
				uint8_t ind_bank = ptr < 0xc000 ? memory_get_ram_bank() : memory_get_rom_bank();
384
				*eff_addr = debug_read6502(ptr, bank, ind_bank) | (debug_read6502(ptr + 1, bank, ind_bank) << 8);
385
				if (isYrel)
386
					*eff_addr += regs.y;
387
			} else {
388
				*eff_addr = debug_read6502(pc + 1, bank, x16Bank) | (debug_read6502(pc + 2, bank, x16Bank) << 8);
389
				if (isXrel)
390
					*eff_addr += regs.x;
391
				if (isYrel)
392
					*eff_addr += regs.y;
393
			}
394
		}
395
		if (opcode == 0x00 || opcode == 0x02) {
396
			// BRK and COP instructions are 2 bytes long according to WDC datasheet.
397
			// CPU skips the second byte when it executes a BRK / COP.
398
			length = 2;
399
		}
400
	}
401
402
	if (where) {
403
		free((char *) mnemonic);
404
	}
405
406
	if (*eff_addr >= 0x10000) {
407
		*eff_addr &= 0xffff;
408
	}
409
410
	return length;
411
}