/* | |
* 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 alot 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: OS 9 on Macintosh needs LOWFIRST 0 | |
Priority 4: Use LOWFIRST 1 as default | |
*/ | |
#ifndef LOWFIRST | |
#ifdef __BIG_ENDIAN__ | |
#if __BIG_ENDIAN__ | |
#define LOWFIRST 0 | |
#else | |
#define LOWFIRST 1 | |
#endif | |
#elif defined (__MWERKS__) && defined (macintosh) | |
#define LOWFIRST 0 | |
#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 structrure. 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 */ | |
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); |