/* ======================================================================== */
/* =============================== COPYRIGHT ============================== */
/* ======================================================================== */
/*

SPC700 CPU Emulator V0.90

Copyright (c) 2000 Karl Stenerud
All rights reserved.

Permission is granted to use this source code for non-commercial purposes.
To use this code for commercial purposes, you must get permission from the
author (Karl Stenerud) at karl@higashiyama-unet.ocn.ne.jp.


*/


#include "spc700ds.h"
#include <stdio.h>

#include <retro_inline.h>



typedef struct
{
	unsigned char name;
	unsigned char args[2];
} opcode_struct;

enum
{
	IMP , A   , X   , Y   , YA  , SP  , PSW , C   , REL , UPAG, IMM , XI  ,
	XII , YI  , DP  , DPX , DPY , DPI , DXI , DIY , ABS , ABX , ABY , AXI , N0  ,
	N1  , N2  , N3  , N4  , N5  , N6  , N7  , N8  , N9  , N10 , N11 , N12 ,
	N13 , N14 , N15 , DP0 , DP1 , DP2 , DP3 , DP4 , DP5 , DP6 , DP7 , MEMN,
	MEMI
};


enum
{
	ADC   ,  ADDW  ,  AND   ,  AND1  ,  ASL   ,  BBC   ,  BBS   ,  BCC   ,
	BCS   ,  BEQ   ,  BMI   ,  BNE   ,  BPL   ,  BRA   ,  BRK   ,  BVC   ,
	BVS   ,  CALL  ,  CBNE  ,  CLR1  ,  CLRC  ,  CLRP  ,  CLRV  ,  CMP   ,
	CMPW  ,  DAA   ,  DAS   ,  DBNZ  ,  DEC   ,  DECW  ,  DI    ,  DIV   ,
	EI    ,  EOR   ,  EOR1  ,  INC   ,  INCW  ,  JMP   ,  LSR   ,  MOV   ,
	MOV1  ,  MOVW  ,  MUL   ,  NOP   ,  NOT1  ,  NOTQ  ,  NOTC  ,  OR    ,
	OR1   ,  PCALL ,  POP   ,  PUSH  ,  RET   ,  RETI  ,  ROL   ,  ROR   ,
	SBC   ,  SET1  ,  SETC  ,  SETP  ,  SLEEP ,  STOP  ,  SUBW  ,  TCALL ,
	TCLR1 ,  TSET1 ,  XCN
};


static const char* g_opnames[] =
{
	"ADC  ", "ADDW ", "AND  ", "AND1 ", "ASL  ", "BBC  ", "BBS  ", "BCC  ",
	"BCS  ", "BEQ  ", "BMI  ", "BNE  ", "BPL  ", "BRA  ", "BRK  ", "BVC  ",
	"BVS  ", "CALL ", "CBNE ", "CLR1 ", "CLRC ", "CLRP ", "CLRV ", "CMP  ",
	"CMPW ", "DAA  ", "DAS  ", "DBNZ ", "DEC  ", "DECW ", "DI   ", "DIV  ",
	"EI   ", "EOR  ", "EOR1 ", "INC  ", "INCW ", "JMP  ", "LSR  ", "MOV  ",
	"MOV1 ", "MOVW ", "MUL  ", "NOP  ", "NOT1 ", "NOTQ ", "NOTC ", "OR   ",
	"OR1  ", "PCALL", "POP  ", "PUSH ", "RET  ", "RETI ", "ROL  ", "ROR  ",
	"SBC  ", "SET1 ", "SETC ", "SETP ", "SLEEP", "STOP ", "SUBW ", "TCALL",
	"TCLR1", "TSET1", "XCN  "
};

static opcode_struct g_opcodes[256] =
{
/* 00 */ {NOP    , {IMP , IMP }},
/* 01 */ {TCALL  , {N0  , IMP }},
/* 02 */ {SET1   , {DP0 , IMP }},
/* 03 */ {BBS    , {DP0 , REL }},
/* 04 */ {OR     , {A   , DP  }},
/* 05 */ {OR     , {A   , ABS }},
/* 06 */ {OR     , {A   , XI  }},
/* 07 */ {OR     , {A   , DXI }},
/* 08 */ {OR     , {A   , IMM }},
/* 09 */ {OR     , {DP  , DP  }},
/* 0A */ {OR1    , {C   , MEMN}},
/* 0B */ {ASL    , {DP  , IMP }},
/* 0C */ {ASL    , {ABS , IMP }},
/* 0D */ {PUSH   , {PSW , IMP }},
/* 0E */ {TSET1  , {ABS , IMP }},
/* 0F */ {BRK    , {IMM , IMP }},
/* 10 */ {BPL    , {REL , IMP }},
/* 11 */ {TCALL  , {N1  , IMP }},
/* 12 */ {CLR1   , {DP0 , IMP }},
/* 13 */ {BBC    , {DP0 , REL }},
/* 14 */ {OR     , {A   , DPX }},
/* 15 */ {OR     , {A   , ABX }},
/* 16 */ {OR     , {A   , ABY }},
/* 17 */ {OR     , {A   , DIY }},
/* 18 */ {OR     , {DP  , IMM }},
/* 19 */ {OR     , {XI  , YI  }},
/* 1A */ {DECW   , {DP  , IMP }},
/* 1B */ {ASL    , {DPX , IMP }},
/* 1C */ {ASL    , {A   , IMP }},
/* 1D */ {DEC    , {X   , IMP }},
/* 1E */ {CMP    , {X   , ABS }},
/* 1F */ {JMP    , {AXI , IMP }},
/* 20 */ {CLRP   , {IMP , IMP }},
/* 21 */ {TCALL  , {N2  , IMP }},
/* 22 */ {SET1   , {DP1 , IMP }},
/* 23 */ {BBS    , {DP1 , REL }},
/* 24 */ {AND    , {A   , DP  }},
/* 25 */ {AND    , {A   , ABS }},
/* 26 */ {AND    , {A   , XI  }},
/* 27 */ {AND    , {A   , DXI }},
/* 28 */ {AND    , {A   , IMM }},
/* 29 */ {AND    , {DP  , DP  }},
/* 2A */ {OR1    , {C   , MEMI}},
/* 2B */ {ROL    , {DP  , IMP }},
/* 2C */ {ROL    , {ABS , IMP }},
/* 2D */ {PUSH   , {A   , IMP }},
/* 2E */ {CBNE   , {DP  , REL }},
/* 2F */ {BRA    , {REL , IMP }},
/* 30 */ {BMI    , {REL , IMP }},
/* 31 */ {TCALL  , {N3  , IMP }},
/* 32 */ {CLR1   , {DP1 , IMP }},
/* 33 */ {BBC    , {DP1 , REL }},
/* 34 */ {AND    , {A   , DPX }},
/* 35 */ {AND    , {A   , ABX }},
/* 36 */ {AND    , {A   , ABY }},
/* 37 */ {AND    , {A   , DIY }},
/* 38 */ {AND    , {DP  , IMM }},
/* 39 */ {AND    , {XI  , YI  }},
/* 3A */ {INCW   , {DI  , IMP }},
/* 3B */ {ROL    , {DPX , IMP }},
/* 3C */ {ROL    , {A   , IMP }},
/* 3D */ {INC    , {X   , IMP }},
/* 3E */ {CMP    , {X   , DP  }},
/* 3F */ {CALL   , {ABS , IMP }},
/* 40 */ {SETP   , {IMP , IMP }},
/* 41 */ {TCALL  , {N4  , IMP }},
/* 42 */ {SET1   , {DP2 , IMP }},
/* 43 */ {BBS    , {DP2 , REL }},
/* 44 */ {EOR    , {A   , DP  }},
/* 45 */ {EOR    , {A   , ABS }},
/* 46 */ {EOR    , {A   , XI  }},
/* 47 */ {EOR    , {A   , DXI }},
/* 48 */ {EOR    , {A   , IMM }},
/* 49 */ {EOR    , {DP  , DP  }},
/* 4A */ {AND1   , {C   , MEMN}},
/* 4B */ {LSR    , {DP  , IMP }},
/* 4C */ {LSR    , {ABS , IMP }},
/* 4D */ {PUSH   , {X   , IMP }},
/* 4E */ {TCLR1  , {ABS , IMP }},
/* 4F */ {PCALL  , {UPAG, IMP }},
/* 50 */ {BVC    , {REL , IMP }},
/* 51 */ {TCALL  , {N5  , IMP }},
/* 52 */ {CLR1   , {DP2 , IMP }},
/* 53 */ {BBC    , {DP2 , REL }},
/* 54 */ {EOR    , {A   , DPX }},
/* 55 */ {EOR    , {A   , ABX }},
/* 56 */ {EOR    , {A   , ABY }},
/* 57 */ {EOR    , {A   , DIY }},
/* 58 */ {EOR    , {DP  , IMM }},
/* 59 */ {EOR    , {XI  , YI  }},
/* 5A */ {CMPW   , {DP  , IMP }},
/* 5B */ {LSR    , {DPX , IMP }},
/* 5C */ {LSR    , {A   , IMP }},
/* 5D */ {MOV    , {X   , A   }},
/* 5E */ {CMP    , {Y   , ABS }},
/* 5F */ {JMP    , {ABS , IMP }},
/* 60 */ {CLRC   , {IMP , IMP }},
/* 61 */ {TCALL  , {N6  , IMP }},
/* 62 */ {SET1   , {DP3 , IMP }},
/* 63 */ {BBS    , {DP3 , REL }},
/* 64 */ {CMP    , {A   , DP  }},
/* 65 */ {CMP    , {A   , ABS }},
/* 66 */ {CMP    , {A   , XI  }},
/* 67 */ {CMP    , {A   , DXI }},
/* 68 */ {CMP    , {A   , IMM }},
/* 69 */ {CMP    , {DP  , DP  }},
/* 6A */ {AND1   , {C   , MEMI}},
/* 6B */ {ROR    , {DP  , IMP }},
/* 6C */ {ROR    , {ABS , IMP }},
/* 6D */ {PUSH   , {Y   , IMP }},
/* 6E */ {DBNZ   , {DP  , REL }},
/* 6F */ {RET    , {IMP , IMP }},
/* 70 */ {BVS    , {REL , IMP }},
/* 71 */ {TCALL  , {N7  , IMP }},
/* 72 */ {CLR1   , {DP3 , IMP }},
/* 73 */ {BBC    , {DP3 , REL }},
/* 74 */ {CMP    , {A   , DPX }},
/* 75 */ {CMP    , {A   , ABX }},
/* 76 */ {CMP    , {A   , ABY }},
/* 77 */ {CMP    , {A   , DIY }},
/* 78 */ {CMP    , {DP  , IMM }},
/* 79 */ {CMP    , {XI  , YI  }},
/* 7A */ {ADDW   , {DI  , IMP }},
/* 7B */ {ROR    , {DPX , IMP }},
/* 7C */ {ROR    , {A   , IMP }},
/* 7D */ {MOV    , {A   , X   }},
/* 7E */ {CMP    , {Y   , DP  }},
/* 7F */ {RETI   , {IMP , IMP }},
/* 80 */ {SETC   , {IMP , IMP }},
/* 81 */ {TCALL  , {N8  , IMP }},
/* 82 */ {SET1   , {DP4 , IMP }},
/* 83 */ {BBS    , {DP4 , REL }},
/* 84 */ {ADC    , {A   , DP  }},
/* 85 */ {ADC    , {A   , ABS }},
/* 86 */ {ADC    , {A   , XI  }},
/* 87 */ {ADC    , {A   , DXI }},
/* 88 */ {ADC    , {A   , IMM }},
/* 89 */ {ADC    , {DP  , DP  }},
/* 8A */ {EOR1   , {C   , MEMN}},
/* 8B */ {DEC    , {DP  , IMP }},
/* 8C */ {DEC    , {ABS , IMP }},
/* 8D */ {MOV    , {Y   , IMM }},
/* 8E */ {POP    , {PSW , IMP }},
/* 8F */ {MOV    , {DP  , IMM }},
/* 90 */ {BCC    , {REL , IMP }},
/* 91 */ {TCALL  , {N9  , IMP }},
/* 92 */ {CLR1   , {DP4 , IMP }},
/* 93 */ {BBC    , {DP4 , REL }},
/* 94 */ {ADC    , {A   , DPX }},
/* 95 */ {ADC    , {A   , ABX }},
/* 96 */ {ADC    , {A   , ABY }},
/* 97 */ {ADC    , {A   , DIY }},
/* 98 */ {ADC    , {DP  , IMM }},
/* 99 */ {ADC    , {XI  , YI  }},
/* 9A */ {SUBW   , {DP  , IMP }},
/* 9B */ {DEC    , {DPX , IMP }},
/* 9C */ {DEC    , {A   , IMP }},
/* 9D */ {MOV    , {X   , SP  }},
/* 9E */ {DIV    , {YA  , X   }},
/* 9F */ {XCN    , {A   , IMP }},
/* A0 */ {EI     , {IMP , IMP }},
/* A1 */ {TCALL  , {N10 , IMP }},
/* A2 */ {SET1   , {DP5 , IMP }},
/* A3 */ {BBS    , {DP5 , REL }},
/* A4 */ {SBC    , {A   , DP  }},
/* A5 */ {SBC    , {A   , ABS }},
/* A6 */ {SBC    , {A   , XI  }},
/* A7 */ {SBC    , {A   , DXI }},
/* A8 */ {SBC    , {A   , IMM }},
/* A9 */ {SBC    , {DP  , DP  }},
/* AA */ {MOV1   , {C   , MEMN}},
/* AB */ {INC    , {DP  , IMP }},
/* AC */ {INC    , {ABS , IMP }},
/* AD */ {CMP    , {Y   , IMM }},
/* AE */ {POP    , {A   , IMP }},
/* AF */ {MOV    , {XII , A   }},
/* B0 */ {BCS    , {REL , IMP }},
/* B1 */ {TCALL  , {N11 , IMP }},
/* B2 */ {CLR1   , {DP5 , IMP }},
/* B3 */ {BBC    , {DP5 , REL }},
/* B4 */ {SBC    , {A   , DPX }},
/* B5 */ {SBC    , {A   , ABX }},
/* B6 */ {SBC    , {A   , ABY }},
/* B7 */ {SBC    , {A   , DIY }},
/* B8 */ {SBC    , {DP  , IMM }},
/* B9 */ {SBC    , {XI  , YI  }},
/* BA */ {MOVW   , {YA  , DP  }},
/* BB */ {INC    , {DPX , IMP }},
/* BC */ {INC    , {A   , IMP }},
/* BD */ {MOV    , {SP  , X   }},
/* BE */ {DAS    , {A   , IMP }},
/* BF */ {MOV    , {A   , XII }},
/* C0 */ {DI     , {IMP , IMP }},
/* C1 */ {TCALL  , {N12 , IMP }},
/* C2 */ {SET1   , {DP6 , IMP }},
/* C3 */ {BBS    , {DP6 , REL }},
/* C4 */ {MOV    , {DP  , A   }},
/* C5 */ {MOV    , {ABS , A   }},
/* C6 */ {MOV    , {XI  , A   }},
/* C7 */ {MOV    , {DXI , A   }},
/* C8 */ {CMP    , {X   , IMM }},
/* C9 */ {MOV    , {ABS , X   }},
/* CA */ {MOV1   , {MEMN, C   }},
/* CB */ {MOV    , {DP  , Y   }},
/* CC */ {MOV    , {ABS , Y   }},
/* CD */ {MOV    , {X   , IMM }},
/* CE */ {POP    , {X   , IMP }},
/* CF */ {MUL    , {YA  , IMP }},
/* D0 */ {BNE    , {REL , IMP }},
/* D1 */ {TCALL  , {N13 , IMP }},
/* D2 */ {CLR1   , {DP6 , IMP }},
/* D3 */ {BBC    , {DP6 , REL }},
/* D4 */ {MOV    , {DPX , A   }},
/* D5 */ {MOV    , {ABX , A   }},
/* D6 */ {MOV    , {ABY , A   }},
/* D7 */ {MOV    , {DIY , A   }},
/* D8 */ {MOV    , {DP  , X   }},
/* D9 */ {MOV    , {DPY , X   }},
/* DA */ {MOVW   , {DP  , YA  }},
/* DB */ {MOV    , {DPX , Y   }},
/* DC */ {DEC    , {Y   , IMP }},
/* DD */ {MOV    , {A   , Y   }},
/* DE */ {CBNE   , {DPX , REL }},
/* DF */ {DAA    , {IMP , IMP }},
/* E0 */ {CLRV   , {IMP , IMP }},
/* E1 */ {TCALL  , {N14 , IMP }},
/* E2 */ {SET1   , {DP7 , IMP }},
/* E3 */ {BBS    , {DP7 , REL }},
/* E4 */ {MOV    , {A   , DP  }},
/* E5 */ {MOV    , {A   , ABS }},
/* E6 */ {MOV    , {A   , XI  }},
/* E7 */ {MOV    , {A   , DXI }},
/* E8 */ {MOV    , {A   , IMM }},
/* E9 */ {MOV    , {X   , ABS }},
/* EA */ {NOT1   , {IMP , IMP }},
/* EB */ {MOV    , {Y   , DP  }},
/* EC */ {MOV    , {Y   , ABS }},
/* ED */ {NOTC   , {IMP , IMP }},
/* EE */ {POP    , {Y   , IMP }},
/* EF */ {SLEEP  , {IMP , IMP }},
/* F0 */ {BEQ    , {REL , IMP }},
/* F1 */ {TCALL  , {N15 , IMP }},
/* F2 */ {CLR1   , {DP7 , IMP }},
/* F3 */ {BBC    , {DP7 , REL }},
/* F4 */ {MOV    , {A   , DPX }},
/* F5 */ {MOV    , {A   , ABX }},
/* F6 */ {MOV    , {A   , ABY }},
/* F7 */ {MOV    , {A   , DIY }},
/* F8 */ {MOV    , {X   , DP  }},
/* F9 */ {MOV    , {X   , DPY }},
/* FA */ {MOV    , {DP  , DP  }},
/* FB */ {MOV    , {Y   , DPX }},
/* FC */ {INC    , {Y   , IMP }},
/* FD */ {MOV    , {Y   , A   }},
/* FE */ {DBNZ   , {Y   , REL }},
/* FF */ {STOP   , {IMP , IMP }},
};

static unsigned int g_pc;

static INLINE unsigned int read_8_immediate(void)
{
	g_pc++;
	return spc700_read_8_disassembler(g_pc-1);
}

static INLINE unsigned int read_16_immediate(void)
{
	g_pc += 2;
	return	spc700_read_8_disassembler(g_pc-2) |
			(spc700_read_8_disassembler(g_pc-1)<<8);
}

int spc700_disassemble(char* buff, unsigned int pc)
{
	opcode_struct* opcode;
	char* ptr;
	int var;
	int i;

	g_pc = pc;
	opcode = g_opcodes + read_8_immediate();

	sprintf(buff, "%s ", g_opnames[opcode->name]);
	ptr = buff + strlen(buff);

	for(i=0;i<2;i++)
	{
		if(i == 1 && opcode->args[0] != IMP && opcode->args[1] != IMP)
		{
			sprintf(ptr, ",");
			ptr += strlen(ptr);
		}

		switch(opcode->args[i])
		{
			case IMP:  break;
			case A:    sprintf(ptr, "A"); break;
			case X:    sprintf(ptr, "X"); break;
			case Y:    sprintf(ptr, "Y"); break;
			case YA:   sprintf(ptr, "YA"); break;
			case SP:   sprintf(ptr, "SP"); break;
			case PSW:  sprintf(ptr, "PSW"); break;
			case C:    sprintf(ptr, "C"); break;
			case REL:  sprintf(ptr, "%04x", ((g_pc + (char)read_8_immediate())&0xffff)); break;
			case UPAG: sprintf(ptr, "$%02x", read_8_immediate()); break;
			case IMM:  sprintf(ptr, "#$%02x", read_8_immediate()); break;
			case XI:   sprintf(ptr, "(X)"); break;
			case XII:  sprintf(ptr, "(X)+"); break;
			case YI:   sprintf(ptr, "(Y)"); break;
			case DP:   sprintf(ptr, "$%02x", read_8_immediate()); break;
			case DPX:  sprintf(ptr, "$%02x+X", read_8_immediate()); break;
			case DPY:  sprintf(ptr, "$%02x+Y", read_8_immediate()); break;
			case DPI:  sprintf(ptr, "($%02x)", read_8_immediate()); break;
			case DXI:  sprintf(ptr, "($%02x+X)", read_8_immediate()); break;
			case DIY:  sprintf(ptr, "($%02x)+Y", read_8_immediate()); break;
			case ABS:  sprintf(ptr, "$%04x", read_16_immediate()); break;
			case ABX:  sprintf(ptr, "$%04x+X", read_16_immediate()); break;
			case ABY:  sprintf(ptr, "$%04x+Y", read_16_immediate()); break;
			case AXI:  sprintf(ptr, "($%04x+X)", read_16_immediate()); break;
			case N0:   sprintf(ptr, "0"); break;
			case N1:   sprintf(ptr, "1"); break;
			case N2:   sprintf(ptr, "2"); break;
			case N3:   sprintf(ptr, "3"); break;
			case N4:   sprintf(ptr, "4"); break;
			case N5:   sprintf(ptr, "5"); break;
			case N6:   sprintf(ptr, "6"); break;
			case N7:   sprintf(ptr, "7"); break;
			case N8:   sprintf(ptr, "8"); break;
			case N9:   sprintf(ptr, "9"); break;
			case N10:  sprintf(ptr, "10"); break;
			case N11:  sprintf(ptr, "11"); break;
			case N12:  sprintf(ptr, "12"); break;
			case N13:  sprintf(ptr, "13"); break;
			case N14:  sprintf(ptr, "14"); break;
			case N15:  sprintf(ptr, "15"); break;
			case DP0:  sprintf(ptr, "$%02x.0", read_8_immediate()); break;
			case DP1:  sprintf(ptr, "$%02x.1", read_8_immediate()); break;
			case DP2:  sprintf(ptr, "$%02x.2", read_8_immediate()); break;
			case DP3:  sprintf(ptr, "$%02x.3", read_8_immediate()); break;
			case DP4:  sprintf(ptr, "$%02x.4", read_8_immediate()); break;
			case DP5:  sprintf(ptr, "$%02x.5", read_8_immediate()); break;
			case DP6:  sprintf(ptr, "$%02x.6", read_8_immediate()); break;
			case DP7:  sprintf(ptr, "$%02x.7", read_8_immediate()); break;
			case MEMN:
				var = read_16_immediate();
				sprintf(ptr, "%04x.%d", var&0x1fff, var>>13);
				break;
			case MEMI:
				var = read_16_immediate();
				sprintf(ptr, "/%04x.%d", var&0x1fff, var>>13);
				break;
		}
		ptr += strlen(ptr);
	}
	return g_pc - pc;
}
