/*
 * 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 edops.c - operation prefixed by 0xED.
 */

#include "z80.h"
#include "edops.h"

#include "auxfuncs.c"

static uns8  work8, work8_2;
static uns16 work16;

#ifdef ZX_IF1
extern void rs232_ip_trap();
extern void rs232_op_trap();
#endif

#ifdef LEVEL_LOADER
/* from spectrum.c */
extern void level_loader_trap();
#endif

PFV edops[256] = {
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	in_b__c_, out__c__b, sbc_hl_bc, ld__NN__bc,
	neg, retn, im_0, ld_i_a,
	in_c__c_, out__c__c, adc_hl_bc, ld_bc__NN_,
	Undocumented, reti, Undocumented, ld_r_a,
	in_d__c_, out__c__d, sbc_hl_de, ld__NN__de,
	Undocumented, Undocumented, im_1, ld_a_i,
	in_e__c_, out__c__e, adc_hl_de, ld_de__NN_,
	Undocumented, Undocumented, im_2, ld_a_r,
	in_h__c_, out__c__h, sbc_hl_hl, ld__nn__hl,
	Undocumented, Undocumented, Undocumented, rrd,
	in_l__c_, out__c__l, adc_hl_hl, ld_hl__NN_,
	Undocumented, Undocumented, Undocumented, rld,
	in_f__c_, Undocumented, sbc_hl_sp, ld__NN__sp,
	Undocumented, Undocumented, Undocumented, Undocumented,
	in_a__c_, out__c__a, adc_hl_sp, ld_sp__NN_,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	ldi, cpi, ini, outi,
	Undocumented, Undocumented, Undocumented, Undocumented,
	ldd, cpd, ind, outd,
	Undocumented, Undocumented, Undocumented, Undocumented,
	ldir, cpir, inir, otir,
	Undocumented, Undocumented, Undocumented, Undocumented,
	lddr, cpdr, indr, otdr,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
	Undocumented, Undocumented, Undocumented, Undocumented,
#ifdef LEVEL_LOADER
	Undocumented, Undocumented, Undocumented, level_loader_trap,
#else
	Undocumented, Undocumented, Undocumented, Undocumented,
#endif
#ifdef ZX_IF1
	Undocumented, Undocumented, rs232_ip_trap, rs232_op_trap,
#else
	Undocumented, Undocumented, Undocumented, Undocumented,
#endif
};


/* Undocumented instructions force the emulator to fall into the 'monitor'
 * for the moment. */
void Undocumented()
{
	extern void PrintCpuState();

	PrintCpuState();
}

void in_b__c_()		/* in b,(c) */
{
	*b = in_byte(*bc);
}

void out__c__b()
{
	out_byte(*bc, *b);
}

void sbc_hl_bc()
{
	*hl = subtract_and_test16(*hl, *bc, Tst(carryFlag) ? 1 : 0);
}

void ld__NN__bc()		/* ld (NN), bc */
{
	work16 = GetNext2Ops;
	mem_write(work16,*c);
	mem_write(work16 + 1,*b);
}

/* WARNING: neg - half-carry not implemented */
void neg()
{
	*a = subtract_and_test(0, *a, 0);
#if 0
	carryFlag = !(*a);
	par_overFlag = (*a == 0x80);
	*a = (uns8)(0 - (sgn8) *a);
	/* *a = (*a ^ 0xff) + 1; */
	zeroFlag = !(*a);
	signFlag = *a & 0x80;
	Set(addsubFlag);
#endif
}

/* WARNING: retn not fully implemented */
void retn()
{
	*pc = PopValue;
}

void im_0()
{
	theProcessor->im = 0;
}

void ld_i_a()
{
	theProcessor->int_vec = *a;
}

void in_c__c_()
{
	*c = in_byte(*bc);
}

void out__c__c()
{
	out_byte(*bc, *c);
}

void adc_hl_bc()
{
	*hl = add_and_test16(*hl, *bc, Tst(carryFlag)? 1 : 0);
}

void ld_bc__NN_()		/* ld bc,(NN) */
{
	work16 = GetNext2Ops;
	*c = theMemory[work16];
	*b = theMemory[work16 + 1];
}

/* for our purposes, this is equivalent to ret */
void reti()
{
	*pc = PopValue;
}

void ld_r_a()
{
	theProcessor->refresh = *a;
}

void in_d__c_()
{
	*d = in_byte(*bc);
}

void out__c__d()
{
	out_byte(*bc, *d);
}

void sbc_hl_de()
{
	*hl = subtract_and_test16(*hl, *de, Tst(carryFlag) ? 1 : 0);
}

void ld__NN__de()
{
	work16 = GetNext2Ops;
	mem_write(work16,*e);
	mem_write(work16 + 1,*d);
}

void im_1()
{
	theProcessor->im = 1;
}

void ld_a_i()
{
	*a = theProcessor->int_vec;
	par_overFlag = theProcessor->iff2;
	signFlag = (*a & 0x80);
	zeroFlag = !(*a);
}

void in_e__c_()
{
	*e = in_byte(*bc);
}

void out__c__e()
{
	out_byte(*bc, *e);
}

void adc_hl_de()
{
	*hl = add_and_test16(*hl, *de, Tst(carryFlag)? 1 : 0);
}

void ld_de__NN_()
{
	work16 = GetNext2Ops;
	*e = theMemory[work16];
	*d = theMemory[work16 + 1];
}

void im_2()
{
	theProcessor->im = 2;
}

void ld_a_r()
{
	*a = theProcessor->refresh; /* = rand() & 0xff; */
	par_overFlag = theProcessor->iff2;
	signFlag = (*a & 0x80);
	zeroFlag = !(*a);
}

void in_h__c_()
{
	*h = in_byte(*bc);
}

void out__c__h()
{
	out_byte(*bc, *h);
}

void sbc_hl_hl()
{
	*hl = subtract_and_test16(*hl, *hl, Tst(carryFlag) ? 1 : 0);
}

void ld__nn__hl()
{
	work16 = GetNext2Ops;
	mem_write(work16,*l);
	mem_write(work16 + 1,*h);
}

void rrd()
{
	work8 = theMemory[*hl];
	work8_2 = (work8 >> 4) | ((*a & 0x0f) << 4);
	*a = (*a & 0xf0) | (work8 & 0x0f);
	signFlag = *a & 0x80;
	zeroFlag = !(*a);
	par_overFlag = parity_tbl[*a];
	mem_write(*hl,work8_2);
}

void in_l__c_()
{
	*l = in_byte(*bc);
}

void out__c__l()
{
	out_byte(*bc, *l);
}

void adc_hl_hl()
{
	*hl = add_and_test16(*hl, *hl, Tst(carryFlag)? 1 : 0);
}

/* ld_hl__NN_ defined in z80ops.c */

void rld()
{
	work8 = theMemory[*hl];
	work8_2 = ((work8 <<4) | (*a & 0x0f)) & 0xff;
	*a = (*a & 0xf0) | (work8 >> 4);
	signFlag = *a & 0x80;
	zeroFlag = !(*a);
	par_overFlag = parity_tbl[*a];
	mem_write(*hl, work8_2);
}

void in_f__c_()
{
	*f = in_byte(*bc);
	RetrieveFlags();
}

void sbc_hl_sp()
{
	*hl = subtract_and_test16(*hl, *sp, Tst(carryFlag) ? 1 : 0);
}

void ld__NN__sp()
{
	work16 = GetNext2Ops;
	mem_write(work16,(uns8)(*sp & 0xff));
	mem_write(work16 + 1,(uns8)(*sp >> 8));
}

void in_a__c_()
{
	*a = in_byte(*bc);
}

void out__c__a()
{
	out_byte(*bc, *a);
}

void adc_hl_sp()
{
	*hl = add_and_test16(*hl, *sp, Tst(carryFlag)? 1 : 0);
}

void ld_sp__NN_()
{
	work16 = GetNext2Ops;
	*sp = theMemory[work16] + (theMemory[work16 + 1] << 8);
}

void ldi()
{
	mem_write(*de,theMemory[*hl]);
	(*de)++; (*hl)++; (*bc)--;
	par_overFlag = *bc; Clr(hcarryFlag); Clr(addsubFlag);
}

void cpi()
{
	compare_and_test(theMemory[*hl]);
	(*hl)++; (*bc)--;
	par_overFlag = *bc;
}

void ini()
{
	mem_write(*hl, in_byte(*bc));
	(*b)--; (*hl)++;
	zeroFlag = !(*b);
	Set(addsubFlag);
}

void outi()
{
	out_byte(*bc, theMemory[*hl]);
	(*b)--; (*hl)++;
	zeroFlag = !(*b);
	Set(addsubFlag);
}

void ldd()
{
	mem_write(*de,theMemory[*hl]);
	(*de)--; (*hl)--; (*bc)--;
	par_overFlag = *bc; Clr(hcarryFlag); Clr(addsubFlag);
}

void cpd()
{
	compare_and_test(theMemory[*hl]);
	(*hl)--; (*bc)--;
	par_overFlag = *bc;
}

void ind()
{
	mem_write(*hl, in_byte(*bc));
	(*b)--; (*hl)--;
	zeroFlag = !(*b);
	Set(addsubFlag);
}

void outd()
{
	out_byte(*bc, theMemory[*hl]);
	(*b)--; (*hl)--;
	zeroFlag = !(*b);
	Set(addsubFlag);
}

void ldir()
{
	do {
		mem_write(*de,theMemory[*hl]);
		(*hl)++; (*de)++; (*bc)--;
	} while (*bc);

	Clr(hcarryFlag); Clr(par_overFlag); Clr(addsubFlag);
}

void cpir()
{
	do {
		compare_and_test(theMemory[*hl]);
		(*hl)++; (*bc)--;
		par_overFlag = *bc;
	} while (*bc && !zeroFlag);
}

void inir()
{
	do {
		mem_write(*hl, in_byte(*bc));
		(*b)--; (*hl)++;
	} while (*b);

	Set(zeroFlag); Set (addsubFlag);
}

void otir()
{
	do {
		out_byte(*bc, theMemory[*hl]);
		(*b)--; (*hl)++;
	} while (*b);

	Set(zeroFlag); Set(addsubFlag);
}

void lddr()
{
	do {
		mem_write(*de,theMemory[*hl]);
		(*hl)--; (*de)--; (*bc)--;
	} while (*bc);

	Clr(hcarryFlag); Clr(par_overFlag); Clr(addsubFlag);
}

void cpdr()
{
	do {
		compare_and_test(theMemory[*hl]);
		(*hl)--; (*bc)--;
		par_overFlag = *bc;
	} while (*bc && !zeroFlag);
}

void indr()
{
	do {
		mem_write(*hl, in_byte(*bc));
		(*b)--; (*hl)--;
	} while (*b);

	Set(zeroFlag); Set (addsubFlag);
}

void otdr()
{
	do {
		out_byte(*bc, theMemory[*hl]);
		(*b)--; (*hl)--;
	} while (*b);

	Set(zeroFlag); Set(addsubFlag);
}
