/*
 * Copyright (c) Des Herriott 1993, 1994
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the name of the copyright holder not be used in
 * advertising or publicity pertaining to distribution of the software without
 * specific, written prior permission.  The copyright holder makes no
 * representations about the suitability of this software for any purpose.  It
 * is provided "as is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 *
 * Author: Des Herriott
 */

/*
 * Source file z80ops.c - the standard z80 instructions.
 */

#include "z80.h"
#include "z80ops.h"

#include "auxfuncs.c"

extern void StoreFlags(), RetrieveFlags();

extern void PrintCpuState();

/* the following values are global to prevent automatic variable overheads */
static uns16 work16;	/* 16-bit temporary workspace */
static uns8  work8;		/* 8-bit  temporary workspace */
static uns8  work8_2;	/* 8-bit  temporary secondary workspace */
static uns32 work32;	/* 32-bit temporary workspace */
static sgn16 work16s;	/* 16-bit temporary workspace (signed) */
static sgn8  work8s;	/* 8-bit  temporary workspace (signed) */
static sgn32 work32s;	/* 32-bit temporary workspace (signed) */
static uns8  cin7;		/* carry-in on bit 7 (test for overflow) */


/* Hold an array of pointers to void functions;
 * these can be then referenced by using the value pointed to by the PC.
 */

PFV z80ops[256] = {
	nop, ld_bcNN, ld__bc_a, inc_bc, inc_b, dec_b, ld_bN, rlca,
	ex_afaf, add_hlbc, ld_a_bc_, dec_bc, inc_c, dec_c, ld_cN_, rrca,
	djnz_D, ld_deNN, ld__de_a, inc_de, inc_d, dec_d, ld_dN, rla,
	jr_D, add_hlde, ld_a_de_, dec_de, inc_e, dec_e, ld_eN, rra,
	jr_nzD, ld_hlNN, ld__NN_hl, inc_hl, inc_h, dec_h, ld_hN, daa,
	jr_zD, add_hlhl, ld_hl__NN_, dec_hl, inc_l, dec_l, ld_lN, cpl,
	jr_ncD, ld_spNN, ld__NN_a, inc_sp, inc__hl_, dec__hl_, ld__hl_N, scf,
	jr_cD_, add_hlsp, ld_a_NN__, dec_sp, inc_a, dec_a, ld_aN, ccf,
	ld_bb, ld_bc, ld_bd, ld_be, ld_bh, ld_bl, ld_b_hl_, ld_ba,
	ld_cb, ld_cc, ld_cd, ld_ce, ld_ch, ld_cl, ld_c_hl_, ld_ca,
	ld_db, ld_dc, ld_dd, ld_de, ld_dh, ld_dl, ld_d_hl_, ld_da,
	ld_eb, ld_ec, ld_ed, ld_ee, ld_eh, ld_el, ld_e_hl_, ld_ea,
	ld_hb, ld_hc, ld_hd, ld_he, ld_hh, ld_hl, ld_h_hl_, ld_ha,
	ld_lb, ld_lc, ld_ld, ld_le, ld_lh, ld_ll, ld_l_hl_, ld_la,
	ld__hl_b, ld__hl_c, ld__hl_d, ld__hl_e, ld__hl_h, ld__hl_l, halt, ld__hl_a,
	ld_ab, ld_ac, ld_ad, ld_ae, ld_ah, ld_al, ld_a_hl_, ld_aa,
	add_ab, add_ac, add_ad, add_ae, add_ah, add_al, add_a_hl_, add_aa,
	adc_ab, adc_ac, adc_ad, adc_ae, adc_ah, adc_al, adc_a_hl_, adc_aa,
	sub_b, sub_c, sub_d, sub_e, sub_h, sub_l, sub__hl_, sub_a,
	sbc_ab, sbc_ac, sbc_ad, sbc_ae, sbc_ah, sbc_al, sbc_a_hl_, sbc_aa,
	and_b, and_c, and_d, and_e, and_h, and_l, and__hl_, and_a,
	xor_b, xor_c, xor_d, xor_e, xor_h, xor_l, xor__hl_, xor_a,
	or_b, or_c, or_d, or_e, or_h, or_l, or__hl_, or_a,
	cp_b, cp_c, cp_d, cp_e, cp_h, cp_l, cp__hl_, cp_a,
	ret_nz, pop_bc, jp_nzNN, jp_NN, call_nzNN, push_bc, add_aN, rst_0x0,
	ret_z, ret, jp_zNN, EXTEND_203, call_zNN, call_NN, adc_aN, rst_0x8,
	ret_nc, pop_de, jp_ncNN, out__N_a, call_ncNN, push_de, sub_N, rst_0x10,
	ret_c, exx, jp_cNN, in_a_N_, call_cNN, EXTEND_221, sbc_aN, rst_0x18,
	ret_po, pop_hl, jp_poNN, ex__sp_hl, call_poNN, push_hl, and_N, rst_0x20,
	ret_pe, jp__hl_, jp_peNN, ex_dehl, call_peNN, EXTEND_237, xor_N, rst_0x28,
	ret_p, pop_af, jp_pNN, di, call_pNN, push_af, or_N, rst_0x30,
	ret_m, ld_sphl, jp_mNN, ei, call_mNN, EXTEND_253, cp_N, rst_0x38,
};


/***********************************************************
 * Now follow the definitions of all the operator functions.
 ***********************************************************/

void nop()
{

}

void ld_bcNN()
{
	*bc = GetNext2Ops;
}

void ld__bc_a()
{
	mem_write(*bc,*a);
}

void inc_bc()
{
	(*bc)++;
}

void inc_b()
{
	(*b)++;
	inc_test(*b);
}

void dec_b() 
{
	(*b)--;
	dec_test(*b);
}

void ld_bN()
{
	*b = GetNextOp;
}

void rlca()
{
	if (carryFlag = (*a & 0x80)) {
		*a = (*a << 1) | 0x01;
	} else
		*a <<= 1;

	Clr(hcarryFlag); Clr(addsubFlag);
}

/* remember to store and retrieve the flags! */
void ex_afaf()
{
	StoreFlags();

	if (af == &(theProcessor->af_pair)) {	/* currently using non-primes */
		af = &(theProcessor->af_alt);
#ifdef LITTLE_ENDIAN
		a  = (uns8 *)&(theProcessor->af_alt) + 1;
		f  = (uns8 *)&(theProcessor->af_alt);
#else
		a  = (uns8 *)&(theProcessor->af_alt);
		f  = (uns8 *)&(theProcessor->af_alt) + 1;
#endif
	} else {								/* currently using primes */
		af = &(theProcessor->af_pair);
#ifdef LITTLE_ENDIAN
		a  = (uns8 *)&(theProcessor->af_pair) + 1;
		f  = (uns8 *)&(theProcessor->af_pair);
#else
		a  = (uns8 *)&(theProcessor->af_pair);
		f  = (uns8 *)&(theProcessor->af_pair) + 1;
#endif
	}

	RetrieveFlags();
}

void add_hlbc()
{
	work32 = *hl + *bc;
	carryFlag = work32 & 0x10000;
	Clr(addsubFlag);
	*hl = (uns16)work32;
}

void ld_a_bc_()
{
	*a = theMemory[*bc];
}

void dec_bc()
{
	(*bc)--;
}

void inc_c() 
{
	(*c)++;
	inc_test(*c);
}

void dec_c()
{
	(*c)--;
	dec_test(*c);
}

void ld_cN_()
{
	*c = GetNextOp;
}

void rrca()
{
	if (carryFlag = (*a & 0x01)) {
		*a = (*a >> 1) | 0x80;
	} else
		*a >>= 1;

	Clr(hcarryFlag); Clr(addsubFlag);
}

void djnz_D()
{
	work8 = GetNextOp;	/* the offset */

	(*b)--;
	if (*b)
		*pc += (sgn8)work8;
}

void ld_deNN()
{
	*de = GetNext2Ops;
}

void ld__de_a()
{
	mem_write(*de,*a);
}

void inc_de()
{
	(*de)++;
}

void inc_d()
{
	(*d)++;
	inc_test(*d);
}

void dec_d()
{
	(*d)--;
	dec_test(*d);
}

void ld_dN()
{
	*d = GetNextOp;
}

void rla()
{
	work8 = carryFlag ? 0x01 : 0;
	carryFlag = *a & 0x80;
	*a = (*a << 1) | work8;

	Clr(hcarryFlag); Clr(addsubFlag);
}

void jr_D()
{
	*pc += (sgn8)GetNextOp;
}

void add_hlde()
{
	work32 = *hl + *de;
	carryFlag = work32 & 0x10000;
	Clr(addsubFlag);
	*hl = (uns16)work32;
}

void ld_a_de_()
{
	*a = theMemory[*de];
}

void dec_de()
{
	(*de)--;
}

void inc_e()
{
	(*e)++;
	inc_test(*e);
}

void dec_e()
{
	(*e)--;
	dec_test(*e);
}

void ld_eN()
{
	*e = GetNextOp;
}

void rra()
{
	work8 = carryFlag ? 0x80 : 0;
	carryFlag = *a & 0x01;
	*a = (*a >> 1) | work8;

	Clr(hcarryFlag); Clr(addsubFlag);
}

void jr_nzD()	/* jump if z flag is not set */
{
	work8 = GetNextOp;

	if(!Tst(zeroFlag))
		*pc += (sgn8)work8;
}

void ld_hlNN()
{
	*hl = GetNext2Ops;
}

void ld__NN_hl()
{ 
	work16 = GetNext2Ops;

	mem_write(work16,*l);
	mem_write(work16 + 1,*h);
}

void inc_hl()
{
	(*hl)++;
}

void inc_h()
{
	(*h)++;
	inc_test(*h);
}

void dec_h()
{
	(*h)--;
	dec_test(*h);
}

void ld_hN()
{
	*h = GetNextOp;
}

/* This one's going to be fun :-) */
void daa()
{
	static uns8 high_nib, low_nib;
	static uns8 add, carry, subflg;

	subflg = addsubFlag;

	high_nib = (*a & 0xf0) >> 4; low_nib  = *a &0x0f;

	if (!Tst(addsubFlag)) {
		if (!Tst(carryFlag)) {
			if (!Tst(hcarryFlag)) {
				if (low_nib < 10) {
					if (high_nib < 10) {
						add = 0x00; carry = 0;
					} else {
						add = 0x60; carry = 1;
					}
				} else {
					if (high_nib < 9) {
						add = 0x06; carry = 0;
					} else {
						add = 0x66; carry = 1;
					}
				}
			} else {	/* hcarryFlag */
				if (high_nib < 10) {
					add = 0x06; carry = 0;
				} else {
					add = 0x66; carry = 1;
				}
			}	/* hcarryFlag */
		} else {	/* carryFlag */
			if (!Tst(hcarryFlag)) {
				if (low_nib < 10) {
					add = 0x60; carry = 1;
				} else {
					add = 0x66; carry = 1;
				}
			} else {
				add = 0x66; carry = 1;
			}
		}	/* carryFlag */
	} else {	/* addsubFlag */
		if (!Tst(carryFlag)) {
			if (!Tst(hcarryFlag)) {
				add = 0x00; carry = 0;
			} else {
				add = 0xFA; carry = 0;
			}
		} else {
			if (!Tst(hcarryFlag)) {
				add = 0xA0; carry = 1;
			} else {
				add = 0x9A; carry = 1;
			}
		}
	}
	*a = add_and_test(*a, add, 0);
	addsubFlag = subflg;
	par_overFlag = parity_tbl[*a];
	carryFlag = carry;
}

void jr_zD()
{
	work8 = GetNextOp;

	if(Tst(zeroFlag))
		*pc += (sgn8)work8;
}

/* add_hlhl: WARNING - half carry flag not implemented yet */
void add_hlhl()
{
	work32 = *hl << 1;
	carryFlag = work32 & 0x10000;
	Clr(addsubFlag);
	*hl = (uns16)work32;
}

void ld_hl__NN_()	/* ld hl,(NN) */
{
	work16 = GetNext2Ops;
	*l = theMemory[work16];
	*h = theMemory[work16 + 1];
}

void dec_hl()
{
	(*hl)--;
}

void inc_l()
{
	(*l)++;
	inc_test(*l);
}

void dec_l()
{
	(*l)--;
	dec_test(*l);
}

void ld_lN()
{
	*l = GetNextOp;
}

void cpl()
{
	*a ^= 0xff;
	Set(addsubFlag); Set(hcarryFlag);
}

void jr_ncD()
{
	work8 = GetNextOp;

	if(!Tst(carryFlag))
		*pc += (sgn8)work8;
}

void ld_spNN()
{
	*sp = GetNext2Ops;
}

void ld__NN_a()
{
	work16 = GetNext2Ops;
	mem_write(work16,*a);
}

void inc_sp()
{
	(*sp)++;
}

void inc__hl_()
{
	mem_write(*hl, theMemory[*hl] + 1);
	inc_test(theMemory[*hl]);
}

void dec__hl_()
{
	mem_write(*hl, theMemory[*hl] - 1);
	dec_test(theMemory[*hl]);
}

void ld__hl_N()		/* ld (hl),N */
{
	mem_write(*hl,GetNextOp);
}

void scf()
{
	Set(carryFlag);
	Clr(hcarryFlag); Clr(addsubFlag);
}

void jr_cD_()
{
	work8 = GetNextOp;

	if(Tst(carryFlag))
		*pc += (sgn8)work8;
}

void add_hlsp()
{
	work32 = *hl + *sp;
	carryFlag = work32 & 0x10000;
	Clr(addsubFlag);
	*hl = (uns16)work32;
}

void ld_a_NN__()	/* ld a,(NN) */
{
	*a = theMemory[GetNext2Ops];
}

void dec_sp()
{
	(*sp)--;
}

void inc_a()
{
	(*a)++;
	inc_test(*a);
}

void dec_a()
{
	(*a)--;
	dec_test(*a);
}

void ld_aN()
{
	*a = GetNextOp;
}

void ccf()
{
	Cpl(carryFlag);
	Clr(addsubFlag);
	/* half carry is set to a "don't care" value */
}

void ld_bb() { /* pointless */ }

void ld_bc() { *b = *c; }

void ld_bd() { *b = *d; }

void ld_be() { *b = *e; }

void ld_bh() { *b = *h; }

void ld_bl() { *b = *l; }

void ld_b_hl_() { *b = theMemory[*hl]; }

void ld_ba() { *b = *a; }

void ld_cb() { *c = *b; }

void ld_cc() { }

void ld_cd() { *c = *d; }

void ld_ce() { *c = *e; }

void ld_ch() { *c = *h; }

void ld_cl() { *c = *l; }

void ld_c_hl_() { *c = theMemory[*hl]; }

void ld_ca() { *c = *a; }

void ld_db() { *d = *b; }

void ld_dc() { *d = *c; }

void ld_dd() { }

void ld_de() { *d = *e; }

void ld_dh() { *d = *h; }

void ld_dl() { *d = *l; }

void ld_d_hl_() { *d = theMemory[*hl]; }

void ld_da() { *d = *a; }

void ld_eb() { *e = *b; }

void ld_ec() { *e = *c; }

void ld_ed() { *e = *d; }

void ld_ee() { }

void ld_eh() { *e = *h; }

void ld_el() { *e = *l; }

void ld_e_hl_() { *e = theMemory[*hl]; }

void ld_ea() { *e = *a; }

void ld_hb() { *h = *b; }

void ld_hc() { *h = *c; }

void ld_hd() { *h = *d; }

void ld_he() { *h = *e; }

void ld_hh() { }

void ld_hl() { *h = *l; }

void ld_h_hl_() { *h = theMemory[*hl]; }

void ld_ha() { *h = *a; }

void ld_lb() { *l = *b; }

void ld_lc() { *l = *c; }

void ld_ld() { *l = *d; }

void ld_le() { *l = *e; }

void ld_lh() { *l = *h; }

void ld_ll() { }

void ld_l_hl_() { *l = theMemory[*hl]; }

void ld_la() { *l = *a; }

void ld__hl_b() { mem_write(*hl,*b); }

void ld__hl_c() { mem_write(*hl,*c); }

void ld__hl_d() { mem_write(*hl,*d); }

void ld__hl_e() { mem_write(*hl,*e); }

void ld__hl_h() { mem_write(*hl,*h); }

void ld__hl_l() { mem_write(*hl,*l); }

/* Stop until an interrupt is received */
void halt() { *pc -= 1; } 

void ld__hl_a() { mem_write(*hl,*a); }

void ld_ab() { *a = *b; }

void ld_ac() { *a = *c; }

void ld_ad() { *a = *d; }

void ld_ae() { *a = *e; }

void ld_ah() { *a = *h; }

void ld_al() { *a = *l; }

void ld_a_hl_() { *a = theMemory[*hl]; }

void ld_aa() { }


/* Now follow the adding routines.  All the flags work is done by a 
 * subsidiary function. */

void add_ab()
{
	*a = add_and_test(*a, *b, 0);
}

void add_ac()
{
	*a = add_and_test(*a, *c, 0);
}

void add_ad()
{
	*a = add_and_test(*a, *d, 0);
}

void add_ae()
{
	*a = add_and_test(*a, *e, 0);
}

void add_ah()
{
	*a = add_and_test(*a, *h, 0);
}

void add_al()
{
	*a = add_and_test(*a, *l, 0);
}

void add_a_hl_()
{
	*a = add_and_test(*a, theMemory[*hl], 0);
}

void add_aa()
{
	*a = add_and_test(*a, *a, 0);
}

/* Add with carry... */
void adc_ab()
{
	*a = add_and_test(*a, *b, Tst(carryFlag)? 1 : 0);
}

void adc_ac()
{
	*a = add_and_test(*a, *c, Tst(carryFlag)? 1 : 0);
}

void adc_ad()
{
	*a = add_and_test(*a, *d, Tst(carryFlag)? 1 : 0);
}

void adc_ae()
{
	*a = add_and_test(*a, *e, Tst(carryFlag)? 1 : 0);
}

void adc_ah()
{
	*a = add_and_test(*a, *h, Tst(carryFlag)? 1 : 0);
}

void adc_al()
{
	*a = add_and_test(*a, *l, Tst(carryFlag)? 1 : 0);
}

void adc_a_hl_()
{
	*a = add_and_test(*a, theMemory[*hl], Tst(carryFlag)? 1 : 0);
}

void adc_aa()
{
	*a = add_and_test(*a, *a, Tst(carryFlag)? 1 : 0);
}

/* And now for the subtractions... */

void sub_b()
{
	*a = subtract_and_test(*a, *b, 0);
}

void sub_c()
{
	*a = subtract_and_test(*a, *c, 0);
}

void sub_d()
{
	*a = subtract_and_test(*a, *d, 0);
}

void sub_e()
{
	*a = subtract_and_test(*a, *e, 0);
}

void sub_h()
{
	*a = subtract_and_test(*a, *h, 0);
}

void sub_l()
{
	*a = subtract_and_test(*a, *l, 0);
}

void sub__hl_()
{
	*a = subtract_and_test(*a, theMemory[*hl], 0);
}

/* special case */
void sub_a()
{
	*a = 0;
	Set(zeroFlag); Clr(carryFlag); Clr(par_overFlag);
	Clr(signFlag); Set(addsubFlag);
}

/* subtract with carry... */
void sbc_ab()
{
	*a = subtract_and_test(*a, *b, Tst(carryFlag)? 1: 0);
}

void sbc_ac()
{
	*a = subtract_and_test(*a, *c, Tst(carryFlag)? 1: 0);
}

void sbc_ad()
{
	*a = subtract_and_test(*a, *d, Tst(carryFlag)? 1: 0);
}

void sbc_ae()
{
	*a = subtract_and_test(*a, *e, Tst(carryFlag)? 1: 0);
}

void sbc_ah()
{
	*a = subtract_and_test(*a, *h, Tst(carryFlag)? 1: 0);
}

void sbc_al()
{
	*a = subtract_and_test(*a, *l, Tst(carryFlag)? 1: 0);
}

void sbc_a_hl_()
{
	*a = subtract_and_test(*a, theMemory[*hl], Tst(carryFlag)? 1: 0);
}

/* sbc_aa could be done more efficiently */
void sbc_aa()
{
	*a = subtract_and_test(*a, *a, Tst(carryFlag)? 1: 0);
}


/* Bitwise AND... */

void and_b()
{
	*a &= *b;
	do_and_flags(*a);
}

void and_c()
{
	*a &= *c;
	do_and_flags(*a);
}

void and_d()
{
	*a &= *d;
	do_and_flags(*a);
}

void and_e()
{
	*a &= *e;
	do_and_flags(*a);
}

void and_h()
{
	*a &= *h;
	do_and_flags(*a);
}

void and_l()
{
	*a &= *l;
	do_and_flags(*a);
}

void and__hl_()
{
	*a &= theMemory[*hl];
	do_and_flags(*a);
}

/* Don't need to do the AND operation here */
void and_a()
{
	do_and_flags(*a);
}


/* Bitwise XOR... */
void xor_b()
{
	*a ^= *b;
	do_xor_flags(*a);
}

void xor_c()
{
	*a ^= *c;
	do_xor_flags(*a);
}

void xor_d()
{
	*a ^= *d;
	do_xor_flags(*a);
}

void xor_e()
{
	*a ^= *e;
	do_xor_flags(*a);
}

void xor_h()
{
	*a ^= *h;
	do_xor_flags(*a);
}

void xor_l()
{
	*a ^= *l;
	do_xor_flags(*a);
}

void xor__hl_()
{
	*a ^= theMemory[*hl];
	do_xor_flags(*a);
}

/* NOTE: xor_a is a special case */
void xor_a()
{
	*a = 0;
	Clr(hcarryFlag); Clr(carryFlag); Clr(addsubFlag);
	Set(zeroFlag); Clr(signFlag); Set(par_overFlag);
}


/* And bitwise OR... */
void or_b()
{
	*a |= *b;
	do_or_flags(*a);
}

void or_c()
{
	*a |= *c;
	do_or_flags(*a);
}

void or_d()
{
	*a |= *d;
	do_or_flags(*a);
}

void or_e()
{
	*a |= *e;
	do_or_flags(*a);
}

void or_h()
{
	*a |= *h;
	do_or_flags(*a);
}

void or_l()
{
	*a |= *l;
	do_or_flags(*a);
}

void or__hl_()
{
	*a |= theMemory[*hl];
	do_or_flags(*a);
}

/* don't need to do the OR operation here */
void or_a()
{
	do_or_flags(*a);
}


/* Register/register compare... */
void cp_b()
{
	compare_and_test(*b);
}

void cp_c()
{
	compare_and_test(*c);
}

void cp_d()
{
	compare_and_test(*d);
}

void cp_e()
{
	compare_and_test(*e);
}

void cp_h()
{
	compare_and_test(*h);
}

void cp_l()
{
	compare_and_test(*l);
}

void cp__hl_()
{
	compare_and_test(theMemory[*hl]);
}

/* NOTE: cp_a is a special case */
void cp_a()
{
	Set(zeroFlag); Clr(carryFlag); Clr(par_overFlag);
	Clr(signFlag); Set(addsubFlag);
}


void ret_nz()
{
	if (!Tst(zeroFlag))
		*pc = PopValue;
}

void pop_bc()
{
	*bc = PopValue;
}

void jp_nzNN()
{
	if (!Tst(zeroFlag))
		*pc = GetNext2Ops;
	else
		*pc += 2;
}

void jp_NN()
{
	*pc = GetNext2Ops;
}

void call_nzNN()
{
	if (!Tst(zeroFlag)) {
		work16 = GetNext2Ops;
		PushValue(*pc);
		*pc = work16;
	} else
		*pc += 2;
}

void push_bc()
{
	PushValue(*bc);
}

void add_aN()
{
	work8 = GetNextOp;
	*a = add_and_test(*a, work8, 0);
}

void rst_0x0()
{
	PushValue(*pc);
	*pc = 0x0000;
}

void ret_z()
{
	if (Tst(zeroFlag))
		*pc = PopValue;
}

void ret()
{
	*pc = PopValue;
}

void jp_zNN()
{
	if (Tst(zeroFlag))
		*pc = GetNext2Ops;
	else
		*pc += 2;
}

void EXTEND_203()
{
	extern PFV cbops[];

	work8 = GetNextOp;
	cbops[work8]();
}

void call_zNN()
{
	if (Tst(zeroFlag)) {
		work16 = GetNext2Ops;
		PushValue(*pc);
		*pc = work16;
	} else
		*pc += 2;
}

void call_NN()
{
	work16 = GetNext2Ops;
	PushValue(*pc);
	*pc = work16;
}

void adc_aN()
{
	work8 = GetNextOp;
	*a = add_and_test(*a, work8, Tst(carryFlag) ? 1 : 0);
}

void rst_0x8()
{
	PushValue(*pc);
	*pc = 0x0008;
}

void ret_nc()
{
	if (!Tst(carryFlag))
		*pc = PopValue;
}

void pop_de()
{
	*de = PopValue;
}

void jp_ncNN()
{
	if (!Tst(carryFlag))
		*pc = GetNext2Ops;
	else
		*pc += 2;
}

void out__N_a()
{
	out_byte((*a << 8) + GetNextOp, *a);
}

void call_ncNN()
{
	if (!Tst(carryFlag)) {
		work16 = GetNext2Ops;
		PushValue(*pc);
		*pc = work16;
	} else
		*pc += 2;
}

void push_de()
{
	PushValue(*de);
}

void sub_N()
{
	work8 = GetNextOp;
	*a = subtract_and_test(*a, work8, 0);
}

void rst_0x10()
{

/* DEBUG printf("<%c> 0x%x\n", *a, *a); */

	PushValue(*pc);
	*pc = 0x0010;
}

void ret_c()
{
	if (Tst(carryFlag))
		*pc = PopValue;
}

/* swap into the alternate reg. set */
void exx()
{
	void exx_alt();		/* forward declaration */

	bc = &(theProcessor->bc_alt);
	de = &(theProcessor->de_alt);
	hl = &(theProcessor->hl_alt);
#ifdef LITTLE_ENDIAN
    h  = (uns8 *)&(theProcessor->hl_alt) + 1;
	l  = (uns8 *)&(theProcessor->hl_alt);
	b  = (uns8 *)&(theProcessor->bc_alt) + 1;
	c  = (uns8 *)&(theProcessor->bc_alt);
	d  = (uns8 *)&(theProcessor->de_alt) + 1;
	e  = (uns8 *)&(theProcessor->de_alt);
#else
    h  = (uns8 *)&(theProcessor->hl_alt);
    l  = (uns8 *)&(theProcessor->hl_alt) + 1;
    b  = (uns8 *)&(theProcessor->bc_alt);
    c  = (uns8 *)&(theProcessor->bc_alt) + 1;
    d  = (uns8 *)&(theProcessor->de_alt);
    e  = (uns8 *)&(theProcessor->de_alt) + 1;
#endif

	/* this way, no testing has to be done (0xd9 being the opcode for exx) */
	z80ops[0xd9] = exx_alt;
}

/* swap out of the alternate reg. set */
void exx_alt()
{
	bc = &(theProcessor->bc_pair);
	de = &(theProcessor->de_pair);
	hl = &(theProcessor->hl_pair);
#ifdef LITTLE_ENDIAN
    h  = (uns8 *)&(theProcessor->hl_pair) + 1;
	l  = (uns8 *)&(theProcessor->hl_pair);
	b  = (uns8 *)&(theProcessor->bc_pair) + 1;
	c  = (uns8 *)&(theProcessor->bc_pair);
	d  = (uns8 *)&(theProcessor->de_pair) + 1;
	e  = (uns8 *)&(theProcessor->de_pair);
#else
    h  = (uns8 *)&(theProcessor->hl_pair);
    l  = (uns8 *)&(theProcessor->hl_pair) + 1;
    b  = (uns8 *)&(theProcessor->bc_pair);
    c  = (uns8 *)&(theProcessor->bc_pair) + 1;
    d  = (uns8 *)&(theProcessor->de_pair);
    e  = (uns8 *)&(theProcessor->de_pair) + 1;
#endif
	z80ops[0xd9] = exx;
}

void jp_cNN()
{
	if (Tst(carryFlag))
		*pc = GetNext2Ops;
	else
		*pc += 2;
}

void in_a_N_()		/* in a,N */
{
	*a = in_byte((*a << 8) + GetNextOp);
}

void call_cNN()
{
	if (Tst(carryFlag)) {
		work16 = GetNext2Ops;
		PushValue(*pc);
		*pc = work16;
	} else
		*pc += 2;
}

/* opcode 0xdd - ix register handling */
void EXTEND_221()
{
	static uns16 *safe_hl;
	static sgn8 d;
	extern PFV cbops[], z80ops[];
	extern uns8 ixiy_tbl[];

	safe_hl = hl;
	hl = ix;	/* hl now points at the ix register */

	if (theMemory[*pc] == 0xcb) { /* DD CB .. ops. always have displacements */
		(*pc)++;
		d     = GetNextOp;		/* the displacement byte */
		work8 = GetNextOp;		/* the CB opcode */
		*hl += d;				/* ix <- ix+d */
		cbops[work8]();			/* do the biz */
	} else {
		work8 = GetNextOp;		/* the CB opcode */
		if (ixiy_tbl[work8])	/* does it need a displacement? */
			d = GetNextOp;
		else {
			d = 0;
#ifdef LITTLE_ENDIAN
        	h = (uns8 *)hl + 1; l = (uns8 *)hl;
#else
        	h = (uns8 *)hl; l = (uns8 *)hl + 1;
#endif
        }
		*hl += d;				/* ix <- ix+d */
#if 0
printf("DD %02x\n", work8);
#endif
		z80ops[work8]();		/* do the biz */
	}	
	*hl -= d;					/* restore ix */
	hl = safe_hl;				/* hl points at hl (or hl') again */
#ifdef LITTLE_ENDIAN
	h = (uns8 *)hl + 1; l = (uns8 *)hl;
#else
	h = (uns8 *)hl; l = (uns8 *)hl + 1;
#endif
}

void sbc_aN()
{
	work8 = GetNextOp;
	*a = subtract_and_test(*a, work8, Tst(carryFlag) ? 1 : 0);
}

void rst_0x18()
{
	PushValue(*pc);
	*pc = 0x0018;
}

void ret_po()
{
	if (!Tst(par_overFlag))
		*pc = PopValue;
}

void pop_hl()
{
	*hl = PopValue;
}

void jp_poNN()
{
	if (!Tst(par_overFlag))
		*pc = GetNext2Ops;
	else
		*pc += 2;
}

void ex__sp_hl()	/* ex (sp),hl */
{
	work16 = theMemory[*sp] + (theMemory[*sp + 1] << 8);
	mem_write(*sp,*l);
	mem_write(*sp + 1,*h);
	*hl = work16;
}

void call_poNN()
{
	if (!Tst(par_overFlag)) {
		work16 = GetNext2Ops;
		PushValue(*pc);
		*pc = work16;
	} else
		*pc += 2;
}

void push_hl()
{
	PushValue(*hl);
}

void and_N()
{
	work8 = GetNextOp;
	*a &= work8;
	do_and_flags(*a);
}

void rst_0x20()
{
	PushValue(*pc);
	*pc = 0x0020;
}

void ret_pe()
{
	if (Tst(par_overFlag))
		*pc = PopValue;
}

void jp__hl_()
{
	*pc = *hl;
}

void jp_peNN()
{
	if (Tst(par_overFlag))
		*pc = GetNext2Ops;
	else
		*pc += 2;
}

void ex_dehl()
{
	work16 = *de; *de = *hl; *hl = work16;
}

void call_peNN()
{
	if (Tst(par_overFlag)) {
		work16 = GetNext2Ops;
		PushValue(*pc);
		*pc = work16;
	} else
		*pc += 2;
}

void EXTEND_237()
{
	extern PFV edops[];

	work8 = GetNextOp;
	edops[work8]();
}

void xor_N()
{
	work8 = GetNextOp;
	*a ^= work8;
	do_xor_flags(*a);
}

void rst_0x28()
{
	PushValue(*pc);
	*pc = 0x0028;
}

void ret_p()
{
	if (!Tst(signFlag))
		*pc = PopValue;
}

void pop_af()
{
	*af = PopValue;
	RetrieveFlags();
}

void jp_pNN()
{
	if (!Tst(signFlag)) {
		*pc = GetNext2Ops;
	} else
		*pc += 2;
}

void di()
{
	theProcessor->iff1 = 0;
	theProcessor->iff2 = 0;
}

void call_pNN()
{
	if (!Tst(signFlag)) {
		work16 = GetNext2Ops;
		PushValue(*pc);
		*pc = work16;
	} else
		*pc += 2;
}

void push_af()
{
	StoreFlags();
	PushValue(*af);
}

void or_N()
{
	work8 = GetNextOp;
	*a |= work8;
	do_or_flags(*a);
}

void rst_0x30()
{
	PushValue(*pc);
	*pc = 0x0030;
}

void ret_m()
{
	if (Tst(signFlag))
		*pc = PopValue;
}

void ld_sphl()
{
	*sp = *hl;
}

void jp_mNN()
{
	if (Tst(signFlag)) {
		*pc = GetNext2Ops;
	} else
		*pc += 2;
}

void ei()
{
	theProcessor->iff1 = 1;
	theProcessor->iff2 = 1;
}

void call_mNN()
{
	if (Tst(signFlag)) {
		work16 = GetNext2Ops;
		PushValue(*pc);
		*pc = work16;
	} else
		*pc += 2;
}

/* opcode 0xfd - iy register handling */
void EXTEND_253()
{
	static uns16 *safe_hl;
	static sgn8 d;
	extern PFV cbops[], z80ops[];
	extern uns8 ixiy_tbl[];

	safe_hl = hl;
	hl = iy;	/* hl now points at the iy register */

	if (theMemory[*pc] == 0xcb) { /* DD CB .. ops. always have displacements */
		(*pc)++;
		d     = GetNextOp;		/* the displacement byte */
		work8 = GetNextOp;		/*  the CB opcode */
		*hl += d;				/* iy <- iy+d */
		cbops[work8]();			/* do the biz */
	} else {
		work8 = GetNextOp;
		if (ixiy_tbl[work8]) {	/* does it need a displacement? */
			d = GetNextOp;
		} else {
#ifdef LITTLE_ENDIAN
		    h = (uns8 *)hl + 1; l = (uns8 *)hl;
#else
		    h = (uns8 *)hl; l = (uns8 *)hl + 1;
#endif
			d = 0;
		}
		*hl += d;				/* iy <- iy+d */
#if 0
printf("FD %02x\n", work8);
#endif
		z80ops[work8]();		/* do the biz */
	}	
	*hl -= d;					/* restore ix */
	hl = safe_hl;				/* hl points at hl (or hl') again */
#ifdef LITTLE_ENDIAN
	h = (uns8 *)hl + 1; l = (uns8 *)hl;
#else
	h = (uns8 *)hl; l = (uns8 *)hl + 1;
#endif
}

void cp_N()
{
	work8  = GetNextOp;
	compare_and_test(work8);
}

void rst_0x38()
{
	PushValue(*pc);
	*pc = 0x0038;
}
