Mercurial > repos > blastem
view 68kinst.c @ 3:a4ad0e3e3e0e
Finish mulu.w, muls.w and abcd parameter decoding
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Sat, 03 Nov 2012 22:15:55 -0700 |
parents | 5df303bf72e6 |
children | 6f6a2d7cc889 |
line wrap: on
line source
#include "68kinst.h" #include <string.h> #include <stdio.h> uint32_t sign_extend16(uint32_t val) { return (val & 0x8000) ? val | 0xFFFF0000 : val; } uint32_t sign_extend8(uint32_t val) { 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_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; 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: //TODO: Implement me break; case MOVE_BYTE: case MOVE_LONG: case MOVE_WORD: decoded->op = M68K_MOVE; decoded->extra.size = optype == MOVE_BYTE ? OPSIZE_BYTE : (optype == MOVE_WORD ? OPSIZE_WORD : OPSIZE_LONG); 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: 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 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.params.regs.pri = *istream & 0x7; break; case 7: //TRAPcc #ifdef M68020 decoded->op = M68K_TRAPCC; 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; 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_IMMEDIATE; istream = m68k_decode_op(istream, size, &(decoded->dst)); immed = m68K_reg_quick_field(*istream); if (!immed) { immed = 8; } switch (size) { case OPSIZE_BYTE: decoded->src.params.u8 = immed; break; case OPSIZE_WORD: decoded->src.params.u16 = immed; break; case OPSIZE_LONG: decoded->src.params.u32 = immed; break; } 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) { //<ea> 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 <ea> is the source (operand order bit of 1) if (*istream & 0x100) { if ((*istream & 0xC0) == 0xC0) { decoded->op = M68K_MULS; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); } else if(!(*istream & 0xF0)) { decoded->op = M68K_ABCD; decoded->extra.size = OPSIZE_BYTE; decoded->src.params.regs.pri = *istream & 0x7; decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); decoded->dst.addr_mode = decoded->src.addr_mode = (*istream & 8) ? MODE_AREG_PREDEC : MODE_REG; } 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; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = m68K_reg_quick_field(*istream); 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) { //<ea> 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; }