/* * (C) Copyright 2014,2018 Leo C. * */ #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; }