/* 
   Pcm: a PC eMulator
   Copyright (C) 1992 Electronetics, Inc.  All rights reserved.

   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.
 */

/*
 * mov, stos, lods and similar instructions
 */
#include "sim.h"

unsigned char *i_88(pc)
unsigned char *pc; {
  pc = byte_operand(pc);
  *EAPTRL = *REGPTR;
  return pc;
}

unsigned char *i_89(pc)
unsigned char *pc; {
  pc = word_operand(pc);
  *EAPTRH = *REGPTRH;
  *EAPTRL = *REGPTRL;
  return pc;
}

unsigned char *i_8a(pc)
unsigned char *pc; {
  pc = byte_operand(pc);
  *REGPTR = *EAPTRL;
  return pc;
}

unsigned char *i_8b(pc)
unsigned char *pc; {
  pc = word_operand(pc);
  *REGPTRH = *EAPTRH;
  *REGPTRL = *EAPTRL;
  return pc;
}

unsigned char *i_8c(pc)
unsigned char *pc; {
  pc = word_operand(pc);
  switch((unsigned short *)REGPTR-(unsigned short *)qmem) {
  case 0: /* AX */
    *EAPTRL = ESL;
    *EAPTRH = ESH;
  break;
  case 1: /* CX */
    *EAPTRL = CSL;
    *EAPTRH = CSH;
  break;
  case 2: /* DX */
    *EAPTRL = SSL;
    *EAPTRH = SSH;
  break;
  case 3: /* BX */
    *EAPTRL = DSL;
    *EAPTRH = DSH;
  break;
  default:
    unk_op(pc);
  break;
  }
  return pc;
}

unsigned char *i_8e(pc)
unsigned char *pc; {
  pc = word_operand(pc);
  switch((unsigned short *)REGPTR-(unsigned short *)qmem) {
  case 0: /* AX */
    ESL = *EAPTRL;
    ESH = *EAPTRH; 
    SETES;
  break;
  case 2: /* DX */
    SSL = *EAPTRL;
    SSH = *EAPTRH; 
    SETSS;
  break;
  case 3: /* BX */
    DSL = *EAPTRL;
    DSH = *EAPTRH;
    SETDS;
  break;
  default:
    unk_op(pc);
  break;
  }
  return pc;
}

unsigned char *i_a0(pc)
unsigned char *pc; {
  unsigned short offset;
  LOADW2(offset, pc++);
  AL = *(DSEA + offset);
  return pc;
}

unsigned char *i_a1(pc)
unsigned char *pc; {
  unsigned short offset;
  LOADW2(offset, pc++);
  AL = *(DSEA + offset);
  AH = *(DSEA + (offset+1));
  return pc;
}

unsigned char *i_a2(pc)
unsigned char *pc; {
  unsigned short offset;
  LOADW2(offset, pc++);
  *(DSEA + offset) = AL;
  return pc;
}

unsigned char *i_a3(pc)
unsigned char *pc; {
  unsigned short offset;
  LOADW2(offset, pc++);
  *(DSEA + offset) = AL;
  *(DSEA + (offset+1)) = AH;
  return pc;
}

unsigned char *i_a4(pc)
unsigned char *pc; {
  *(ESPTR+DI) = *(DSEA+SI);
  if (DIRECTION) {
    SI--;
    DI--;
  }
  else {
    SI++;
    DI++;
  }
  return pc;
}

unsigned char *i_a5(pc)
unsigned char *pc; {
  *(ESPTR+DI) = *(DSEA+SI);
  *(ESPTR+(DI+1)) = *(DSEA+(SI+1));
  if (DIRECTION) {
    SI -= 2;
    DI -= 2;
  }
  else {
    SI += 2;
    DI += 2;
  }
  return pc;
}

unsigned char *i_a6(pc)
unsigned char *pc; {
  unsigned short opr1, opr2;
  PFLAG3(*(DSEA+SI), *(ESPTR+DI), SUBB);
  if (DIRECTION) {
    SI--;
    DI--;
  }
  else {
    SI++;
    DI++;
  }
  PFLAG1(SUBW);  
  return pc;
}

unsigned char *i_a7(pc)
unsigned char *pc; {
  unsigned short opr1, opr2;
  FL1L = *(DSEA+SI);
  FL1H = *(DSEA+(SI+1));
  FL2L = *(ESPTR+DI);
  FL2H = *(ESPTR+(DI+1));
  if (DIRECTION) {
    SI -= 2;
    DI -= 2;
  }
  else {
    SI += 2;
    DI += 2;
  }
  PFLAG1(SUBW);  
  return pc;
}

unsigned char *i_aa(pc)
unsigned char *pc; {
  *(ESPTR+DI) = AL;
  DIRECTION ? DI-- : DI++;
  return pc;
}

unsigned char *i_ab(pc)
unsigned char *pc; {
  *(ESPTR+DI) = AL;
  *(ESPTR+(DI+1)) = AH;
  if (DIRECTION) {
    DI -= 2;
  }
  else {
    DI += 2;
  }
  return pc;
}

unsigned char *i_ac(pc)
unsigned char *pc; {
  AL = *(DSEA+SI);
  if (DIRECTION) {
    SI--;
  }
  else {
    SI++;
  }
  return pc;
}

unsigned char *i_ad(pc)
unsigned char *pc; {
  AL = *(DSEA+SI);
  AH = *(DSEA+(SI+1));
  if (DIRECTION) {
    SI -= 2;
  }
  else {
    SI += 2;
  }
  return pc;
}

unsigned char *i_ae(pc)
unsigned char *pc; {
  PFLAG3(AL,*(ESPTR+DI),SUBB);
  if (DIRECTION) {
    DI--;
  }
  else {
    DI++;
  }
  return pc;
}

unsigned char *i_af(pc)
unsigned char *pc; {
  unsigned short opr;
  opr = *(ESPTR+DI);
  opr |= (unsigned int)*(ESPTR+DI+1) << 8;
  PFLAG3(AX,opr,SUBW);
  if (DIRECTION) {
    DI -= 2;
  }
  else {
    DI += 2;
  }
  return pc;
}

unsigned char *i_b0(pc)
unsigned char *pc; {
  AL = *pc++;
  return pc;
}

unsigned char *i_b1(pc)
unsigned char *pc; {
  CL = *pc++;
  return pc;
}

unsigned char *i_b2(pc)
unsigned char *pc; {
  DL = *pc++;
  return pc;
}

unsigned char *i_b3(pc)
unsigned char *pc; {
  BL = *pc++;
  return pc;
}

unsigned char *i_b4(pc)
unsigned char *pc; {
  AH = *pc++;
  return pc;
}

unsigned char *i_b5(pc)
unsigned char *pc; {
  CH = *pc++;
  return pc;
}

unsigned char *i_b6(pc)
unsigned char *pc; {
  DH = *pc++;
  return pc;
}

unsigned char *i_b7(pc)
unsigned char *pc; {
  BH = *pc++;
  return pc;
}

unsigned char *i_b8(pc)
unsigned char *pc; {
  AL = *pc++;
  AH = *pc++;
  return pc;
}

unsigned char *i_b9(pc)
unsigned char *pc; {
  CL = *pc++;
  CH = *pc++;
  return pc;
}

unsigned char *i_ba(pc)
unsigned char *pc; {
  DL = *pc++;
  DH = *pc++;
  return pc;
}

unsigned char *i_bb(pc)
unsigned char *pc; {
  BL = *pc++;
  BH = *pc++;
  return pc;
}

unsigned char *i_bc(pc) 
unsigned char *pc; {
  SPL = *pc++;
  SPH = *pc++;
  return pc;
}

unsigned char *i_bd(pc) 
unsigned char *pc; {
  BPL = *pc++;
  BPH = *pc++;
  return pc;
}

unsigned char *i_be(pc) 
unsigned char *pc; {
  SIL = *pc++;
  SIH = *pc++;
  return pc;
}

unsigned char *i_bf(pc) 
unsigned char *pc; {
  DIL = *pc++;
  DIH = *pc++;
  return pc;
}

unsigned char *i_c4(pc)
unsigned char *pc; {
  pc = word_operand(pc);
  *REGPTRH = *EAPTRH;
  *REGPTRL = *EAPTRL;
  ESL = *(EAPTRL+2);
  ESH = *(EAPTRL+3);
  SETES;
  return pc;
}

unsigned char *i_c5(pc)
unsigned char *pc; {
  pc = word_operand(pc);
  *REGPTRH = *EAPTRH;
  *REGPTRL = *EAPTRL;
  DSL = *(EAPTRL+2);
  DSH = *(EAPTRL+3);
  SETDS;
  return pc;
}

unsigned char *i_c6(pc)
unsigned char *pc; {
  pc = byte_operand(pc);
  *EAPTRL = *pc++;
  return pc;
}

unsigned char *i_c7(pc)
unsigned char *pc; {
  unsigned short opr;
  pc = word_operand(pc);
  *EAPTRL = *pc++;
  *EAPTRH = *pc++;
  return pc;
}

unsigned char *i_f2(pc)
unsigned char *pc; {
  unsigned char rep_code;
  unsigned char *repds;
  unsigned char *srcptr, *dstptr;
  unsigned short opr1;
  int count, samt;
  repds = DSEA;
again_f2:
  rep_code = *pc++;
  switch(rep_code) {
  case 0xa4:
    samt = 0;
    goto rep_common_f2;
  case 0xa5:
    samt = 1;
rep_common_f2:
    srcptr = repds + SI;
    dstptr = ESPTR + DI;
    count = CX << samt;
    if (DIRECTION) {
      bblcopy (srcptr, dstptr, count, samt); /* watch out for overlap! */
    }
    else {
      fblcopy (srcptr, dstptr, count, samt); /* watch out for overlap! */
    }
    if (DIRECTION) {
      SI -= count;
      DI -= count;
    }
    else {
      SI += count;
      DI += count;
    }
    CX = 0;
  break;
  case 0xae:
    count = DIRECTION ? -1 : 1;
    dstptr = ESPTR + DI;
    ZERO = 0;
    FLAGS_OK = 1;
    while (CX) {
      if (*dstptr == AL) {
        ZERO = 1;
        dstptr += count;
        DI += count;
        CX--;
        break;
      }
      dstptr += count;
      DI += count;
      CX--;
    }
    dstptr -= count;
    CARRY = ((unsigned int)AL < (unsigned int)*dstptr);
  break;
  case 0xaf:
    count = DIRECTION ? -2 : 2;
    dstptr = ESPTR + DI;
    ZERO = 0;
    FLAGS_OK = 1;
    while (CX) {
      if (*dstptr == AL && *(dstptr+1) == AH) {
        ZERO = 1;
        dstptr += count;
        DI += count;
        CX--;
        break;
      }
      dstptr += count;
      DI += count;
      CX--;
    }
    dstptr -= count;
    opr1 = *dstptr | (unsigned int)*(dstptr+1) << 8;
    CARRY = AX < opr1;
  break;
  default: printf("i_f2: ");
    return unk_op(pc); break;
  }
  return pc;
}

unsigned char *i_f3(pc)
unsigned char *pc; {
  unsigned char rep_code;
  unsigned char *repds;
  unsigned char *srcptr, *dstptr;
  unsigned short opr1, opr2;
  int count, samt;
  repds = DSEA;
again_f3:
  rep_code = *pc++;
  switch(rep_code) {
  case 0x26:
    repds = ESPTR;
    goto again_f3;
  case 0xa4:
    samt = 0;
    goto rep_common_f3;
  case 0xa5:
    samt = 1;
rep_common_f3:
    srcptr = repds + SI;
    dstptr = ESPTR + DI;
    count = CX << samt;
    if (DIRECTION) {
      bblcopy (srcptr, dstptr, count, samt); /* watch out for overlap! */
    }
    else {
      fblcopy (srcptr, dstptr, count, samt); /* watch out for overlap! */
    }
    if (DIRECTION) {
      SI -= count;
      DI -= count;
    }
    else {
      SI += count;
      DI += count;
    }
    CX = 0;
  break;
  case 0xa6:
    count = DIRECTION ? -1 : 1;
    dstptr = ESPTR + DI;
    srcptr = repds + SI;
    ZERO = 1;
    while (CX) {
      if (*dstptr != *srcptr) {
        ZERO = 0;
      }
      dstptr += count;
      srcptr += count;
      DI += count;
      SI += count;
      CX--;
      if (!ZERO) {
        break;
      }
    }
    srcptr -= count;
    dstptr -= count;
    CARRY = *srcptr < *dstptr;
    FLAGS_OK = 1;
  break;
  case 0xa7:
    count = DIRECTION ? -2 : 2;
    dstptr = ESPTR + DI;
    srcptr = repds + SI;
    ZERO = 1;
    while (CX) {
      if (*dstptr != *srcptr ||
          *(dstptr+1) != *(srcptr+1)) {
        ZERO = 0;
      }
      dstptr += count;
      srcptr += count;
      DI += count;
      SI += count;
      CX--;
      if (!ZERO) {
        break;
      }
    }
    srcptr -= count;
    dstptr -= count;
    if (*(srcptr+1) == *(dstptr+1)) {
      CARRY = *srcptr < *dstptr;
    }
    else {
      CARRY = *(srcptr+1) < *(dstptr+1);
    }
    FLAGS_OK = 1;
  break;
  case 0xaa:
    count = DIRECTION ? -1 : 1;
    dstptr = ESPTR + DI;
    while (CX) {
      CX--;
      *dstptr = AL;
      dstptr += count;
    }
    DI = dstptr - ESPTR;
  break;
  case 0xab:
    count = DIRECTION ? -2 : 2;
    dstptr = ESPTR + DI;
    while (CX) {
      CX--;
      *dstptr = AL;
      *(dstptr+1) = AH;
      dstptr += count;
    }
    DI = dstptr - ESPTR;
  break;
  case 0xae:
    count = DIRECTION ? -1 : 1;
    dstptr = ESPTR + DI;
    ZERO = 1;
    FLAGS_OK = 1;
    while (CX) {
      if (*dstptr != AL) {
        ZERO = 0;
        dstptr += count;
        DI += count;
        CX--;
        break;
      }
      dstptr += count;
      DI += count;
      CX--;
    }
    dstptr -= count;
    CARRY = ((unsigned int)AL < (unsigned int)*dstptr);
  break;
  case 0xaf:
    count = DIRECTION ? -2 : 2;
    dstptr = ESPTR + DI;
    ZERO = 1;
    FLAGS_OK = 1;
    while (CX) {
      if (*dstptr != AL || *(dstptr+1) != AH) {
        ZERO = 0;
        dstptr += count;
        DI += count;
        CX--;
        break;
      }
      dstptr += count;
      DI += count;
      CX--;
    }
    dstptr -= count;
    opr1 = *dstptr | (unsigned int)*(dstptr+1) << 8;
    CARRY = AX < opr1;
  break;
  default:  printf("i_f3: ");
    return unk_op(pc); break;
  }
  return pc;
}
