/*====================================================================/*
  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(r_B, r_BC); AddCycles( 4+4+4 ); break;
  case  IN_C_xC     :  IN(r_C, r_BC); AddCycles( 4+4+4 ); break;
  case  IN_D_xC     :  IN(r_D, r_BC); AddCycles( 4+4+4 ); break;
  case  IN_E_xC     :  IN(r_E, r_BC); AddCycles( 4+4+4 ); break;
  case  IN_L_xC     :  IN(r_L, r_BC); AddCycles( 4+4+4 ); break;
  case  IN_H_xC     :  IN(r_H, r_BC); AddCycles( 4+4+4 ); break;
  case  IN_A_xC     :  IN(r_A, r_BC); AddCycles( 4+4+4 ); break;
  case  IN_F_xC     :  IN(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_BC ? FLAG_V:0 ) | ( r_meml & FLAG_3 ) |
                       ((r_meml & 0x02) ? FLAG_5 : 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_BC ? FLAG_V : 0 ) | ( r_meml & FLAG_3 ) |
                       ((r_meml & 0x02) ? FLAG_5 : 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;

/*
                       // OUTI contributed by Alvaro Alea
  case OUTI :           Z80OutPort( regs, r_BC, Z80ReadMem( r_HL )) ;
                        r_HL++ ; r_B-- ;
                        if (r_B==0)
                            r_F |= FLAG_Z;
                        else
                            r_F &= !FLAG_Z;
                        r_F |= FLAG_N;
                        AddCycles( 4+4+4+4 ); break;
*/
                    
  // I/O block instructions by Metalbrain - 14-5-2001
  case  IND         :  r_meml = Z80InPort((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((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((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((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 */
///!!!    if(regs->DecodingErrors)
///!!!      printf( "z80 core: Unknown instruction: ED %02Xh at PC=%04Xh.\n",
///!!!               Z80ReadMem(r_PC-1),r_PC-2 );
    break;
}