/*====================================================================
  opcodes_ed.c -> This file executes the ED opcodes.

  Another prefix that "creates" new instructions. This prefix also
  introduces some undocumented opcodes that we've tried to include
  here. Maybe their implementation it's wrong: if you can find any
  mistake about how we have implemented/interpreted them, please
  let us know.

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

 Copyright (c) 2000 Santiago Romero Iglesias.
 Email: sromero@escomposlinux.org
 =====================================================================*/

/* 8 clock cycles minimum = ED opcode = 4 + 4 */

opcode = Z80ReadMem (r_PC);
r_PC++;

switch (opcode)
  {
  case LD_BC_xNNe:
    LOAD_rr_nn (r_BC);
    AddCycles (4 + 4 + 12);
    break;
  case LD_DE_xNNe:
    LOAD_rr_nn (r_DE);
    AddCycles (4 + 4 + 12);
    break;
  case LD_HL_xNNe:
    LOAD_rr_nn (r_HL);
    AddCycles (4 + 4 + 12);
    break;
  case LD_SP_xNNe:
    LOAD_rr_nn (r_SP);
    AddCycles (4 + 4 + 12);
    break;

  case LD_xNNe_BC:
    STORE_nn_rr (r_BC);
    AddCycles (4 + 4 + 12);
    break;
  case LD_xNNe_DE:
    STORE_nn_rr (r_DE);
    AddCycles (4 + 4 + 12);
    break;
  case LD_xNNe_HL:
    STORE_nn_rr (r_HL);
    AddCycles (4 + 4 + 12);
    break;
  case LD_xNNe_SP:
    STORE_nn_rr (r_SP);
    AddCycles (4 + 4 + 12);
    break;

  case NEG:
  case ED_5C:
  case ED_74:
  case ED_7C:
  case ED_6C:
  case ED_54:
  case ED_4C:
  case ED_64:
    NEG_A ();
    AddCycles (4 + 4);
    break;

  case RETI:
  case RETN:
  case ED_65:
  case ED_6D:
  case ED_75:
  case ED_7D:
  case ED_5D:
  case ED_55:
    r_IFF1 = r_IFF2;
    RET_nn ();
    AddCycles (4 + 4 + 6);
    break;

  case IM_0:
  case ED_4E:			/* * IM 0/1 */
  case ED_6E:
  case ED_66:
    regs->IM = 0;
    AddCycles (4 + 4);
    break;			/* * IM 0 */


  case IM_1:
  case ED_76:
    regs->IM = 1;
    AddCycles (4 + 4);
    break;

  case IM_2:
  case ED_7E:
    regs->IM = 2;
    AddCycles (4 + 4);
    break;

  case ED_77:
  case ED_7F:
    AddCycles (4 + 4);
    break;			/* * NOP */

  case OUT_xC_B:
    Z80OutPort (regs, r_BC, r_B);
    AddCycles (4 + 4 + 4);
    break;
  case OUT_xC_C:
    Z80OutPort (regs, r_BC, r_C);
    AddCycles (4 + 4 + 4);
    break;
  case OUT_xC_D:
    Z80OutPort (regs, r_BC, r_D);
    AddCycles (4 + 4 + 4);
    break;
  case OUT_xC_E:
    Z80OutPort (regs, r_BC, r_E);
    AddCycles (4 + 4 + 4);
    break;
  case OUT_xC_H:
    Z80OutPort (regs, r_BC, r_H);
    AddCycles (4 + 4 + 4);
    break;
  case OUT_xC_L:
    Z80OutPort (regs, r_BC, r_L);
    AddCycles (4 + 4 + 4);
    break;
  case OUT_xC_A:
    Z80OutPort (regs, r_BC, r_A);
    AddCycles (4 + 4 + 4);
    break;
    /* * OUT (C), 0 */
  case ED_71:
    Z80OutPort (regs, r_BC, 0);
    AddCycles (4 + 4 + 4);
    break;

  case IN_B_xC:
    IN_PORT (r_B, r_BC);
    AddCycles (4 + 4 + 4);
    break;
  case IN_C_xC:
    IN_PORT (r_C, r_BC);
    AddCycles (4 + 4 + 4);
    break;
  case IN_D_xC:
    IN_PORT (r_D, r_BC);
    AddCycles (4 + 4 + 4);
    break;
  case IN_E_xC:
    IN_PORT (r_E, r_BC);
    AddCycles (4 + 4 + 4);
    break;
  case IN_L_xC:
    IN_PORT (r_L, r_BC);
    AddCycles (4 + 4 + 4);
    break;
  case IN_H_xC:
    IN_PORT (r_H, r_BC);
    AddCycles (4 + 4 + 4);
    break;
  case IN_A_xC:
    IN_PORT (r_A, r_BC);
    AddCycles (4 + 4 + 4);
    break;
  case IN_F_xC:
    IN_PORT (r_meml, r_BC);
    AddCycles (4 + 4 + 4);
    break;

  case LD_A_I:
    r_A = regs->I;
    r_F = (r_F & FLAG_C) | sz53_table[r_A] | (regs->IFF2 ? FLAG_V : 0);
    AddCycles (4 + 4 + 1);
    break;

  case LD_I_A:
    regs->I = r_A;
    AddCycles (4 + 4 + 1);
    break;


  case LD_A_R:
    r_A = (regs->R.W & 0x7f) | (regs->R.W & 0x80);
    r_F = (r_F & FLAG_C) | sz53_table[r_A] | (regs->IFF2 ? FLAG_V : 0);
    AddCycles (4 + 4 + 1);
    break;

  case LD_R_A:
    regs->R.W = r_A;
    AddCycles (4 + 4 + 1);
    break;


  case ADC_HL_BC:
    ADC_WORD (r_BC);
    AddCycles (4 + 4 + 4 + 1 + 2);
    break;
  case ADC_HL_DE:
    ADC_WORD (r_DE);
    AddCycles (4 + 4 + 4 + 1 + 2);
    break;
  case ADC_HL_HL:
    ADC_WORD (r_HL);
    AddCycles (4 + 4 + 4 + 1 + 2);
    break;
  case ADC_HL_SP:
    ADC_WORD (r_SP);
    AddCycles (4 + 4 + 4 + 1 + 2);
    break;

  case SBC_HL_BC:
    SBC_WORD (r_BC);
    AddCycles (4 + 4 + 4 + 1 + 2);
    break;
  case SBC_HL_DE:
    SBC_WORD (r_DE);
    AddCycles (4 + 4 + 4 + 1 + 2);
    break;
  case SBC_HL_HL:
    SBC_WORD (r_HL);
    AddCycles (4 + 4 + 4 + 1 + 2);
    break;
  case SBC_HL_SP:
    SBC_WORD (r_SP);
    AddCycles (4 + 4 + 4 + 1 + 2);
    break;

  case RRD:
    r_meml = Z80ReadMem (r_HL);
    Z80WriteMem (r_HL, (r_A << 4) | (r_meml >> 4), regs);
    r_A = (r_A & 0xf0) | (r_meml & 0x0f);
    r_F = (r_F & FLAG_C) | sz53p_table[r_A];
    AddCycles (4 + 4 + 10);
    break;

  case RLD:
    r_meml = Z80ReadMem (r_HL);
    Z80WriteMem (r_HL, (r_meml << 4) | (r_A & 0x0f), regs);
    r_A = (r_A & 0xf0) | (r_meml >> 4);
    r_F = (r_F & FLAG_C) | sz53p_table[r_A];
    AddCycles (4 + 4 + 10);
    break;

  case LDI:
    r_meml = Z80ReadMem (r_HL);
    r_HL++;
    Z80WriteMem (r_DE, r_meml, regs);
    r_DE++;
    r_BC--;
    r_meml += r_A;
    r_F = (r_F & (FLAG_C | FLAG_Z | FLAG_S)) |
      (r_BC ? FLAG_V : 0) | (r_meml & FLAG_3) |
      ((r_meml & 0x02) ? FLAG_5 : 0);
    AddCycles (4 + 4 + 4 + 4);
    break;

  case LDIR:
    r_meml = Z80ReadMem (r_HL);
    r_HL++;
    Z80WriteMem (r_DE, r_meml, regs);
    r_DE++;
    r_BC--;
    r_meml += r_A;
    r_F = (r_F & (FLAG_C | FLAG_Z | FLAG_S)) 
      	| (r_meml & FLAG_3) | ((r_meml & 0x02) ? FLAG_5 : 0) ;
//      | (r_BC ? FLAG_V : 0) ;
    AddCycles (4 + 4 + 4 + 4);
    if (r_BC)
      {
	r_PC -= 2;
	AddCycles (5);
      }
    break;
  case LDD:
    r_meml = Z80ReadMem (r_HL);
    r_HL--;
    Z80WriteMem (r_DE, r_meml, regs);
    r_DE--;
    r_BC--;
    r_meml += r_A;
    r_F = (r_F & (FLAG_C | FLAG_Z | FLAG_S)) |
      (r_BC ? FLAG_V : 0) | (r_meml & FLAG_3) |
      ((r_meml & 0x02) ? FLAG_5 : 0);
    AddCycles (4 + 4 + 4 + 4);
    break;


  case LDDR:
    r_meml = Z80ReadMem (r_HL);
    Z80WriteMem (r_DE, r_meml, regs);
    r_HL--;
    r_DE--;
    r_BC--;
    r_meml += r_A;
    r_F = (r_F & (FLAG_C | FLAG_Z | FLAG_S)) 
      	|  (r_meml & FLAG_3) | ((r_meml & 0x02) ? FLAG_5 : 0) ;
//      | (r_BC ? FLAG_V : 0) ; 
    AddCycles (4 + 4 + 4 + 4 + 1);
    if (r_BC)
      {
	r_PC -= 2;
	AddCycles (4);
      }
    break;

    // I had lots of problems with CPI, INI, CPD, IND, OUTI, OUTD and so...
    // Thanks a lot to Philip Kendall for letting me to take a look to his
    // fuse emulator and allowing me to use their flag routines :-)
  case CPI:
    r_meml = Z80ReadMem (r_HL);
    r_memh = r_A - r_meml;
    r_opl = ((r_A & 0x08) >> 3) |
      (((r_meml) & 0x08) >> 2) | ((r_meml & 0x08) >> 1);
    r_HL++;
    r_BC--;
    r_F = (r_F & FLAG_C) |
      (r_BC ? (FLAG_V | FLAG_N) : FLAG_N) |
      halfcarry_sub_table[r_opl] | (r_memh ? 0 : FLAG_Z) | (r_memh & FLAG_S);
    if (r_F & FLAG_H)
      r_memh--;
    r_F |= (r_memh & FLAG_3) | ((r_memh & 0x02) ? FLAG_5 : 0);
    AddCycles (4 + 4 + 4 + 4);
    break;

  case CPIR:
    r_meml = Z80ReadMem (r_HL);
    r_memh = r_A - r_meml;
    r_opl = ((r_A & 0x08) >> 3) |
      (((r_meml) & 0x08) >> 2) | ((r_meml & 0x08) >> 1);
    r_HL++;
    r_BC--;
    r_F = (r_F & FLAG_C) |
      (r_BC ? (FLAG_V | FLAG_N) : FLAG_N) |
      halfcarry_sub_table[r_opl] | (r_memh ? 0 : FLAG_Z) | (r_memh & FLAG_S);
    if (r_F & FLAG_H)
      r_memh--;
    r_F |= (r_memh & FLAG_3) | ((r_memh & 0x02) ? FLAG_5 : 0);
    if ((r_F & (FLAG_V | FLAG_Z)) == FLAG_V)
      {
	AddCycles (5);
	r_PC -= 2;
      }
    AddCycles (4 + 4 + 4 + 4);
    break;

  case CPD:
    r_meml = Z80ReadMem (r_HL);
    r_memh = r_A - r_meml;
    r_opl = ((r_A & 0x08) >> 3) |
      (((r_meml) & 0x08) >> 2) | ((r_memh & 0x08) >> 1);
    r_HL--;
    r_BC--;
    r_F = (r_F & FLAG_C) |
      (r_BC ? (FLAG_V | FLAG_N) : FLAG_N) |
      halfcarry_sub_table[r_opl] | (r_memh ? 0 : FLAG_Z) | (r_memh & FLAG_S);
    if (r_F & FLAG_H)
      r_memh--;
    r_F |= (r_memh & FLAG_3) | ((r_memh & 0x02) ? FLAG_5 : 0);
    AddCycles (4 + 4 + 4 + 4);
    break;

  case CPDR:
    r_meml = Z80ReadMem (r_HL);
    r_memh = r_A - r_meml;
    r_opl = ((r_A & 0x08) >> 3) |
      (((r_meml) & 0x08) >> 2) | ((r_memh & 0x08) >> 1);
    r_HL--;
    r_BC--;
    r_F = (r_F & FLAG_C) |
      (r_BC ? (FLAG_V | FLAG_N) : FLAG_N) |
      halfcarry_sub_table[r_opl] | (r_memh ? 0 : FLAG_Z) | (r_memh & FLAG_S);
    if (r_F & FLAG_H)
      r_memh--;
    r_F |= (r_memh & FLAG_3) | ((r_memh & 0x02) ? FLAG_5 : 0);
    if ((r_F & (FLAG_V | FLAG_Z)) == FLAG_V)
      {
	AddCycles (5);
	r_PC -= 2;
      }
    AddCycles (4 + 4 + 4 + 4);
    break;

    // I/O block instructions by Metalbrain - 14-5-2001
  case IND:
    r_meml = Z80InPort (regs, (r_BC));
    r_memh = 0;
    r_F = (r_F & FLAG_C) | ((r_B) & 0x0f ? 0 : FLAG_H) | FLAG_N;
    (r_B)--;
    r_F |= ((r_B) == 0x7f ? FLAG_V : 0) | sz53_table[(r_B)];
    r_F &= 0xE8;
    Z80WriteMem (r_HL, r_meml, regs);
    r_F |= ((r_meml & 0x80) >> 6);
    r_opl = r_C;
    r_oph = 0;
    r_opl--;
    r_op += r_mem;
    r_oph += (r_oph << 4);
    r_F |= r_oph;
    r_opl = (r_meml & 7) + ((r_C & 7) << 3);
    r_F |= (ioblock_2_table[(r_B)] ^ ioblock_dec1_table[(r_opl)]);
    r_HL--;
    AddCycles (4 + 4 + 4 + 4);
    break;

  case INDR:
    r_meml = Z80InPort (regs, (r_BC));
    r_memh = 0;
    r_F = (r_F & FLAG_C) | ((r_B) & 0x0f ? 0 : FLAG_H) | FLAG_N;
    (r_B)--;
    r_F |= ((r_B) == 0x7f ? FLAG_V : 0) | sz53_table[(r_B)];
    r_F &= 0xE8;
    Z80WriteMem (r_HL, r_meml, regs);
    r_F |= ((r_meml & 0x80) >> 6);
    r_opl = r_C;
    r_oph = 0;
    r_opl--;
    r_op += r_mem;
    r_oph += (r_oph << 4);
    r_F |= r_oph;
    r_opl = (r_meml & 7) + ((r_C & 7) << 3);
    r_F |= (ioblock_2_table[(r_B)] ^ ioblock_dec1_table[(r_opl)]);
    r_HL--;
    if (r_B)
      {
	r_PC -= 2;
	AddCycles (5);
      }
    AddCycles (4 + 4 + 4 + 4);
    break;

  case INI:
    r_meml = Z80InPort (regs, (r_BC));
    r_memh = 0;
    r_F = (r_F & FLAG_C) | ((r_B) & 0x0f ? 0 : FLAG_H) | FLAG_N;
    (r_B)--;
    r_F |= ((r_B) == 0x7f ? FLAG_V : 0) | sz53_table[(r_B)];
    r_F &= 0xE8;
    Z80WriteMem (r_HL, r_meml, regs);
    r_F |= ((r_meml & 0x80) >> 6);
    r_opl = r_C;
    r_oph = 0;
    r_opl++;
    r_op += r_mem;
    r_oph += (r_oph << 4);
    r_F |= r_oph;
    r_opl = (r_meml & 7) + ((r_C & 7) << 3);
    r_F |= (ioblock_2_table[(r_B)] ^ ioblock_inc1_table[(r_opl)]);
    r_HL++;
    AddCycles (4 + 4 + 4 + 4);
    break;


  case INIR:
    r_meml = Z80InPort (regs, (r_BC));
    r_memh = 0;
    r_F = (r_F & FLAG_C) | ((r_B) & 0x0f ? 0 : FLAG_H) | FLAG_N;
    (r_B)--;
    r_F |= ((r_B) == 0x7f ? FLAG_V : 0) | sz53_table[(r_B)];
    r_F &= 0xE8;
    Z80WriteMem (r_HL, r_meml, regs);
    r_F |= ((r_meml & 0x80) >> 6);
    r_opl = r_C;
    r_oph = 0;
    r_opl++;
    r_op += r_mem;
    r_oph += (r_oph << 4);
    r_F |= r_oph;
    r_opl = (r_meml & 7) + ((r_C & 7) << 3);
    r_F |= (ioblock_2_table[(r_B)] ^ ioblock_inc1_table[(r_opl)]);
    r_HL++;
    if (r_B)
      {
	r_PC -= 2;
	AddCycles (5);
      }
    AddCycles (4 + 4 + 4 + 4);
    break;

  case OUTI:
    r_meml = Z80ReadMem (r_HL);
    r_memh = 0;
    r_F = (r_F & FLAG_C) | ((r_B) & 0x0f ? 0 : FLAG_H) | FLAG_N;
    (r_B)--;
    r_F |= ((r_B) == 0x7f ? FLAG_V : 0) | sz53_table[(r_B)];
    r_F &= 0xE8;
    Z80OutPort (regs, r_BC, r_meml);
    r_F |= ((r_meml & 0x80) >> 6);
    r_opl = r_C;
    r_oph = 0;
    r_opl++;
    r_op += r_mem;
    r_oph += (r_oph << 4);
    r_F |= r_oph;
    r_opl = (r_meml & 7) + ((r_C & 7) << 3);
    r_F |= (ioblock_2_table[(r_B)] ^ ioblock_inc1_table[(r_opl)]);
    r_HL++;
    AddCycles (4 + 4 + 4 + 4);
    break;

  case OTIR:
    r_meml = Z80ReadMem (r_HL);
    r_memh = 0;
    r_F = (r_F & FLAG_C) | ((r_B) & 0x0f ? 0 : FLAG_H) | FLAG_N;
    (r_B)--;
    r_F |= ((r_B) == 0x7f ? FLAG_V : 0) | sz53_table[(r_B)];
    r_F &= 0xE8;
    Z80OutPort (regs, r_BC, r_meml);
    r_F |= ((r_meml & 0x80) >> 6);
    r_opl = r_C;
    r_oph = 0;
    r_opl++;
    r_op += r_mem;
    r_oph += (r_oph << 4);
    r_F |= r_oph;
    r_opl = (r_meml & 7) + ((r_C & 7) << 3);
    r_F |= (ioblock_2_table[(r_B)] ^ ioblock_inc1_table[(r_opl)]);
    r_HL++;
    if (r_B)
      {
	r_PC -= 2;
	AddCycles (5);
      }
    AddCycles (4 + 4 + 4 + 4);
    break;


  case OUTD:
    r_meml = Z80ReadMem (r_HL);
    r_memh = 0;
    r_F = (r_F & FLAG_C) | ((r_B) & 0x0f ? 0 : FLAG_H) | FLAG_N;
    (r_B)--;
    r_F |= ((r_B) == 0x7f ? FLAG_V : 0) | sz53_table[(r_B)];
    r_F &= 0xE8;
    Z80OutPort (regs, r_BC, r_meml);
    r_F |= ((r_meml & 0x80) >> 6);
    r_opl = r_C;
    r_oph = 0;
    r_opl--;
    r_op += r_mem;
    r_oph += (r_oph << 4);
    r_F |= r_oph;
    r_opl = (r_meml & 7) + ((r_C & 7) << 3);
    r_F |= (ioblock_2_table[(r_B)] ^ ioblock_dec1_table[(r_opl)]);
    r_HL--;
    AddCycles (4 + 4 + 4 + 4);
    break;

  case OTDR:
    r_meml = Z80ReadMem (r_HL);
    r_memh = 0;
    r_F = (r_F & FLAG_C) | ((r_B) & 0x0f ? 0 : FLAG_H) | FLAG_N;
    (r_B)--;
    r_F |= ((r_B) == 0x7f ? FLAG_V : 0) | sz53_table[(r_B)];
    r_F &= 0xE8;
    Z80OutPort (regs, r_BC, r_meml);
    r_F |= ((r_meml & 0x80) >> 6);
    r_opl = r_C;
    r_oph = 0;
    r_opl--;
    r_op += r_mem;
    r_oph += (r_oph << 4);
    r_F |= r_oph;
    r_opl = (r_meml & 7) + ((r_C & 7) << 3);
    r_F |= (ioblock_2_table[(r_B)] ^ ioblock_dec1_table[(r_opl)]);
    r_HL--;
    if (r_B)
      {
	r_PC -= 2;
	AddCycles (5);
      }
    AddCycles (4 + 4 + 4 + 4);
    break;

// End of Metalbrain's contribution

  case PREFIX_ED:
    AddCycles (4);		/* ED ED xx = 12 cycles min = 4+8 */
    r_PC--;
    break;

  default:
// exit(1);
    AddCycles (4 + 4);		/* Just a NOP */
    break;
  }
