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

/*
 * routines to deal with the 86 flags
 */
#include "sim.h"

unsigned char parity[256];

init_parity() {
  int i, j, result;
  for (i = 0; i < 256; i++) {
    result = 1;
    for (j = 0; j < 8; j++) {
      result ^= (i >> j) & 01;
    }
    parity[i] = result;
  }
}

unsigned char *i_9e(pc)
unsigned char *pc; {
  SIGN = (AH >> 7) & 01;
  ZERO = (AH >> 6) & 01;
  CARRY = AH  & 01;
  FLAGS_OK = 1;
  return pc;
}

unsigned char *i_9f(pc)
unsigned char *pc; {
  compute_flags();
  AH = (SIGN << 7) | (ZERO << 6) /* | (PARITY << 2) */ | CARRY;
  return pc;
}

unsigned char *i_f5(pc)
unsigned char *pc; {
  compute_flags();
  CARRY = !CARRY;
  return pc;
}

unsigned char *i_f8(pc)
unsigned char *pc; {
  compute_flags();
  CARRY = 0;
  return pc;
}

unsigned char *i_f9(pc)
unsigned char *pc; {
  compute_flags();
  CARRY = 1;
  return pc;
}

unsigned char *i_fa(pc)
unsigned char *pc; {
  INTERRUPT = 0;
  return pc;
}

unsigned char *i_fb(pc)
unsigned char *pc; {
/*
 * Pending interrupts are not recognized until
 * the instruction following STI has executed.
 */
  pc = atomic_interpret(pc);
  INTERRUPT = 1;
  return do_interrupt(pc);
}

unsigned char *i_fc(pc)
unsigned char *pc; {
  DIRECTION = 0;
  return pc;
}

unsigned char *i_fd(pc)
unsigned char *pc; {
  DIRECTION = 1;
  return pc;
}

/*
 * compute flags
 */
void do_compute_flags() {
  register int s;
  switch(FL4) {
  case ADCB:
    s = FL1 + FL2;
    OVERFLOW = (!((FL1 ^ FL2) & 0x80) &&
      ((FL1 ^ s) & 0x80));
    if (FL3) { /* saved_carry = 1 */
      OVERFLOW |= (s++ == 0x7f);
    }
    CARRY = (s & 0xffffff00) != 0;
    SIGN = ((s & 0x80) != 0);
    ZERO = ((char)s == 0);
  break;
  case ADCW:
    s = FL1 + FL2;
    OVERFLOW = (!((FL1 ^ FL2) & 0x8000) &&
      ((FL1 ^ s) & 0x8000));
    if (FL3) { /* saved_carry = 1 */
      OVERFLOW |= (s++ == 0x7fff);
    }
    CARRY = (s & 0xffff0000) != 0;
    SIGN = ((s & 0x8000) != 0);
    ZERO = ((short)s == 0);
  break;
  case ADDW:
    s = FL1 + FL2;
    CARRY = (s & 0xffff0000) != 0;
    SIGN = ((s & 0x8000) != 0);
/*
 * If operands have opposite sign or first
 * operand has same sign as result, then
 * overflow = 0, else 1
 */
    OVERFLOW = (!((FL1 ^ FL2) & 0x8000) &&
      ((FL1 ^ s) & 0x8000));
    ZERO = ((short)s == 0);
  break;
  case ADDB:
    s = FL1 + FL2;
    CARRY = (s & 0xffffff00) != 0;
    SIGN = ((s & 0x80) != 0);
/*
 * If operand have opposite sign or first
 * operand has same sign as result, then
 * overflow = 0, else 1
 */
    OVERFLOW = (!((FL1 ^ FL2) & 0x80) &&
      ((FL1 ^ s) & 0x80));
    ZERO = ((char)s == 0);
  break;
  case WBOOL:
    s = (short)FL1;
    ZERO = (s == 0);
    SIGN = (s < 0);
    CARRY = OVERFLOW = 0;
  break;
  case CBOOL:
    s = (char)FL1;
    ZERO = (s == 0);
    SIGN = (s < 0);
    CARRY = OVERFLOW = 0;
  break;
  case SUBB:
    s = FL1 - FL2;
    CARRY = s < 0;
    SIGN = ((s & 0x80) != 0);
/*
 * If operands have same sign
 * or first operand has same sign as result
 * then overflow = 0, else 1
 */
    OVERFLOW = (((FL1 ^ FL2) & 0x80) &&
      ((FL1 ^ s) & 0x80));
    ZERO = ((char)s == 0);
  break;
  case SUBW:
    s = FL1 - FL2;
    CARRY = s < 0;
    SIGN = ((s & 0x8000) != 0);
/*
 * If operands have same sign
 * or first operand has same sign as result
 * then overflow = 0, else 1
 */
    OVERFLOW = (((FL1 ^ FL2) & 0x8000) &&
      ((FL1 ^ s) & 0x8000));
    ZERO = ((short)s == 0);
  break;
  case SBBW:
    s = FL1 - FL2;
    OVERFLOW = (((FL1 ^ FL2) & 0x8000) &&
      ((FL1 ^ s) & 0x8000));
    if (FL3) { /* saved_carry = 1 */
      OVERFLOW |= (s-- == 0x8000);
    }
    CARRY = (s & 0xffff0000) != 0;
    SIGN = ((s & 0x8000) != 0);
    ZERO = ((short)s == 0);
  break;
  case INCW:
    s = (short)(FL1+1);
    SIGN = (s < 0);
    ZERO = (s == 0);
    OVERFLOW = (FL1 == 0x7fff);
  break;
  case DECW:
    s = (short)(FL1-1);
    SIGN = (s < 0);
    ZERO = (s == 0);
    OVERFLOW = (FL1 == 0x8000);
  break;
  case DECB:
    s = (char)(FL1-1);
    SIGN = (s < 0);
    ZERO = (s == 0);
    OVERFLOW = (FL1 == 0x80);
  break;
  case INCB:
    s = (char)(FL1+1);
    SIGN = (s < 0);
    ZERO = (s == 0);
    OVERFLOW = (FL1 == 0x7f);
  break;
  default:
    pcmexit("bad case %d in do_compute_flags()\n",FL4);
  break;
  }
  PARITY = parity[s & 0xff];
  FLAGS_OK = 1;
}

/*
 * Create a copy of the PSW in flagshort
 */
void putflags() {
  flagshort = CARRY | (PARITY << 2) | (ZERO << 6) | (SIGN << 7) |
    ((int)TRAP << 8)  | ((int)INTERRUPT << 9)
      | ((int)DIRECTION << 10) | ((int)OVERFLOW << 11) | 0xF000;
}

void getflags() {
  CARRY = flagshort & 01;
  PARITY = (flagshort >> 2) & 01;
  ZERO   = (flagshort >> 6) & 01;
  SIGN   = (flagshort >> 7) & 01;
  TRAP   = (flagshort >> 8) & 01;
  INTERRUPT = (flagshort >> 9) & 01;
  DIRECTION = (flagshort >> 10) & 01;
  OVERFLOW = (flagshort >> 11) & 01;
  FLAGS_OK = 1;
}

