/*****************************************************************************
 *
 *   scops.inc
 *   portable sharp 61860 emulator interface
 *   (sharp pocket computers)
 *
 *   Copyright Peter Trauner, all rights reserved.
 *
 *   - This source code is released as freeware for non-commercial purposes.
 *   - You are free to use and redistribute this code in modified or
 *     unmodified form, provided you list me in the credits.
 *   - If you modify this source code, you must add a notice to each modified
 *     source file that it has been changed.  If you're a nice person, you
 *     will clearly mark each change too.  :)
 *   - If you wish to use this for commercial purposes, please contact me at
 *     peter.trauner@jk.uni-linz.ac.at
 *   - The author of this copywritten work reserves the right to change the
 *     terms of its usage and license at any time, including retroactively
 *   - This entire notice must remain in the source code.
 *
 * History of changes:
 * 21.07.2001 Several changes listed below were made by Mario Konegger
 *            (konegger@itp.tu-graz.ac.at)
 *        replaced buggy BCD-commands add_bcd, sub_bcd, add_bcd_a,
 *            sub_bcd_a and changed out_c, to implement HLT-mode of the CPU.
 *
 *****************************************************************************/

UINT8 sc61860_device::READ_OP()
{
	return m_direct->read_decrypted_byte(m_pc++);
}

UINT8 sc61860_device::READ_OP_ARG()
{
	return m_direct->read_raw_byte(m_pc++);
}

UINT16 sc61860_device::READ_OP_ARG_WORD()
{
	UINT16 t=m_direct->read_decrypted_byte(m_pc++)<<8;
	t|=m_direct->read_decrypted_byte(m_pc++);
	return t;
}

UINT8 sc61860_device::READ_BYTE(UINT16 adr)
{
	return m_program->read_byte(adr);
}

void sc61860_device::WRITE_BYTE(UINT16 a, UINT8 v)
{
	m_program->write_byte(a, v);
}

UINT8 sc61860_device::READ_RAM(int r)
{
	return m_ram[r];
}

void sc61860_device::WRITE_RAM(int r, UINT8 v)
{
	m_ram[r] = v;
}

void sc61860_device::PUSH(UINT8 v)
{
	m_r--;
	WRITE_RAM(m_r, v);
}

UINT8 sc61860_device::POP()
{
	UINT8 t = READ_RAM(m_r);
	m_r++;
	return t;
}

void sc61860_device::sc61860_load_imm(int r, UINT8 v)
{
	WRITE_RAM(r, v);
}

void sc61860_device::sc61860_load()
{
	WRITE_RAM(A, READ_RAM(m_p));
}

void sc61860_device::sc61860_load_imm_p(UINT8 v)
{
	m_p=v&0x7f;
}

void sc61860_device::sc61860_load_imm_q(UINT8 v)
{
	m_q=v&0x7f;
}

void sc61860_device::sc61860_load_r()
{
	m_r = READ_RAM(A) & 0x7f;
}

void sc61860_device::sc61860_load_ext(int r)
{
	WRITE_RAM(r, READ_BYTE(m_dp));
}

void sc61860_device::sc61860_load_dp()
{
	m_dp=READ_OP_ARG_WORD();
}

void sc61860_device::sc61860_load_dl()
{
	m_dp=(m_dp&~0xff)|READ_OP_ARG();
}

void sc61860_device::sc61860_store_p()
{
	WRITE_RAM(A, m_p);
}

void sc61860_device::sc61860_store_q()
{
	WRITE_RAM(A, m_q);
}

void sc61860_device::sc61860_store_r()
{
	WRITE_RAM(A, m_r);
}

void sc61860_device::sc61860_store_ext(int r)
{
	WRITE_BYTE(m_dp, READ_RAM(r));
}

void sc61860_device::sc61860_exam(int a, int b)
{
	UINT8 t = READ_RAM(a);
	WRITE_RAM(a, READ_RAM(b));
	WRITE_RAM(b, t);
}

void sc61860_device::sc61860_test(int reg, UINT8 value)
{
	m_zero=(READ_RAM(reg) & value)==0;
}

void sc61860_device::sc61860_test_ext()
{
	m_zero=(READ_BYTE(m_dp)&READ_OP_ARG())==0;
}

void sc61860_device::sc61860_and(int reg, UINT8 value)
{
	UINT8 t = READ_RAM(reg) & value;
	WRITE_RAM(reg,  t);
	m_zero=t==0;
}

void sc61860_device::sc61860_and_ext()
{
	UINT8 t = READ_BYTE(m_dp) & READ_OP_ARG();
	m_zero=t==0;
	WRITE_BYTE(m_dp, t);
}

void sc61860_device::sc61860_or(int reg, UINT8 value)
{
	UINT8 t = READ_RAM(reg) | value;
	WRITE_RAM(reg, t);
	m_zero=t==0;
}

void sc61860_device::sc61860_or_ext()
{
	UINT8 t=READ_BYTE(m_dp)|READ_OP_ARG();
	m_zero=t==0;
	WRITE_BYTE(m_dp, t);
}

void sc61860_device::sc61860_rotate_right()
{
	int t = READ_RAM(A);
	if (m_carry) t|=0x100;
	m_carry=t&1;
	WRITE_RAM(A, t>>1);
}

void sc61860_device::sc61860_rotate_left()
{
	int t = READ_RAM(A) << 1;
	if (m_carry) t|=1;
	m_carry=t&0x100;
	WRITE_RAM(A, t);
}

void sc61860_device::sc61860_swap()
{
	int t = READ_RAM(A);
	WRITE_RAM(A, (t<<4)|((t>>4)&0xf));
}

// q=reg sideeffect
void sc61860_device::sc61860_inc(int reg)
{
	UINT8 t = READ_RAM(reg) + 1;
	m_q=reg;
	WRITE_RAM(reg, t);
	m_zero=t==0;
	m_carry=t==0;
}

void sc61860_device::sc61860_inc_p()
{
	m_p++;
}

// q=reg sideeffect
void sc61860_device::sc61860_dec(int reg)
{
	UINT8 t = READ_RAM(reg) - 1;
	m_q=reg;
	WRITE_RAM(reg, t);
	m_zero=t==0;
	m_carry=t==0xff;
}

void sc61860_device::sc61860_dec_p()
{
	m_p--;
}

void sc61860_device::sc61860_add(int reg, UINT8 value)
{
	int t = READ_RAM(reg) + value;
	WRITE_RAM(reg, t);
	m_zero=(t&0xff)==0;
	m_carry=t>=0x100;
}

void sc61860_device::sc61860_add_carry()
{
	int t = READ_RAM(m_p) + READ_RAM(A);
	if (m_carry) t++;
	WRITE_RAM(m_p, t);
	m_zero=(t&0xff)==0;
	m_carry=t>=0x100;
}

// p++ sideeffect
void sc61860_device::sc61860_add_word()
{
	int t = READ_RAM(m_p) + READ_RAM(A), t2;
	WRITE_RAM(m_p, t);
	m_p++;
	t2 = READ_RAM(m_p) + READ_RAM(B);
	if (t>=0x100) t2++;
	WRITE_RAM(m_p, t2);
	m_zero=(t2&0xff)==0 &&(t&0xff)==0;
	m_carry=t2>=0x100;
}


void sc61860_device::sc61860_sub(int reg, UINT8 value)
{
	int t = READ_RAM(reg) - value;
	WRITE_RAM(reg, t);
	m_zero=(t&0xff)==0;
	m_carry=t<0;
}

void sc61860_device::sc61860_sub_carry()
{
	int t = READ_RAM(m_p) - READ_RAM(A);
	if (m_carry) t--;
	WRITE_RAM(m_p, t);
	m_zero=(t&0xff)==0;
	m_carry=t<0;
}


// p++ sideeffect
void sc61860_device::sc61860_sub_word()
{
	int t = READ_RAM(m_p) - READ_RAM(A), t2;
	WRITE_RAM(m_p, t);
	m_p++;
	t2 = READ_RAM(m_p) - READ_RAM(B);
	if (t<0) t2--;
	WRITE_RAM(m_p, t2);
	m_zero=(t2&0xff)==0 && (t&0xff)==0;
	m_carry=t2<0;
}

void sc61860_device::sc61860_cmp(int reg, UINT8 value)
{
	int t = READ_RAM(reg) - value;
	m_zero=t==0;
	m_carry=t<0;
}

void sc61860_device::sc61860_pop()
{
	WRITE_RAM(A, POP());
}

void sc61860_device::sc61860_push()
{
	PUSH(READ_RAM(A));
}

void sc61860_device::sc61860_prepare_table_call()
{
	int adr;
	m_h=READ_OP();
	adr=READ_OP_ARG_WORD();
	PUSH(adr>>8);
	PUSH(adr&0xff);
}

void sc61860_device::sc61860_execute_table_call()
{
	int i, v, adr;
	for (i=0; i<m_h; i++) {
		v=READ_OP();
		adr=READ_OP_ARG_WORD();
		m_zero=v==READ_RAM(A);
		if (m_zero) {
			m_pc=adr;
			return;
		}
	}
	m_pc=READ_OP_ARG_WORD();
}


void sc61860_device::sc61860_call(UINT16 adr)
{
	PUSH(m_pc>>8);
	PUSH(m_pc&0xff);
	m_pc=adr;
}

void sc61860_device::sc61860_return()
{
	UINT16 t=POP();
	t|=POP()<<8;
	m_pc=t;
}

void sc61860_device::sc61860_jump(int yes)
{
	UINT16 adr = READ_OP_ARG_WORD();
	if (yes) {
		m_pc=adr;
	}
}

void sc61860_device::sc61860_jump_rel_plus(int yes)
{
	UINT16 adr = m_pc + READ_OP_ARG();
	if (yes) {
		m_pc=adr;
		m_icount-=3;
	}
}

void sc61860_device::sc61860_jump_rel_minus(int yes)
{
	UINT16 adr = m_pc - READ_OP_ARG();
	if (yes) {
		m_pc=adr;
		m_icount-=3;
	}
}

void sc61860_device::sc61860_loop()
{
	UINT16 adr = m_pc - READ_OP_ARG();
	UINT8 t = READ_RAM(m_r) - 1;
	WRITE_RAM(m_r, t);
	m_zero=t==0;
	m_carry=t==0xff;
	if (!m_carry) {
		m_pc=adr;
		adr=POP();
		m_icount-=3;
	}
}

void sc61860_device::sc61860_leave()
{
	WRITE_RAM(m_r, 0);
}

void sc61860_device::sc61860_wait()
{
	int t=READ_OP();
	m_icount-=t;
	m_icount-=t;
	m_icount-=3;
}

void sc61860_device::sc61860_set_carry()
{
	m_carry=1;
	m_zero=1;
}

void sc61860_device::sc61860_reset_carry()
{
	m_carry=0;
	m_zero=1;
}

void sc61860_device::sc61860_out_a()
{
	m_q=IA;
	m_outa(READ_RAM(IA));
}

void sc61860_device::sc61860_out_b()
{
	m_q=IB;
	m_outb( READ_RAM(IB));
}

void sc61860_device::sc61860_out_f()
{
	m_q=F0;
	/*READ_RAM(F0); */
}


/*   c0 display on
   c1 counter reset
   c2 cpu halt
   c3 computer off
   c4 beeper frequency (1 4khz, 0 2khz), or (c5=0) membran pos1/pos2
   c5 beeper on
   c6 beeper steuerung*/
void sc61860_device::sc61860_out_c()
{
	m_q=C;
	m_outc( READ_RAM(C));
	m_c = READ_RAM(C);
}

void sc61860_device::sc61860_in_a()
{
	int data=0;
	data=m_ina();
	WRITE_RAM(A, data);
	m_zero=data==0;
}

void sc61860_device::sc61860_in_b()
{
	int data=0;
	data=m_inb();
	WRITE_RAM(A, data);
	m_zero=data==0;
}

/* 0 systemclock 512ms
   1 systemclock 2ms
   2 ?
   3 brk/on key
   4 ?
   5 ?
   6 reset
   7 cassette input */
void sc61860_device::sc61860_test_special()
{
	int t=0;
	if (m_timer.t512ms) t|=1;
	if (m_timer.t2ms) t|=2;
	if (!m_brk.isnull()&&m_brk()) t|=8;
	if (!m_reset.isnull()&&m_reset()) t|=0x40;
	if (!m_x.isnull()&&m_x()) t|=0x80;

	m_zero=(t&READ_OP())==0;
}

/************************************************************************************
 "string" operations
***********************************************************************************/

// p-=I+1 sideeffect
void sc61860_device::sc61860_add_bcd_a()
{
	UINT8 help = READ_RAM(A);
	int i, hlp, hlp1 = 0;
	m_zero=1;
	for (i=0; i <= READ_RAM(I); i++) {
		int t = READ_RAM(m_p);
		hlp1 = (t & 0x0f) + (help & 0x0f) + hlp1;
		if (hlp1 > 9) { hlp = hlp1 - 0x0a; hlp1 = 0x10; }
		else { hlp = hlp1; hlp1 = 0x00; }
		hlp1 = (t & 0xf0) + (help & 0xf0) + hlp1;
		if (hlp1 > 0x90) { WRITE_RAM(m_p, hlp1 - 0xa0 + hlp); hlp1 = 1; }
		else { WRITE_RAM(m_p, hlp1 + hlp); hlp1 = 0; }
		if ( READ_RAM(m_p) != 0 ) m_zero = 0;
		m_p--;
		help = 0;
	}
	m_carry= ( hlp1 ) ? 1 : 0;
	m_icount-=3*(READ_RAM(I)+1);
}


// p-=I+1, q-=I+2 sideeffect
void sc61860_device::sc61860_add_bcd()
{
	int i, hlp, hlp1 = 0;
	m_zero=1;
	for (i=0; i <= READ_RAM(I); i++) {
		int t = READ_RAM(m_p);
		int t2 = READ_RAM(m_q);
		hlp1 = (t & 0x0f) + (t2 & 0x0f) + hlp1;
		if (hlp1 > 9) { hlp = hlp1 - 0x0a; hlp1 = 0x10; }
		else { hlp = hlp1; hlp1 = 0x00; }
		hlp1 = (t & 0xf0) + (t2 & 0xf0) + hlp1;
		m_q--;
		if (hlp1 > 0x90) { WRITE_RAM(m_p, hlp1 - 0xa0 + hlp); hlp1 = 1; }
		else { WRITE_RAM(m_p, hlp1 + hlp); hlp1 = 0; }
		if ( READ_RAM(m_p) != 0 ) m_zero = 0;
		m_p--;
	}
	m_carry= ( hlp1 ) ? 1 : 0;
	m_icount-=3*(READ_RAM(I)+1);
	m_q--;
}


// p-=I+1 sideeffect
void sc61860_device::sc61860_sub_bcd_a()
{
	UINT8 help = READ_RAM(A);
	int i, hlp, hlp1 = 0;
	m_zero=1;
	for (i=0; i <= READ_RAM(I); i++) {
		int t = READ_RAM(m_p);
		hlp1 = (t & 0x0f) - (help & 0x0f) - hlp1;
		if ( hlp1 < 0 ) { hlp = hlp1 + 0x0a; hlp1 = 0x10; }
		else { hlp = hlp1; hlp1 = 0x00; }
		hlp1 = (t & 0xf0) - (help & 0xf0) - hlp1;
		if ( hlp1 < 0 ) { WRITE_RAM(m_p, hlp1 + 0xa0 + hlp); hlp1 = 1; }
		else { WRITE_RAM(m_p, hlp1 + hlp); hlp1 = 0; }
		if ( READ_RAM(m_p) != 0 ) m_zero = 0;
		m_p--;
		help = 0;
	}
	m_carry= ( hlp1 ) ? 1 : 0;
	m_icount-=3*(READ_RAM(I)+1);
}


// p-=I+1, q-=I+2 sideeffect
void sc61860_device::sc61860_sub_bcd()
{
	int i, hlp, hlp1 = 0;
	m_zero=1;
	for (i=0; i <= READ_RAM(I); i++) {
		int t = READ_RAM(m_p);
		int t2 = READ_RAM(m_q);
		hlp1 = (t & 0x0f) - (t2 & 0x0f) - hlp1;
		if ( hlp1 < 0 ) { hlp = hlp1 + 0x0a; hlp1 = 0x10; }
		else { hlp = hlp1; hlp1 = 0x00; }
		hlp1 = (t & 0xf0) - (t2 & 0xf0) - hlp1;
		m_q--;
		if ( hlp1 < 0 ) { WRITE_RAM(m_p, hlp1 + 0xa0 + hlp); hlp1 = 1; }
		else { WRITE_RAM(m_p, hlp1 + hlp); hlp1 = 0; }
		if ( READ_RAM(m_p) != 0 ) m_zero = 0;
		m_p--;
	}
	m_carry= ( hlp1 ) ? 1 : 0;
	m_icount-=3*(READ_RAM(I)+1);
	m_q--;
}

/* side effect p-i-1 -> p correct! */
void sc61860_device::sc61860_shift_left_nibble()
{
	int i,t=0;
	for (i=0; i<=READ_RAM(I); i++) {
		t |= READ_RAM(m_p)<<4;
		WRITE_RAM(m_p, t);
		m_p--;
		t>>=8;
		m_icount--;
	}
}

/* side effect p+i+1 -> p correct! */
void sc61860_device::sc61860_shift_right_nibble()
{
	int i,t=0;
	for (i=0; i<=READ_RAM(I); i++) {
		t |= READ_RAM(m_p);
		WRITE_RAM(m_p, t>>4);
		m_p++;
		t=(t<<8)&0xf00;
		m_icount--;
	}
}

// q=reg+1 sideeffect
void sc61860_device::sc61860_inc_load_dp(int reg)
{
	UINT8 t = READ_RAM(reg) + 1;
	UINT8 t2 = READ_RAM(reg + 1);
	WRITE_RAM(reg, t);
	if (t == 0) { t2++; WRITE_RAM(reg + 1, t2); }
	m_dp=t|(t2<<8);
	m_q=reg+1;
}

// q=reg+1 sideeffect
void sc61860_device::sc61860_dec_load_dp(int reg)
{
	UINT8 t = READ_RAM(reg) - 1;
	UINT8 t2 = READ_RAM(reg + 1);
	WRITE_RAM(reg, t);
	if (t == 0xff) { t2--; WRITE_RAM(reg + 1, t2); }
	m_dp=t|(t2<<8);
	m_q=reg+1;
}

// q=XH sideeffect
void sc61860_device::sc61860_inc_load_dp_load()
{
	sc61860_inc_load_dp(XL);
	WRITE_RAM(A, READ_BYTE(m_dp));
}

// q=XH sideeffect
void sc61860_device::sc61860_dec_load_dp_load()
{
	sc61860_dec_load_dp(XL);
	WRITE_RAM(A, READ_BYTE(m_dp));
}

// q=YH sideeffect
void sc61860_device::sc61860_inc_load_dp_store()
{
	sc61860_inc_load_dp(YL);
	WRITE_BYTE(m_dp, READ_RAM(A));
}

// q=YH sideeffect
void sc61860_device::sc61860_dec_load_dp_store()
{
	sc61860_dec_load_dp(YL);
	WRITE_BYTE(m_dp, READ_RAM(A));
}

void sc61860_device::sc61860_fill()
{
	int i;
	for (i=0;i<=READ_RAM(I);i++) {
		WRITE_RAM(m_p, READ_RAM(A)); /* could be overwritten? */
		m_p++;
		m_icount--;
	}
}

void sc61860_device::sc61860_fill_ext()
{
	int i;
	for (i=0;i<=READ_RAM(I);i++) {
		WRITE_BYTE(m_dp, READ_RAM(A));
		if (i!=READ_RAM(I)) m_dp++;
		m_icount-=3;
	}
}

// p+=count+1, q+=count+1 sideeffects
void sc61860_device::sc61860_copy(int count)
{
	int i;
	for (i=0; i<=count; i++) {
		WRITE_RAM(m_p, READ_RAM(m_q));
		m_p++;
		m_q++;
		m_icount-=2;
	}

}

// p+=count+1, dp+=count sideeffects
void sc61860_device::sc61860_copy_ext(int count)
{
	int i;
	for (i=0; i<=count; i++) {
		WRITE_RAM(m_p, READ_BYTE(m_dp));
		m_p++;
		if (i!=count) m_dp++;
		m_icount-=4;
	}
}

void sc61860_device::sc61860_copy_int(int count)
{
	int i;
	for (i=0; i<=count; i++) {
		UINT8 t = READ_BYTE((READ_RAM(A)|(READ_RAM(B)<<8))); /* internal rom! */
		WRITE_RAM(m_p, t);
		m_p++;
		if (i!=count) {
			t = READ_RAM(A) + 1;
			WRITE_RAM(A, t);
			if (t==0) {
				t = READ_RAM(B) + 1;
				WRITE_RAM(B, t);
			}
		}
		m_icount-=4;
	}
}

void sc61860_device::sc61860_exchange(int count)
{
	int i;
	UINT8 t;
	for (i=0; i<=count; i++) {
		t = READ_RAM(m_p);
		WRITE_RAM(m_p, READ_RAM(m_q));
		WRITE_RAM(m_q, t);
		m_p++;
		m_q++;
		m_icount-=3;
	}
}

void sc61860_device::sc61860_exchange_ext(int count)
{
	int i;
	UINT8 t;
	for (i=0; i<=count; i++) {
		t = READ_RAM(m_p);
		WRITE_RAM(m_p, READ_BYTE(m_dp));
		m_p++;
		WRITE_BYTE(m_dp, t);
		if (i!=count) m_dp++;
		m_icount-=6;
	}
}

// undocumented
// only 1 opcode working in pc1403
// both opcodes working in pc1350
void sc61860_device::sc61860_wait_x(int level)
{
	int c;
	m_zero=level;

	if (!m_x.isnull()) {
		for (c=READ_RAM(I); c>=0; c--) {
			UINT8 t = (READ_RAM(m_p)+1)&0x7f;
			WRITE_RAM(m_p, t);
			m_zero=m_x();
			m_icount-=4;
			if (level != m_zero) break;
		}
	}
}
