view 68kinst.c @ 465:dc322bc77ea2

Fix stateview. Update "all" target in Makefile.
author Mike Pavone <pavone@retrodev.com>
date Tue, 10 Sep 2013 21:07:13 -0700
parents 00d5a2b532f4
children 140af5509ce7
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:
		#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);
}