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

/* 8086 support structs and definitions */
/* definition of the registers */

/* general EAX,EBX,ECX, EDX type registers.
   Note that for portability, and speed, the issue of byte
   swapping is not addressed in the registers.  All registers
   are stored in the default format available on the
   host machine.  The only critical issue is that the
   registers should line up EXACTLY in the same manner as
   they do in the 386.  That is:

       EAX & 0xff  === AL
       EAX & 0xffff == AX

   etc.  The result is that a lot of the calculations can then be
   done using the native instruction set fully.
*/

/* Endian Logic
Priority 1: If LOWFIRST is defined, use it. LOWFIRST must be 1 if the
            lower part of a 16 bit quantity comes first in memory, otherwise
            LOWFIRST must be 0
Priority 2: If __BIG_ENDIAN__ is defined, use it to define LOWFIRST accordingly
Priority 3: Use LOWFIRST 1 as default
*/

#ifndef LOWFIRST
#ifdef __BIG_ENDIAN__
#if __BIG_ENDIAN__
#define LOWFIRST 0
#else
#define LOWFIRST 1
#endif
#else
#define LOWFIRST 1
#endif
#endif

#if LOWFIRST
typedef struct  { uint16 x_reg; }       I16_reg_t;
typedef struct  { uint8 l_reg, h_reg; } I8_reg_t;
#else
typedef struct  { uint16 x_reg; }       I16_reg_t;
typedef struct  { uint8 h_reg, l_reg; } I8_reg_t;
#endif

typedef union
{
    I16_reg_t    I16_reg;
    I8_reg_t     I8_reg;
} i386_general_register;

struct i386_general_regs
{
    i386_general_register A, B, C, D;
};

typedef struct i386_general_regs Gen_reg_t;

struct i386_special_regs
{
    i386_general_register SP, BP, SI, DI, IP;
    uint32 FLAGS;
};

/*
 *  segment registers here represent the 16 bit quantities
 *  CS, DS, ES, SS
 *
 *  segment pointers --- used to speed up  the expressions:
 *  q = m->R_CSP + m->R_IP;
 *  fetched = *q;
 *  m->R_IP += 1;
 *  compared to:
 *  fetched = GetBYTEExtended(((uint32)m->R_CS << 4) + (m->R_IP++));
 *  Save at least one shift, more if doing two byte moves.
 */
struct i386_segment_regs
{
    uint16 CS, DS, SS, ES, FS, GS;
};

/* 8 bit registers */
#define R_AH  Gn_regs.A.I8_reg.h_reg
#define R_AL  Gn_regs.A.I8_reg.l_reg
#define R_BH  Gn_regs.B.I8_reg.h_reg
#define R_BL  Gn_regs.B.I8_reg.l_reg
#define R_CH  Gn_regs.C.I8_reg.h_reg
#define R_CL  Gn_regs.C.I8_reg.l_reg
#define R_DH  Gn_regs.D.I8_reg.h_reg
#define R_DL  Gn_regs.D.I8_reg.l_reg

/* 16 bit registers */
#define R_AX  Gn_regs.A.I16_reg.x_reg
#define R_BX  Gn_regs.B.I16_reg.x_reg
#define R_CX  Gn_regs.C.I16_reg.x_reg
#define R_DX  Gn_regs.D.I16_reg.x_reg

/* special registers */
#define R_SP  Sp_regs.SP.I16_reg.x_reg
#define R_BP  Sp_regs.BP.I16_reg.x_reg
#define R_SI  Sp_regs.SI.I16_reg.x_reg
#define R_DI  Sp_regs.DI.I16_reg.x_reg
#define R_IP  Sp_regs.IP.I16_reg.x_reg
#define R_FLG Sp_regs.FLAGS

/* segment registers */
#define R_CS  Sg_regs.CS
#define R_DS  Sg_regs.DS
#define R_SS  Sg_regs.SS
#define R_ES  Sg_regs.ES

/* 8088 has top 4 bits of the flags set to 1 */
/* Also, bit#1 is set.  This is (not well) documented  behavior. */
/* see note in userman.tex about the subtleties of dealing with  */
/* code which attempts to detect the host processor.             */
/* This is def'd as F_ALWAYS_ON */
#define F_ALWAYS_ON  (0xf002)        /* flag bits always on */

/* following bits masked in to a 16bit quantity */
#define F_CF   0x1  /* CARRY flag  */
#define F_PF   0x4  /* PARITY flag */
#define F_AF  0x10  /* AUX  flag   */
#define F_ZF  0x40  /* ZERO flag   */
#define F_SF  0x80  /* SIGN flag   */
#define F_TF 0x100  /* TRAP flag   */
#define F_IF 0x200  /* INTERRUPT ENABLE flag */
#define F_DF 0x400  /* DIR flag    */
#define F_OF 0x800  /* OVERFLOW flag */

/*
 *   DEFINE A MASK FOR ONLY THOSE FLAG BITS WE WILL EVER PASS BACK
 *   (via PUSHF)
 */
#define F_MSK (F_CF|F_PF|F_AF|F_ZF|F_SF|F_TF|F_IF|F_DF|F_OF)

#define TOGGLE_FLAG(M,FLAG) (M)->R_FLG ^= FLAG
#define SET_FLAG(M,FLAG)    (M)->R_FLG |= FLAG
#define CLEAR_FLAG(M, FLAG) (M)->R_FLG &= ~FLAG
#define ACCESS_FLAG(M,FLAG) ((M)->R_FLG & (FLAG))

#define CONDITIONAL_SET_FLAG(COND,M,FLAG) \
  if (COND) SET_FLAG(M,FLAG); else CLEAR_FLAG(M,FLAG)

/* emulator machine state. */
/* segment usage control */
#define SYSMODE_SEG_DS_SS   0x01
#define SYSMODE_SEGOVR_CS   0x02
#define SYSMODE_SEGOVR_DS   0x04
#define SYSMODE_SEGOVR_ES   0x08
#define SYSMODE_SEGOVR_SS   0x10

#define SYSMODE_SEGMASK  (SYSMODE_SEG_DS_SS | SYSMODE_SEGOVR_CS |   \
    SYSMODE_SEGOVR_DS | SYSMODE_SEGOVR_ES | SYSMODE_SEGOVR_SS)

#define SYSMODE_PREFIX_REPE     0x20
#define SYSMODE_PREFIX_REPNE    0x40

#define INTR_SYNCH          0x1
#define INTR_HALTED         0x4
#define INTR_ILLEGAL_OPCODE 0x8

/* INSTRUCTION DECODING STUFF */
#define FETCH_DECODE_MODRM(m,mod,rh,rl) fetch_decode_modrm(m,&mod,&rh,&rl)
#define DECODE_RM_BYTE_REGISTER(m,r)    decode_rm_byte_register(m,r)
#define DECODE_RM_WORD_REGISTER(m,r)    decode_rm_word_register(m,r)
#define DECODE_CLEAR_SEGOVR(m)          m->sysmode &= ~(SYSMODE_SEGMASK)

typedef struct pc_env PC_ENV;
struct  pc_env
{
   /*   The registers!!     */
   struct i386_general_regs Gn_regs;
   struct i386_special_regs Sp_regs;
   struct i386_segment_regs Sg_regs;
   /* our flags structure.  This contains information on
           REPE prefix           2 bits  repe,repne
           SEGMENT overrides     5 bits  normal,DS,SS,CS,ES
           Delayed flag set      3 bits  (zero, signed, parity)
           reserved              6 bits
           interrupt #           8 bits  instruction raised interrupt
           BIOS video segregs    4 bits
           Interrupt Pending     1 bits
           Extern interrupt      1 bits
           Halted                1 bits
    */
   long sysmode;
   uint8 intno;
};

/* GLOBAL */
extern volatile int intr;

void halt_sys (PC_ENV *sys);
void fetch_decode_modrm (PC_ENV *m, uint16 *mod, uint16 *regh, uint16 *regl);
uint8 *decode_rm_byte_register (PC_ENV *m, int reg);
uint16 *decode_rm_word_register (PC_ENV *m, int reg);
uint16 *decode_rm_seg_register (PC_ENV *m, int reg);
uint8 fetch_byte_imm (PC_ENV *m);
uint16 fetch_word_imm (PC_ENV *m);
uint16 decode_rm00_address (PC_ENV *m, int rm);
uint16 decode_rm01_address (PC_ENV *m, int rm);
uint16 decode_rm10_address (PC_ENV *m, int rm);
uint8 fetch_data_byte (PC_ENV *m, uint16 offset);
uint8 fetch_data_byte_abs (PC_ENV *m, uint16 segment, uint16 offset);
uint16 fetch_data_word (PC_ENV *m, uint16 offset);
uint16 fetch_data_word_abs (PC_ENV *m, uint16 segment, uint16 offset);
void store_data_byte (PC_ENV *m, uint16 offset, uint8 val);
void store_data_byte_abs (PC_ENV *m, uint16 segment, uint16 offset, uint8 val);
void store_data_word (PC_ENV *m, uint16 offset, uint16 val);
void store_data_word_abs (PC_ENV *m, uint16 segment, uint16 offset, uint16 val);

typedef void (*OP)(PC_ENV *m);
extern OP i86_optab[256];

/* PRIMITIVE OPERATIONS */

uint8   aad_word (PC_ENV *m, uint16 d);
uint16  aam_word (PC_ENV *m, uint8 d);
uint8   adc_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  adc_word (PC_ENV *m, uint16 d, uint16 s);
uint8   add_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  add_word (PC_ENV *m, uint16 d, uint16 s);
uint8   and_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  and_word (PC_ENV *m, uint16 d, uint16 s);
uint8   cmp_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  cmp_word (PC_ENV *m, uint16 d, uint16 s);
uint8   dec_byte (PC_ENV *m, uint8 d);
uint16  dec_word (PC_ENV *m, uint16 d);
uint8   inc_byte (PC_ENV *m, uint8 d);
uint16  inc_word (PC_ENV *m, uint16 d);
uint8   or_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  or_word (PC_ENV *m, uint16 d, uint16 s);
uint8   neg_byte (PC_ENV *m, uint8 s);
uint16  neg_word (PC_ENV *m, uint16 s);
uint8   not_byte (PC_ENV *m, uint8 s);
uint16  not_word (PC_ENV *m, uint16 s);
uint16  mem_access_word (PC_ENV *m, int addr);
void    push_word (PC_ENV *m, uint16 w);
uint16  pop_word (PC_ENV *m);
uint8   rcl_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  rcl_word (PC_ENV *m, uint16 d, uint16 s);
uint8   rcr_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  rcr_word (PC_ENV *m, uint16 d, uint16 s);
uint8   rol_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  rol_word (PC_ENV *m, uint16 d, uint16 s);
uint8   ror_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  ror_word (PC_ENV *m, uint16 d, uint16 s);
uint8   shl_byte (PC_ENV *m, uint8 d, uint8 s) ;
uint16  shl_word (PC_ENV *m, uint16 d, uint16 s);
uint8   shr_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  shr_word (PC_ENV *m, uint16 d, uint16 s);
uint8   sar_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  sar_word (PC_ENV *m, uint16 d, uint16 s);
uint8   sbb_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  sbb_word (PC_ENV *m, uint16 d, uint16 s);
uint8   sub_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  sub_word (PC_ENV *m, uint16 d, uint16 s);
void    test_byte (PC_ENV *m, uint8 d, uint8 s);
void    test_word (PC_ENV *m, uint16 d, uint16 s);
uint8   xor_byte (PC_ENV *m, uint8 d, uint8 s);
uint16  xor_word (PC_ENV *m, uint16 d, uint16 s);
void    imul_byte (PC_ENV *m, uint8 s);
void    imul_word (PC_ENV *m, uint16 s);
void    mul_byte (PC_ENV *m, uint8 s);
void    mul_word (PC_ENV *m, uint16 s);
void    idiv_byte (PC_ENV *m, uint8 s);
void    idiv_word (PC_ENV *m, uint16 s);
void    div_byte (PC_ENV *m, uint8 s);
void    div_word (PC_ENV *m, uint16 s);
