Mercurial > repos > blastem
view 68kinst.c @ 504:7b0df1aaf384
Add support for left and right shift keys
author | Mike Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 31 Oct 2013 01:00:32 -0700 |
parents | 140af5509ce7 |
children | 775802dab98f |
line wrap: on
line source
/* Copyright 2013 Michael Pavone This file is part of BlastEm. BlastEm is free software distributed under the terms of the GNU General Public License version 3 or greater. See COPYING for full license text. */ #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: #ifdef M68020 //TODO: implement me for M68020+ support #else dst->addr_mode = MODE_AREG_INDEX_DISP8; dst->params.regs.pri = reg; ext = *(++cur); dst->params.regs.sec = ext >> 11;//includes areg/dreg bit, reg num and word/long bit dst->params.regs.displacement = sign_extend8(ext&0xFF); #endif break; case MODE_PC_INDIRECT_ABS_IMMED: switch(reg) { case 0: dst->addr_mode = MODE_ABSOLUTE_SHORT; ext = *(++cur); dst->params.immed = sign_extend16(ext); break; case 1: dst->addr_mode = MODE_ABSOLUTE; ext = *(++cur); dst->params.immed = ext << 16 | *(++cur); break; case 3: #ifdef M68020 //TODO: Implement me for M68020+ support; #else dst->addr_mode = MODE_PC_INDEX_DISP8; ext = *(++cur); dst->params.regs.sec = ext >> 11;//includes areg/dreg bit, reg num and word/long bit dst->params.regs.displacement = sign_extend8(ext&0xFF); #endif 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.immed = ext & 0xFF; break; case OPSIZE_WORD: dst->params.immed = ext; break; case OPSIZE_LONG: dst->params.immed = ext << 16 | *(++cur); break; } break; default: return NULL; } 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, uint32_t address) { uint16_t *start = istream; uint8_t optype = *istream >> 12; uint8_t size; uint8_t reg; uint8_t opmode; uint32_t immed; decoded->op = M68K_INVALID; decoded->src.addr_mode = decoded->dst.addr_mode = MODE_UNUSED; decoded->variant = VAR_NORMAL; decoded->address = address; switch(optype) { case BIT_MOVEP_IMMED: if ((*istream & 0x138) == 0x108) { //MOVEP decoded->op = M68K_MOVEP; decoded->extra.size = *istream & 0x40 ? OPSIZE_LONG : OPSIZE_WORD; if (*istream & 0x80) { //memory dest decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); decoded->dst.addr_mode = MODE_AREG_DISPLACE; decoded->dst.params.regs.pri = *istream & 0x7; decoded->dst.params.regs.displacement = *(++istream); } else { //memory source decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); decoded->src.addr_mode = MODE_AREG_DISPLACE; decoded->src.params.regs.pri = *istream & 0x7; decoded->src.params.regs.displacement = *(++istream); } } else if (*istream & 0x100) { //BTST, BCHG, BCLR, BSET switch ((*istream >> 6) & 0x3) { case 0: decoded->op = M68K_BTST; break; case 1: decoded->op = M68K_BCHG; break; case 2: decoded->op = M68K_BCLR; break; case 3: decoded->op = M68K_BSET; break; } decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); decoded->extra.size = OPSIZE_BYTE; istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } if (decoded->dst.addr_mode == MODE_REG) { decoded->extra.size = OPSIZE_LONG; } } else if ((*istream & 0xF00) == 0x800) { //BTST, BCHG, BCLR, BSET switch ((*istream >> 6) & 0x3) { case 0: decoded->op = M68K_BTST; break; case 1: decoded->op = M68K_BCHG; break; case 2: decoded->op = M68K_BCLR; break; case 3: decoded->op = M68K_BSET; break; } opmode = (*istream >> 3) & 0x7; reg = *istream & 0x7; decoded->src.addr_mode = MODE_IMMEDIATE_WORD; decoded->src.params.immed = *(++istream) & 0xFF; decoded->extra.size = OPSIZE_BYTE; istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } if (decoded->dst.addr_mode == MODE_REG) { decoded->extra.size = OPSIZE_LONG; } } else if ((*istream & 0xC0) == 0xC0) { #ifdef M68020 //CMP2, CHK2, CAS, CAS2, RTM, CALLM #endif } else { switch ((*istream >> 9) & 0x7) { case 0: if ((*istream & 0xFF) == 0x3C) { decoded->op = M68K_ORI_CCR; decoded->extra.size = OPSIZE_BYTE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream) & 0xFF; } else if((*istream & 0xFF) == 0x7C) { decoded->op = M68K_ORI_SR; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream); } else { decoded->op = M68K_OR; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } break; case 1: //ANDI, ANDI to CCR, ANDI to SR if ((*istream & 0xFF) == 0x3C) { decoded->op = M68K_ANDI_CCR; decoded->extra.size = OPSIZE_BYTE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream) & 0xFF; } else if((*istream & 0xFF) == 0x7C) { decoded->op = M68K_ANDI_SR; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream); } else { decoded->op = M68K_AND; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } break; case 2: decoded->op = M68K_SUB; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } break; case 3: decoded->op = M68K_ADD; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } break; case 4: //BTST, BCHG, BCLR, BSET switch ((*istream >> 6) & 0x3) { case 0: decoded->op = M68K_BTST; break; case 1: decoded->op = M68K_BCHG; break; case 2: decoded->op = M68K_BCLR; break; case 3: decoded->op = M68K_BSET; break; } decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream) & 0xFF; istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } break; case 5: //EORI, EORI to CCR, EORI to SR if ((*istream & 0xFF) == 0x3C) { decoded->op = M68K_EORI_CCR; decoded->extra.size = OPSIZE_BYTE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream) & 0xFF; } else if((*istream & 0xFF) == 0x7C) { decoded->op = M68K_EORI_SR; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *(++istream); } else { decoded->op = M68K_EOR; decoded->variant = VAR_IMMEDIATE; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = size = (*istream >> 6) & 3; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = immed << 16 | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } break; case 6: decoded->op = M68K_CMP; decoded->variant = VAR_IMMEDIATE; decoded->extra.size = (*istream >> 6) & 0x3; decoded->src.addr_mode = MODE_IMMEDIATE; reg = *istream & 0x7; opmode = (*istream >> 3) & 0x7; switch (decoded->extra.size) { case OPSIZE_BYTE: decoded->src.params.immed = *(++istream) & 0xFF; break; case OPSIZE_WORD: decoded->src.params.immed = *(++istream); break; case OPSIZE_LONG: immed = *(++istream); decoded->src.params.immed = (immed << 16) | *(++istream); break; } istream = m68k_decode_op_ex(istream, opmode, reg, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } break; case 7: break; } } 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); opmode = (*istream >> 6) & 0x7; reg = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, decoded->extra.size, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); if (!istream || decoded->dst.addr_mode == MODE_IMMEDIATE) { decoded->op = M68K_INVALID; return start+1; } 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else { if (*istream & 0x100) { decoded->op = M68K_CHK; if ((*istream & 0x180) == 0x180) { decoded->extra.size = OPSIZE_WORD; } else { //only on M68020+ #ifdef M68020 decoded->extra.size = OPSIZE_LONG; #else decoded->op = M68K_INVALID; break; #endif } 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else { opmode = (*istream >> 3) & 0x7; if ((*istream & 0xB80) == 0x880 && opmode != MODE_REG && opmode != MODE_AREG) { //TODO: Check for invalid modes that are dependent on direction decoded->op = M68K_MOVEM; decoded->extra.size = *istream & 0x40 ? OPSIZE_LONG : OPSIZE_WORD; reg = *istream & 0x7; if(*istream & 0x400) { decoded->dst.addr_mode = MODE_REG; decoded->dst.params.immed = *(++istream); istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } if (decoded->src.addr_mode == MODE_PC_DISPLACE || decoded->src.addr_mode == MODE_PC_INDEX_DISP8) { //adjust displacement to account for extra instruction word decoded->src.params.regs.displacement += 2; } } else { decoded->src.addr_mode = MODE_REG; decoded->src.params.immed = *(++istream); istream = m68k_decode_op_ex(istream, opmode, reg, decoded->extra.size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } } else { optype = (*istream >> 9) & 0x7; size = (*istream >> 6) & 0x3; switch(optype) { case 0: //Move from SR or NEGX if (size == OPSIZE_INVALID) { decoded->op = M68K_MOVE_FROM_SR; size = OPSIZE_WORD; } else { decoded->op = M68K_NEGX; } decoded->extra.size = size; istream= m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } break; case 1: //MOVE from CCR or CLR if (size == OPSIZE_INVALID) { #ifdef M68010 decoded->op = M68K_MOVE_FROM_CCR; size = OPSIZE_WORD; #else return istream+1; #endif } else { decoded->op = M68K_CLR; } decoded->extra.size = size; istream= m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } break; case 2: //MOVE to CCR or NEG if (size == OPSIZE_INVALID) { decoded->op = M68K_MOVE_CCR; size = OPSIZE_WORD; istream= m68k_decode_op(istream, size, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else { decoded->op = M68K_NEG; istream= m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } decoded->extra.size = size; break; case 3: //MOVE to SR or NOT if (size == OPSIZE_INVALID) { decoded->op = M68K_MOVE_SR; size = OPSIZE_WORD; istream= m68k_decode_op(istream, size, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else { decoded->op = M68K_NOT; istream= m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } decoded->extra.size = size; break; case 4: //EXT, EXTB, LINK.l, NBCD, SWAP, BKPT, PEA switch((*istream >> 3) & 0x3F) { case 1: #ifdef M68020 decoded->op = M68K_LINK; decoded->extra.size = OPSIZE_LONG; reg = *istream & 0x7; immed = *(++istream) << 16; immed |= *(++istream); #endif break; case 8: decoded->op = M68K_SWAP; decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = *istream & 0x7; decoded->extra.size = OPSIZE_WORD; break; case 9: #ifdef M68010 decoded->op = M68K_BKPT; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->extra.size = OPSIZE_UNSIZED; decoded->src.params.immed = *istream & 0x7; #endif break; case 0x10: decoded->op = M68K_EXT; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = *istream & 0x7; decoded->extra.size = OPSIZE_WORD; break; case 0x18: decoded->op = M68K_EXT; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = *istream & 0x7; decoded->extra.size = OPSIZE_LONG; break; case 0x38: #ifdef M68020 #endif break; default: if (!(*istream & 0x1C0)) { decoded->op = M68K_NBCD; decoded->extra.size = OPSIZE_BYTE; istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else if((*istream & 0x1C0) == 0x40) { decoded->op = M68K_PEA; decoded->extra.size = OPSIZE_LONG; istream = m68k_decode_op(istream, OPSIZE_LONG, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } } break; case 5: //BGND, ILLEGAL, TAS, TST optype = *istream & 0xFF; if (optype == 0xFA) { //BGND - CPU32 only } else if (optype == 0xFC) { decoded->op = M68K_ILLEGAL; decoded->extra.size = OPSIZE_UNSIZED; } else { 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } } break; case 6: //MULU, MULS, DIVU, DIVUL, DIVS, DIVSL #ifdef M68020 //TODO: Implement these for 68020+ support #endif 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 if (*istream & 0x40) { decoded->op = M68K_JMP; } else { decoded->op = M68K_JSR; } decoded->extra.size = OPSIZE_UNSIZED; istream = m68k_decode_op(istream, OPSIZE_UNSIZED, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } 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 decoded->op = M68K_TRAP; decoded->extra.size = OPSIZE_UNSIZED; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = *istream & 0xF; break; case 2: //LINK.w decoded->op = M68K_LINK; decoded->extra.size = OPSIZE_WORD; decoded->src.addr_mode = MODE_AREG; decoded->src.params.regs.pri = *istream & 0x7; decoded->dst.addr_mode = MODE_IMMEDIATE; decoded->dst.params.immed = sign_extend16(*(++istream)); break; case 3: //UNLK decoded->op = M68K_UNLK; decoded->extra.size = OPSIZE_UNSIZED; decoded->dst.addr_mode = MODE_AREG; decoded->dst.params.regs.pri = *istream & 0x7; break; case 4: case 5: //MOVE USP decoded->op = M68K_MOVE_USP; if (*istream & 0x8) { decoded->dst.addr_mode = MODE_AREG; decoded->dst.params.regs.pri = *istream & 0x7; } else { decoded->src.addr_mode = MODE_AREG; decoded->src.params.regs.pri = *istream & 0x7; } break; case 6: decoded->extra.size = OPSIZE_UNSIZED; switch(*istream & 0x7) { case 0: decoded->op = M68K_RESET; break; case 1: decoded->op = M68K_NOP; break; case 2: decoded->op = M68K_STOP; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed =*(++istream); break; case 3: decoded->op = M68K_RTE; break; case 4: #ifdef M68010 decoded->op = M68K_RTD; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed =*(++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 #ifdef M68010 #endif break; } } break; } } } } break; case QUICK_ARITH_LOOP: size = (*istream >> 6) & 3; if (size == 0x3) { //DBcc, TRAPcc or Scc m68k_decode_cond(*istream, decoded); if (((*istream >> 3) & 0x7) == 1) { decoded->op = M68K_DBCC; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = *istream & 0x7; decoded->src.params.immed = sign_extend16(*(++istream)); } else if(((*istream >> 3) & 0x7) == 1 && (*istream & 0x7) > 1 && (*istream & 0x7) < 5) { #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 } else { decoded->op = M68K_SCC; decoded->extra.cond = (*istream >> 8) & 0xF; istream = m68k_decode_op(istream, OPSIZE_BYTE, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } } else { //ADDQ, SUBQ decoded->variant = VAR_QUICK; decoded->extra.size = size; decoded->src.addr_mode = MODE_IMMEDIATE; immed = m68k_reg_quick_field(*istream); if (!immed) { immed = 8; } decoded->src.params.immed = immed; if (*istream & 0x100) { decoded->op = M68K_SUB; } else { decoded->op = M68K_ADD; } istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } 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); #ifdef M68020 } else if (immed == 0xFF) { decoded->variant = VAR_LONG; immed = *(++istream) << 16; immed |= *(++istream); #endif } else { decoded->variant = VAR_BYTE; immed = sign_extend8(immed); } decoded->src.params.immed = immed; break; case MOVEQ: if (*istream & 0x100) { decoded->op = M68K_INVALID; return start+1; } decoded->op = M68K_MOVE; decoded->variant = VAR_QUICK; decoded->extra.size = OPSIZE_LONG; decoded->src.addr_mode = MODE_IMMEDIATE; decoded->src.params.immed = 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: //for OR, if opmode bit 2 is 1, then src = Dn, dst = <ea> opmode = (*istream >> 6) & 0x7; size = opmode & 0x3; if (size == OPSIZE_INVALID || (opmode & 0x4 && !(*istream & 0x30))) { switch(opmode) { case 3: decoded->op = M68K_DIVU; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; return start+1; } break; case 4: decoded->op = M68K_SBCD; decoded->dst.addr_mode = decoded->src.addr_mode = *istream & 0x8 ? MODE_AREG_PREDEC : MODE_REG; decoded->src.params.regs.pri = *istream & 0x7; decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; break; case 5: #ifdef M68020 #endif break; case 6: #ifdef M68020 #endif break; case 7: decoded->op = M68K_DIVS; decoded->extra.size = OPSIZE_WORD; decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->src)); if (!istream || decoded->src.addr_mode == MODE_AREG) { decoded->op = M68K_INVALID; return start+1; } break; } } else { decoded->op = M68K_OR; decoded->extra.size = size; if (opmode & 0x4) { decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = (*istream >> 9) & 0x7; istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else { decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = (*istream >> 9) & 0x7; istream = m68k_decode_op(istream, size, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } } break; case SUB_SUBX: size = (*istream >> 6) & 0x3; decoded->op = M68K_SUB; if (*istream & 0x100) { //<ea> destination, SUBA.l or SUBX if (*istream & 0x30 || size == OPSIZE_INVALID) { if (size == OPSIZE_INVALID) { //SUBA.l 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, OPSIZE_LONG, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else { decoded->extra.size = size; decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } } else { //SUBX decoded->op = M68K_SUBX; decoded->extra.size = size; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); decoded->src.params.regs.pri = *istream & 0x7; if (*istream & 0x8) { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_AREG_PREDEC; } else { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_REG; } } } 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } break; case RESERVED: break; case CMP_XOR: size = (*istream >> 6) & 0x3; decoded->op = M68K_CMP; if (*istream & 0x100) { //CMPM or CMPA.l or EOR if (size == OPSIZE_INVALID) { 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else { reg = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } decoded->extra.size = size; if (decoded->dst.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 = reg; } else { //EOR decoded->op = M68K_EOR; decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = reg; } } } else { //CMP or CMPA.w if (size == OPSIZE_INVALID) { 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } 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) & 0x3; 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } } 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else { decoded->op = M68K_AND; decoded->extra.size = (*istream >> 6) & 0x3; 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } } break; case ADD_ADDX: size = (*istream >> 6) & 0x3; decoded->op = M68K_ADD; if (*istream & 0x100) { //<ea> destination, ADDA.l or ADDX if (*istream & 0x30 || size == OPSIZE_INVALID) { if (size == OPSIZE_INVALID) { //ADDA.l 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, OPSIZE_LONG, &(decoded->src)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else { decoded->extra.size = size; decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = m68k_reg_quick_field(*istream); istream = m68k_decode_op(istream, size, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } } else { //ADDX decoded->op = M68K_ADDX; decoded->extra.size = size; decoded->dst.params.regs.pri = m68k_reg_quick_field(*istream); decoded->src.params.regs.pri = *istream & 0x7; if (*istream & 0x8) { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_AREG_PREDEC; } else { decoded->dst.addr_mode = decoded->src.addr_mode = MODE_REG; } } } 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)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } break; case SHIFT_ROTATE: if ((*istream & 0x8C0) == 0xC0) { switch((*istream >> 8) & 0x7) { case 0: decoded->op = M68K_ASR; break; case 1: decoded->op = M68K_ASL; break; case 2: decoded->op = M68K_LSR; break; case 3: decoded->op = M68K_LSL; break; case 4: decoded->op = M68K_ROXR; break; case 5: decoded->op = M68K_ROXL; break; case 6: decoded->op = M68K_ROR; break; case 7: decoded->op = M68K_ROL; break; } decoded->extra.size = OPSIZE_WORD; istream = m68k_decode_op(istream, OPSIZE_WORD, &(decoded->dst)); if (!istream) { decoded->op = M68K_INVALID; return start+1; } } else if((*istream & 0xC0) != 0xC0) { switch(((*istream >> 2) & 0x6) | ((*istream >> 8) & 1)) { case 0: decoded->op = M68K_ASR; break; case 1: decoded->op = M68K_ASL; break; case 2: decoded->op = M68K_LSR; break; case 3: decoded->op = M68K_LSL; break; case 4: decoded->op = M68K_ROXR; break; case 5: decoded->op = M68K_ROXL; break; case 6: decoded->op = M68K_ROR; break; case 7: decoded->op = M68K_ROL; break; } decoded->extra.size = (*istream >> 6) & 0x3; immed = (*istream >> 9) & 0x7; if (*istream & 0x20) { decoded->src.addr_mode = MODE_REG; decoded->src.params.regs.pri = immed; } else { decoded->src.addr_mode = MODE_IMMEDIATE; if (!immed) { immed = 8; } decoded->src.params.immed = immed; } decoded->dst.addr_mode = MODE_REG; decoded->dst.params.regs.pri = *istream & 0x7; } else { #ifdef M68020 //TODO: Implement bitfield instructions for M68020+ support #endif } 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", "unlk", "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, char *dst, int need_comma, uint8_t labels, uint32_t address) { 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_AREG_DISPLACE: return sprintf(dst, "%s (%d, a%d)", c, decoded->params.regs.displacement, decoded->params.regs.pri); case MODE_AREG_INDEX_DISP8: return sprintf(dst, "%s (%d, a%d, %c%d.%c)", c, decoded->params.regs.displacement, decoded->params.regs.pri, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w'); case MODE_IMMEDIATE: case MODE_IMMEDIATE_WORD: return sprintf(dst, (decoded->params.immed <= 128 ? "%s #%d" : "%s #$%X"), c, decoded->params.immed); case MODE_ABSOLUTE_SHORT: if (labels) { return sprintf(dst, "%s ADR_%X.w", c, decoded->params.immed); } else { return sprintf(dst, "%s $%X.w", c, decoded->params.immed); } case MODE_ABSOLUTE: if (labels) { return sprintf(dst, "%s ADR_%X.l", c, decoded->params.immed); } else { return sprintf(dst, "%s $%X", c, decoded->params.immed); } case MODE_PC_DISPLACE: if (labels) { return sprintf(dst, "%s ADR_%X(pc)", c, address + 2 + decoded->params.regs.displacement); } else { return sprintf(dst, "%s (%d, pc)", c, decoded->params.regs.displacement); } case MODE_PC_INDEX_DISP8: return sprintf(dst, "%s (%d, pc, %c%d.%c)", c, decoded->params.regs.displacement, (decoded->params.regs.sec & 0x10) ? 'a': 'd', (decoded->params.regs.sec >> 1) & 0x7, (decoded->params.regs.sec & 1) ? 'l': 'w'); default: return 0; } } int m68k_disasm_movem_op(m68k_op_info *decoded, m68k_op_info *other, char *dst, int need_comma, uint8_t labels, uint32_t address) { int8_t dir, reg, bit, regnum, last=-1, lastreg, first=-1; char *rtype, *last_rtype; int oplen; if (decoded->addr_mode == MODE_REG) { if (other->addr_mode == MODE_AREG_PREDEC) { bit = 15; dir = -1; } else { dir = 1; bit = 0; } if (need_comma) { strcat(dst, ", "); oplen = 2; } else { strcat(dst, " "); oplen = 1; } for (reg=0; bit < 16 && bit > -1; bit += dir, reg++) { if (decoded->params.immed & (1 << bit)) { if (reg > 7) { rtype = "a"; regnum = reg - 8; } else { rtype = "d"; regnum = reg; } if (last >= 0 && last == regnum - 1 && lastreg == reg - 1) { last = regnum; lastreg = reg; } else if(last >= 0) { if (first != last) { oplen += sprintf(dst + oplen, "-%s%d/%s%d",last_rtype, last, rtype, regnum); } else { oplen += sprintf(dst + oplen, "/%s%d", rtype, regnum); } first = last = regnum; last_rtype = rtype; lastreg = reg; } else { oplen += sprintf(dst + oplen, "%s%d", rtype, regnum); first = last = regnum; last_rtype = rtype; lastreg = reg; } } } if (last >= 0 && last != first) { oplen += sprintf(dst + oplen, "-%s%d", last_rtype, last); } return oplen; } else { return m68k_disasm_op(decoded, dst, need_comma, labels, address); } } int m68k_disasm_ex(m68kinst * decoded, char * dst, uint8_t labels) { int ret,op1len; uint8_t size; char * special_op = "CCR"; switch (decoded->op) { case M68K_BCC: case M68K_DBCC: case M68K_SCC: ret = strlen(mnemonics[decoded->op]) - 2; memcpy(dst, mnemonics[decoded->op], ret); dst[ret] = 0; strcpy(dst+ret, cond_mnem[decoded->extra.cond]); ret = strlen(dst); if (decoded->op != M68K_SCC) { if (labels) { if (decoded->op == M68K_DBCC) { ret += sprintf(dst+ret, " d%d, ADR_%X", decoded->dst.params.regs.pri, decoded->address + 2 + decoded->src.params.immed); } else { ret += sprintf(dst+ret, " ADR_%X", decoded->address + 2 + decoded->src.params.immed); } } else { if (decoded->op == M68K_DBCC) { ret += sprintf(dst+ret, " d%d, #%d <%X>", decoded->dst.params.regs.pri, decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed); } else { ret += sprintf(dst+ret, " #%d <%X>", decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed); } } return ret; } break; case M68K_BSR: if (labels) { ret = sprintf(dst, "bsr%s ADR_%X", decoded->variant == VAR_BYTE ? ".s" : "", decoded->address + 2 + decoded->src.params.immed); } else { ret = sprintf(dst, "bsr%s #%d <%X>", decoded->variant == VAR_BYTE ? ".s" : "", decoded->src.params.immed, decoded->address + 2 + decoded->src.params.immed); } return ret; case M68K_MOVE_FROM_SR: ret = sprintf(dst, "%s", mnemonics[decoded->op]); ret += sprintf(dst + ret, " SR"); ret += m68k_disasm_op(&(decoded->dst), dst + ret, 1, labels, decoded->address); return ret; case M68K_ANDI_SR: case M68K_EORI_SR: case M68K_MOVE_SR: case M68K_ORI_SR: special_op = "SR"; case M68K_ANDI_CCR: case M68K_EORI_CCR: case M68K_MOVE_CCR: case M68K_ORI_CCR: ret = sprintf(dst, "%s", mnemonics[decoded->op]); ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address); ret += sprintf(dst + ret, ", %s", special_op); return ret; case M68K_MOVE_USP: ret = sprintf(dst, "%s", mnemonics[decoded->op]); if (decoded->src.addr_mode != MODE_UNUSED) { ret += m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address); ret += sprintf(dst + ret, ", USP"); } else { ret += sprintf(dst + ret, "USP, "); ret += m68k_disasm_op(&(decoded->dst), dst + ret, 0, labels, decoded->address); } return ret; default: size = decoded->extra.size; ret = sprintf(dst, "%s%s%s", mnemonics[decoded->op], decoded->variant == VAR_QUICK ? "q" : (decoded->variant == VAR_IMMEDIATE ? "i" : ""), size == OPSIZE_BYTE ? ".b" : (size == OPSIZE_WORD ? ".w" : (size == OPSIZE_LONG ? ".l" : ""))); } if (decoded->op == M68K_MOVEM) { op1len = m68k_disasm_movem_op(&(decoded->src), &(decoded->dst), dst + ret, 0, labels, decoded->address); ret += op1len; ret += m68k_disasm_movem_op(&(decoded->dst), &(decoded->src), dst + ret, op1len, labels, decoded->address); } else { op1len = m68k_disasm_op(&(decoded->src), dst + ret, 0, labels, decoded->address); ret += op1len; ret += m68k_disasm_op(&(decoded->dst), dst + ret, op1len, labels, decoded->address); } return ret; } int m68k_disasm(m68kinst * decoded, char * dst) { return m68k_disasm_ex(decoded, dst, 0); } int m68k_disasm_labels(m68kinst * decoded, char * dst) { return m68k_disasm_ex(decoded, dst, 1); }