zymosis: renamed `len` to `inslen`, because it is "instruction length"
[zymosis.git] / src / libzymosis / zymosis_utils_disasm.c
blob6d9ee6623f3209c45f7aa5a3cada122c5320467c
1 /*
2 * Z80 CPU emulation engine v0.1.1
3 * coded by Ketmar // Invisible Vector (psyc://ketmar.no-ip.org/~ketmar)
4 * Understanding is not required. Only obedience.
6 * This program is free software. It comes without any warranty, to
7 * the extent permitted by applicable law. You can redistribute it
8 * and/or modify it under the terms of the Do What The Fuck You Want
9 * To Public License, Version 2, as published by Sam Hocevar. See
10 * http://www.wtfpl.net/txt/copying/ for more details.
12 #include <stdlib.h>
13 #include <string.h>
15 #include "zymosis_utils.h"
17 #if defined(__GNUC__)
18 # ifndef ZYMDIS_INLINE
19 /*# define ZYMDIS_INLINE __attribute__((always_inline)) inline*/
20 # define ZYMDIS_INLINE
21 # endif
22 # ifndef ZYMDIS_PURE
23 # define ZYMDIS_PURE __attribute__((pure))
24 # endif
25 # ifndef ZYMDIS_CONST
26 # define ZYMDIS_CONST __attribute__((const))
27 # endif
28 # ifndef ZYMDIS_FUNC
29 # define ZYMDIS_FUNC __attribute__((warn_unused_result))
30 # endif
31 #endif
33 #ifndef ZYMDIS_INLINE
34 # define ZYMDIS_INLINE
35 #endif
36 #ifndef ZYMDIS_PURE
37 # define ZYMDIS_PURE
38 #endif
39 #ifndef ZYMDIS_CONST
40 # define ZYMDIS_CONST
41 #endif
42 #ifndef ZYMDIS_FUNC
43 # define ZYMDIS_FUNC
44 #endif
47 zym_disasm_getlabel_fn zym_disasm_getlabel = NULL;
49 const char *zym_disasm_mnemo_end = NULL;
50 const char *zym_disasm_num_start = NULL;
51 const char *zym_disasm_num_end = NULL;
54 /******************************************************************************/
55 /* very simple disassembler */
56 /******************************************************************************/
57 typedef struct {
58 zym_cpu_t *z80;
59 const uint8_t *mem;
60 zym_disop_t *nfo;
61 uint16_t stpc;
62 uint16_t pc;
63 unsigned mempos;
64 uint32_t dbpos;
65 uint32_t dbstpos;
66 uint8_t curarg;
67 int argstarted;
68 zym_bool gotDD;
69 const char *DD;
70 int disp;
71 } ZymDisWorkArea;
74 static uint8_t zym_dis_read_byte (ZymDisWorkArea *wkp) {
75 uint8_t res;
76 if (wkp->z80) {
77 res = wkp->z80->mem_read(wkp->z80, wkp->pc, ZYM_MEMIO_OTHER);
78 } else {
79 res = wkp->mem[wkp->mempos++];
81 wkp->pc = (wkp->pc+1)&0xffff;
82 return res;
86 static ZYMDIS_INLINE uint16_t zym_dis_read_word (ZymDisWorkArea *wkp) {
87 uint16_t tmptmpb0 = zym_dis_read_byte(wkp);
88 uint16_t tmptmpb1 = zym_dis_read_byte(wkp);
89 return tmptmpb0|(tmptmpb1<<8);
93 static void zym_dis_put_str (ZymDisWorkArea *wkp, const char *s) {
94 if (!s) return;
95 for (; s[0]; ++s) {
96 char ch = s[0];
97 if (wkp->nfo->locase) {
98 if (ch >= 'A' && ch <= 'Z') ch += 32;
99 } else {
100 if (ch >= 'a' && ch <= 'z') ch -= 32;
102 if (wkp->dbpos+2 < sizeof(wkp->nfo->disbuf)) wkp->nfo->disbuf[wkp->dbpos++] = ch;
107 static void zym_dis_put_str_as_is (ZymDisWorkArea *wkp, const char *s) {
108 if (!s) return;
109 for (; s[0]; ++s) {
110 if (wkp->dbpos+2 < sizeof(wkp->nfo->disbuf)) wkp->nfo->disbuf[wkp->dbpos++] = s[0];
115 static ZYMDIS_INLINE void zym_dis_end_mnemo (ZymDisWorkArea *wkp) {
116 /*if (nfo->mnemo.length) assert(0, "internal error");*/
117 if (zym_disasm_mnemo_end) zym_dis_put_str_as_is(wkp, zym_disasm_mnemo_end); else zym_dis_put_str_as_is(wkp, "\t");
118 wkp->nfo->mnemo = wkp->nfo->disbuf;
119 wkp->nfo->mnemolen = wkp->dbpos;
120 wkp->dbstpos = wkp->dbpos;
124 static ZYMDIS_INLINE void zym_dis_end_arg (ZymDisWorkArea *wkp) {
125 /*if (curarg >= nfo->args.length) assert(0, "internal error");*/
126 wkp->nfo->args[wkp->curarg] = wkp->nfo->disbuf+wkp->dbstpos;
127 wkp->nfo->argslen[wkp->curarg] = (int)(wkp->dbpos-wkp->dbstpos);
128 ++wkp->curarg;
129 wkp->dbstpos = wkp->dbpos;
130 wkp->argstarted = 0;
134 static ZYMDIS_INLINE void zym_dis_start_arg (ZymDisWorkArea *wkp) {
135 if (!wkp->argstarted && wkp->curarg) zym_dis_put_str_as_is(wkp, ",");
136 wkp->argstarted = 1;
140 static ZYMDIS_INLINE void zym_dis_put_mnemo (ZymDisWorkArea *wkp, const char *s) {
141 zym_dis_put_str(wkp, s);
142 zym_dis_end_mnemo(wkp);
146 static ZYMDIS_INLINE void zym_dis_put_arg_part (ZymDisWorkArea *wkp, const char *s) {
147 if (s && s[0]) {
148 if (!wkp->argstarted && wkp->curarg) zym_dis_put_str_as_is(wkp, ",");
149 wkp->argstarted = 1;
150 zym_dis_put_str(wkp, s);
155 static ZYMDIS_INLINE void zym_dis_put_arg (ZymDisWorkArea *wkp, const char *s) {
156 zym_dis_put_arg_part(wkp, s);
157 zym_dis_end_arg(wkp);
161 static void zym_dis_put_uint_dec (ZymDisWorkArea *wkp, int n) {
162 uint32_t ntmp = (uint32_t)(n&0xffffU);
163 char buf[16];
164 unsigned bpos = (unsigned)sizeof(buf);
165 buf[--bpos] = 0;
166 do { buf[--bpos] = (char)('0'+ntmp%10); } while ((ntmp /= 10) != 0);
167 if (zym_disasm_num_start) zym_dis_put_str_as_is(wkp, zym_disasm_num_start);
168 zym_dis_put_str(wkp, buf+bpos);
169 if (zym_disasm_num_end) zym_dis_put_str_as_is(wkp, zym_disasm_num_end);
173 static void zym_dis_put_uint_hex_len (ZymDisWorkArea *wkp, int n, int len) {
174 uint32_t ntmp = (uint32_t)(n&0xffffU);
175 int lentmp = len;
176 char buf[17];
177 unsigned bpos = (unsigned)sizeof(buf);
178 if (lentmp > 12) lentmp = 12;
179 buf[--bpos] = 0;
180 do { buf[--bpos] = (char)('0'+ntmp%16+(ntmp%16 > 9 ? 7 : 0)); } while ((ntmp /= 16) != 0);
181 while (sizeof(buf)-bpos < lentmp+1) buf[--bpos] = '0';
182 buf[--bpos] = '#';
183 if (zym_disasm_num_start) zym_dis_put_str_as_is(wkp, zym_disasm_num_start);
184 zym_dis_put_str(wkp, buf+bpos);
185 if (zym_disasm_num_end) zym_dis_put_str_as_is(wkp, zym_disasm_num_end);
189 static ZYMDIS_INLINE void zym_dis_put_uint_hexlen (ZymDisWorkArea *wkp, int n, int len) {
190 if (wkp->nfo->decimal) zym_dis_put_uint_dec(wkp, n); else zym_dis_put_uint_hex_len(wkp, n, len);
194 static ZYMDIS_INLINE void zym_dis_put_uint_addr_pc (ZymDisWorkArea *wkp, int n, int len) {
195 const char *lbl = (zym_disasm_getlabel ? zym_disasm_getlabel(n&0xffff, ZYM_DIS_ATYPE_PC_ADDR) : NULL);
196 if (lbl) zym_dis_put_str_as_is(wkp, lbl);
197 else if (wkp->nfo->decimal) zym_dis_put_uint_dec(wkp, n); else zym_dis_put_uint_hex_len(wkp, n, len);
201 static ZYMDIS_INLINE void zym_dis_put_uint_addr_data (ZymDisWorkArea *wkp, int n, int len) {
202 const char *lbl = (zym_disasm_getlabel ? zym_disasm_getlabel(n&0xffff, ZYM_DIS_ATYPE_DATA_ADDR) : NULL);
203 if (lbl) zym_dis_put_str_as_is(wkp, lbl);
204 else if (wkp->nfo->decimal) zym_dis_put_uint_dec(wkp, n); else zym_dis_put_uint_hex_len(wkp, n, len);
208 static ZYMDIS_INLINE void zym_dis_put_uint_data_byte (ZymDisWorkArea *wkp, int n, int len) {
209 const char *lbl = (zym_disasm_getlabel ? zym_disasm_getlabel(n&0xff, ZYM_DIS_ATYPE_BYTE) : NULL);
210 if (lbl) zym_dis_put_str_as_is(wkp, lbl);
211 else if (wkp->nfo->decimal) zym_dis_put_uint_dec(wkp, n); else zym_dis_put_uint_hex_len(wkp, n, len);
215 static ZYMDIS_INLINE void zym_dis_put_uint_data_word (ZymDisWorkArea *wkp, int n, int len) {
216 const char *lbl = (zym_disasm_getlabel ? zym_disasm_getlabel(n&0xffff, ZYM_DIS_ATYPE_WORD) : NULL);
217 if (lbl) zym_dis_put_str_as_is(wkp, lbl);
218 else if (wkp->nfo->decimal) zym_dis_put_uint_dec(wkp, n); else zym_dis_put_uint_hex_len(wkp, n, len);
222 static ZYMDIS_INLINE void zym_dis_put_uint_port_byte (ZymDisWorkArea *wkp, int n, int len) {
223 const char *lbl = (zym_disasm_getlabel ? zym_disasm_getlabel(n&0xff, ZYM_DIS_ATYPE_PORT8) : NULL);
224 if (lbl) zym_dis_put_str_as_is(wkp, lbl);
225 else if (wkp->nfo->decimal) zym_dis_put_uint_dec(wkp, n); else zym_dis_put_uint_hex_len(wkp, n, len);
229 static ZYMDIS_INLINE void zym_dis_put_disp (ZymDisWorkArea *wkp, int n) {
230 const int nnn = n;
231 if (nnn < 0) {
232 if (zym_disasm_num_start) zym_dis_put_str_as_is(wkp, zym_disasm_num_start);
233 zym_dis_put_str_as_is(wkp, "-");
234 if (zym_disasm_num_end) zym_dis_put_str_as_is(wkp, zym_disasm_num_end);
235 zym_dis_put_uint_dec(wkp, -nnn);
236 } else if (nnn > 0) {
237 /*if (zym_disasm_num_start) zym_dis_put_str_as_is(wkp, zym_disasm_num_start);*/
238 zym_dis_put_str_as_is(wkp, "+");
239 /*if (zym_disasm_num_end) zym_dis_put_str_as_is(wkp, zym_disasm_num_end);*/
240 zym_dis_put_uint_dec(wkp, nnn);
245 static ZYMDIS_INLINE void zym_dis_put_cc (ZymDisWorkArea *wkp, unsigned n) {
246 const char *conds[8] = {"NZ", "Z", "NC", "C", "PO", "PE", "P", "M"};
247 zym_dis_put_arg(wkp, conds[n&0x07]);
251 static ZYMDIS_INLINE void zym_dis_put_r8 (ZymDisWorkArea *wkp, const char *hlspec, unsigned n) {
252 switch (n) {
253 case 0: zym_dis_put_arg(wkp, "B"); break;
254 case 1: zym_dis_put_arg(wkp, "C"); break;
255 case 2: zym_dis_put_arg(wkp, "D"); break;
256 case 3: zym_dis_put_arg(wkp, "E"); break;
257 case 4: zym_dis_put_arg(wkp, "H"); break;
258 case 5: zym_dis_put_arg(wkp, "L"); break;
259 case 6:
260 if (hlspec && hlspec[0]) {
261 zym_dis_put_arg(wkp, hlspec);
262 } else {
263 zym_dis_put_arg_part(wkp, "(");
264 zym_dis_put_arg_part(wkp, wkp->DD);
265 if (wkp->gotDD) zym_dis_put_disp(wkp, wkp->disp);
266 zym_dis_put_arg(wkp, ")");
268 break;
269 case 7: zym_dis_put_arg(wkp, "A"); break;
274 static ZYMDIS_INLINE void zym_dis_put_r16sp (ZymDisWorkArea *wkp, const char *hlspec, unsigned n) {
275 switch (n) {
276 case 0: zym_dis_put_arg(wkp, "BC"); break;
277 case 1: zym_dis_put_arg(wkp, "DE"); break;
278 case 2: if (hlspec && hlspec[0]) zym_dis_put_arg(wkp, hlspec); else zym_dis_put_arg(wkp, wkp->DD); break;
279 case 3: zym_dis_put_arg(wkp, "SP"); break;
284 static ZYMDIS_INLINE void zym_dis_put_r16af (ZymDisWorkArea *wkp, unsigned n) {
285 switch (n) {
286 case 0: zym_dis_put_arg(wkp, "BC"); break;
287 case 1: zym_dis_put_arg(wkp, "DE"); break;
288 case 2: zym_dis_put_arg(wkp, wkp->DD); break;
289 case 3: zym_dis_put_arg(wkp, "AF"); break;
294 static ZYMDIS_INLINE void zym_finish (ZymDisWorkArea *wkp) {
295 wkp->nfo->inslen = (wkp->pc < wkp->stpc ? wkp->pc+0x10000-wkp->stpc : wkp->pc-wkp->stpc);
296 /* remove trailing blanks if we have no operands */
297 if (wkp->dbstpos > 0 && (unsigned)(wkp->nfo->disbuf[wkp->dbstpos-1]&0xffU) <= 32) --wkp->dbstpos;
298 wkp->nfo->disbuf[wkp->dbstpos] = 0;
302 // ////////////////////////////////////////////////////////////////////////// //
303 // main code
305 void zym_disasm_init (zym_disop_t *nfo) {
306 memset(nfo, 0, sizeof(*nfo));
307 nfo->mnemo = NULL;
308 nfo->args[0] = nfo->args[1] = nfo->args[2] = NULL;
312 static void zym_disasm_one_internal (zym_cpu_t *z80, zym_disop_t *nfo, uint16_t origpc, const uint8_t mem[8]) {
313 uint8_t tmpB;
314 uint16_t tmpW;
316 ZymDisWorkArea wk;
318 wk.z80 = z80;
319 wk.mem = mem;
320 wk.nfo = nfo;
321 wk.pc = origpc;
322 wk.stpc = origpc;
323 wk.mempos = 0;
324 wk.dbpos = 0;
325 wk.dbstpos = 0;
326 wk.curarg = 0;
327 wk.argstarted = 0;
329 nfo->inslen = 0;
330 nfo->mnemo = NULL;
331 nfo->mnemolen = 0;
332 nfo->args[0] = nfo->args[1] = nfo->args[2] = NULL;
333 nfo->argslen[0] = nfo->argslen[1] = nfo->argslen[2] = 0;
335 uint8_t opcode = zym_dis_read_byte(&wk);
336 wk.disp = 0;
337 wk.gotDD = 0/*false*/;
338 wk.DD = "HL";
340 // check for I[XY] prefix
341 if (opcode == 0xdd || opcode == 0xfd) {
342 const uint32_t withIndexBmp[8] = {0x00,0x700000,0x40404040,0x40bf4040,0x40404040,0x40404040,0x0800,0x00};
343 // IX/IY prefix
344 wk.DD = (opcode == 0xdd ? "IX" : "IY");
345 // read opcode -- OCR(4)
346 opcode = zym_dis_read_byte(&wk);
347 // test if this instruction have (HL)
348 if (withIndexBmp[opcode>>5]&(1<<(opcode&0x1f))) {
349 // 3rd byte is always DISP here
350 wk.disp = zym_dis_read_byte(&wk);
351 if (wk.disp > 127) wk.disp -= 256;
352 } else if (opcode == 0xdd && opcode == 0xfd) {
353 // double prefix; put special NOP
354 --wk.pc; // rollback for correct length
355 zym_dis_put_mnemo(&wk, "NOP");
356 if (opcode == 0xdd) zym_dis_put_arg(&wk, "#DD"); else zym_dis_put_arg(&wk, "#FD");
357 zym_finish(&wk);
358 return;
360 wk.gotDD = 1/*true*/;
362 // ED-prefixed instructions
363 if (opcode == 0xed) {
364 wk.DD = "HL"; // Á ÎÁÓ -- ÒÁÔØ!
365 // read opcode -- OCR(4)
366 opcode = zym_dis_read_byte(&wk);
367 switch (opcode) {
368 // LDI, LDIR, LDD, LDDR
369 case 0xa0: zym_dis_put_mnemo(&wk, "LDI"); zym_finish(&wk); return;
370 case 0xb0: zym_dis_put_mnemo(&wk, "LDIR"); zym_finish(&wk); return;
371 case 0xa8: zym_dis_put_mnemo(&wk, "LDD"); zym_finish(&wk); return;
372 case 0xb8: zym_dis_put_mnemo(&wk, "LDDR"); zym_finish(&wk); return;
373 // CPI, CPIR, CPD, CPDR
374 case 0xa1: zym_dis_put_mnemo(&wk, "CPI"); zym_finish(&wk); return;
375 case 0xb1: zym_dis_put_mnemo(&wk, "CPIR"); zym_finish(&wk); return;
376 case 0xa9: zym_dis_put_mnemo(&wk, "CPD"); zym_finish(&wk); return;
377 case 0xb9: zym_dis_put_mnemo(&wk, "CPDR"); zym_finish(&wk); return;
378 // OUTI, OTIR, OUTD, OTDR
379 case 0xa3: zym_dis_put_mnemo(&wk, "OUTI"); zym_finish(&wk); return;
380 case 0xb3: zym_dis_put_mnemo(&wk, "OTIR"); zym_finish(&wk); return;
381 case 0xab: zym_dis_put_mnemo(&wk, "OUTD"); zym_finish(&wk); return;
382 case 0xbb: zym_dis_put_mnemo(&wk, "OTDR"); zym_finish(&wk); return;
383 // INI, INIR, IND, INDR
384 case 0xa2: zym_dis_put_mnemo(&wk, "INI"); zym_finish(&wk); return;
385 case 0xb2: zym_dis_put_mnemo(&wk, "INIR"); zym_finish(&wk); return;
386 case 0xaa: zym_dis_put_mnemo(&wk, "IND"); zym_finish(&wk); return;
387 case 0xba: zym_dis_put_mnemo(&wk, "INDR"); zym_finish(&wk); return;
388 // not strings, but some good instructions anyway
389 default:
390 if ((opcode&0xc0) == 0x40) {
391 // 0x40...0x7f
392 switch (opcode&0x07) {
393 case 0: // IN r8,(C)
394 case 1: // OUT (C),r8
395 if (opcode&0x07) { zym_dis_put_mnemo(&wk, "OUT"); zym_dis_put_arg(&wk, "(C)"); } else zym_dis_put_mnemo(&wk, "IN");
396 if (opcode&0x07) zym_dis_put_r8(&wk, "0", ((opcode>>3)&0x07)); else zym_dis_put_r8(&wk, "F", ((opcode>>3)&0x07)); // 0 on NMOS, 255 on CMOS
397 if ((opcode&0x07) == 0) zym_dis_put_arg(&wk, "(C)");
398 zym_finish(&wk);
399 return;
400 // SBC HL,rr/ADC HL,rr
401 case 2:
402 zym_dis_put_mnemo(&wk, opcode&0x08 ? "ADC" : "SBC");
403 zym_dis_put_arg(&wk, "HL");
404 zym_dis_put_r16sp(&wk, "HL", ((opcode>>4)&0x03));
405 zym_finish(&wk);
406 return;
407 // LD (nn),rr/LD rr,(nn)
408 case 3:
409 zym_dis_put_mnemo(&wk, "LD");
410 if ((opcode&0x08) == 0) {
411 // LD (nn),rr
412 zym_dis_put_arg_part(&wk, "(");
413 tmpW = zym_dis_read_word(&wk);
414 zym_dis_put_uint_addr_data(&wk, tmpW, 4);
415 zym_dis_put_arg(&wk, ")");
417 zym_dis_put_r16sp(&wk, "HL", ((opcode>>4)&0x03));
418 if ((opcode&0x08) != 0) {
419 // LD rr,(nn)
420 zym_dis_put_arg_part(&wk, "(");
421 tmpW = zym_dis_read_word(&wk);
422 zym_dis_put_uint_addr_data(&wk, tmpW, 4);
423 zym_dis_put_arg(&wk, ")");
425 zym_finish(&wk);
426 return;
427 // NEG
428 case 4:
429 zym_dis_put_mnemo(&wk, "NEG");
430 zym_finish(&wk);
431 return;
432 // RETI/RETN
433 case 5:
434 // RETI: 0x4d, 0x5d, 0x6d, 0x7d
435 // RETN: 0x45, 0x55, 0x65, 0x75
436 if (opcode&0x08) zym_dis_put_mnemo(&wk, "RETI"); else zym_dis_put_mnemo(&wk, "RETN");
437 zym_finish(&wk);
438 return;
439 // IM n
440 case 6:
441 zym_dis_put_mnemo(&wk, "IM");
442 switch (opcode) {
443 case 0x56: case 0x76: zym_dis_put_arg(&wk, "1"); break;
444 case 0x5e: case 0x7e: zym_dis_put_arg(&wk, "2"); break;
445 default: zym_dis_put_arg(&wk, "0"); break;
447 zym_finish(&wk);
448 return;
449 // specials
450 case 7:
451 switch (opcode) {
452 case 0x47: // LD I,A
453 zym_dis_put_mnemo(&wk, "LD");
454 zym_dis_put_arg(&wk, "I");
455 zym_dis_put_arg(&wk, "A");
456 zym_finish(&wk);
457 return;
458 case 0x4f: // LD R,A
459 zym_dis_put_mnemo(&wk, "LD");
460 zym_dis_put_arg(&wk, "R");
461 zym_dis_put_arg(&wk, "A");
462 zym_finish(&wk);
463 return;
464 case 0x57: // LD A,I
465 zym_dis_put_mnemo(&wk, "LD");
466 zym_dis_put_arg(&wk, "A");
467 zym_dis_put_arg(&wk, "I");
468 zym_finish(&wk);
469 return;
470 case 0x5f: // LD A,R
471 zym_dis_put_mnemo(&wk, "LD");
472 zym_dis_put_arg(&wk, "A");
473 zym_dis_put_arg(&wk, "R");
474 zym_finish(&wk);
475 return;
476 case 0x67: // RRD
477 zym_dis_put_mnemo(&wk, "RRD");
478 zym_finish(&wk);
479 return;
480 case 0x6F: // RLD
481 zym_dis_put_mnemo(&wk, "RLD");
482 zym_finish(&wk);
483 return;
486 } else {
487 // slt and other traps
488 if (opcode == 0xFB) {
489 zym_dis_put_mnemo(&wk, "TSLT");
490 } else {
491 zym_dis_put_mnemo(&wk, "TRAP");
492 zym_dis_start_arg(&wk);
493 zym_dis_put_uint_hex_len(&wk, opcode, 2);
494 zym_dis_end_arg(&wk);
497 zym_finish(&wk);
498 return;
500 // NOP
501 zym_dis_put_mnemo(&wk, "NOP");
502 zym_dis_put_arg(&wk, "#ED");
503 zym_dis_start_arg(&wk);
504 zym_dis_put_uint_hex_len(&wk, opcode, 2);
505 zym_dis_end_arg(&wk);
506 zym_finish(&wk);
507 return;
508 } // 0xed done
509 // CB-prefixed instructions
510 if (opcode == 0xcb) {
511 // shifts and bit operations
512 // read opcode
513 opcode = zym_dis_read_byte(&wk);
514 // mnemonics
515 switch ((opcode>>3)&0x1f) {
516 case 0: zym_dis_put_mnemo(&wk, "RLC"); break;
517 case 1: zym_dis_put_mnemo(&wk, "RRC"); break;
518 case 2: zym_dis_put_mnemo(&wk, "RL"); break;
519 case 3: zym_dis_put_mnemo(&wk, "RR"); break;
520 case 4: zym_dis_put_mnemo(&wk, "SLA"); break;
521 case 5: zym_dis_put_mnemo(&wk, "SRA"); break;
522 case 6: zym_dis_put_mnemo(&wk, "SLL"); break;
523 case 7: zym_dis_put_mnemo(&wk, "SRL"); break;
524 default:
525 switch ((opcode>>6)&0x03) {
526 case 1: zym_dis_put_mnemo(&wk, "BIT"); break;
527 case 2: zym_dis_put_mnemo(&wk, "RES"); break;
528 case 3: zym_dis_put_mnemo(&wk, "SET"); break;
530 zym_dis_start_arg(&wk);
531 zym_dis_put_uint_dec(&wk, (opcode>>3)&0x07);
532 zym_dis_end_arg(&wk);
533 break;
535 // R8X arg
536 if (wk.gotDD) {
537 zym_dis_put_arg_part(&wk, "(");
538 zym_dis_put_arg_part(&wk, wk.DD);
539 zym_dis_put_disp(&wk, wk.disp);
540 zym_dis_put_arg(&wk, ")");
541 if ((opcode&0x07) != 6) zym_dis_put_r8(&wk, "", (opcode&0x07));
542 } else {
543 zym_dis_put_r8(&wk, "(HL)", (opcode&0x07));
545 zym_finish(&wk);
546 return;
547 } // 0xcb done
548 // normal things
549 switch (opcode&0xc0) {
550 // 0x00..0x3F
551 case 0x00:
552 switch (opcode&0x07) {
553 // misc,DJNZ,JR,JR cc
554 case 0:
555 if (opcode&0x30) {
556 // branches
557 if (opcode&0x20) {
558 // JR cc
559 zym_dis_put_mnemo(&wk, "JR");
560 zym_dis_put_cc(&wk, (opcode>>3)&0x03);
561 } else {
562 // DJNZ/JR
563 zym_dis_put_mnemo(&wk, opcode&0x08 ? "JR" : "DJNZ");
565 wk.disp = zym_dis_read_byte(&wk);
566 if (wk.disp > 127) wk.disp -= 256; // convert to int8_t
567 uint16_t addr = (uint16_t)(wk.pc+wk.disp);
568 zym_dis_start_arg(&wk);
569 zym_dis_put_uint_addr_pc(&wk, addr, 4);
570 zym_dis_end_arg(&wk);
571 } else {
572 // EX AF,AF' or NOP
573 if (opcode != 0) {
574 zym_dis_put_mnemo(&wk, "EX");
575 zym_dis_put_arg(&wk, "AF");
576 zym_dis_put_arg(&wk, "AF'");
577 } else {
578 zym_dis_put_mnemo(&wk, "NOP");
581 zym_finish(&wk);
582 return;
583 // LD rr,nn/ADD HL,rr
584 case 1:
585 if (opcode&0x08) {
586 // ADD HL,rr
587 zym_dis_put_mnemo(&wk, "ADD");
588 zym_dis_put_arg(&wk, wk.DD);
589 zym_dis_put_r16sp(&wk, "", ((opcode>>4)&0x03));
590 } else {
591 // LD rr,nn
592 zym_dis_put_mnemo(&wk, "LD");
593 zym_dis_put_r16sp(&wk, "", ((opcode>>4)&0x03));
594 tmpW = zym_dis_read_word(&wk);
595 zym_dis_start_arg(&wk);
596 zym_dis_put_uint_data_word(&wk, tmpW, 4);
597 zym_dis_end_arg(&wk);
599 zym_finish(&wk);
600 return;
601 // LD xxx,xxx
602 case 2:
603 zym_dis_put_mnemo(&wk, "LD");
604 switch ((opcode>>3)&0x07) {
605 // LD (BC),A
606 case 0: zym_dis_put_arg(&wk, "(BC)"); zym_dis_put_arg(&wk, "A"); break;
607 // LD A,(BC)
608 case 1: zym_dis_put_arg(&wk, "A"); zym_dis_put_arg(&wk, "(BC)"); break;
609 // LD (DE),A
610 case 2: zym_dis_put_arg(&wk, "(DE)"); zym_dis_put_arg(&wk, "A"); break;
611 // LD A,(DE)
612 case 3: zym_dis_put_arg(&wk, "A"); zym_dis_put_arg(&wk, "(DE)"); break;
613 // LD (nn),HL
614 case 4: zym_dis_put_arg_part(&wk, "("); tmpW = zym_dis_read_word(&wk); zym_dis_put_uint_addr_data(&wk, tmpW, 4); zym_dis_put_arg(&wk, ")"); zym_dis_put_arg(&wk, wk.DD); break;
615 // LD HL,(nn)
616 case 5: zym_dis_put_arg(&wk, wk.DD); zym_dis_put_arg_part(&wk, "("); tmpW = zym_dis_read_word(&wk); zym_dis_put_uint_addr_data(&wk, tmpW, 4); zym_dis_put_arg(&wk, ")"); break;
617 // LD (nn),A
618 case 6: zym_dis_put_arg_part(&wk, "("); tmpW = zym_dis_read_word(&wk); zym_dis_put_uint_addr_data(&wk, tmpW, 4); zym_dis_put_arg(&wk, ")"); zym_dis_put_arg(&wk, "A"); break;
619 // LD A,(nn)
620 case 7: zym_dis_put_arg(&wk, "A"); zym_dis_put_arg_part(&wk, "("); tmpW = zym_dis_read_word(&wk); zym_dis_put_uint_addr_data(&wk, tmpW, 4); zym_dis_put_arg(&wk, ")"); break;
622 zym_finish(&wk);
623 return;
624 // INC rr/DEC rr
625 case 3:
626 zym_dis_put_mnemo(&wk, opcode&0x08 ? "DEC" : "INC");
627 zym_dis_put_r16sp(&wk, "", ((opcode>>4)&0x03));
628 zym_finish(&wk);
629 return;
630 // INC/DEC r8; LD r8,n
631 case 4: // INC r8
632 case 5: // DEC r8
633 case 6: // LD r8,n
634 switch (opcode&0x07) {
635 case 4: zym_dis_put_mnemo(&wk, "INC"); break;
636 case 5: zym_dis_put_mnemo(&wk, "DEC"); break;
637 case 6: zym_dis_put_mnemo(&wk, "LD"); break;
639 switch ((opcode>>3)&0x07) {
640 case 0: zym_dis_put_arg(&wk, "B"); break;
641 case 1: zym_dis_put_arg(&wk, "C"); break;
642 case 2: zym_dis_put_arg(&wk, "D"); break;
643 case 3: zym_dis_put_arg(&wk, "E"); break;
644 case 4: if (wk.gotDD) zym_dis_put_arg_part(&wk, wk.DD); zym_dis_put_arg(&wk, "H"); break;
645 case 5: if (wk.gotDD) zym_dis_put_arg_part(&wk, wk.DD); zym_dis_put_arg(&wk, "L"); break;
646 case 6:
647 zym_dis_put_arg_part(&wk, "(");
648 zym_dis_put_arg_part(&wk, wk.DD);
649 if (wk.gotDD) zym_dis_put_disp(&wk, wk.disp);
650 zym_dis_put_arg(&wk, ")");
651 break;
652 case 7: zym_dis_put_arg(&wk, "A"); break;
654 // LD?
655 if ((opcode&0x07) == 6) {
656 tmpB = zym_dis_read_byte(&wk);
657 zym_dis_start_arg(&wk);
658 zym_dis_put_uint_data_byte(&wk, tmpB, 2);
659 zym_dis_end_arg(&wk);
661 zym_finish(&wk);
662 return;
663 // swim-swim-hungry
664 case 7:
665 switch ((opcode>>3)&0x07) {
666 case 0: zym_dis_put_mnemo(&wk, "RLCA"); break;
667 case 1: zym_dis_put_mnemo(&wk, "RRCA"); break;
668 case 2: zym_dis_put_mnemo(&wk, "RLA"); break;
669 case 3: zym_dis_put_mnemo(&wk, "RRA"); break;
670 case 4: zym_dis_put_mnemo(&wk, "DAA"); break;
671 case 5: zym_dis_put_mnemo(&wk, "CPL"); break;
672 case 6: zym_dis_put_mnemo(&wk, "SCF"); break;
673 case 7: zym_dis_put_mnemo(&wk, "CCF"); break;
675 zym_finish(&wk);
676 return;
678 /*assert(0);*/
679 abort();
680 // 0x40..0x7F: LD r8,r8
681 case 0x40:
682 if (opcode == 0x76) { zym_dis_put_mnemo(&wk, "HALT"); zym_finish(&wk); return; }
683 zym_dis_put_mnemo(&wk, "LD");
684 const uint8_t rsrc = (opcode&0x07);
685 const uint8_t rdst = ((opcode>>3)&0x07);
686 switch (rdst) {
687 case 0: zym_dis_put_arg(&wk, "B"); break;
688 case 1: zym_dis_put_arg(&wk, "C"); break;
689 case 2: zym_dis_put_arg(&wk, "D"); break;
690 case 3: zym_dis_put_arg(&wk, "E"); break;
691 case 4: if (wk.gotDD && rsrc != 6) zym_dis_put_arg_part(&wk, wk.DD); zym_dis_put_arg(&wk, "H"); break;
692 case 5: if (wk.gotDD && rsrc != 6) zym_dis_put_arg_part(&wk, wk.DD); zym_dis_put_arg(&wk, "L"); break;
693 case 6:
694 zym_dis_put_arg_part(&wk, "(");
695 zym_dis_put_arg_part(&wk, wk.DD);
696 if (wk.gotDD) zym_dis_put_disp(&wk, wk.disp);
697 zym_dis_put_arg(&wk, ")");
698 break;
699 case 7: zym_dis_put_arg(&wk, "A"); break;
701 switch (rsrc) {
702 case 0: zym_dis_put_arg(&wk, "B"); break;
703 case 1: zym_dis_put_arg(&wk, "C"); break;
704 case 2: zym_dis_put_arg(&wk, "D"); break;
705 case 3: zym_dis_put_arg(&wk, "E"); break;
706 case 4: if (wk.gotDD && rdst != 6) zym_dis_put_arg_part(&wk, wk.DD); zym_dis_put_arg(&wk, "H"); break;
707 case 5: if (wk.gotDD && rdst != 6) zym_dis_put_arg_part(&wk, wk.DD); zym_dis_put_arg(&wk, "L"); break;
708 case 6:
709 zym_dis_put_arg_part(&wk, "(");
710 zym_dis_put_arg_part(&wk, wk.DD);
711 if (wk.gotDD) zym_dis_put_disp(&wk, wk.disp);
712 zym_dis_put_arg(&wk, ")");
713 break;
714 case 7: zym_dis_put_arg(&wk, "A"); break;
716 zym_finish(&wk);
717 return;
718 // 0x80..0xBF: ALU A,r8
719 case 0x80:
720 switch ((opcode>>3)&0x07) {
721 case 0: zym_dis_put_mnemo(&wk, "ADD"); zym_dis_put_arg(&wk, "A"); break;
722 case 1: zym_dis_put_mnemo(&wk, "ADC"); zym_dis_put_arg(&wk, "A"); break;
723 case 2: zym_dis_put_mnemo(&wk, "SUB"); break;
724 case 3: zym_dis_put_mnemo(&wk, "SBC"); zym_dis_put_arg(&wk, "A"); break;
725 case 4: zym_dis_put_mnemo(&wk, "AND"); break;
726 case 5: zym_dis_put_mnemo(&wk, "XOR"); break;
727 case 6: zym_dis_put_mnemo(&wk, "OR"); break;
728 case 7: zym_dis_put_mnemo(&wk, "CP"); break;
730 zym_dis_put_r8(&wk, "", (opcode&0x07));
731 zym_finish(&wk);
732 return;
733 // 0xC0..0xFF
734 case 0xC0:
735 switch (opcode&0x07) {
736 // RET cc
737 case 0:
738 zym_dis_put_mnemo(&wk, "RET");
739 zym_dis_put_cc(&wk, (opcode>>3)&0x07);
740 break;
741 // POP rr/special0
742 case 1:
743 if (opcode&0x08) {
744 // special 0
745 switch ((opcode>>4)&0x03) {
746 // RET
747 case 0: zym_dis_put_mnemo(&wk, "RET"); break;
748 // EXX
749 case 1: zym_dis_put_mnemo(&wk, "EXX"); break;
750 // JP (HL)
751 case 2: zym_dis_put_mnemo(&wk, "JP"); zym_dis_put_arg_part(&wk, "("); zym_dis_put_arg_part(&wk, wk.DD); zym_dis_put_arg(&wk, ")"); break;
752 // LD SP,HL
753 case 3: zym_dis_put_mnemo(&wk, "LD"); zym_dis_put_arg(&wk, "SP"); zym_dis_put_arg(&wk, wk.DD); break;
755 } else {
756 // POP rr
757 zym_dis_put_mnemo(&wk, "POP");
758 zym_dis_put_r16af(&wk, (opcode>>4)&0x03);
760 break;
761 // JP cc,nn
762 case 2:
763 zym_dis_put_mnemo(&wk, "JP");
764 zym_dis_put_cc(&wk, (opcode>>3)&0x07);
765 tmpW = zym_dis_read_word(&wk);
766 zym_dis_start_arg(&wk);
767 zym_dis_put_uint_addr_pc(&wk, tmpW, 4);
768 zym_dis_end_arg(&wk);
769 break;
770 // special1/special3
771 case 3:
772 switch ((opcode>>3)&0x07) {
773 // JP nn
774 case 0: zym_dis_put_mnemo(&wk, "JP"); tmpW = zym_dis_read_word(&wk); zym_dis_start_arg(&wk); zym_dis_put_uint_addr_pc(&wk, tmpW, 4); zym_dis_end_arg(&wk); break;
775 // OUT (n),A
776 case 2: zym_dis_put_mnemo(&wk, "OUT"); zym_dis_put_arg_part(&wk, "("); tmpB = zym_dis_read_byte(&wk); zym_dis_put_uint_port_byte(&wk, tmpB, 2); zym_dis_put_arg(&wk, ")"); zym_dis_put_arg(&wk, "A"); break;
777 // IN A,(n)
778 case 3: zym_dis_put_mnemo(&wk, "IN"); zym_dis_put_arg(&wk, "A"); zym_dis_put_arg_part(&wk, "("); tmpB = zym_dis_read_byte(&wk); zym_dis_put_uint_port_byte(&wk, tmpB, 2); zym_dis_put_arg(&wk, ")"); break;
779 // EX (SP),HL
780 case 4: zym_dis_put_mnemo(&wk, "EX"); zym_dis_put_arg(&wk, "(SP)"); zym_dis_put_arg(&wk, wk.DD); break;
781 // EX DE,HL
782 case 5: zym_dis_put_mnemo(&wk, "EX"); zym_dis_put_arg(&wk, "DE"); zym_dis_put_arg(&wk, "HL"); break;
783 // DI
784 case 6: zym_dis_put_mnemo(&wk, "DI"); break;
785 // EI
786 case 7: zym_dis_put_mnemo(&wk, "EI"); break;
788 break;
789 // CALL cc,nn
790 case 4:
791 zym_dis_put_mnemo(&wk, "CALL");
792 zym_dis_put_cc(&wk, (opcode>>3)&0x07);
793 tmpW = zym_dis_read_word(&wk);
794 zym_dis_start_arg(&wk);
795 zym_dis_put_uint_addr_pc(&wk, tmpW, 4);
796 zym_dis_end_arg(&wk);
797 break;
798 // PUSH rr/special2
799 case 5:
800 if (opcode&0x08) {
801 if (((opcode>>4)&0x03) == 0) {
802 // CALL
803 zym_dis_put_mnemo(&wk, "CALL");
804 tmpW = zym_dis_read_word(&wk);
805 zym_dis_start_arg(&wk);
806 zym_dis_put_uint_addr_pc(&wk, tmpW, 4);
807 zym_dis_end_arg(&wk);
809 } else {
810 // PUSH rr
811 zym_dis_put_mnemo(&wk, "PUSH");
812 zym_dis_put_r16af(&wk, (opcode>>4)&0x03);
814 break;
815 // ALU A,n
816 case 6:
817 switch ((opcode>>3)&0x07) {
818 case 0: zym_dis_put_mnemo(&wk, "ADD"); zym_dis_put_arg(&wk, "A"); break;
819 case 1: zym_dis_put_mnemo(&wk, "ADC"); zym_dis_put_arg(&wk, "A"); break;
820 case 2: zym_dis_put_mnemo(&wk, "SUB"); break;
821 case 3: zym_dis_put_mnemo(&wk, "SBC"); zym_dis_put_arg(&wk, "A"); break;
822 case 4: zym_dis_put_mnemo(&wk, "AND"); break;
823 case 5: zym_dis_put_mnemo(&wk, "XOR"); break;
824 case 6: zym_dis_put_mnemo(&wk, "OR"); break;
825 case 7: zym_dis_put_mnemo(&wk, "CP"); break;
827 tmpB = zym_dis_read_byte(&wk);
828 zym_dis_start_arg(&wk);
829 zym_dis_put_uint_data_byte(&wk, tmpB, 2);
830 zym_dis_end_arg(&wk);
831 break;
832 // RST nnn
833 case 7:
834 zym_dis_put_mnemo(&wk, "RST");
835 zym_dis_start_arg(&wk);
836 zym_dis_put_uint_hexlen(&wk, opcode&0x38, 2);
837 zym_dis_end_arg(&wk);
838 break;
840 break;
841 } // end switch
842 zym_finish(&wk);
846 void zym_disasm_one (zym_cpu_t *z80, zym_disop_t *nfo, uint16_t pc) {
847 if (!z80) abort();
848 zym_disasm_one_internal(z80, nfo, pc, NULL);
852 void zym_disasm_one_ex (zym_disop_t *nfo, const uint8_t mem[8], uint16_t pc) {
853 if (!mem) abort();
854 zym_disasm_one_internal(NULL, nfo, pc, mem);