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

/*
 * arithmetic instructions
 */
#include "sim.h"

unsigned char *i_00(pc)
unsigned char *pc; {
if (!*pc && !*(pc+1) && !*(pc+2) && !*(pc+3)) {
  pcmexit("error: executing zero block\n");
}
  pc = byte_operand(pc); /* Not done yet */
  PFLAG3(*EAPTRL,*REGPTR,ADDB);
  *EAPTRL += *REGPTR;
  return pc;
}

unsigned char *i_01(pc)
unsigned char *pc; {
  unsigned short opr;
  pc = word_operand(pc);
  opr =  *EAPTRL | (unsigned int)*EAPTRH << 8;
  PFLAG3(opr, *(unsigned short *)REGPTR, ADDW);
  opr += *(unsigned short *)REGPTR;
  *EAPTRL = opr;
  *EAPTRH = opr >> 8;
  return pc;
}

unsigned char *i_02(pc)
unsigned char *pc; {
  pc = byte_operand(pc);
  PFLAG3(*REGPTR,*EAPTRL,ADDB);
  *REGPTR += *EAPTRL;
  return pc;
}

unsigned char *i_03(pc)
unsigned char *pc; {
  unsigned short opr;
  pc = word_operand(pc);
  opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
  PFLAG3(*(unsigned short *)REGPTR,opr,ADDW);
  *(unsigned short *)REGPTR += opr;
  return pc;
}

unsigned char *i_04(pc)
unsigned char *pc; {
  unsigned char opr;
  opr = *pc++;
  PFLAG3(AL, opr, ADDB);
  AL += opr;
  return pc;
}

unsigned char *i_05(pc)
unsigned char *pc; {
  unsigned short opr;
  LOADW2(opr,pc++);
  PFLAG3(AX, opr, ADDW);
  AX += opr;
  return pc;
}

unsigned char *i_10(pc)
unsigned char *pc; {
  compute_flags();
  pc = byte_operand(pc);
  PFLAG4(*EAPTRL, *REGPTR, CARRY, ADCB);
  *EAPTRL += *REGPTR + CARRY;
  return pc;
}

unsigned char *i_11(pc)
unsigned char *pc; {
  unsigned short opr;
  compute_flags();
  pc = word_operand(pc);
  opr = *EAPTRL | ((unsigned int)*EAPTRH << 8);
  PFLAG4(opr, *(unsigned short *)REGPTR, CARRY, ADCW);
  opr += *(unsigned short *)REGPTR + CARRY;
  *EAPTRL = opr;
  *EAPTRH = opr >> 8;
  return pc;
}

unsigned char *i_12(pc)
unsigned char *pc; {
  compute_flags();
  pc = byte_operand(pc);
  PFLAG4(*EAPTRL, *REGPTR, CARRY, ADCB);
  *REGPTR += *EAPTRL + CARRY;
  return pc;
}

unsigned char *i_13(pc)
unsigned char *pc; {
  unsigned short opr;
  compute_flags();
  pc = word_operand(pc);
  opr = *EAPTRL | ((unsigned int)*EAPTRH << 8);
  PFLAG4(opr, *(unsigned short *)REGPTR, CARRY, ADCW);
  *(unsigned short *)REGPTR += opr + CARRY;
  return pc;
}

unsigned char *i_14(pc)
unsigned char *pc; {
  compute_flags();
  PFLAG4(AL, *pc, CARRY, ADCB);
  AL += *pc++ + CARRY;
  return pc;
}

unsigned char *i_15(pc)
unsigned char *pc; {
  unsigned short opr;
  compute_flags();
  opr = *pc++;
  opr |= (unsigned int)*pc++ << 8;
  PFLAG4(AX, opr, CARRY, ADCW);
  AX += opr + CARRY;
  return pc;
}

unsigned char *i_18(pc)
unsigned char *pc; {
  compute_flags();
  pc = byte_operand(pc);
  PFLAG4(*EAPTRL,*(unsigned short *)REGPTR, CARRY, SBBB);
  *EAPTRL -= *(unsigned short *)REGPTR + CARRY;
  return pc;
}

unsigned char *i_19(pc)
unsigned char *pc; {
  unsigned short opr;
  compute_flags();
  pc = word_operand(pc);
  opr = *EAPTRL | ((unsigned int)*EAPTRH << 8);
  PFLAG4(opr,*(unsigned short *)REGPTR, CARRY, SBBW);
  opr -= *(unsigned short *)REGPTR + CARRY;
  *EAPTRL = opr;
  *EAPTRH = opr >> 8;
  return pc;
}

unsigned char *i_1a(pc)
unsigned char *pc; {
  compute_flags();
  pc = byte_operand(pc);
  PFLAG4(*REGPTR, *EAPTRL, CARRY, SBBB);
  *REGPTR -= *EAPTRL + CARRY;
  return pc;
}

unsigned char *i_1b(pc)
unsigned char *pc; {
  unsigned short opr;
  compute_flags();
  pc = word_operand(pc);
  opr = *EAPTRL | ((unsigned int)*EAPTRH << 8);
  PFLAG4(*(unsigned short *)REGPTR, opr, CARRY, SBBW);
  *(unsigned short *)REGPTR -= opr + CARRY;
  return pc;
}

unsigned char *i_1c(pc)
unsigned char *pc; {
  compute_flags();
  PFLAG4(AL, *pc, CARRY, SBBB);
  AL -= *pc++ + CARRY;
  return pc;
}

unsigned char *i_1d(pc)
unsigned char *pc; {
  unsigned short opr;
  compute_flags();
  LOADW2(opr,pc++);
  PFLAG4(AX, opr, CARRY, SBBW);
  AX -= opr + CARRY;
  return pc;
}

unsigned char *i_2a(pc)
unsigned char *pc; {
  pc = byte_operand(pc);
  PFLAG3(*REGPTR,*EAPTRL,SUBB);
  *REGPTR -= *EAPTRL;
  return pc;
}

unsigned char *i_2b(pc)
unsigned char *pc; {
  unsigned short opr;
  pc = word_operand(pc);
  opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
  PFLAG3(*(unsigned short *)REGPTR,opr,SUBW);
  *(unsigned short *)REGPTR -= opr;
  return pc;
}

unsigned char *i_28(pc)
unsigned char *pc; {
  pc = byte_operand(pc);
  PFLAG3(*EAPTRL,*REGPTR,SUBB);
  *EAPTRL -= *REGPTR;
  return pc;
}

unsigned char *i_29(pc)
unsigned char *pc; {
  unsigned short opr;
  pc = word_operand(pc);
  opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
  PFLAG3(opr,*(unsigned short *)REGPTR,SUBW);
  opr -= *(unsigned short *)REGPTR;
  *EAPTRL = opr;
  *EAPTRH = opr >> 8;
  return pc;
}

unsigned char *i_2c(pc)
unsigned char *pc; {
  unsigned char opr;
  opr = *pc++;
  PFLAG3(AL,opr,SUBB);
  AL -= opr;
  return pc;
}

unsigned char *i_2d(pc)
unsigned char *pc; {
  unsigned short opr;
  LOADW2(opr,pc++);
  PFLAG3(AX,opr,SUBW);
  AX -= opr;
  return pc;
}

unsigned char *i_38(pc)
unsigned char *pc; {
  pc = byte_operand(pc);
  PFLAG3(*EAPTRL,*REGPTR,SUBB);
  return pc;
}

unsigned char *i_39(pc)
unsigned char *pc; {
  unsigned short opr;
  pc = word_operand(pc);
  FL1L = *EAPTRL;
  FL1H = *EAPTRH;
  PFLAG3(FL1,*(unsigned short *)REGPTR,SUBW);
  return pc;
}

unsigned char *i_3a(pc)
unsigned char *pc; {
  unsigned short opr;
  pc = byte_operand(pc);
  PFLAG3(*REGPTR,*EAPTRL,SUBB);
  return pc;
}

unsigned char *i_3b(pc)
unsigned char *pc; {
  unsigned short opr;
  pc = word_operand(pc);
  FL2L = *EAPTRL;
  FL2H = *EAPTRH;
  PFLAG3(*(unsigned short *)REGPTR,FL2,SUBW);
  return pc;
}

unsigned char *i_3c(pc)
unsigned char *pc; {
  PFLAG3(AL,*pc++,SUBB);
  return pc;
}

unsigned char *i_3d(pc)
unsigned char *pc; {
  FL2L = *pc++;
  FL2H = *pc++;
  PFLAG3(AX,FL2,SUBW);
  return pc;
}

unsigned char *i_40(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(AX, INCW);
  AX++;
  return pc;
}

unsigned char *i_41(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(CX, INCW);
  CX++;
  return pc;
}

unsigned char *i_42(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(DX, INCW);
  DX++;
  return pc;
}

unsigned char *i_43(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(BX, INCW);
  BX++;
  return pc;
}

unsigned char *i_44(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(SP, INCW);
  SP++;
  return pc;
}

unsigned char *i_45(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(BP, INCW);
  BP++;
  return pc;
}

unsigned char *i_46(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(SI, INCW);
  SI++;
  return pc;
}

unsigned char *i_47(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(DI, INCW);
  DI++;
  return pc;
}

unsigned char *i_48(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(AX, DECW);
  AX--;
  return pc;
}

unsigned char *i_49(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(CX, DECW);
  CX--;
  return pc;
}

unsigned char *i_4a(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(DX, DECW);
  DX--;
  return pc;
}

unsigned char *i_4b(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(BX, DECW);
  BX--;
  return pc;
}

unsigned char *i_4c(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(SP, DECW);
  SP--;
  return pc;
}

unsigned char *i_4d(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(BP, DECW);
  BP--;
  return pc;
}

unsigned char *i_4e(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(SI, DECW);
  SI--;
  return pc;
}

unsigned char *i_4f(pc)
unsigned char *pc; {
  compute_flags(); /* Need the old carry bit */
  PFLAG2(DI, DECW);
  DI--;
  return pc;
}

unsigned char *i_80(pc)
unsigned char *pc; {
  unsigned char b;
  pc = byte_operand(pc);
  b  = *pc++;
  switch ((unsigned char *)REGPTR-(unsigned char *)qmem) {
  case _AL: /* AL */
    PFLAG3(*EAPTRL, b, ADDB);
    *EAPTRL += b;
  break;
  case _CL: /* CL */
    *EAPTRL |= b;
    PFLAG2(*EAPTRL, CBOOL);
  break;
  case _DL: /* DL */
    compute_flags();
    *EAPTRL += b + CARRY;
    PFLAG4(*EAPTRL, b, CARRY, ADCB);
  break;
  case _BL: /* BL */
    compute_flags();
    *EAPTRL -= b + CARRY;
    PFLAG4(*EAPTRL, b, CARRY, SBBB);
  break;
  case _AH: /* AH */
    *EAPTRL &= b;
    PFLAG2(*EAPTRL, CBOOL);
  break;
  case _CH: /* CH */
    PFLAG3(*EAPTRL, b, SUBB);
    *EAPTRL -= b;
  break;
  case _DH: /* DH */
    *EAPTRL ^= b;
    PFLAG2(*EAPTRL, CBOOL);
  break;
  case _BH: /* BH */
    PFLAG3(*EAPTRL, b, SUBB);
  break;
  }
  return pc;
}

unsigned char *i_81(pc)
unsigned char *pc; {
  unsigned short b, opr;
  pc = word_operand(pc);
  b  = *pc++;
  b |= ((unsigned int)*pc++ << 8);
  opr = *EAPTRL | ((unsigned int)*EAPTRH << 8);
  switch ((unsigned short *)REGPTR-(unsigned short *)qmem) {
  case 0: /* AX */
    PFLAG3(opr, b, ADDW);
    opr += b;
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 1: /* CX */
    opr |= b;
    PFLAG2(opr, WBOOL);
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 2: /* DX */
    compute_flags();
    PFLAG4(opr, b, CARRY, ADCW);
    opr += b + CARRY;
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 3: /* BX */
    compute_flags();
    PFLAG4(opr, b, CARRY, SBBW);
    opr -= b + CARRY;
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 4: /* SP */
    opr &= b;
    PFLAG2(opr, WBOOL);
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 5: /* BP */
    PFLAG3(opr, b, SUBW);
    opr -= b;
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 6: /* SI */
    opr ^= b;
    PFLAG2(opr, WBOOL);
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 7: /* DI */
    PFLAG3(opr, b, SUBW);
  break;
  }
  return pc;
}

unsigned char *i_83(pc)
unsigned char *pc; {
  unsigned short opr1, opr2;
  pc = word_operand(pc);
  opr1 = *EAPTRL | (unsigned int)*EAPTRH << 8;
  opr2 = (char)*pc++;
  switch((unsigned short *)REGPTR-(unsigned short *)qmem) {
  case 0: /* AX */
    PFLAG3(opr1,opr2,ADDW);
    opr1 += opr2;
    *EAPTRL = opr1;
    *EAPTRH = opr1 >> 8;
  break;
  case 1: /* CX */
    opr1 |= opr2;
    *EAPTRL = opr1;
    *EAPTRH = opr1 >> 8;
    PFLAG2(opr1, WBOOL);
  break;
  case 2: /* DX */
    compute_flags();
    PFLAG4(opr1,opr2,CARRY,ADCW);
    opr1 += opr2 + CARRY;
    *EAPTRL = opr1;
    *EAPTRH = opr1 >> 8;
  break;
  case 3: /* BX */
    compute_flags();
    PFLAG4(opr1,opr2,CARRY,SBBW);
    opr1 -= opr2 + CARRY;
    *EAPTRL = opr1;
    *EAPTRH = opr1 >> 8;
  break;
  case 4: /* SP */
    opr1 &= opr2;
    *EAPTRL = opr1;
    *EAPTRH = opr1 >> 8;
    PFLAG2(opr1, WBOOL);
  break;
  case 5: /* BP */
    PFLAG3(opr1,opr2,SUBW);
    opr1 -= opr2;
    *EAPTRL = opr1;
    *EAPTRH = opr1 >> 8;
  break;
  case 6: /* SI */
    opr1 ^= opr2;
    *EAPTRL = opr1;
    *EAPTRH = opr1 >> 8;
    PFLAG2(opr1, WBOOL);
  break;
  case 7: /* DI */
    PFLAG3(opr1,opr2,SUBW);
  break;
  }
  return pc;
}

unsigned char *i_f6(pc)
unsigned char *pc; {
  unsigned short opr;
  unsigned long  ulong, ulong2;
  long slong, slong2;
  pc = byte_operand(pc);
  switch((unsigned char *)REGPTR-(unsigned char *)qmem) {
  case _AL: /* AL */
    PFLAG2(*pc++ & *EAPTRL, CBOOL);
  break;
  case _CL: /* CL: */
    return unk_op(pc);
  break;
  case _DL: /* DL */
    *EAPTRL = ~*EAPTRL;
  break;
  case _BL: /* BL */
    PFLAG3(0,*EAPTRL,SUBW);
    *EAPTRL = -(char)*EAPTRL;
  break;
  case _AH: /* AH */
    AX = (unsigned int)AL * (unsigned int)*EAPTRL;
    CARRY = OVERFLOW = !(AX < 256);
    FLAGS_OK = 1;
  break;
  case _CH: /* CH */
    AX = (int)AL * (int)*EAPTRL;
    switch(AH) {
    case 0: CARRY = OVERFLOW = (AL > 127); break;
    case 0xff: CARRY = OVERFLOW = (AL < 128); break;
    default: CARRY = OVERFLOW = 1; break;
    }
    FLAGS_OK = 1;
  break;
  case _DH: /* DH */
    FLAGS_OK = 1;
    if (*EAPTRL == 0) return interrupt(pc,0);
    opr = (unsigned int)AX/(unsigned int)*EAPTRL;
    if (opr > 0xff) return interrupt(pc,0);
    AH = AX % *EAPTRL;
    AL = opr;
  break;
  case _BH: /* BH */
    FLAGS_OK = 1;
    if (*EAPTRL == 0) return interrupt(pc,0);
    opr = (int)AX/(int)*EAPTRL;
    if (opr > 0x7f) return interrupt(pc,0);
    AH = AX % *EAPTRL;
    AL = opr;
  break;
  }
  return pc;
}

unsigned char *i_f7(pc)
unsigned char *pc; {
  unsigned short opr;
  unsigned long  ulong, ulong2;
  long slong, slong2;
  pc = word_operand(pc);
  switch((unsigned short *)REGPTR-(unsigned short *)qmem) {
  case 0: /* AX */
    FL1L = *pc++ & *EAPTRL;
    FL1H = *pc++ & *EAPTRH;
    PFLAG1(WBOOL);
  break;
  case 1: /* CX */
    return unk_op(pc);
  break;
  case 2: /* DX */
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    opr = ~opr;
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 3: /* BX */
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    PFLAG3(0,opr,SUBW);
    opr = -(short)opr;
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 4: /* SP */
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    AX = ulong = (unsigned long)AX * (unsigned long)opr;
    DX = ulong >> 16;
    OVERFLOW = CARRY = (DX != 0); 
    FLAGS_OK = 1;
  break;
  case 5: /* BP */
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    AX = slong = (int)AX * (int)opr;
    switch(DX = (slong >> 16)) {
    case 0: CARRY = OVERFLOW = (AX > 32767); break;
    case 0xffff: CARRY = OVERFLOW = (AX < 32768); break;
    default: CARRY = OVERFLOW = 1; break;
    }
    FLAGS_OK = 1;
  break;
  case 6: /* SI */
    FLAGS_OK = 1;
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    if (opr == 0) return interrupt(pc,0);
    ulong = ((unsigned int)DX << 16) | AX;
    ulong2 = ulong/(unsigned long)opr;
    if (ulong2 > 0xffff) return interrupt(pc,0);
    AX = ulong2;
    DX = ulong % (unsigned long)opr;
  break;
  case 7: /* DI */
    FLAGS_OK = 1;
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    if (opr == 0) return interrupt(pc,0);
    slong = ((long)DX << 16) | AX;
    slong2 = slong/(long)opr;
    if ((unsigned long)slong2 > 0x7fff) return interrupt(pc,0);
    AX = slong2;
    DX = slong % (long)opr;
  break;
  }
  return pc;
}

unsigned char *i_fe(pc)
unsigned char *pc; {
  pc = byte_operand(pc);
  switch ((unsigned char *)REGPTR-(unsigned char *)qmem) {
  case _AL: /* AL */
    compute_flags();
    PFLAG2(*EAPTRL, INCB);
    (*EAPTRL)++;
  break;
  case _CL: /* CL */
    compute_flags();
    PFLAG2(*EAPTRL, DECB);
    (*EAPTRL)--;
  break;
  default: return unk_op(pc);
  }
  return pc;
}

unsigned char *i_ff(pc)
unsigned char *pc; {
  unsigned short opr;
  pc = word_operand(pc);
  switch((unsigned short *)REGPTR-(unsigned short *)qmem) {
  case 0: /* AX */
    compute_flags();
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    PFLAG2(opr, INCW);
    opr++;
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 1: /* CX */
    compute_flags();
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    PFLAG2(opr, DECW);
    opr--;
    *EAPTRL = opr;
    *EAPTRH = opr >> 8;
  break;
  case 2: /* DX */
    opr = pc - CSPTR;
    *(SSPTR+--SP) = opr >> 8;
    *(SSPTR+--SP) = opr;
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    pc = CSPTR + opr;
  break;
  case 3: /* BX */
    opr = pc - CSPTR;
    *(SSPTR+--SP) = CSH;
    *(SSPTR+--SP) = CSL;
    *(SSPTR+--SP) = opr >> 8;
    *(SSPTR+--SP) = opr;
    opr = *EAPTRL | ((unsigned int)*EAPTRH << 8);
    CSL = *(EAPTRL+2);
    CSH = *(EAPTRL+3);
    CSPTR = pc_mem + ((unsigned int)CS << 4);
    pc = CSPTR + opr;
  break;
  case 4: /* SP */
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    pc = CSPTR + opr;
  break;
  case 5: /* BP */
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    CSL = *(EAPTRL+2);
    CSH = *(EAPTRL+3);
    CSPTR = pc_mem + ((unsigned int)CS << 4);
    pc = CSPTR + opr;
  break;
  case 6: /* SI */
    opr = *EAPTRL | (unsigned int)*EAPTRH << 8;
    *(SSPTR+--SP) = opr >> 8;
    *(SSPTR+--SP) = opr;
  break;
  default: return unk_op(pc);
  }
  return pc;
}



