/*	
 *	clipssmi.c
 *
 *	P/ECE S-SMP (SPC700) Emulator
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2004 Naoyuki Sawa
 *
 *	* Sun Jan 04 21:33:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Sun Oct 10 20:31q:00 JST 2004 Naoyuki Sawa
 *	- TSET1/TCLR1を修正しました。
 *	  TSET1/TCLR1を使っているSPCファイルがすぐに見つからなかったので、
 *	  今回の修正による挙動の違いをまだ確認していません。
 */
#include "clip.h"
#include "clipssmi.h"	/*SSMPエミュレータ実装用マクロ*/

/****************************************************************************
 *
 ****************************************************************************/

/* 無効命令を処理します。
 */
SSMPFN_(ERROR)
{
	DIE();
}

/****************************************************************************
 *
 ****************************************************************************/

#ifndef SSMP_ASM

unsigned char ssmp_ADC(SSMP* ssmp, unsigned char a, unsigned char b) {
	int c = PSW & 1;
	int tmp = a + b + c;
	PSW = (PSW & ~0xcb) |
	      (tmp & 0x80) |				/* N */
	      ((~(a ^ b) & (a ^ tmp) & 0x80) >> 1) |	/* V */
	      (((a & 0xf) + (b & 0xf) + c) >> 1 & 8) |	/* H */
	      (!(tmp & 0xff) << 1) |			/* Z */
	      ((tmp >> 8) & 1);				/* C */
	return (unsigned char)tmp;
}

unsigned char ssmp_SBC(SSMP* ssmp, unsigned char a, unsigned char b) {
	int c = ~PSW & 1;
	int tmp = a - b - c;
	PSW = (PSW & ~0xcb) |
	      (tmp & 0x80) |				/* N */
	      (((a ^ b) & (a ^ tmp) & 0x80) >> 1) |	/* V */
	      (((a & 0xf) - (b & 0xf) - c) >> 1 & 8) |	/* H */
	      (!(tmp & 0xff) << 1) |			/* Z */
	      (~(tmp >> 8) & 1);			/* C */
	return (unsigned char)tmp;
}

void ssmp_CMP(SSMP* ssmp, unsigned char a, unsigned char b) {
	int tmp = a - b;
	PSW = (PSW & ~0x83) |
	      (tmp & 0x80) |				/* N */
	      (!tmp << 1) |				/* Z */
	      (~(tmp >> 8) & 1);			/* C */
}

unsigned char ssmp_AND(SSMP* ssmp, unsigned char a, unsigned char b) {
	a &= b;
	TEST(a);
	return a;
}

unsigned char ssmp_OR(SSMP* ssmp, unsigned char a, unsigned char b) {
	a |= b;
	TEST(a);
	return a;
}

unsigned char ssmp_EOR(SSMP* ssmp, unsigned char a, unsigned char b) {
	a ^= b;
	TEST(a);
	return a;
}

unsigned char ssmp_INC(SSMP* ssmp, unsigned char a) {
	a++;
	TEST(a);
	return a;
}

unsigned char ssmp_DEC(SSMP* ssmp, unsigned char a) {
	a--;
	TEST(a);
	return a;
}

#endif /*SSMP_ASM*/

unsigned char ssmp_ASL(SSMP* ssmp, unsigned char a) {
	unsigned char tmp = a << 1;
	PSW = (PSW & ~0x83) |
	      (tmp & 0x80) |		/* N */
	      (!tmp << 1) |		/* Z */
	      (a >> 7);			/* C */
	return tmp; 
}

unsigned char ssmp_LSR(SSMP* ssmp, unsigned char a) {
	unsigned char tmp = a >> 1;
	PSW = (PSW & ~0x83) |
					/* N */
	      (!tmp << 1) |		/* Z */
	      (a & 1);			/* C */
	return tmp; 
}

unsigned char ssmp_ROL(SSMP* ssmp, unsigned char a) {
	unsigned char tmp = (a << 1) | (PSW & 1);
	PSW = (PSW & ~0x83) |
	      (tmp & 0x80) |		/* N */
	      (!tmp << 1) |		/* Z */
	      (a >> 7);			/* C */
	return tmp; 
}

unsigned char ssmp_ROR(SSMP* ssmp, unsigned char a) {
	unsigned char tmp = (a >> 1) | (PSW << 7);
	PSW = (PSW & ~0x83) |
	      (tmp & 0x80) |		/* N */
	      (!tmp << 1) |		/* Z */
	      (a & 1);			/* C */
	return tmp; 
}

unsigned short ssmp_ADDW(SSMP* ssmp, unsigned short a, unsigned short b) {
	int tmp = a + b;
	PSW = (PSW & ~0xcb) |
	      ((tmp >> 8) & 0x80) |			/* N */
	      ((~(a ^ b) & (a ^ tmp) & 0x8000) >> 9) |	/* V */
	      /* ※TODO:仕様が不明です */		/* H */
	      (!(tmp & 0xffff) << 1) |			/* Z */
	      ((tmp >> 16) & 1);			/* C */
	return (unsigned short)tmp;
}

unsigned short ssmp_SUBW(SSMP* ssmp, unsigned short a, unsigned short b) {
	int tmp = a - b;
	PSW = (PSW & ~0xcb) |
	      ((tmp >> 8) & 0x80) |			/* N */
	      (((a ^ b) & (a ^ tmp) & 0x8000) >> 9) |	/* V */
	      /* ※TODO:仕様が不明です */		/* H */
	      (!(tmp & 0xffff) << 1) |			/* Z */
	      (~(tmp >> 16) & 1);			/* C */
	return (unsigned short)tmp;
}

/* ※「APU MANUAL IN TXT BY LEDI. (1997)」(spc700_apu_manual.txt)には、フラグ変化はPSW(N,Z)のみと書いてありますが、
 *   既存のプレイヤー(WinSPC,OpenSPC)のソースを見ると、PSW(C)も変化させるのが正しいようです。
 */
void ssmp_CMPW(SSMP* ssmp, unsigned short a, unsigned short b) {
	int tmp = a - b;
	PSW = (PSW & ~0x83) |
	      ((tmp >> 8) & 0x80) |			/* N */
	      (!(tmp & 0xffff) << 1) |			/* Z */
	      (~(tmp >> 16) & 1);			/* C */
}

unsigned short ssmp_INCW(SSMP* ssmp, unsigned short a) {
	a++;
	TESTW(a);
	return a;
}

unsigned short ssmp_DECW(SSMP* ssmp, unsigned short a) {
	a--;
	TESTW(a);
	return a;
}

/****************************************************************************
 *	8-BIT DATA TRANSMISSION COMMANDS GROUP 1
 ****************************************************************************/

#ifndef SSMP_ASM

SSMPFN_(MOV_A_DATA)    { A = arg1; TEST(A); }
SSMPFN_(MOV_X_DATA)    { X = arg1; TEST(X); }
SSMPFN_(MOV_Y_DATA)    { Y = arg1; TEST(Y); }
SSMPFN_(MOV_A_ADDRESS) { A = READ(arg1); TEST(A); }
SSMPFN_(MOV_X_ADDRESS) { X = READ(arg1); TEST(X); }
SSMPFN_(MOV_Y_ADDRESS) { Y = READ(arg1); TEST(Y); }

#endif /*SSMP_ASM*/

/****************************************************************************
 *	8-BIT DATA TRANSMISSION COMMANDS GROUP 2
 ****************************************************************************/

#ifndef SSMP_ASM

SSMPFN_(MOV_ADDRESS_A) { WRITE(arg1, A); }
SSMPFN_(MOV_ADDRESS_X) { WRITE(arg1, X); }
SSMPFN_(MOV_ADDRESS_Y) { WRITE(arg1, Y); }

#endif /*SSMP_ASM*/

/****************************************************************************
 *	8-BIT DATA TRANSMISSION COMMANDS GROUP 3
 ****************************************************************************/

#ifndef SSMP_ASM

SSMPFN_(MOV_A_X)  { A = X;  TEST(A); }
SSMPFN_(MOV_A_Y)  { A = Y;  TEST(A); }
SSMPFN_(MOV_X_A)  { X = A;  TEST(X); }
SSMPFN_(MOV_Y_A)  { Y = A;  TEST(Y); }
SSMPFN_(MOV_X_SP) { X = SP; TEST(X); }
SSMPFN_(MOV_SP_X) { SP = X; }
SSMPFN_(MOV_ADDRESS_DATA   ) { WRITE(arg1,      arg2 ); }
SSMPFN_(MOV_ADDRESS_ADDRESS) { WRITE(arg1, READ(arg2)); }

#endif /*SSMP_ASM*/

/****************************************************************************
 *	8-BIT ARITHMETIC OPERATION COMMANDS
 ****************************************************************************/

#ifndef SSMP_ASM

SSMPFN_(ADC_A_DATA) { A = ADC(A, arg1 ); }
SSMPFN_(SBC_A_DATA) { A = SBC(A, arg1 ); }
SSMPFN_(CMP_A_DATA) { CMP(A, arg1); }
SSMPFN_(CMP_X_DATA) { CMP(X, arg1); }
SSMPFN_(CMP_Y_DATA) { CMP(Y, arg1); }

SSMPFN_(ADC_A_ADDRESS) { A = ADC(A, READ(arg1)); }
SSMPFN_(SBC_A_ADDRESS) { A = SBC(A, READ(arg1)); }
SSMPFN_(CMP_A_ADDRESS) { CMP(A, READ(arg1)); }
SSMPFN_(CMP_X_ADDRESS) { CMP(X, READ(arg1)); }
SSMPFN_(CMP_Y_ADDRESS) { CMP(Y, READ(arg1)); }

SSMPFN_(ADC_ADDRESS_DATA) { WRITE(arg1, ADC(READ(arg1), arg2 )); }
SSMPFN_(SBC_ADDRESS_DATA) { WRITE(arg1, SBC(READ(arg1), arg2 )); }
SSMPFN_(CMP_ADDRESS_DATA) { CMP(READ(arg1), arg2); }

SSMPFN_(ADC_ADDRESS_ADDRESS) { WRITE(arg1, ADC(READ(arg1), READ(arg2))); }
SSMPFN_(SBC_ADDRESS_ADDRESS) { WRITE(arg1, SBC(READ(arg1), READ(arg2))); }
SSMPFN_(CMP_ADDRESS_ADDRESS) { CMP(READ(arg1), READ(arg2)); }

#endif /*SSMP_ASM*/

/****************************************************************************
 *	8-BIT LOGIC OPERATION COMMANDS
 ****************************************************************************/

#ifndef SSMP_ASM

SSMPFN_(AND_A_DATA) { A = AND(A, arg1); }
SSMPFN_(OR_A_DATA)  { A = OR(A, arg1); }
SSMPFN_(EOR_A_DATA) { A = EOR(A, arg1); }

SSMPFN_(AND_A_ADDRESS) { A = AND(A, READ(arg1)); }
SSMPFN_(OR_A_ADDRESS)  { A = OR(A, READ(arg1)); }
SSMPFN_(EOR_A_ADDRESS) { A = EOR(A, READ(arg1)); }

SSMPFN_(AND_ADDRESS_DATA) { WRITE(arg1, AND(READ(arg1), arg2)); }
SSMPFN_(OR_ADDRESS_DATA)  { WRITE(arg1, OR(READ(arg1), arg2)); }
SSMPFN_(EOR_ADDRESS_DATA) { WRITE(arg1, EOR(READ(arg1), arg2)); }

SSMPFN_(AND_ADDRESS_ADDRESS) { WRITE(arg1, AND(READ(arg1), READ(arg2))); }
SSMPFN_(OR_ADDRESS_ADDRESS)  { WRITE(arg1, OR(READ(arg1), READ(arg2))); }
SSMPFN_(EOR_ADDRESS_ADDRESS) { WRITE(arg1, EOR(READ(arg1), READ(arg2))); }

#endif /*SSMP_ASM*/

/****************************************************************************
 *	ADDITION & SUBTRACTION COMMANDS
 ****************************************************************************/

#ifndef SSMP_ASM

SSMPFN_(INC_A) { A = INC(A); }
SSMPFN_(INC_X) { X = INC(X); }
SSMPFN_(INC_Y) { Y = INC(Y); }

SSMPFN_(DEC_A) { A = DEC(A); }
SSMPFN_(DEC_X) { X = DEC(X); }
SSMPFN_(DEC_Y) { Y = DEC(Y); }

SSMPFN_(INC_ADDRESS) { WRITE(arg1, INC(READ(arg1))); }
SSMPFN_(DEC_ADDRESS) { WRITE(arg1, DEC(READ(arg1))); }

#endif /*SSMP_ASM*/

/****************************************************************************
 *	SHIFT, ROTATION COMMANDS
 ****************************************************************************/

SSMPFN_(ASL_A) { A = ASL(A); }
SSMPFN_(LSR_A) { A = LSR(A); }
SSMPFN_(ROL_A) { A = ROL(A); }
SSMPFN_(ROR_A) { A = ROR(A); }
SSMPFN_(XCN_A) { A = (A >> 4) | (A << 4); TEST(A); }

SSMPFN_(ASL_ADDRESS) { WRITE(arg1, ASL(READ(arg1))); }
SSMPFN_(LSR_ADDRESS) { WRITE(arg1, LSR(READ(arg1))); }
SSMPFN_(ROL_ADDRESS) { WRITE(arg1, ROL(READ(arg1))); }
SSMPFN_(ROR_ADDRESS) { WRITE(arg1, ROR(READ(arg1))); }

/****************************************************************************
 *	16-BIT TRANSMISION COMMANDS
 ****************************************************************************/

SSMPFN_(MOVW_YA_ADDRESS) { YA = READW(arg1); TESTW(YA); }
SSMPFN_(MOVW_ADDRESS_YA) { WRITEW(arg1, YA); }

/****************************************************************************
 *	16-BIT OPERATION COMMANDS
 ****************************************************************************/

SSMPFN_(INCW_ADDRESS) { unsigned short tmp = INCW(READ(arg1)); WRITEW(arg1, tmp); }
SSMPFN_(DECW_ADDRESS) { unsigned short tmp = DECW(READ(arg1)); WRITEW(arg1, tmp); }
SSMPFN_(ADDW_YA_ADDRESS) { YA = ADDW(YA, READW(arg1)); }
SSMPFN_(SUBW_YA_ADDRESS) { YA = SUBW(YA, READW(arg1)); }
SSMPFN_(CMPW_YA_ADDRESS) {      CMPW(YA, READW(arg1)); }

/****************************************************************************
 *	MULTIPLICATION & DIVISON COMMANDS
 ****************************************************************************/

SSMPFN_(MUL_YA) {
	unsigned short tmp = A * Y;
	A = LO(tmp);
	Y = HI(tmp);
	TESTW(tmp);
}

/* ※「APU MANUAL IN TXT BY LEDI. (1997)」(spc700_apu_manual.txt)にはPSW(H)も変化すると書いてありますが、仕様が不明です。
 *   既存のプレイヤー(WinSPC)も暫定処置としてPSW(N,V,Z)だけ変化させていて、PSW(H)は変化させていないようです。
 *   そもそもDIV命令の仕様自体よくわからないので、WinSPCの実装に合わせてみました。
 */
SSMPFN_(DIV_YA_X) {
	unsigned short tmp = A | Y << 8;
	if(X) {
		A = (unsigned char)(tmp / X);
		Y = (unsigned char)(tmp % X);
		PSW &= ~0x40; /* Clear Overflow */
	} else {
		A = 0xff;
		Y = 0xff;
		PSW |= 0x40; /* Set Overflow */
	}	
	TEST(A); /* とりあえずPSW(N,Z)のみ対応 */
}

/****************************************************************************
 *	DECIMAL COMPENSATION COMMANDS
 ****************************************************************************/

SSMPFN_(DAA_A) { DIE(); } /* ※TODO: 未実装 */
SSMPFN_(DAS_A) { DIE(); } /* ※TODO: 未実装 */

/****************************************************************************
 *	BRANCHING COMMANDS
 ****************************************************************************/

/* 注意!!
 * 分岐先アドレスがarg1に入っているかarg2に入っているか気をつけてコーディングして!!
 */

#ifndef SSMP_ASM

SSMPFN_(JMP_ADDRESS) { PC = arg1; }
SSMPFN_(BCC_ADDRESS) { if(!(PSW &    1)) { PC = arg1; CYCLE -= 2; } }
SSMPFN_(BCS_ADDRESS) { if( (PSW &    1)) { PC = arg1; CYCLE -= 2; } }
SSMPFN_(BNE_ADDRESS) { if(!(PSW &    2)) { PC = arg1; CYCLE -= 2; } }
SSMPFN_(BEQ_ADDRESS) { if( (PSW &    2)) { PC = arg1; CYCLE -= 2; } }
SSMPFN_(BVC_ADDRESS) { if(!(PSW & 0x40)) { PC = arg1; CYCLE -= 2; } }
SSMPFN_(BVS_ADDRESS) { if( (PSW & 0x40)) { PC = arg1; CYCLE -= 2; } }
SSMPFN_(BPL_ADDRESS) { if(!(PSW & 0x80)) { PC = arg1; CYCLE -= 2; } }
SSMPFN_(BMI_ADDRESS) { if( (PSW & 0x80)) { PC = arg1; CYCLE -= 2; } }

#endif /*SSMP_ASM*/

/* BBC0〜7命令は、命令コードの上位3ビットが調べるビット位置(0〜7)を示しています。 */
SSMPFN_(BBC_ADDRESS_ADDRESS) { int bit = code >> 5; if(!(READ(arg1) & (1 << bit))) { PC = arg2; CYCLE -= 2; } }
SSMPFN_(BBS_ADDRESS_ADDRESS) { int bit = code >> 5; if( (READ(arg1) & (1 << bit))) { PC = arg2; CYCLE -= 2; } }
SSMPFN_(CBNE_ADDRESS_ADDRESS) { if(A != READ(arg1)) { PC = arg2; CYCLE -= 2; } }
SSMPFN_(DBNZ_ADDRESS_ADDRESS) { unsigned char tmp = READ(arg1) - 1; WRITE(arg1, tmp); if(tmp) { PC = arg2; CYCLE -= 2; } }
SSMPFN_(DBNZ_Y_ADDRESS) { Y--; if(Y) { PC = arg1; CYCLE -= 2; } }

/****************************************************************************
 *	SUB-ROUTINE CALL RETURN COMMANDS
 ****************************************************************************/

#ifndef SSMP_ASM

SSMPFN_(CALL_ADDRESS) { PUSH(PCH); PUSH(PCL); PC = arg1; }
SSMPFN_(RET) { POP(PCL); POP(PCH); }

#endif /*SSMP_ASM*/

/* S-SMPはSPC700の割り込み機能を使っていないので、実際にはRETIは使われないはずですが、一応実装しておきました。 */
SSMPFN_(RETI) { POP(PSW); POP(PCL); POP(PCH); }

/****************************************************************************
 *	STACK OPERATION COMMANDS
 ****************************************************************************/

#ifndef SSMP_ASM

SSMPFN_(PUSH_A) { PUSH(A); }
SSMPFN_(PUSH_X) { PUSH(X); }
SSMPFN_(PUSH_Y) { PUSH(Y); }
SSMPFN_(PUSH_PSW) { PUSH(PSW); }

SSMPFN_(POP_A) { POP(A); } /* 6502と違ってフラグ変化なし!! */
SSMPFN_(POP_X) { POP(X); } /* 6502と違ってフラグ変化なし!! */
SSMPFN_(POP_Y) { POP(Y); } /* 6502と違ってフラグ変化なし!! */
SSMPFN_(POP_PSW) { POP(PSW); }

#endif /*SSMP_ASM*/

/****************************************************************************
 *	BIT OPERATION COMMANDS
 ****************************************************************************/

/* CLR/SET0〜7命令は、命令コードの上位3ビットが調べるビット位置(0〜7)を示しています。 */
SSMPFN_(CLR_ADDRESS) { int bit = code >> 5; WRITE(arg1, READ(arg1) & ~(1 << bit)); }
SSMPFN_(SET_ADDRESS) { int bit = code >> 5; WRITE(arg1, READ(arg1) |  (1 << bit)); }

/* 要注意: TESTするのは、TSET1/TCLR1いずれの場合も「READ(arg1) & Areg」です。 */
//SSMPFN_(TSET1_ADDRESS) { unsigned char tmp = READ(arg1); tmp &= A; TEST(tmp); WRITE(arg1, tmp |  A); }
//SSMPFN_(TCLR1_ADDRESS) { unsigned char tmp = READ(arg1); tmp &= A; TEST(tmp); WRITE(arg1, tmp & ~A); }
//↓2004/10/10 修正 (要確認)
SSMPFN_(TSET1_ADDRESS) { unsigned char tmp = READ(arg1); WRITE(arg1, tmp |  A); TEST(tmp & A); }
SSMPFN_(TCLR1_ADDRESS) { unsigned char tmp = READ(arg1); WRITE(arg1, tmp & ~A); TEST(tmp & A); }

SSMPFN_(AND1_C_ADDRESS) { DIE(); }
SSMPFN_(AND1_C_NOT_ADDRESS) { DIE(); }
SSMPFN_(OR1_C_ADDRESS) { DIE(); }
SSMPFN_(OR1_C_NOT_ADDRESS) { DIE(); }
SSMPFN_(EOR1_C_ADDRESS) { DIE(); }
SSMPFN_(NOT1_ADDRESS) { DIE(); }
SSMPFN_(MOV1_C_ADDRESS) { DIE(); }
SSMPFN_(MOV1_ADDRESS_C) { DIE(); }

/****************************************************************************
 *	PROGRAM STATUS FLAG OPERATION COMMANDS
 ****************************************************************************/

SSMPFN_(CLRC) { PSW &= ~   1; }
SSMPFN_(SETC) { PSW |=     1; }
SSMPFN_(NOTC) { PSW ^=     1; }
SSMPFN_(DI)   { PSW &= ~   4; }
SSMPFN_(EI)   { PSW |=     4; }
SSMPFN_(CLRP) { PSW &= ~0x20; }
SSMPFN_(SETP) { PSW |=  0x20; }
SSMPFN_(CLRV) { PSW &= ~0x40; }

/****************************************************************************
 *	OTHER COMMANDS
 ****************************************************************************/

SSMPFN_(NOP) { /** nop **/ }
SSMPFN_(SLEEP) { SLEEP = 1; }	/* 割り込み信号待ち */
SSMPFN_(STOP) { DIE(); }	/* リセット信号待ち (※未対応) */

/****************************************************************************
 *
 ****************************************************************************/
