# HG changeset patch # User Mike Pavone # Date 1352003908 25200 # Node ID 5df303bf72e6eee2ebc51ef26d6474220d97da3a # Parent 5a2c1da6dd0f933093e1ff25448113fd55ccb17d Improve 68K instruction decoding. Add simple disassembler. diff -r 5a2c1da6dd0f -r 5df303bf72e6 68kinst.c --- a/68kinst.c Sat Nov 03 21:37:38 2012 -0700 +++ b/68kinst.c Sat Nov 03 21:38:28 2012 -0700 @@ -1,25 +1,104 @@ #include "68kinst.h" +#include +#include + +uint32_t sign_extend16(uint32_t val) +{ + return (val & 0x8000) ? val | 0xFFFF0000 : val; +} -void m68k_decode_op(uint16_t op, m68k_op_info *dst) +uint32_t sign_extend8(uint32_t val) { - uint8_t mode = (op >> 3) & 0x7; - uint8_t reg = op & 0x7; + return (val & 0x80) ? val | 0xFFFFFF00 : val; +} + +uint16_t *m68k_decode_op_ex(uint16_t *cur, uint8_t mode, uint8_t reg, uint8_t size, m68k_op_info *dst) +{ + uint16_t ext; dst->addr_mode = mode; switch(mode) { case MODE_REG: case MODE_AREG: + case MODE_AREG_INDIRECT: + case MODE_AREG_POSTINC: + case MODE_AREG_PREDEC: dst->params.regs.pri = reg; break; - case MODE_ + case MODE_AREG_DISPLACE: + ext = *(++cur); + dst->params.regs.pri = reg; + dst->params.regs.displacement = sign_extend16(ext); + break; + case MODE_AREG_INDEX_MEM: + //TODO: implement me + break; + case MODE_PC_INDIRECT_ABS_IMMED: + switch(reg) + { + case 0: + dst->addr_mode = MODE_ABSOLUTE_SHORT; + ext = *(++cur); + dst->params.u32 = sign_extend16(ext); + break; + case 1: + dst->addr_mode = MODE_ABSOLUTE; + ext = *(++cur); + dst->params.u32 = ext << 16 | *(++cur); + break; + case 2: + dst->addr_mode = MODE_PC_DISPLACE; + ext = *(++cur); + dst->params.regs.displacement = sign_extend16(ext); + break; + case 4: + dst->addr_mode = MODE_IMMEDIATE; + ext = *(++cur); + switch (size) + { + case OPSIZE_BYTE: + dst->params.u8 = ext; + break; + case OPSIZE_WORD: + dst->params.u16 = ext; + break; + case OPSIZE_LONG: + dst->params.u32 = ext << 16 | *(++cur); + break; + } + break; + //TODO: implement the rest of these + } + break; } + return cur; +} + +uint16_t *m68k_decode_op(uint16_t *cur, uint8_t size, m68k_op_info *dst) +{ + uint8_t mode = (*cur >> 3) & 0x7; + uint8_t reg = *cur & 0x7; + return m68k_decode_op_ex(cur, mode, reg, size, dst); +} + +void m68k_decode_cond(uint16_t op, m68kinst * decoded) +{ + decoded->extra.cond = (op >> 0x8) & 0xF; +} + +uint8_t m68K_reg_quick_field(uint16_t op) +{ + return (op >> 9) & 0x7; } uint16_t * m68K_decode(uint16_t * istream, m68kinst * decoded) { uint8_t optype = *istream >> 12; uint8_t size; - uint8_t immed; + uint32_t immed; + decoded->op = M68K_INVALID; + decoded->src.addr_mode = decoded->dst.addr_mode = MODE_UNUSED; + decoded->variant = VAR_NORMAL; switch(optype) { case BIT_MOVEP_IMMED: @@ -30,63 +109,529 @@ case MOVE_WORD: decoded->op = M68K_MOVE; decoded->extra.size = optype == MOVE_BYTE ? OPSIZE_BYTE : (optype == MOVE_WORD ? OPSIZE_WORD : OPSIZE_LONG); - m68k_decode_op(*istream, &(decoded->src)); - m68k_decode_op(((*istream >> 9) & 0x7) | , &(decoded->dst)); + istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); + istream = m68k_decode_op_ex(istream, (*istream >> 6) & 0x7, m68K_reg_quick_field(*istream), decoded->extra.size, &(decoded->dst)); break; case MISC: - //TODO: Implement me + + if ((*istream & 0x1C0) == 0x1C0) { + decoded->op = M68K_LEA; + decoded->extra.size = OPSIZE_LONG; + decoded->dst.addr_mode = MODE_AREG; + decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); + istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); + } else { + if (*istream & 0x100) { + decoded->op = M68K_CHK; + if ((*istream & 0x180) == 0x180) { + decoded->extra.size = OPSIZE_WORD; + } else { + //only on M68020+ + decoded->extra.size = OPSIZE_LONG; + } + istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); + decoded->dst.addr_mode = MODE_REG; + decoded->dst.addr_mode = m68K_reg_quick_field(*istream); + } else { + optype = (*istream >> 9) & 0x7; + switch(optype) + { + case 0: + //Move from SR or NEGX + break; + case 1: + //MOVE from CCR or CLR + break; + case 2: + //MOVE to CCR or NEG + break; + case 3: + //MOVE to SR or NOT + break; + case 4: + //EXT, EXTB, LINK.l, NBCD, SWAP, BKPT, PEA, MOVEM + break; + case 5: + //BGND, ILLEGAL, TAS, TST + optype = *istream & 0xFF; + if (optype == 0xFA) { + //BGND - CPU32 only + } else if (optype == 0xFC) { + decoded->op = M68K_ILLEGAL; + } else { + size = (*istream & 0xC0) >> 6; + if (size == OPSIZE_INVALID) { + decoded->op = M68K_TAS; + } else { + decoded->op = M68K_TST; + decoded->extra.size = size; + istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); + } + } + break; + case 6: + //MULU, MULS, DIVU, DIVUL, DIVS, DIVSL, MOVEM + break; + case 7: + //TRAP, LINK.w, UNLNK, MOVE USP, RESET, NOP, STOP, RTE, RTD, RTS, TRAPV, RTR, MOVEC, JSR, JMP + if (*istream & 0x80) { + //JSR, JMP + } else { + //it would appear bit 6 needs to be set for it to be a valid instruction here + switch((*istream >> 3) & 0x7) + { + case 0: + case 1: + //TRAP + break; + case 2: + //LINK.w + break; + case 3: + //UNLNK + break; + case 4: + case 5: + //MOVE USP + break; + case 6: + switch(*istream & 0x7) + { + case 0: + decoded->op = M68K_RESET; + break; + case 1: + decoded->op = M68K_NOP; + break; + case 2: + decoded->op = M68K_STOP; + decoded->extra.size = OPSIZE_WORD; + decoded->src.addr_mode = MODE_IMMEDIATE; + decoded->src.params.u16 =*(++istream); + break; + case 3: + decoded->op = M68K_RTE; + break; + case 4: +#ifdef M68010 + decoded->op = M68K_RTD; + decoded->extra.size = OPSIZE_WORD; + decoded->src.addr_mode = MODE_IMMEDIATE; + decoded->src.params.u16 =*(++istream); +#endif + break; + case 5: + decoded->op = M68K_RTS; + break; + case 6: + decoded->op = M68K_TRAPV; + break; + case 7: + decoded->op = M68K_RTR; + break; + } + break; + case 7: + //MOVEC + break; + } + } + break; + } + } + } break; case QUICK_ARITH_LOOP: size = (*istream >> 6) & 3; if (size == 0x3) { //DBcc, TRAPcc or Scc - decoded->extra.cond = (*istream >> 0x8) & 0xF; + m68k_decode_cond(*istream, decoded); switch ((*istream >> 3) & 0x7) { case 1: //DBcc decoded->op = M68K_DBCC; + decoded->src.addr_mode = MODE_IMMEDIATE; + decoded->src.params.u16 = *(++istream); decoded->dst.addr_mode = MODE_REG; - decoded->dst.regs.pri = *istream & 0x7; + decoded->dst.params.regs.pri = *istream & 0x7; break; case 7: //TRAPcc +#ifdef M68020 decoded->op = M68K_TRAPCC; - decoded->src.addr_mode = MODE_PC_INDIRECT_ABS_IMMED; - decoded->src.regs.pri = MODE_IMMEDIATE; + decoded->src.addr_mode = MODE_IMMEDIATE; //TODO: Figure out what to do with OPMODE and optional extention words +#endif break; default: //Scc decoded->op = M68K_SCC; - M68k_decode_op(*istream, &(decoded->dst)); + istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); break; } } else { //ADDQ, SUBQ decoded->variant = VAR_QUICK; decoded->extra.size = size; - decoded->src.addr_mode = MODE_PC_INDIRECT_ABS_IMMED; - decoded->src.regs.pri = MODE_IMMEDIATE; - immed = (*istream >> 9) & 0x7 + decoded->src.addr_mode = MODE_IMMEDIATE; + istream = m68k_decode_op(istream, size, &(decoded->dst)); + immed = m68K_reg_quick_field(*istream); if (!immed) { immed = 8; } switch (size) { - case OPSIZE_BYTE; + case OPSIZE_BYTE: decoded->src.params.u8 = immed; break; case OPSIZE_WORD: decoded->src.params.u16 = immed; break; case OPSIZE_LONG: - decoded->src.params.u38 = immed; + decoded->src.params.u32 = immed; break; } - if (*istream & 0x10) { + if (*istream & 0x100) { decoded->op = M68K_SUB; } else { decoded->op = M68K_ADD; } } break; + case BRANCH: + m68k_decode_cond(*istream, decoded); + decoded->op = decoded->extra.cond == COND_FALSE ? M68K_BSR : M68K_BCC; + decoded->src.addr_mode = MODE_IMMEDIATE; + immed = *istream & 0xFF; + if (immed == 0) { + decoded->variant = VAR_WORD; + immed = *(++istream); + immed = sign_extend16(immed); + } else if (immed == 0xFF) { + decoded->variant = VAR_LONG; + immed = *(++istream) << 16; + immed |= *(++istream); + } else { + decoded->variant = VAR_BYTE; + immed = sign_extend8(immed); + } + decoded->src.params.u32 = immed; + break; + case MOVEQ: + decoded->op = M68K_MOVE; + decoded->variant = VAR_QUICK; + decoded->src.addr_mode = MODE_IMMEDIATE; + decoded->src.params.u32 = sign_extend8(*istream & 0xFF); + decoded->dst.addr_mode = MODE_REG; + decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); + immed = *istream & 0xFF; + break; + case OR_DIV_SBCD: + //TODO: Implement me + break; + case SUB_SUBX: + size = *istream >> 6 & 0x3; + decoded->op = M68K_SUB; + if (*istream & 0x100) { + // destination, SUBA.l or SUBX + if (*istream & 0x6) { + if (size == OPSIZE_INVALID) { + //SUBA.l + decoded->extra.size = OPSIZE_LONG; + decoded->dst.addr_mode = MODE_AREG; + istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src)); + } else { + decoded->extra.size = size; + decoded->src.addr_mode = MODE_REG; + istream = m68k_decode_op(istream, size, &(decoded->dst)); + } + } else { + //SUBX + decoded->op = M68K_SUBX; + decoded->extra.size = size; + istream = m68k_decode_op(istream, size, &(decoded->src)); + decoded->dst.addr_mode = decoded->src.addr_mode; + decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); + } + } else { + if (size == OPSIZE_INVALID) { + //SUBA.w + decoded->extra.size = OPSIZE_WORD; + decoded->dst.addr_mode = MODE_AREG; + } else { + decoded->extra.size = size; + decoded->dst.addr_mode = MODE_REG; + } + decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); + istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); + } + break; + case RESERVED: + //TODO: implement me + break; + case CMP_XOR: + size = *istream >> 6 & 0x3; + decoded->op = M68K_CMP; + if (*istream & 0x100) { + //CMPM or EOR + istream = m68k_decode_op(istream, size, &(decoded->dst)); + if (decoded->src.addr_mode == MODE_AREG) { + //CMPM + decoded->src.addr_mode = decoded->dst.addr_mode = MODE_AREG_POSTINC; + decoded->src.params.regs.pri = decoded->dst.params.regs.pri; + decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); + } else { + //EOR + decoded->op = M68K_EOR; + decoded->extra.size = size; + decoded->src.addr_mode = MODE_REG; + decoded->src.params.regs.pri = m68K_reg_quick_field(*istream); + } + } else { + //CMP + decoded->extra.size = size; + decoded->dst.addr_mode = MODE_REG; + decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); + istream = m68k_decode_op(istream, size, &(decoded->src)); + } + break; + case AND_MUL_ABCD_EXG: + //page 575 for summary + //EXG opmodes: + //01000 -data regs + //01001 -addr regs + //10001 -one of each + //AND opmodes: + //operand order bit + 2 size bits (00 - 10) + //no address register direct addressing + //data register direct not allowed when is the source (operand order bit of 1) + if (*istream & 0x100) { + if ((*istream & 0xC0) == 0xC0) { + decoded->op = M68K_MULS; + decoded->extra.size = OPSIZE_WORD; + istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); + } else if(!(*istream & 0xF0)) { + decoded->op = M68K_ABCD; + } else if(!(*istream & 0x30)) { + decoded->op = M68K_EXG; + decoded->extra.size = OPSIZE_LONG; + decoded->src.params.regs.pri = m68K_reg_quick_field(*istream); + decoded->dst.params.regs.pri = *istream & 0x7; + if (*istream & 0x8) { + if (*istream & 0x80) { + decoded->src.addr_mode = MODE_REG; + decoded->dst.addr_mode = MODE_AREG; + } else { + decoded->src.addr_mode = decoded->dst.addr_mode = MODE_AREG; + } + } else { + decoded->src.addr_mode = decoded->dst.addr_mode = MODE_REG; + } + } else { + decoded->op = M68K_AND; + decoded->extra.size = (*istream >> 6); + decoded->dst.addr_mode = MODE_REG; + decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); + istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); + } + } else { + if ((*istream & 0xC0) == 0xC0) { + decoded->op = M68K_MULU; + decoded->extra.size = OPSIZE_WORD; + istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); + } else { + decoded->op = M68K_AND; + decoded->extra.size = (*istream >> 6); + decoded->src.addr_mode = MODE_REG; + decoded->src.params.regs.pri = m68K_reg_quick_field(*istream); + istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst)); + } + } + break; + case ADD_ADDX: + size = *istream >> 6 & 0x3; + decoded->op = M68K_ADD; + if (*istream & 0x100) { + // destination, ADDA.l or ADDX + if (*istream & 0x6) { + if (size == OPSIZE_INVALID) { + //ADDA.l + decoded->extra.size = OPSIZE_LONG; + decoded->dst.addr_mode = MODE_AREG; + istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src)); + } else { + decoded->extra.size = size; + decoded->src.addr_mode = MODE_REG; + istream = m68k_decode_op(istream, size, &(decoded->dst)); + } + } else { + //ADDX + decoded->op = M68K_ADDX; + //FIXME: Size is not technically correct + decoded->extra.size = size; + istream = m68k_decode_op(istream, size, &(decoded->src)); + decoded->dst.addr_mode = decoded->src.addr_mode; + decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); + } + } else { + if (size == OPSIZE_INVALID) { + //ADDA.w + decoded->extra.size = OPSIZE_WORD; + decoded->dst.addr_mode = MODE_AREG; + } else { + decoded->extra.size = size; + decoded->dst.addr_mode = MODE_REG; + } + decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); + istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); + } + break; + case SHIFT_ROTATE: + //TODO: Implement me + break; + case COPROC: + //TODO: Implement me + break; + } + return istream+1; +} + +char * mnemonics[] = { + "abcd", + "add", + "addx", + "and", + "andi_ccr", + "andi_sr", + "asl", + "asr", + "bcc", + "bchg", + "bclr", + "bset", + "bsr", + "btst", + "chk", + "clr", + "cmp", + "dbcc", + "divs", + "divu", + "eor", + "eori_ccr", + "eori_sr", + "exg", + "ext", + "illegal", + "jmp", + "jsr", + "lea", + "link", + "lsl", + "lsr", + "move", + "move_ccr", + "move_from_sr", + "move_sr", + "move_usp", + "movem", + "movep", + "muls", + "mulu", + "nbcd", + "neg", + "negx", + "nop", + "not", + "or", + "ori_ccr", + "ori_sr", + "pea", + "reset", + "rol", + "ror", + "roxl", + "roxr", + "rte", + "rtr", + "rts", + "sbcd", + "scc", + "stop", + "sub", + "subx", + "swap", + "tas", + "trap", + "trapv", + "tst", + "unlnk", + "invalid" +}; + +char * cond_mnem[] = { + "ra", + "f", + "hi", + "ls", + "cc", + "cs", + "ne", + "eq", + "vc", + "vs", + "pl", + "mi", + "ge", + "lt", + "gt", + "le" +}; + +int m68K_disasm_op(m68k_op_info *decoded, uint8_t size, char *dst, int need_comma) +{ + char * c = need_comma ? "," : ""; + switch(decoded->addr_mode) + { + case MODE_REG: + return sprintf(dst, "%s d%d", c, decoded->params.regs.pri); + case MODE_AREG: + return sprintf(dst, "%s a%d", c, decoded->params.regs.pri); + case MODE_AREG_INDIRECT: + return sprintf(dst, "%s (a%d)", c, decoded->params.regs.pri); + case MODE_AREG_POSTINC: + return sprintf(dst, "%s (a%d)+", c, decoded->params.regs.pri); + case MODE_AREG_PREDEC: + return sprintf(dst, "%s -(a%d)", c, decoded->params.regs.pri); + case MODE_IMMEDIATE: + return sprintf(dst, "%s #%d", c, size == OPSIZE_LONG ? decoded->params.u32 : (size == OPSIZE_WORD ? decoded->params.u16 : decoded->params.u8)); + default: + return 0; } } + +int m68k_disasm(m68kinst * decoded, char * dst) +{ + int ret,op1len; + uint8_t size; + if (decoded->op == M68K_BCC || decoded->op == M68K_DBCC || decoded->op == M68K_SCC) { + ret = strlen(mnemonics[decoded->op]) - 2; + memcpy(dst, mnemonics[decoded->op], ret); + dst[ret] = 0; + strcat(dst, cond_mnem[decoded->extra.cond]); + ret = strlen(dst); + size = decoded->op = M68K_BCC ? OPSIZE_LONG : OPSIZE_WORD; + } else if (decoded->op == M68K_BSR) { + size = OPSIZE_LONG; + ret = sprintf(dst, "bsr%s", decoded->variant == VAR_BYTE ? ".s" : ""); + } else { + size = decoded->extra.size; + ret = sprintf(dst, "%s%s.%c", + mnemonics[decoded->op], + decoded->variant == VAR_QUICK ? "q" : "", + decoded->extra.size == OPSIZE_BYTE ? 'b' : (size == OPSIZE_WORD ? 'w' : 'l')); + } + op1len = m68K_disasm_op(&(decoded->src), size, dst + ret, 0); + ret += op1len; + ret += m68K_disasm_op(&(decoded->dst), size, dst + ret, op1len); + return ret; +} + diff -r 5a2c1da6dd0f -r 5df303bf72e6 68kinst.h --- a/68kinst.h Sat Nov 03 21:37:38 2012 -0700 +++ b/68kinst.h Sat Nov 03 21:38:28 2012 -0700 @@ -49,7 +49,7 @@ M68K_JMP, M68K_JSR, M68K_LEA, - M68K_LINK + M68K_LINK, M68K_LSL, M68K_LSR, M68K_MOVE, @@ -86,23 +86,29 @@ M68K_SWAP, M68K_TAS, M68K_TRAP, - M68k_TRAPV + M68K_TRAPV, M68K_TST, - M68K_UNLNK + M68K_UNLNK, + M68K_INVALID } m68K_op; typedef enum { VAR_NORMAL, - VAR_QUICK + VAR_QUICK, + VAR_BYTE, + VAR_WORD, + VAR_LONG } m68K_variant; typedef enum { OPSIZE_BYTE=0, OPSIZE_WORD, - OPSIZE_LONG + OPSIZE_LONG, + OPSIZE_INVALID } m68K_opsizes; typedef enum { +//actual addressing mode field values MODE_REG = 0, MODE_AREG, MODE_AREG_INDIRECT, @@ -110,15 +116,34 @@ MODE_AREG_PREDEC, MODE_AREG_DISPLACE, MODE_AREG_INDEX_MEM, //bunch of relatively complicated modes - MODE_PC_INDIRECT_ABS_IMMED //Modes that use the program counter, an absolute address or immediate value + MODE_PC_INDIRECT_ABS_IMMED, //Modes that use the program counter, an absolute address or immediate value +//expanded values + MODE_ABSOLUTE_SHORT, + MODE_ABSOLUTE, + MODE_PC_DISPLACE, + MODE_PC_INDEX, + MODE_IMMEDIATE, + MODE_UNUSED } m68k_addr_modes; typedef enum { - MODE_ABSOLUTE=0, - MODE_PC_DISPLACE, - MODE_PC_INDEX, - MODE_IMMEDIATE -} m68k_addr_extended; + COND_TRUE, + COND_FALSE, + COND_HIGH, + COND_LOW_SAME, + COND_CARRY_CLR, + COND_CARRY_SET, + COND_NOT_EQ, + COND_EQ, + COND_OVERF_CLR, + COND_OVERF_SET, + COND_PLUS, + COND_MINUS, + COND_GREATER_EQ, + COND_LESS, + COND_GREATER, + COND_LESS_EQ +} m68K_condition; typedef struct { uint8_t addr_mode; @@ -147,4 +172,5 @@ uint16_t * m68K_decode(uint16_t * istream, m68kinst * dst); uint32_t m68k_cycles(m68kinst * inst); +int m68K_disasm(m68kinst * decoded, char * dst); diff -r 5a2c1da6dd0f -r 5df303bf72e6 dis.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dis.c Sat Nov 03 21:38:28 2012 -0700 @@ -0,0 +1,33 @@ +#include "68kinst.h" +#include +#include + +int main(int argc, char ** argv) +{ + long filesize; + unsigned short *filebuf; + char disbuf[1024]; + m68kinst instbuf; + unsigned short * cur; + FILE * f = fopen(argv[1], "rb"); + fseek(f, 0, SEEK_END); + filesize = ftell(f); + fseek(f, 0, SEEK_SET); + filebuf = malloc(filesize); + fread(filebuf, 2, filesize/2, f); + fclose(f); + for(cur = filebuf; cur - filebuf < (filesize/2); ++cur) + { + //printf("%x:", *cur); + *cur = (*cur >> 8) | (*cur << 8); + //printf("%x\n", *cur); + } + for(cur = filebuf; (cur - filebuf) < (filesize/2); ++cur) + { + printf("cur: %p: %x\n", cur, *cur); + cur = m68K_decode(cur, &instbuf); + m68k_disasm(&instbuf, disbuf); + puts(disbuf); + } + return 0; +}