summaryrefslogtreecommitdiff
path: root/avr/disas_z180.c
diff options
context:
space:
mode:
authorLeo C.2024-07-10 20:02:26 +0200
committerLeo C.2024-07-10 20:02:26 +0200
commit18d57acbf4ccd7220e59fe1f2ac1556837c5da83 (patch)
treedde83807d4a6df5df25b3da3dfc462235f7befc7 /avr/disas_z180.c
parent976db69ffa80c4d499e53f6f22c26d784fdac0a1 (diff)
downloadz180-stamp-18d57acbf4ccd7220e59fe1f2ac1556837c5da83.zip
new command: dissassemle - Disassemble Z180 code from memory
Diffstat (limited to 'avr/disas_z180.c')
-rw-r--r--avr/disas_z180.c725
1 files changed, 725 insertions, 0 deletions
diff --git a/avr/disas_z180.c b/avr/disas_z180.c
new file mode 100644
index 0000000..b7a7eae
--- /dev/null
+++ b/avr/disas_z180.c
@@ -0,0 +1,725 @@
+/*
+ * (C) Copyright 2014,2018 Leo C. <erbl259-lmu@yahoo.de>
+ *
+ */
+
+#include "common.h"
+#include "z80-if.h"
+#include "disas_z180.h"
+
+/*
+ * t_MNEMONICS
+ */
+enum {
+ /* 1-byte other */
+ i_, iNOP, iLD, iINC, iDEC, iDJNZ, iJR, iHALT, /* 0 .. 7 */
+ iRET, iPOP, iJP, iCALL, iPUSH, iRST, iOUT, iEXX, /* 8 .. 15 */
+ iIN, iEX, iDI, iEI, /* 16 .. 19 */
+ /* CB bit */
+ iBIT, iRES, iSET, /* 20 .. 22 */
+ /* ED */
+ iNEG, iRETN, iRETI, iIM, iRRD, iRLD, /* 23 .. 28 */
+ /* ED Z180 */
+ iIN0, iOUT0, iTST, iMLT, iTSTIO,
+ iSLP, iOTIM, iOTDM, iOTIMR, iOTDMR, /* 29 .. 38 */
+
+//i_opc_alu equ opc_index
+ /* 1-byte "alu" */
+ iADD, iADC, iSUB, iSBC, iAND, iXOR, iOR, iCP, /* 39 .. 46 */
+ /* 1-byte no arguments */
+ iRLCA, iRRCA, iRLA, iRRA, iDAA, iCPL, iSCF, iCCF, /* 47 .. 54 */
+ /* CB rot */
+ iRLC, iRRC, iRL, iRR, iSLA, iSRA, iSLL, iSRL, /* 55 .. 62 */
+
+//i_opc_bli equ opc_index
+ /* ED Block instr (bli) */
+ iLDI, iCPI, iINI, iOUTI, iLDD, iCPD, iIND, iOUTD, /* 63 .. 70 */
+ iLDIR,iCPIR,iINIR,iOTIR, iLDDR,iCPDR,iINDR,iOTDR /* 71 .. 78 */
+} mnemo_index;
+
+#define i_opc_alu 39
+#define i_opc_bli 63
+
+//t_MNEMONICS:
+const FLASH uint8_t mnemonics[] = {
+ '?' + 0x80, /* 1-byte other */
+ 'N','O','P', + 0x80,
+ 'L','D' + 0x80,
+ 'I','N','C' + 0x80,
+ 'D','E','C' + 0x80,
+ 'D','J','N','Z' + 0x80,
+ 'J','R' + 0x80,
+ 'H','A','L','T' + 0x80,
+ 'R','E','T' + 0x80,
+ 'P','O','P' + 0x80,
+ 'J','P' + 0x80,
+ 'C','A','L','L' + 0x80,
+ 'P','U','S','H' + 0x80,
+ 'R','S','T' + 0x80,
+ 'O','U','T' + 0x80,
+ 'E','X','X' + 0x80,
+ 'I','N' + 0x80,
+ 'E','X' + 0x80,
+ 'D','I' + 0x80,
+ 'E','I' + 0x80,
+ 'B','I','T' + 0x80, /* CB bit */
+ 'R','E','S' + 0x80,
+ 'S','E','T' + 0x80,
+ 'N','E','G' + 0x80,
+ 'R','E','T','N' + 0x80,
+ 'R','E','T','I' + 0x80,
+ 'I','M' + 0x80,
+ 'R','R','D' + 0x80,
+ 'R','L','D' + 0x80,
+ 'I','N','0' + 0x80, /* Z180 */
+ 'O','U','T','0' + 0x80,
+ 'T','S','T' + 0x80,
+ 'M','L','T' + 0x80,
+ 'T','S','T','I','O' + 0x80,
+ 'S','L','P' + 0x80,
+ 'O','T','I','M' + 0x80,
+ 'O','T','D','M' + 0x80,
+ 'O','T','I','M','R' + 0x80,
+ 'O','T','D','M','R' + 0x80,
+ 'A','D','D' + 0x80, /* 1-byte "alu" */
+ 'A','D','C' + 0x80,
+ 'S','U','B' + 0x80,
+ 'S','B','C' + 0x80,
+ 'A','N','D' + 0x80,
+ 'X','O','R' + 0x80,
+ 'O','R' + 0x80,
+ 'C','P' + 0x80,
+ 'R','L','C','A' + 0x80, /* 1-byte no arguments */
+ 'R','R','C','A' + 0x80,
+ 'R','L','A' + 0x80,
+ 'R','R','A' + 0x80,
+ 'D','A','A' + 0x80,
+ 'C','P','L' + 0x80,
+ 'S','C','F' + 0x80,
+ 'C','C','F' + 0x80,
+ 'R','L','C' + 0x80, /* CB rot */
+ 'R','R','C' + 0x80,
+ 'R','L' + 0x80,
+ 'R','R' + 0x80,
+ 'S','L','A' + 0x80,
+ 'S','R','A' + 0x80,
+ 'S','L','L' + 0x80,
+ 'S','R','L' + 0x80,
+ 'L','D','I' + 0x80, /* ED Block instr (bli) */
+ 'C','P','I' + 0x80,
+ 'I','N','I' + 0x80,
+ 'O','U','T','I' + 0x80,
+ 'L','D','D' + 0x80,
+ 'C','P','D' + 0x80,
+ 'I','N','D' + 0x80,
+ 'O','U','T','D' + 0x80,
+ 'L','D','I','R' + 0x80,
+ 'C','P','I','R' + 0x80,
+ 'I','N','I','R' + 0x80,
+ 'O','T','I','R' + 0x80,
+ 'L','D','D','R' + 0x80,
+ 'C','P','D','R' + 0x80,
+ 'I','N','D','R' + 0x80,
+ 'O','T','D','R' + 0x80,
+};
+
+//-------------------------------------------------------------------------------
+//
+// GROUP2, GROUP1 and GROUP3 are instruction decoding tables and have
+// the following structure:
+//
+// [ mask { mode ( match , index ) } 0FFH ] 0.
+//
+// The repeating group ( match , index ) terminates when the MSB of the
+// index byte is set. The interpretation of the "mode" byte is
+// explained in the documentation to datasheet OPRNDZ.
+//
+
+//
+
+//const FLASH uint8_t dec_tab2
+
+struct dec_tab {
+ uint8_t group2[18]; /* GROUP2: CB group */
+ uint8_t group1[123]; /* GROUP1: Main group */
+ uint8_t group3[92]; /* GROUP3, ED group */
+} __attribute__((__packed__));
+
+const FLASH struct dec_tab dec_tab = {
+ .group2 = {
+ 0xC0,0x36, //mask, mode
+ 0x40,iBIT, // bit b,g
+ 0x80,iRES, // res b,g
+ 0xC0,iSET+0x80, // set b,g
+ 0xFF, //
+ 0xF8,0x00, //mask, mode
+ 0x30,i_+0x80, // (sll g)
+ 0xFF, //
+ 0xC0,0x06, //mask, mode
+ 0x00,iRLC+0x80, // rlc g ... srl g
+ },
+ .group1 = {
+ 0xFF,0x00, //mask, mode
+ 0x00,iNOP, // NOP
+ 0x76,iHALT, // HALT
+ 0xC9,iRET, // RET
+ 0xD9,iEXX, // EXX
+ 0xF3,iDI, // DI
+ 0xFB,iEI+0x80, // EI //14
+ 0x04, //mode
+ 0x08,iEX+0x80, //
+ 0x01,
+ 0x10,iDJNZ,
+ 0x18,iJR+0x80,
+ 0xAF,
+ 0x22,iLD+0x80,
+ 0xFA,
+ 0x2A,iLD+0x80,
+ 0xA7,
+ 0x32,iLD+0x80,
+ 0x7A,
+ 0x3A,iLD+0x80,
+ 0x03,
+ 0xC3,iJP,
+ 0xCD,iCALL+0x80,
+ 0x97,
+ 0xD3,iOUT+0x80,
+ 0x79,
+ 0xDB,iIN+0x80,
+ 0x5F,
+ 0xE3,iEX+0x80,
+ 0x0E,
+ 0xE9,iJP+0x80,
+ 0x05,
+ 0xEB,iEX+0x80, //
+ 0xDF,
+ 0xF9,iLD+0x80,
+ 0xFF, //44
+
+ 0xC0, //mask
+ 0xB6, //mode r[y],r[z]
+ 0x40,iLD+0x80,
+ 0x06, //mode alu[y],r[z]
+ 0x80,iADD+0x80,
+ 0xFF, //8
+
+ 0xC7, //mask
+ 0x0B,
+ 0x04,iINC, // inc r[y]
+ 0x05,iDEC+0x80, // dec r[y]
+ 0xB2,
+ 0x06,iLD+0x80, // ld r[y],nn
+ 0x00, //mode
+ 0x07,iRLCA+0x80, // rlca ...
+ 0x20,
+ 0xC0,iRET+0x80, // ret cc
+ 0x23,
+ 0xC2,iJP, // jp cc,mn
+ 0xC4,iCALL+0x80, // call cc,mn
+ 0x10,
+ 0xC7,iRST+0x80, // rst
+ 0x02, //mode alu[y] n
+ 0xC6,iADD+0x80, // add ...
+ 0xFF, //27
+
+ 0xCF,
+ 0xD3,
+ 0x01,iLD+0x80, // ld ww,mn
+ 0x0D,
+ 0x03,iINC, // inc rp
+ 0x0B,iDEC+0x80, // dec rp
+ 0xFD,
+ 0x09,iADD+0x80, // add hl,rp
+ 0x60,
+ 0xC1,iPOP, // pop rp2
+ 0xC5,iPUSH+0x80, // push rp2
+ 0xFF, //18
+
+ 0xE7,
+ 0x21,
+ 0x20,iJR+0x80, //jr cc,
+ 0xFF,
+
+ 0xEF,
+ 0xE7,
+ 0x02,iLD+0x80, // ld (rp),a //rp=bc,de
+ 0x7E,
+ 0x0A,iLD+0x80, // ld a,(rp) //rp=bc,de //12
+ },
+ .group3 = {
+ 0xFF,0x00, //
+ 0x44,iNEG, // NEG
+ 0x45,iRETN, // RETN
+ 0x4D,iRETI, // RETI
+ 0x4E,i_, // (IM 0)
+ 0x67,iRRD, // RRD
+ 0x6F,iRLD, // RLD
+ 0x76,iSLP, // slp
+ 0x83,iOTIM, // otim
+ 0x93,iOTIMR, // otimr
+ 0x8B,iOTDM, // otdm
+ 0x9B,iOTDMR, // otdmr
+ 0x31,i_, // (OUT0 (m),0)
+ 0x71,i_+0x80, // (OUT (C),0)
+ 0x09,
+ 0x30,iIN0+0x80, // in0 (m)
+ 0x02,
+ 0x64,iTST, // tst m
+ 0x74,iTSTIO+0x80, // tstio m
+ 0x80,
+ 0x70,iIN+0x80, // IN (C)
+ 0xFF, //40
+
+ 0xC7,
+ 0x0b,
+ 0x04,iTST+0x80, // tst r
+ 0xB8,
+ 0x40,iIN+0x80, // IN r,(C)
+ 0xB9,
+ 0x00,iIN0+0x80, // IN0 r,(m)
+ 0x8B, //
+ 0x41,iOUT+0x80, // OUT (C),r
+ 0x9B, //
+ 0x01,iOUT0+0x80, // UT0 (m),r
+ 0xFF, // //17
+
+ 0xCF, //
+ 0xFD, //
+ 0x42,iSBC, // sbc hl,rp
+ 0x4A,iADC+0x80, // adc hl,rp
+ 0xAD, //
+ 0x43,iLD+0x80, // LD (nn),rp
+ 0xDA, //
+ 0x4B,iLD+0x80, // LD rp,(nn)
+ 0x0D,
+ 0x4C,iMLT+0x80, //mlt rp
+ 0xFF, // //16
+
+ 0xE7, //
+ 0x40, //
+ 0x46,iIM+0x80, // IM x
+ 0xFF, //
+
+ 0xF7, //
+ 0xC7, //
+ 0x47,iLD+0x80, // LD i|r,A
+ 0x7C, //
+ 0x57,iLD+0x80, // LD A,i|r
+ 0xFF,
+
+ 0xE4, //
+ 0x00, //
+ 0xA0,iLDI+0x80, // LDI ...
+ 0xFF, //
+
+ 0x00 //19
+ }
+};
+
+
+//-------------------------------------------------------------------------------
+//
+// Disassemble and output Z80 machine code operand
+//
+// Index OPRND1 OPRND2
+// -----------------------------------------------------------
+// 1 RST address Relative address
+// 2 Condition Immediate byte
+// 3 Bit number Immediate word
+// 4 Interrupt mode AF,AF'
+// 5 (SP) DE,HL
+// 6 Register pair 8-bit source
+// -----------------------------------------------------------
+// 7 A
+// 8 (C)
+// 9 (port number)
+// A (Absolute address)
+// B 8-bit destination
+// C I or R
+// D 16-bit register
+// E Address in 16-bit register
+// F Index register
+//
+// Input:
+// A: opcode
+// B: operand index
+// DE: Address of next instruction byte
+// HL: Address of next free byte in output buffer
+// (iy+0): Bit = 1: Print opcodes
+// (iy+1): index register flag (1=IX, 2=IY, else 0)
+// (iy+2): displacement for any indexed instruction
+// (iy+3): no. of instraction bytes fetched
+//
+// Output:
+// Operand in output buffer
+// DE, HL updated
+// AF, BC destroyed
+//
+//
+//-------------------------------------------------------------------------------
+
+enum {
+ irR8 = 0, irPA = 6, irRA = 7, irIR = 8,
+ irAFAF= 10, irDEHL = 11, irR16 = 12, irINDX = 16,
+ irSP = 19, irCOND = 20, irINTMOD= 28
+} reg_index;
+
+const FLASH uint8_t op_register[] = {
+ 'B' + 0x80, // 0
+ 'C' + 0x80, //
+ 'D' + 0x80, //
+ 'E' + 0x80, //
+ 'H' + 0x80, //
+ 'L' + 0x80, //
+ '(','C',')' + 0x80, // 6
+ 'A' + 0x80, //
+ 'I' + 0x80, // 8
+ 'R' + 0x80, //
+ 'A','F',',','A','F','\'' + 0x80, //10
+ 'D','E',',','H','L' + 0x80, //
+ 'B','C' + 0x80, //12
+ 'D','E' + 0x80, //
+ 'A','F' + 0x80, //
+ 'S','P' + 0x80, //
+ 'H','L' + 0x80, //16
+ 'I','X' + 0x80, //
+ 'I','Y' + 0x80, //
+ '(','S','P',')' + 0x80, //19
+ 'N','Z' + 0x80, //20
+ 'Z' + 0x80, //
+ 'N','C' + 0x80, //
+ 'C' + 0x80, //
+ 'P','O' + 0x80, //
+ 'P','E' + 0x80, //
+ 'P' + 0x80, //
+ 'M' + 0x80, //
+ '0' + 0x80, //28
+ '?' + 0x80, //
+ '1' + 0x80, //
+ '2' + 0x80, //
+};
+
+//-------------------------------------------------------------------------------
+
+typedef struct disas_data_s disas_data_t;
+struct disas_data_s {
+ uint32_t address;
+ uint8_t opcode;
+ int8_t opc_displacement;
+ uint8_t indexflag;
+ uint8_t bytes_fetched;
+ int8_t outbufpos;
+ char outbuf[18];
+};
+
+static
+void xtract(uint8_t index, const FLASH uint8_t *strlist, disas_data_t *dat)
+{
+ uint8_t ch;
+
+ while (index != 0) {
+ while ((*strlist++ & 0x80) == 0)
+ ;
+ --index;
+ }
+ do {
+ ch = *strlist++;
+ dat->outbuf[dat->outbufpos++] = ch & 0x7f;
+ } while ((ch & 0x80) == 0);
+}
+
+static
+uint8_t op_fetch(uint32_t addr)
+{
+ uint8_t op = z80_read(addr);
+
+ return op;
+}
+
+static
+uint8_t op_fetch_print(disas_data_t *dat)
+{
+ uint8_t opc = op_fetch(dat->address);
+ ++dat->address;
+ ++dat->bytes_fetched;
+ printf_P(PSTR("%02X "), opc);
+
+ return opc;
+}
+
+
+static
+void put_char(uint8_t ch, disas_data_t *dat)
+{
+ dat->outbuf[dat->outbufpos++] = ch;
+}
+
+static
+void put_digit(uint8_t digit, disas_data_t *dat)
+{
+ digit &= 0xf;
+ if (digit > 9)
+ digit += (-10 + 'A'-'0');
+ digit += '0';
+
+ put_char(digit, dat);
+}
+
+
+
+static
+void put_hex(uint8_t byte, disas_data_t *dat)
+{
+ put_digit(byte >> 4, dat);
+ put_digit(byte, dat);
+}
+
+static
+void put_hex_16(uint16_t word, disas_data_t *dat)
+{
+ put_hex(word >> 8, dat);
+ put_hex(word, dat);
+}
+
+static
+void do_op_reg16(disas_data_t *dat)
+{
+ uint8_t op = dat->opcode >> 3;
+ op = ((op >> 1) & 0x3) + irR16;
+ if (op == 2 + irR16)
+ op = dat->indexflag + irINDX;
+ xtract(op, op_register, dat);
+}
+
+static
+void do_op_8bit_src_dst(uint8_t op, disas_data_t *dat)
+{
+ op = op & 0x07;
+ if (op == 6) {
+ put_char('(', dat);
+ xtract(dat->indexflag + irINDX, op_register, dat);
+ if (dat->indexflag != 0) {
+ char c = '+';
+ int d = dat->opc_displacement;
+ if (d < 0) {
+ c = '-';
+ d = -d;
+ }
+ put_char(c, dat);
+ put_hex (d & 0xff, dat);
+ }
+ put_char(')', dat);
+ } else
+ xtract(op, op_register, dat);
+}
+
+static
+void do_operand(uint8_t mode, disas_data_t *dat)
+{
+ uint8_t op1;
+
+ switch(mode) {
+ case 0:
+ break;
+ case 1: /* RST address */
+ put_hex (dat->opcode & 0x38, dat);
+ break;
+ case 2: /* Op1 i2: Condition */
+ op1 = dat->opcode >> 3;
+ if ((op1 & 0x10) == 0) op1 &= 3;
+ op1 = (op1 & 7) + irCOND;
+ xtract(op1, op_register, dat);
+ break;
+ case 3: /* Bit number */
+ op1 = dat->opcode >> 3;
+ op1 &= 0x7;
+ put_digit (op1, dat);
+ break;
+ case 4: /* Int mode */
+ op1 = dat->opcode >> 3;
+ op1 = (op1 & 0x03) + irINTMOD;
+ xtract(op1, op_register, dat);
+ break;
+ case 5: /* (SP) */
+ xtract(irSP, op_register, dat);
+ break;
+ case 6: /* Register pair */
+ op1 = dat->opcode >> 3;
+ op1 = (op1 >> 1) & 0x03;
+ if (op1 == 2) {
+ op1 = dat->indexflag + irINDX;
+ } else {
+ if (op1 == 3) --op1;
+ op1 += irR16;
+ }
+ xtract(op1, op_register, dat);
+ break;
+ case 7: /* A */
+ xtract(irRA, op_register, dat);
+ break;
+ case 8: /* (C) */
+ xtract(irPA, op_register, dat);
+ break;
+ case 9: /* (port number) */
+ put_char('(', dat);
+ op1 = op_fetch_print(dat);
+ put_hex(op1, dat);
+ put_char(')', dat);
+ break;
+ case 0xA: /* (Absolute address) */
+ put_char('(', dat);
+ uint16_t arg16 = op_fetch_print(dat)
+ + (op_fetch_print(dat) << 8);
+ put_hex_16(arg16, dat);
+ put_char(')', dat);
+ break;
+ case 0xB: /* 8-bit destination */
+ op1 = dat->opcode >> 3;
+ do_op_8bit_src_dst(op1,dat);
+ break;
+ case 0xC: /* I or R */
+ op1 = dat->opcode >> 3;
+ op1 = op1 & 0x9;
+ xtract(op1, op_register, dat);
+ break;
+ case 0xD: /* 16-bit register */
+ do_op_reg16(dat);
+ break;
+ case 0xE: /* Address in 16-bit register */
+ put_char('(', dat);
+ do_op_reg16(dat);
+ put_char(')', dat);
+ break;
+ case 0xF: /* Index register */
+ op1 = dat->indexflag + irINDX;
+ xtract(op1, op_register, dat);
+ break;
+ case 0x10:
+ break;
+ case 0x11: /* Relative address */
+ op1 = op_fetch_print(dat);
+ uint16_t dst = dat->address + (int8_t) op1;
+ put_hex_16(dst, dat);
+ break;
+ case 0x12: /* Immediate byte */
+ op1 = op_fetch_print(dat);
+ put_hex(op1, dat);
+ break;
+ case 0x13: ; /* Immediate word */
+ uint16_t imm16 = op_fetch_print(dat)
+ + (op_fetch_print(dat) << 8);
+ put_hex_16(imm16, dat);
+ break;
+ case 0x14: /* AF,AF' */
+ xtract(irAFAF, op_register, dat);
+ break;
+ case 0x15: /* DE,HL */
+ xtract(irDEHL, op_register, dat);
+ break;
+ case 0x16: /* 8-bit source */
+ do_op_8bit_src_dst(dat->opcode,dat);
+ break;
+ }
+}
+
+uint32_t dis_decode(uint32_t addr)
+{
+ disas_data_t data;
+
+ memset(&data, 0, sizeof(data));
+ data.address = addr;
+
+ uint8_t op;
+ uint8_t iflag;
+ uint8_t mnemi;
+ uint8_t mask;
+ uint8_t mode;
+
+ printf_P(PSTR("%04X "), data.address);
+
+ const FLASH uint8_t *decodep = dec_tab.group3;
+
+ do {
+ iflag = 0;
+ op = op_fetch_print(&data);
+ if (op == 0xed) {
+ } else if (op == 0xdd) {
+ iflag = 1;
+ } else if (op == 0xfd) {
+ iflag = 2;
+ } else {
+ break;
+ }
+ data.indexflag = iflag;
+ } while (iflag != 0);
+
+ if (op != 0xed) {
+ if (data.indexflag != 0) {
+ if ((op == 0xcb) || ((op & 0x44) == 0x04) || ((op & 0xc0) == 0x40)) {
+ data.opc_displacement = op_fetch_print(&data);
+ }
+ }
+ if (op != 0xcb) {
+ decodep = dec_tab.group1;
+ } else {
+ decodep = dec_tab.group2;
+ op = op_fetch_print(&data);
+ }
+ } else {
+ op = op_fetch_print(&data);
+ }
+
+ data.opcode = op;
+
+ mnemi = 0;
+ while ((mask = *decodep++) != 0) { //NEWMSK
+
+ uint8_t opmask = op & mask;
+
+ while ((mode = *decodep++) != 0xff) { //NEWMOD
+
+ uint8_t opmatch;
+ uint8_t imatch;
+ do {
+ opmatch = *decodep++;
+ imatch = *decodep++;
+ if (opmatch == opmask) {
+ mnemi = imatch & 0x7f;
+ goto matchfound;
+ }
+ } while ((imatch & 0x80) == 0);
+ }
+ } //TABEND
+matchfound:
+
+ /* block instruction? */
+ if (mnemi >= iLDI) {
+ mnemi = mnemi + (op & 0x03) + ((op >> 1) & 0x0c);
+ } else
+ /* Alu op? */
+ if ((mnemi >= iADD) && ((mode & 0x80) == 0)) {
+ mnemi = mnemi + ((op >> 3) & 0x07);
+ }
+
+ xtract(mnemi, mnemonics, &data);
+ while (data.outbufpos < 6)
+ put_char(' ', &data);
+
+ uint8_t w = (mode & 0xf0) >> 4;
+ if (w != 0) {
+ do_operand(w, &data);
+ if (mode & 0x0f)
+ put_char(',', &data);
+ }
+ w = mode & 0x0f;
+ if (w != 0) {
+ if (w < 7)
+ w += 0x10;
+ do_operand(w, &data);
+ }
+ for (uint8_t i = data.bytes_fetched; i < 4; i++)
+ printf_P(PSTR(" "));
+ printf_P(PSTR("%s\n"), data.outbuf);
+
+ return data.address;
+}