/*
 * Dos/PC Emulator
 * Copyright (C) 1991 Jim Hudgens
 *
 *
 * The file is part of GDE.
 *
 * GDE 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 1, or (at your option)
 * any later version.
 *
 * GDE 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 GDE; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include "altairz80_defs.h"
#include "i86.h"

extern uint32 GetBYTEExtended(register uint32 Addr);
extern void PutBYTEExtended(register uint32 Addr, const register uint32 Value);

/* $Log: i86_prim_ops.c,v $
 * Revision 0.9  2003-01-10  23:33:10  jce
 * fixed some more warnings under gcc -Wall
 *
 * Revision 0.8  1992/04/11  21:58:13  hudgens
 * fixed some code causing warnings under gcc -Wall
 *
 * Revision 0.7  1991/07/30  02:04:34  hudgens
 * added copyright.
 *
 * Revision 0.6  1991/07/21  16:50:37  hudgens
 * fixed all flags in the bit shift and rotate instructions so that they
 * finally agree.
 * Also fixed the flags associated with IMUL and MUL instructions.
 *
 * Revision 0.5  1991/07/21  01:28:16  hudgens
 * added support for aad and aam primitives.
 *
 * Revision 0.4  1991/07/20  22:26:25  hudgens
 * fixed problem with sign extension in subroutine mem_access_word.
 *
 * Revision 0.3  1991/07/17  03:48:22  hudgens
 * fixed bugs having to do with the setting of flags in the
 * shift and rotate operations.  Also, fixed sign extension problem
 * with push_word and pop_word.
 *
 * Revision 0.2  1991/04/01  02:36:00  hudgens
 * Fixed several nasty bugs dealing with flag setting in the subroutines
 * sub_byte, sub_word, sbb_byte, sbb_word, and test_word.  The results
 * now agree with the PC on both of the testaopb and testaopw tests.
 *
 * Revision 0.1  1991/03/30  21:13:37  hudgens
 * Initial checkin.
 *
 *
 */

/* [JCE] Stop gcc -Wall complaining */
extern void i86_intr_raise(PC_ENV *m, uint8 intrnum);

/* the following table was generated using the following
   code (running on an IBM AT, Turbo C++ 2.0), for all values of i
   between 0 and 255.  AL is loaded with i's value, and then the
   operation "and al,al" sets the parity flag.  The flags are pushed
   onto the stack, and then popped back into AX.  Then AX is
   returned.  So the value of each table entry represents the
   parity of its index into the table.  This results in a somewhat
   faster mechanism for parity calculations than the straightforward
   method.
       andflags(i,res) int *res; {
          int flags;
          _AX = i;      asm and al,al;     asm pushf;     *res = _AX;
          asm pop ax;   flags = _AX;       return flags;
       }
 */

uint8 parity_tab[] = {
/*0*/  1, /*1*/  0, /*2*/  0, /*3*/  1,
/*4*/  0, /*5*/  1, /*6*/  1, /*7*/  0,
/*8*/  0, /*9*/  1, /*a*/  1, /*b*/  0,
/*c*/  1, /*d*/  0, /*e*/  0, /*f*/  1,
/*10*/  0, /*11*/  1, /*12*/  1, /*13*/  0,
/*14*/  1, /*15*/  0, /*16*/  0, /*17*/  1,
/*18*/  1, /*19*/  0, /*1a*/  0, /*1b*/  1,
/*1c*/  0, /*1d*/  1, /*1e*/  1, /*1f*/  0,
/*20*/  0, /*21*/  1, /*22*/  1, /*23*/  0,
/*24*/  1, /*25*/  0, /*26*/  0, /*27*/  1,
/*28*/  1, /*29*/  0, /*2a*/  0, /*2b*/  1,
/*2c*/  0, /*2d*/  1, /*2e*/  1, /*2f*/  0,
/*30*/  1, /*31*/  0, /*32*/  0, /*33*/  1,
/*34*/  0, /*35*/  1, /*36*/  1, /*37*/  0,
/*38*/  0, /*39*/  1, /*3a*/  1, /*3b*/  0,
/*3c*/  1, /*3d*/  0, /*3e*/  0, /*3f*/  1,
/*40*/  0, /*41*/  1, /*42*/  1, /*43*/  0,
/*44*/  1, /*45*/  0, /*46*/  0, /*47*/  1,
/*48*/  1, /*49*/  0, /*4a*/  0, /*4b*/  1,
/*4c*/  0, /*4d*/  1, /*4e*/  1, /*4f*/  0,
/*50*/  1, /*51*/  0, /*52*/  0, /*53*/  1,
/*54*/  0, /*55*/  1, /*56*/  1, /*57*/  0,
/*58*/  0, /*59*/  1, /*5a*/  1, /*5b*/  0,
/*5c*/  1, /*5d*/  0, /*5e*/  0, /*5f*/  1,
/*60*/  1, /*61*/  0, /*62*/  0, /*63*/  1,
/*64*/  0, /*65*/  1, /*66*/  1, /*67*/  0,
/*68*/  0, /*69*/  1, /*6a*/  1, /*6b*/  0,
/*6c*/  1, /*6d*/  0, /*6e*/  0, /*6f*/  1,
/*70*/  0, /*71*/  1, /*72*/  1, /*73*/  0,
/*74*/  1, /*75*/  0, /*76*/  0, /*77*/  1,
/*78*/  1, /*79*/  0, /*7a*/  0, /*7b*/  1,
/*7c*/  0, /*7d*/  1, /*7e*/  1, /*7f*/  0,
/*80*/  0, /*81*/  1, /*82*/  1, /*83*/  0,
/*84*/  1, /*85*/  0, /*86*/  0, /*87*/  1,
/*88*/  1, /*89*/  0, /*8a*/  0, /*8b*/  1,
/*8c*/  0, /*8d*/  1, /*8e*/  1, /*8f*/  0,
/*90*/  1, /*91*/  0, /*92*/  0, /*93*/  1,
/*94*/  0, /*95*/  1, /*96*/  1, /*97*/  0,
/*98*/  0, /*99*/  1, /*9a*/  1, /*9b*/  0,
/*9c*/  1, /*9d*/  0, /*9e*/  0, /*9f*/  1,
/*a0*/  1, /*a1*/  0, /*a2*/  0, /*a3*/  1,
/*a4*/  0, /*a5*/  1, /*a6*/  1, /*a7*/  0,
/*a8*/  0, /*a9*/  1, /*aa*/  1, /*ab*/  0,
/*ac*/  1, /*ad*/  0, /*ae*/  0, /*af*/  1,
/*b0*/  0, /*b1*/  1, /*b2*/  1, /*b3*/  0,
/*b4*/  1, /*b5*/  0, /*b6*/  0, /*b7*/  1,
/*b8*/  1, /*b9*/  0, /*ba*/  0, /*bb*/  1,
/*bc*/  0, /*bd*/  1, /*be*/  1, /*bf*/  0,
/*c0*/  1, /*c1*/  0, /*c2*/  0, /*c3*/  1,
/*c4*/  0, /*c5*/  1, /*c6*/  1, /*c7*/  0,
/*c8*/  0, /*c9*/  1, /*ca*/  1, /*cb*/  0,
/*cc*/  1, /*cd*/  0, /*ce*/  0, /*cf*/  1,
/*d0*/  0, /*d1*/  1, /*d2*/  1, /*d3*/  0,
/*d4*/  1, /*d5*/  0, /*d6*/  0, /*d7*/  1,
/*d8*/  1, /*d9*/  0, /*da*/  0, /*db*/  1,
/*dc*/  0, /*dd*/  1, /*de*/  1, /*df*/  0,
/*e0*/  0, /*e1*/  1, /*e2*/  1, /*e3*/  0,
/*e4*/  1, /*e5*/  0, /*e6*/  0, /*e7*/  1,
/*e8*/  1, /*e9*/  0, /*ea*/  0, /*eb*/  1,
/*ec*/  0, /*ed*/  1, /*ee*/  1, /*ef*/  0,
/*f0*/  1, /*f1*/  0, /*f2*/  0, /*f3*/  1,
/*f4*/  0, /*f5*/  1, /*f6*/  1, /*f7*/  0,
/*f8*/  0, /*f9*/  1, /*fa*/  1, /*fb*/  0,
/*fc*/  1, /*fd*/  0, /*fe*/  0, /*ff*/  1,
};

uint8 xor_0x3_tab[] = { 0, 1, 1, 0 };

/* CARRY CHAIN CALCULATION.
   This represents a somewhat expensive calculation which is
   apparently required to emulate the setting of the OF and
   AF flag.  The latter is not so important, but the former is.
   The overflow flag is the XOR of the top two bits of the
   carry chain for an addition (similar for subtraction).
   Since we do not want to simulate the addition in a bitwise
   manner, we try to calculate the carry chain given the
   two operands and the result.

   So, given the following table, which represents the
   addition of two bits, we can derive a formula for
   the carry chain.

       a   b   cin   r     cout
       0   0   0     0     0
       0   0   1     1     0
       0   1   0     1     0
       0   1   1     0     1
       1   0   0     1     0
       1   0   1     0     1
       1   1   0     0     1
       1   1   1     1     1

    Construction of table for cout:

               ab
         r  \  00   01   11  10
            |------------------
         0  |   0    1    1   1
         1  |   0    0    1   0

    By inspection, one gets:  cc = ab +  r'(a + b)

    That represents alot of operations, but NO CHOICE....

BORROW CHAIN CALCULATION.
   The following table represents the
   subtraction of two bits, from which we can derive a formula for
   the borrow chain.

       a   b   bin   r     bout
       0   0   0     0     0
       0   0   1     1     1
       0   1   0     1     1
       0   1   1     0     1
       1   0   0     1     0
       1   0   1     0     0
       1   1   0     0     0
       1   1   1     1     1

    Construction of table for cout:

               ab
         r  \  00   01   11  10
            |------------------
         0  |   0    1    0   0
         1  |   1    1    1   0

    By inspection, one gets:  bc = a'b +  r(a' + b)

 */

uint8 aad_word(PC_ENV *m, uint16 d)
{
    uint16 l;
    uint8  hb,lb;
    hb = (d>>8)&0xff;
    lb = (d&0xff);
    l = lb + 10 * hb;
    CONDITIONAL_SET_FLAG(l & 0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(l == 0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[l & 0xff], m, F_PF);
    return (uint8) l;
}

uint16 aam_word(PC_ENV *m, uint8 d)
{
    uint16 h,l;
    h = d / 10;
    l = d % 10;
    l |= (h<<8);
    CONDITIONAL_SET_FLAG(l & 0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(l == 0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[l & 0xff], m, F_PF);
    return l;
}

uint8 adc_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint16 res;         /* all operands in native machine order */
    register uint16 cc;
    if (ACCESS_FLAG(m,F_CF) )
      res = 1 + d + s;
    else
      res =  d + s;
    CONDITIONAL_SET_FLAG(res & 0x100, m, F_CF);
    CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the carry chain  SEE NOTE AT TOP.*/
    cc =  (s & d) | ((~res) & (s | d));
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF);
    return (uint8) res;
}

uint16 adc_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 cc;
    if (ACCESS_FLAG(m,F_CF) )
      res = 1 + d + s;
    else
      res =  d + s;
    /* set the carry flag to be bit 8 */
    CONDITIONAL_SET_FLAG(res & 0x10000, m, F_CF);
    CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the carry chain  SEE NOTE AT TOP.*/
    cc =  (s & d) | ((~res) & (s | d));
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF);
    return res;
}

/* Given   flags=f,  and bytes  d (dest)  and  s (source)
   perform the add and set the flags and the result back to
   *d.   USE NATIVE MACHINE ORDER...
*/
uint8 add_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint16 res;         /* all operands in native machine order */
    register uint16 cc;
    res = d + s;
    /* set the carry flag to be bit 8 */
    CONDITIONAL_SET_FLAG(res & 0x100, m, F_CF);
    CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the carry chain  SEE NOTE AT TOP.*/
    cc =  (s & d) | ((~res) & (s | d));
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF);
    return (uint8) res;
}

/* Given   flags=f,  and bytes  d (dest)  and  s (source)
   perform the add and set the flags and the result back to
   *d.   USE NATIVE MACHINE ORDER...
*/
uint16 add_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 cc;
    res = d + s;
    /* set the carry flag to be bit 8 */
    CONDITIONAL_SET_FLAG(res & 0x10000, m, F_CF);
    CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the carry chain  SEE NOTE AT TOP.*/
    cc =  (s & d) | ((~res) & (s | d));
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF);
    return res;
}

/*
   Flags m->R_FLG,  dest *d,  source  *s,  do a bitwise and of the
   source and destination, and then store back to the
   destination.  Size=byte.
*/
uint8 and_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint8 res;         /* all operands in native machine order */
    res = d & s;
    /* set the flags  */
    CLEAR_FLAG(m, F_OF);
    CLEAR_FLAG(m, F_CF);
    CONDITIONAL_SET_FLAG(res&0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(res==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF);
    return res;
}

/*
   Flags m->R_FLG,  dest *d,  source  *s,  do a bitwise and of the
   source and destination, and then store back to the
   destination.  Size=byte.
*/
uint16 and_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint16 res;         /* all operands in native machine order */
    res = d & s;
    /* set the flags  */
    CLEAR_FLAG(m, F_OF);
    CLEAR_FLAG(m, F_CF);
    CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG(res==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    return res;
}

uint8 cmp_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 bc;
    res = d - s;
    CLEAR_FLAG(m, F_CF);
    CONDITIONAL_SET_FLAG(res&0x80, m, F_SF);
    CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the borrow chain.  See note at top */
    bc= (res&(~d|s))|(~d&s);
    CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF);
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return d;  /* long story why this is needed.  Look at opcode
          0x80 in ops.c, for an idea why this is necessary.*/
}

uint16 cmp_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 bc;
    res = d - s;
    CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the borrow chain.  See note at top */
    bc= (res&(~d|s))|(~d&s);
    CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF);
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return d;
}

uint8 dec_byte(PC_ENV *m, uint8 d)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 bc;
    res = d - 1;
    CONDITIONAL_SET_FLAG(res&0x80, m, F_SF);
    CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the borrow chain.  See note at top */
    /* based on sub_byte, uses s==1.  */
    bc= (res&(~d|1))|(~d&1);
    /* carry flag unchanged */
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return res;
}

uint16 dec_word(PC_ENV *m, uint16 d)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 bc;
    res = d - 1;
    CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the borrow chain.  See note at top */
    /* based on the sub_byte routine, with s==1 */
    bc= (res&(~d|1))|(~d&1);
    /* carry flag unchanged */
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return res;
}

/* Given   flags=f,  and byte  d (dest)
   perform the inc and set the flags and the result back to
   d.   USE NATIVE MACHINE ORDER...
*/
uint8 inc_byte(PC_ENV *m, uint8 d)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 cc;
    res = d + 1;
    CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the carry chain  SEE NOTE AT TOP.*/
    cc =  ((1 & d) | (~res)) & (1 | d);
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF);
    return  res;
}

/* Given   flags=f,  and byte  d (dest)
   perform the inc and set the flags and the result back to
   *d.   USE NATIVE MACHINE ORDER...
*/
uint16 inc_word(PC_ENV *m, uint16 d)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 cc;
    res = d + 1;
    CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the carry chain  SEE NOTE AT TOP.*/
    cc =  (1 & d) | ((~res) & (1 | d));
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF);
    return res ;
}

uint8 or_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint8 res;         /* all operands in native machine order */
    res = d | s;
    CLEAR_FLAG(m, F_OF);
    CLEAR_FLAG(m, F_CF);
    CONDITIONAL_SET_FLAG(res&0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(res==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF);
    return res;
}

uint16 or_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint16 res;         /* all operands in native machine order */
    res = d | s;
    /* set the carry flag to be bit 8 */
    CLEAR_FLAG(m, F_OF);
    CLEAR_FLAG(m, F_CF);
    CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG(res==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    return res;
}

uint8 neg_byte(PC_ENV *m, uint8 s)
{
    register uint8 res;
    register uint8 bc;
    CONDITIONAL_SET_FLAG(s!=0, m, F_CF);
    res = -s;
    CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF);
    /* calculate the borrow chain --- modified such that d=0.
       substitutiing d=0 into     bc= res&(~d|s)|(~d&s);
       (the one used for sub) and simplifying, since ~d=0xff...,
       ~d|s == 0xffff..., and res&0xfff... == res.  Similarly
       ~d&s == s.  So the simplified result is:*/
    bc= res|s;
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return res;
}

uint16 neg_word(PC_ENV *m, uint16 s)
{
    register uint16 res;
    register uint16 bc;
    CONDITIONAL_SET_FLAG(s!=0, m, F_CF);
    res = -s;
    CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the borrow chain --- modified such that d=0.
       substitutiing d=0 into     bc= res&(~d|s)|(~d&s);
       (the one used for sub) and simplifying, since ~d=0xff...,
       ~d|s == 0xffff..., and res&0xfff... == res.  Similarly
       ~d&s == s.  So the simplified result is:*/
    bc= res|s;
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return res;
}

uint8 not_byte(PC_ENV *m, uint8 s)
{
    return ~s;
}

uint16 not_word(PC_ENV *m, uint16 s)
{
    return ~s;
}

/* access stuff from absolute location in memory.
   no segment registers are involved.
 */
uint16 mem_access_word(PC_ENV *m, int addr)
{
    /* Load in two steps.  Native byte order independent */
    return GetBYTEExtended(addr) | (GetBYTEExtended(addr + 1) << 8);
}

/*  given the register_set  r, and memory descriptor m,
    and word w, push w onto the stack.
    w ASSUMED IN NATIVE MACHINE ORDER.  Doesn't matter in this case???
 */
void push_word(PC_ENV *m, uint16 w)
{
    m->R_SP --;
    PutBYTEExtended((m->R_SS << 4) + m->R_SP, w >> 8);
    m->R_SP --;
    PutBYTEExtended((m->R_SS << 4) + m->R_SP, w & 0xff);
}

/*  given the  memory descriptor m,
    and word w, pop word from the stack.
 */
uint16 pop_word(PC_ENV *m)
{
    register uint16 res;
    res  = GetBYTEExtended((m->R_SS << 4) + m->R_SP);
    m->R_SP++;
    res |= GetBYTEExtended((m->R_SS << 4) + m->R_SP) << 8;
    m->R_SP++;
    return res;
}

/*****************************************************************
   BEGIN region consisting of bit shifts and rotates,
   much of which may be wrong.  Large hirsute factor.
*****************************************************************/
uint8 rcl_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint32  res, cnt, mask,cf;
    /* s is the rotate distance.  It varies from 0 - 8. */
    /* have
               CF  B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0
       want to rotate through the carry by "s" bits.  We could
       loop, but that's inefficient.  So the width is 9,
       and we split into three parts:
               The new carry flag   (was B_n)
           the stuff in B_n-1 .. B_0
           the stuff in B_7 .. B_n+1
       The new rotate is done mod 9, and given this,
       for a rotation of n bits (mod 9) the new carry flag is
       then located n bits from the MSB.  The low part is
       then shifted up cnt bits, and the high part is or'd
       in.  Using CAPS for new values, and lowercase for the
       original values, this can be expressed as:
           IF n > 0
               1) CF <-  b_(8-n)
           2) B_(7) .. B_(n)  <-  b_(8-(n+1)) .. b_0
           3) B_(n-1) <- cf
           4) B_(n-2) .. B_0 <-  b_7 .. b_(8-(n-1))
       I think this is correct.
       */
    res = d;
    /* [JCE] Extra brackets to stop gcc -Wall moaning */
    if ((cnt = s % 9))     /* not a typo, do nada if cnt==0 */
    {
        /* extract the new CARRY FLAG.  */
        /* CF <-  b_(8-n)               */
        cf =  (d >> (8-cnt)) & 0x1;
        /* get the low stuff which rotated
           into the range B_7 .. B_cnt */
        /* B_(7) .. B_(n)  <-  b_(8-(n+1)) .. b_0  */
        /* note that the right hand side done by the mask */
        res = (d << cnt) & 0xff;
        /* now the high stuff which rotated around
           into the positions B_cnt-2 .. B_0 */
        /* B_(n-2) .. B_0 <-  b_7 .. b_(8-(n-1)) */
        /* shift it downward, 7-(n-2) = 9-n positions.
           and mask off the result before or'ing in.
        */
        mask = (1<<(cnt-1)) - 1;
        res |= (d >> (9-cnt)) & mask;
        /* if the carry flag was set, or it in.  */
        if (ACCESS_FLAG(m,F_CF))   /* carry flag is set */
        {
            /*  B_(n-1) <- cf */
            res |=  1 << (cnt-1);
        }
        /* set the new carry flag, based on the variable "cf" */
        CONDITIONAL_SET_FLAG(cf, m, F_CF);
        /* OVERFLOW is set *IFF* cnt==1, then it is the
           xor of CF and the most significant bit.  Blecck. */
        /* parenthesized this expression since it appears to
           be causing OF to be missed. */
        CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[cf+((res>>6)&0x2)], m, F_OF);
    }
    return res & 0xff;
}

uint16  rcl_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint32  res, cnt, mask,cf;
    /* see analysis above. */
    /* width here is 16 bits + carry bit */
    res = d;
    /* [JCE] Extra brackets to stop gcc -Wall moaning */
    if ((cnt = s % 17))     /* not a typo, do nada if cnt==0 */
    {
        /* extract the new CARRY FLAG. */
        /* CF <-  b_(16-n)             */
        cf =  (d >> (16-cnt)) & 0x1;
        /* get the low stuff which rotated
           into the range B_15 .. B_cnt */
        /* B_(15) .. B_(n)  <-  b_(16-(n+1)) .. b_0  */
        /* note that the right hand side done by the mask */
        res = (d << cnt) & 0xffff;
        /* now the high stuff which rotated around
           into the positions B_cnt-2 .. B_0 */
        /* B_(n-2) .. B_0 <-  b_15 .. b_(16-(n-1)) */
        /* shift it downward, 15-(n-2) = 17-n positions.
           and mask off the result before or'ing in.
         */
        mask = (1<<(cnt-1)) - 1;
        res |= (d >> (17-cnt)) & mask;
        /* if the carry flag was set, or it in.  */
        if (ACCESS_FLAG(m, F_CF))   /* carry flag is set */
        {
            /*  B_(n-1) <- cf */
            res |=  1 << (cnt-1);
        }
        /* set the new carry flag, based on the variable "cf" */
        CONDITIONAL_SET_FLAG(cf, m, F_CF);
        /* OVERFLOW is set *IFF* cnt==1, then it is the
           xor of CF and the most significant bit.  Blecck.
           Note that we're forming a 2 bit word here to index
           into the table. The expression cf+(res>>14)&0x2
           represents the two bit word b_15 CF.
        */
        /* parenthesized following expression... */
        CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[cf+((res>>14)&0x2)], m, F_OF);
    }
    return res & 0xffff;
}

uint8 rcr_byte(PC_ENV *m, uint8 d, uint8 s)
{
    uint8 res, cnt;
    uint8 mask, cf, ocf = 0;
    /* rotate right through carry */
    /*
        s is the rotate distance.  It varies from 0 - 8.
        d is the byte object rotated.
        have
        CF  B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0
        The new rotate is done mod 9, and given this,
        for a rotation of n bits (mod 9) the new carry flag is
        then located n bits from the LSB.  The low part is
        then shifted up cnt bits, and the high part is or'd
        in.  Using CAPS for new values, and lowercase for the
        original values, this can be expressed as:
        IF n > 0
        1) CF <-  b_(n-1)
        2) B_(8-(n+1)) .. B_(0)  <-  b_(7) .. b_(n)
        3) B_(8-n) <- cf
        4) B_(7) .. B_(8-(n-1)) <-  b_(n-2) .. b_(0)
        I think this is correct.
    */
    res = d;
    /* [JCE] Extra brackets to stop gcc -Wall moaning */
    if ((cnt = s % 9))     /* not a typo, do nada if cnt==0 */
    {
        /* extract the new CARRY FLAG. */
        /* CF <-  b_(n-1)              */
        if (cnt == 1)
        {
            cf = d & 0x1;
            /* note hackery here.  Access_flag(..) evaluates to either
               0 if flag not set
               non-zero if flag is set.
               doing access_flag(..) != 0 casts that into either
               0..1 in any representation of the flags register
               (i.e. packed bit array or unpacked.)
            */
            ocf = ACCESS_FLAG(m,F_CF) != 0;
        }
        else
            cf =  (d >> (cnt-1)) & 0x1;
        /* B_(8-(n+1)) .. B_(0)  <-  b_(7) .. b_n  */
        /* note that the right hand side done by the mask
        This is effectively done by shifting the
        object to the right.  The result must be masked,
        in case the object came in and was treated
        as a negative number.  Needed???*/
        mask = (1<<(8-cnt))-1;
        res = (d >> cnt) & mask;
        /* now the high stuff which rotated around
           into the positions B_cnt-2 .. B_0 */
        /* B_(7) .. B_(8-(n-1)) <-  b_(n-2) .. b_(0) */
        /* shift it downward, 7-(n-2) = 9-n positions.
           and mask off the result before or'ing in.
        */
        res |= (d << (9-cnt));
        /* if the carry flag was set, or it in.  */
        if (ACCESS_FLAG(m,F_CF))   /* carry flag is set */
        {
            /*  B_(8-n) <- cf */
            res |=  1 << (8 - cnt);
        }
        /* set the new carry flag, based on the variable "cf" */
        CONDITIONAL_SET_FLAG(cf, m, F_CF);
        /* OVERFLOW is set *IFF* cnt==1, then it is the
        xor of CF and the most significant bit.  Blecck. */
        /* parenthesized... */
        if (cnt == 1)
        { /* [JCE] Explicit braces to stop gcc -Wall moaning */
            CONDITIONAL_SET_FLAG(xor_0x3_tab[ocf+((d>>6)&0x2)], m, F_OF);
        }
    }
    return res;
}

uint16 rcr_word(PC_ENV *m, uint16 d, uint16 s)
{
    uint16 res, cnt;
    uint16 mask, cf, ocf = 0;
    /* rotate right through carry */
    /*
        s is the rotate distance.  It varies from 0 - 8.
        d is the byte object rotated.
        have
        CF  B_15   ...   B_0
        The new rotate is done mod 17, and given this,
        for a rotation of n bits (mod 17) the new carry flag is
        then located n bits from the LSB.  The low part is
        then shifted up cnt bits, and the high part is or'd
        in.  Using CAPS for new values, and lowercase for the
        original values, this can be expressed as:
        IF n > 0
        1) CF <-  b_(n-1)
        2) B_(16-(n+1)) .. B_(0)  <-  b_(15) .. b_(n)
        3) B_(16-n) <- cf
        4) B_(15) .. B_(16-(n-1)) <-  b_(n-2) .. b_(0)
        I think this is correct.
    */
    res = d;
    /* [JCE] Extra brackets to stop gcc -Wall moaning */
    if ((cnt = s % 17))     /* not a typo, do nada if cnt==0 */
    {
        /* extract the new CARRY FLAG. */
        /* CF <-  b_(n-1)              */
        if (cnt==1)
        {
            cf = d & 0x1;
            /* see note above on teh byte version */
            ocf = ACCESS_FLAG(m,F_CF) != 0;
        }
        else
            cf =  (d >> (cnt-1)) & 0x1;
        /* B_(16-(n+1)) .. B_(0)  <-  b_(15) .. b_n  */
        /* note that the right hand side done by the mask
           This is effectively done by shifting the
           object to the right.  The result must be masked,
           in case the object came in and was treated
           as a negative number.  Needed???*/
        mask = (1<<(16-cnt))-1;
        res = (d >> cnt) & mask;
        /* now the high stuff which rotated around
           into the positions B_cnt-2 .. B_0 */
        /* B_(15) .. B_(16-(n-1)) <-  b_(n-2) .. b_(0) */
        /* shift it downward, 15-(n-2) = 17-n positions.
           and mask off the result before or'ing in.
        */
        res |= (d << (17-cnt));
        /* if the carry flag was set, or it in.  */
        if (ACCESS_FLAG(m,F_CF))   /* carry flag is set */
        {
            /*  B_(16-n) <- cf */
            res |=  1 << (16 - cnt);
        }
        /* set the new carry flag, based on the variable "cf" */
        CONDITIONAL_SET_FLAG(cf, m, F_CF);
        /* OVERFLOW is set *IFF* cnt==1, then it is the
           xor of CF and the most significant bit.  Blecck. */
        if (cnt==1)
        { /* [JCE] Explicit braces to stop gcc -Wall moaning */
            CONDITIONAL_SET_FLAG(xor_0x3_tab[ocf+((d>>14)&0x2)], m, F_OF);
        }
    }
    return res;
}

uint8 rol_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint32  res, cnt, mask;
    /* rotate left */
    /*
      s is the rotate distance.  It varies from 0 - 8.
      d is the byte object rotated.
      have
               CF  B_7 ... B_0
      The new rotate is done mod 8.
      Much simpler than the "rcl" or "rcr" operations.
           IF n > 0
           1) B_(7) .. B_(n)  <-  b_(8-(n+1)) .. b_(0)
           2) B_(n-1) .. B_(0) <-  b_(7) .. b_(8-n)
      I think this is correct.
    */
    res =d;
    /* [JCE] Extra brackets to stop gcc -Wall moaning */
    if ((cnt = s % 8))     /* not a typo, do nada if cnt==0 */
    {
       /* B_(7) .. B_(n)  <-  b_(8-(n+1)) .. b_(0) */
       res = (d << cnt);
       /* B_(n-1) .. B_(0) <-  b_(7) .. b_(8-n) */
       mask = (1 << cnt) - 1;
       res |= (d >> (8-cnt)) & mask;
       /* set the new carry flag, Note that it is the low order
          bit of the result!!!                               */
       CONDITIONAL_SET_FLAG(res&0x1, m, F_CF);
       /* OVERFLOW is set *IFF* cnt==1, then it is the
          xor of CF and the most significant bit.  Blecck. */
       CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[(res&0x1)+((res>>6)&0x2)], m, F_OF);
    }
    return res&0xff;
}

uint16 rol_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint32  res, cnt, mask;
    /* rotate left */
    /*
      s is the rotate distance.  It varies from 0 - 8.
      d is the byte object rotated.
      have
               CF  B_15 ... B_0
      The new rotate is done mod 8.
      Much simpler than the "rcl" or "rcr" operations.
           IF n > 0
           1) B_(15) .. B_(n)  <-  b_(16-(n+1)) .. b_(0)
           2) B_(n-1) .. B_(0) <-  b_(16) .. b_(16-n)
      I think this is correct.
    */
    res = d;
    /* [JCE] Extra brackets to stop gcc -Wall moaning */
    if ((cnt = s % 16))     /* not a typo, do nada if cnt==0 */
    {
       /* B_(16) .. B_(n)  <-  b_(16-(n+1)) .. b_(0) */
       res = (d << cnt);
       /* B_(n-1) .. B_(0) <-  b_(15) .. b_(16-n) */
       mask = (1 << cnt) - 1;
       res |= (d >> (16-cnt)) & mask;
       /* set the new carry flag, Note that it is the low order
          bit of the result!!!                               */
       CONDITIONAL_SET_FLAG(res&0x1, m, F_CF);
       /* OVERFLOW is set *IFF* cnt==1, then it is the
          xor of CF and the most significant bit.  Blecck. */
       CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[(res&0x1)+((res>>14)&0x2)], m, F_OF);
    }
    return res&0xffff;
}

uint8 ror_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint32  res, cnt, mask;
    /* rotate right */
    /*
      s is the rotate distance.  It varies from 0 - 8.
      d is the byte object rotated.
      have
               B_7 ... B_0
      The rotate is done mod 8.
           IF n > 0
           1) B_(8-(n+1)) .. B_(0)  <-  b_(7) .. b_(n)
           2) B_(7) .. B_(8-n) <-  b_(n-1) .. b_(0)
    */
    res = d;
    /* [JCE] Extra brackets to stop gcc -Wall moaning */
    if ((cnt = s % 8))     /* not a typo, do nada if cnt==0 */
    {
       /* B_(7) .. B_(8-n) <-  b_(n-1) .. b_(0)*/
       res = (d << (8-cnt));
       /* B_(8-(n+1)) .. B_(0)  <-  b_(7) .. b_(n) */
       mask = (1 << (8-cnt)) - 1;
       res |= (d >> (cnt)) & mask;
       /* set the new carry flag, Note that it is the low order
          bit of the result!!!                               */
       CONDITIONAL_SET_FLAG(res&0x80, m, F_CF);
       /* OVERFLOW is set *IFF* cnt==1, then it is the
          xor of the two most significant bits.  Blecck. */
       CONDITIONAL_SET_FLAG(cnt==1&& xor_0x3_tab[(res>>6)&0x3], m, F_OF);
    }
    return res&0xff;
}

uint16 ror_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint32  res, cnt, mask;
    /* rotate right */
    /*
      s is the rotate distance.  It varies from 0 - 8.
      d is the byte object rotated.
      have
               B_15 ... B_0
      The rotate is done mod 16.
           IF n > 0
           1) B_(16-(n+1)) .. B_(0)  <-  b_(15) .. b_(n)
           2) B_(15) .. B_(16-n) <-  b_(n-1) .. b_(0)
      I think this is correct.
    */
    res =d ;
    /* [JCE] Extra brackets to stop gcc -Wall moaning */
    if ((cnt = s % 16))     /* not a typo, do nada if cnt==0 */
    {
       /* B_(15) .. B_(16-n) <-  b_(n-1) .. b_(0)*/
       res = (d << (16-cnt));
       /* B_(16-(n+1)) .. B_(0)  <-  b_(15) .. b_(n) */
       mask = (1 << (16-cnt)) - 1;
       res |= (d >> (cnt)) & mask;
       /* set the new carry flag, Note that it is the low order
          bit of the result!!!                               */
       CONDITIONAL_SET_FLAG(res&0x8000, m, F_CF);
       /* OVERFLOW is set *IFF* cnt==1, then it is the
          xor of CF and the most significant bit.  Blecck. */
       CONDITIONAL_SET_FLAG(cnt==1  && xor_0x3_tab[(res>>14)&0x3], m, F_OF);
    }
    return res & 0xffff;
}

uint8 shl_byte(PC_ENV *m, uint8 d, uint8 s)
{
    uint32 cnt,res,cf;
    if (s < 8)
    {
        cnt = s % 8;
        /* last bit shifted out goes into carry flag */
        if (cnt>0)
        {
            res = d << cnt;
            cf = d & (1<<(8-cnt));
            CONDITIONAL_SET_FLAG(cf, m, F_CF);
            CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
            CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF);
            CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
        }
        else
        {
            res = (uint8)d;
        }
        if (cnt == 1)
        {
            /* Needs simplification. */
            CONDITIONAL_SET_FLAG(
            (((res&0x80)==0x80) ^
            (ACCESS_FLAG(m,F_CF) != 0)) ,
            /* was (m->R_FLG&F_CF)==F_CF)), */
            m, F_OF);
        }
        else
        {
            CLEAR_FLAG(m,F_OF);
        }
    }
    else
    {
        res = 0;
/*      CLEAR_FLAG(m,F_CF);*/
        CONDITIONAL_SET_FLAG((s == 8) && (d & 1), m, F_CF); /* Peter Schorn bug fix */
        CLEAR_FLAG(m,F_OF);
        CLEAR_FLAG(m,F_SF);
        CLEAR_FLAG(m,F_PF);
        SET_FLAG(m,F_ZF);
    }
    return res & 0xff;
}

uint16 shl_word(PC_ENV *m, uint16 d, uint16 s)
{
    uint32 cnt,res,cf;
    if (s < 16)
    {
        cnt = s % 16;
        if (cnt > 0)
        {
            res = d << cnt;
            /* last bit shifted out goes into carry flag */
            cf = d & (1<<(16-cnt));
            CONDITIONAL_SET_FLAG(cf, m, F_CF);
            CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
            CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF);
            CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
        }
        else
        {
            res = (uint16)d;
        }
        if (cnt == 1)
        {
            /* Needs simplification. */
            CONDITIONAL_SET_FLAG(
            (((res&0x8000)==0x8000) ^
            (ACCESS_FLAG(m,F_CF) != 0)),
            /*((m&F_CF)==F_CF)),*/
            m, F_OF);
        }
        else
        {
            CLEAR_FLAG(m,F_OF);
        }
    }
    else
    {
        res = 0;
        /*      CLEAR_FLAG(m,F_CF);*/
        CONDITIONAL_SET_FLAG((s == 16) && (d & 1), m, F_CF); /* Peter Schorn bug fix */
        CLEAR_FLAG(m,F_OF);
        SET_FLAG(m,F_ZF);
        CLEAR_FLAG(m,F_SF);
        CLEAR_FLAG(m,F_PF);
    }
    return res & 0xffff;
}

uint8 shr_byte(PC_ENV *m, uint8 d, uint8 s)
{
    uint32 cnt,res,cf,mask;
    if (s < 8)
    {
        cnt = s % 8;
        if (cnt > 0)
        {
            mask = (1<<(8-cnt))-1;
            cf = d & (1<<(cnt-1));
            res = (d >> cnt) & mask;
            CONDITIONAL_SET_FLAG(cf, m, F_CF);
            CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
            CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF);
            CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
        }
        else
        {
            res = (uint8) d;
        }
        if (cnt == 1)
        {
            CONDITIONAL_SET_FLAG(xor_0x3_tab[(res>>6)&0x3], m, F_OF);
        }
        else
        {
            CLEAR_FLAG(m,F_OF);
        }
    }
    else
    {
        res = 0;
        /*      CLEAR_FLAG(m,F_CF);*/
        CONDITIONAL_SET_FLAG((s == 8) && (d & 0x80), m, F_CF); /* Peter Schorn bug fix */
        CLEAR_FLAG(m,F_OF);
        SET_FLAG(m,F_ZF);
        CLEAR_FLAG(m,F_SF);
        CLEAR_FLAG(m,F_PF);
    }
    return res & 0xff;
}

uint16 shr_word(PC_ENV *m, uint16 d, uint16 s)
{
    uint32 cnt,res,cf,mask;
    res = d;
    if (s < 16)
    {
        cnt = s % 16;
        if (cnt > 0)
        {
            mask = (1<<(16-cnt))-1;
            cf = d & (1<<(cnt-1));
            res = (d >> cnt) & mask;
            CONDITIONAL_SET_FLAG(cf, m, F_CF);
            CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
            CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF);
            CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
        }
        else
        {
            res = d;
        }
        if (cnt == 1)
        {
            CONDITIONAL_SET_FLAG(xor_0x3_tab[(res>>14)&0x3], m, F_OF);
        }
        else
        {
            CLEAR_FLAG(m,F_OF);
        }
    }
    else
    {
        res = 0;
        /*      CLEAR_FLAG(m,F_CF);*/
        CONDITIONAL_SET_FLAG((s == 16) && (d & 0x8000), m, F_CF); /* Peter Schorn bug fix */
        CLEAR_FLAG(m,F_OF);
        SET_FLAG(m,F_ZF);
        CLEAR_FLAG(m,F_SF);
        CLEAR_FLAG(m,F_PF);
    }
    return res & 0xffff;
}

/* XXXX ??? flags may be wrong??? */
uint8 sar_byte(PC_ENV *m, uint8 d, uint8 s)
{
    uint32 cnt,res,cf,mask,sf;
    res = d;
    sf = d & 0x80;
    cnt = s % 8;
    if (cnt > 0 && cnt < 8)
    {
        mask = (1<<(8-cnt))-1;
        cf = d & (1<<(cnt-1));
        res = (d >> cnt) & mask;
        CONDITIONAL_SET_FLAG(cf, m, F_CF);
        if (sf)
        {
            res |= ~mask;
        }
        CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
        CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
        CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF);
    }
    else if (cnt >= 8)
    {
        if (sf)
        {
            res = 0xff;
            SET_FLAG(m,F_CF);
            CLEAR_FLAG(m,F_ZF);
            SET_FLAG(m, F_SF);
            SET_FLAG(m, F_PF);
        }
        else
        {
            res = 0;
            CLEAR_FLAG(m,F_CF);
            SET_FLAG(m,F_ZF);
            CLEAR_FLAG(m, F_SF);
            CLEAR_FLAG(m, F_PF);
        }
    }
    return res&0xff;
}

uint16 sar_word(PC_ENV *m, uint16 d, uint16 s)
{
    uint32 cnt, res, cf, mask, sf;
    sf = d & 0x8000;
    cnt = s % 16;
    res = d;
    if (cnt > 0 && cnt < 16)
    {
        mask = (1<<(16-cnt))-1;
        cf = d & (1<<(cnt-1));
        res = (d >> cnt) & mask;
        CONDITIONAL_SET_FLAG(cf, m, F_CF);
        if (sf)
        {
            res |= ~mask;
        }
        CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
        CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF);
        CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    }
    else if (cnt >= 16)
    {
        if (sf)
        {
            res = 0xffff;
            SET_FLAG(m,F_CF);
            CLEAR_FLAG(m,F_ZF);
            SET_FLAG(m, F_SF);
            SET_FLAG(m, F_PF);
        }
        else
        {
            res = 0;
            CLEAR_FLAG(m,F_CF);
            SET_FLAG(m,F_ZF);
            CLEAR_FLAG(m, F_SF);
            CLEAR_FLAG(m, F_PF);
        }
    }
    return res & 0xffff;
}

uint8 sbb_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 bc;
    if (ACCESS_FLAG(m,F_CF) )
      res = d - s - 1;
    else
      res =  d - s;
    CONDITIONAL_SET_FLAG(res&0x80, m, F_SF);
    CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the borrow chain.  See note at top */
    bc= (res&(~d|s))|(~d&s);
    CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF);
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return res & 0xff;
}

uint16 sbb_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 bc;
    if (ACCESS_FLAG(m,F_CF))
      res = d - s - 1;
    else
      res =  d - s;
    CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the borrow chain.  See note at top */
    bc= (res&(~d|s))|(~d&s);
    CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF);
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return res & 0xffff;
}

uint8 sub_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 bc;
    res = d - s;
    CONDITIONAL_SET_FLAG(res&0x80, m, F_SF);
    CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the borrow chain.  See note at top */
    bc= (res&(~d|s))|(~d&s);
    CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF);
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return res & 0xff;
}

uint16 sub_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint32 res;         /* all operands in native machine order */
    register uint32 bc;
    res = d - s;
    CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* calculate the borrow chain.  See note at top */
    bc= (res&(~d|s))|(~d&s);
    CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF);
    CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF);
    CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF);
    return res & 0xffff;
}

void test_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint32 res;         /* all operands in native machine order */
    res = d & s;
    CLEAR_FLAG(m, F_OF);
    CONDITIONAL_SET_FLAG(res&0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(res==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* AF == dont care*/
    CLEAR_FLAG(m, F_CF);
}

void test_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint32 res;         /* all operands in native machine order */
    res = d & s;
    CLEAR_FLAG(m, F_OF);
    CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG(res==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    /* AF == dont care*/
    CLEAR_FLAG(m, F_CF);
}

uint8 xor_byte(PC_ENV *m, uint8 d, uint8 s)
{
    register uint8 res;         /* all operands in native machine order */
    res = d ^ s;
    CLEAR_FLAG(m, F_OF);
    CONDITIONAL_SET_FLAG(res&0x80, m, F_SF);
    CONDITIONAL_SET_FLAG(res==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF);
    CLEAR_FLAG(m, F_CF);
    return res;
}

uint16 xor_word(PC_ENV *m, uint16 d, uint16 s)
{
    register uint16 res;         /* all operands in native machine order */
    res = d ^ s;
    /* set the carry flag to be bit 8 */
    CLEAR_FLAG(m, F_OF);
    CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF);
    CONDITIONAL_SET_FLAG(res==0, m, F_ZF);
    CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF);
    CLEAR_FLAG(m, F_CF);
    return res;
}

void imul_byte(PC_ENV *m, uint8 s)
{
    int16 res = (int8)m->R_AL * (int8)s;
    m->R_AX = res;
    /* Undef --- Can't hurt */
    CONDITIONAL_SET_FLAG(res&0x8000,m,F_SF);
    CONDITIONAL_SET_FLAG(res==0,m,F_ZF);
    if (m->R_AH == 0 || m->R_AH == 0xff)
    {
       CLEAR_FLAG(m, F_CF);
       CLEAR_FLAG(m, F_OF);
    }
    else
    {
       SET_FLAG(m, F_CF);
       SET_FLAG(m, F_OF);
    }
}

void imul_word(PC_ENV *m, uint16 s)
{
    int32 res = (int16)m->R_AX * (int16)s;
    m->R_AX = res & 0xffff;
    m->R_DX = (res >> 16) & 0xffff;
    /* Undef --- Can't hurt */
    CONDITIONAL_SET_FLAG(res&0x80000000,m,F_SF);
    CONDITIONAL_SET_FLAG(res==0,m,F_ZF);
    if (m->R_DX == 0 || m->R_DX == 0xffff)
    {
       CLEAR_FLAG(m, F_CF);
       CLEAR_FLAG(m, F_OF);
    }
    else
    {
       SET_FLAG(m, F_CF);
       SET_FLAG(m, F_OF);
    }
}

void mul_byte(PC_ENV *m, uint8 s)
{
    uint16 res = m->R_AL * s;
    m->R_AX = res;
    /* Undef --- Can't hurt */
    CLEAR_FLAG(m,F_SF);
    CONDITIONAL_SET_FLAG(res==0,m,F_ZF);
    if (m->R_AH == 0)
    {
       CLEAR_FLAG(m, F_CF);
       CLEAR_FLAG(m, F_OF);
    }
    else
    {
       SET_FLAG(m, F_CF);
       SET_FLAG(m, F_OF);
    }
}

void mul_word(PC_ENV *m, uint16 s)
{
    uint32 res = m->R_AX * s;
    /* Undef --- Can't hurt */
    CLEAR_FLAG(m,F_SF);
    CONDITIONAL_SET_FLAG(res==0,m,F_ZF);
    m->R_AX = res & 0xffff;
    m->R_DX = (res >> 16) & 0xffff;
    if (m->R_DX == 0)
    {
       CLEAR_FLAG(m, F_CF);
       CLEAR_FLAG(m, F_OF);
    }
    else
    {
       SET_FLAG(m, F_CF);
       SET_FLAG(m, F_OF);
    }
}

void idiv_byte(PC_ENV *m, uint8 s)
{
    int32 dvd,div,mod;
    dvd = (int16)m->R_AX;
    if (s == 0)
    {
       i86_intr_raise(m,0);
       return;
    }
    div = dvd / (int8)s;
    mod = dvd % (int8)s;
    if (abs(div) > 0x7f)
    {
       i86_intr_raise(m,0);
       return;
    }
    /* Undef --- Can't hurt */
    CONDITIONAL_SET_FLAG(div&0x80,m,F_SF);
    CONDITIONAL_SET_FLAG(div==0,m,F_ZF);
    m->R_AL = (int8)div;
    m->R_AH = (int8)mod;
}

void idiv_word(PC_ENV *m, uint16 s)
{
    int32 dvd,dvs,div,mod;
    dvd = m->R_DX;
    dvd = (dvd << 16) | m->R_AX;
    if (s == 0)
    {
       i86_intr_raise(m,0);
       return;
    }
    dvs = (int16)s;
    div = dvd / dvs;
    mod = dvd % dvs;
    if (abs(div) > 0x7fff)
    {
       i86_intr_raise(m,0);
       return;
    }
    /* Undef --- Can't hurt */
    CONDITIONAL_SET_FLAG(div&0x8000,m,F_SF);
    CONDITIONAL_SET_FLAG(div==0,m,F_ZF);
/*    debug_printf(m, "\n%d/%d=%d,%d\n",dvd,dvs,div,mod); */
    m->R_AX = div;
    m->R_DX = mod;
}

void div_byte(PC_ENV *m, uint8 s)
{
    uint32 dvd,dvs,div,mod;
    dvs = s;
    dvd =  m->R_AX;
    if (s == 0)
    {
       i86_intr_raise(m,0);
       return;
    }
    div = dvd / dvs;
    mod = dvd % dvs;
    if (abs(div) > 0xff)
    {
       i86_intr_raise(m,0);
       return;
    }
    /* Undef --- Can't hurt */
    CLEAR_FLAG(m,F_SF);
    CONDITIONAL_SET_FLAG(div==0,m,F_ZF);
    m->R_AL = (uint8)div;
    m->R_AH = (uint8)mod;
}

void div_word(PC_ENV *m, uint16 s)
{
    uint32 dvd,dvs,div,mod;
    dvd = m->R_DX;
    dvd = (dvd << 16) | m->R_AX;
    dvs = s;
    if (dvs == 0)
    {
       i86_intr_raise(m,0);
       return;
    }
    div = dvd / dvs;
    mod = dvd % dvs;
/*    printf("dvd=%x dvs=%x -> div=%x mod=%x\n",dvd, dvs,div, mod);*/
    if (abs(div) > 0xffff)
    {
       i86_intr_raise(m,0);
       return;
    }
    /* Undef --- Can't hurt */
    CLEAR_FLAG(m,F_SF);
    CONDITIONAL_SET_FLAG(div==0,m,F_ZF);
    m->R_AX = div;
    m->R_DX = mod;
}
