/* * Copyright (C) 2002-2004 The DOSBox Team * * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* Lazy flag system was designed after the same kind of system used in Bochs. Probably still some bugs left in here. */ #include "dosbox.h" #include "cpu.h" #include "lazyflags.h" #include "pic.h" LazyFlags lflags; /* CF Carry Flag -- Set on high-order bit carry or borrow; cleared otherwise. */ Bitu get_CF(void) { switch (lflags.type) { case t_UNKNOWN: case t_INCb: case t_INCw: case t_INCd: case t_DECb: case t_DECw: case t_DECd: case t_MUL: case t_RCLb: case t_RCLw: case t_RCLd: return GETFLAG(CF); break; case t_ADDb: return (lf_resb8) return false; else return (lf_var1b >> (8-lf_var2b)) & 1; case t_SHLw: if (lf_var2b>16) return false; else return (lf_var1w >> (16-lf_var2b)) & 1; case t_SHLd: case t_DSHLw: /* Hmm this is not correct for shift higher than 16 */ case t_DSHLd: return (lf_var1d >> (32 - lf_var2b)) & 1; case t_RCRb: case t_SHRb: return (lf_var1b >> (lf_var2b - 1)) & 1; case t_RCRw: case t_SHRw: return (lf_var1w >> (lf_var2b - 1)) & 1; case t_RCRd: case t_SHRd: case t_DSHRw: /* Hmm this is not correct for shift higher than 16 */ case t_DSHRd: return (lf_var1d >> (lf_var2b - 1)) & 1; case t_SARb: return (((Bit8s) lf_var1b) >> (lf_var2b - 1)) & 1; case t_SARw: return (((Bit16s) lf_var1w) >> (lf_var2b - 1)) & 1; case t_SARd: return (((Bit32s) lf_var1d) >> (lf_var2b - 1)) & 1; case t_NEGb: return lf_var1b; case t_NEGw: return lf_var1w; case t_NEGd: return lf_var1d; case t_ROLb: return lf_resb & 1; case t_ROLw: return lf_resw & 1; case t_ROLd: return lf_resd & 1; case t_RORb: return (lf_resb & 0x80); case t_RORw: return (lf_resw & 0x8000); case t_RORd: return (lf_resd & 0x80000000); case t_ORb: case t_ORw: case t_ORd: case t_ANDb: case t_ANDw: case t_ANDd: case t_XORb: case t_XORw: case t_XORd: case t_TESTb: case t_TESTw: case t_TESTd: return false; /* Set to false */ case t_DIV: return false; /* Unkown */ default: LOG(LOG_CPU,LOG_ERROR)("get_CF Unknown %d",lflags.type); } return 0; } /* AF Adjust flag -- Set on carry from or borrow to the low order four bits of AL; cleared otherwise. Used for decimal arithmetic. */ Bitu get_AF(void) { Bitu type=lflags.type; switch (type) { case t_UNKNOWN: case t_ROLb: case t_RORb: case t_RCLb: case t_RCRb: case t_ROLw: case t_RORw: case t_RCLw: case t_RCRw: case t_ROLd: case t_RORd: case t_RCLd: case t_RCRd: return GETFLAG(AF); case t_ADDb: case t_ADCb: case t_SBBb: case t_SUBb: case t_CMPb: return ((lf_var1b ^ lf_var2b) ^ lf_resb) & 0x10; case t_ADDw: case t_ADCw: case t_SBBw: case t_SUBw: case t_CMPw: return ((lf_var1w ^ lf_var2w) ^ lf_resw) & 0x10; case t_ADCd: case t_ADDd: case t_SBBd: case t_SUBd: case t_CMPd: return ((lf_var1d ^ lf_var2d) ^ lf_resd) & 0x10; case t_INCb: return (lf_resb & 0x0f) == 0; case t_INCw: return (lf_resw & 0x0f) == 0; case t_INCd: return (lf_resd & 0x0f) == 0; case t_DECb: return (lf_resb & 0x0f) == 0x0f; case t_DECw: return (lf_resw & 0x0f) == 0x0f; case t_DECd: return (lf_resd & 0x0f) == 0x0f; case t_NEGb: return lf_var1b & 0x0f; case t_NEGw: return lf_var1w & 0x0f; case t_NEGd: return lf_var1d & 0x0f; case t_ORb: case t_ORw: case t_ORd: case t_ANDb: case t_ANDw: case t_ANDd: case t_XORb: case t_XORw: case t_XORd: case t_TESTb: case t_TESTw: case t_TESTd: case t_SHLb: case t_SHLw: case t_SHLd: case t_SHRb: case t_SHRw: case t_SHRd: case t_SARb: case t_SARw: case t_SARd: case t_DSHLw: case t_DSHLd: case t_DSHRw: case t_DSHRd: case t_DIV: case t_MUL: return false; /* Unkown */ default: LOG(LOG_CPU,LOG_ERROR)("get_AF Unknown %d",lflags.type); } return 0; } /* ZF Zero Flag -- Set if result is zero; cleared otherwise. */ Bitu get_ZF(void) { Bitu type=lflags.type; switch (type) { case t_UNKNOWN: case t_ROLb: case t_RORb: case t_RCLb: case t_RCRb: case t_ROLw: case t_RORw: case t_RCLw: case t_RCRw: case t_ROLd: case t_RORd: case t_RCLd: case t_RCRd: return GETFLAG(ZF); case t_ADDb: case t_ORb: case t_ADCb: case t_SBBb: case t_ANDb: case t_XORb: case t_SUBb: case t_CMPb: case t_INCb: case t_DECb: case t_TESTb: case t_SHLb: case t_SHRb: case t_SARb: case t_NEGb: return (lf_resb==0); case t_ADDw: case t_ORw: case t_ADCw: case t_SBBw: case t_ANDw: case t_XORw: case t_SUBw: case t_CMPw: case t_INCw: case t_DECw: case t_TESTw: case t_SHLw: case t_SHRw: case t_SARw: case t_DSHLw: case t_DSHRw: case t_NEGw: return (lf_resw==0); case t_ADDd: case t_ORd: case t_ADCd: case t_SBBd: case t_ANDd: case t_XORd: case t_SUBd: case t_CMPd: case t_INCd: case t_DECd: case t_TESTd: case t_SHLd: case t_SHRd: case t_SARd: case t_DSHLd: case t_DSHRd: case t_NEGd: return (lf_resd==0); case t_DIV: case t_MUL: return false; /* Unkown */ default: LOG(LOG_CPU,LOG_ERROR)("get_ZF Unknown %d",lflags.type); } return false; } /* SF Sign Flag -- Set equal to high-order bit of result (0 is positive, 1 if negative). */ Bitu get_SF(void) { Bitu type=lflags.type; switch (type) { case t_UNKNOWN: case t_ROLb: case t_RORb: case t_RCLb: case t_RCRb: case t_ROLw: case t_RORw: case t_RCLw: case t_RCRw: case t_ROLd: case t_RORd: case t_RCLd: case t_RCRd: return GETFLAG(SF); case t_ADDb: case t_ORb: case t_ADCb: case t_SBBb: case t_ANDb: case t_XORb: case t_SUBb: case t_CMPb: case t_INCb: case t_DECb: case t_TESTb: case t_SHLb: case t_SHRb: case t_SARb: case t_NEGb: return (lf_resb&0x80); case t_ADDw: case t_ORw: case t_ADCw: case t_SBBw: case t_ANDw: case t_XORw: case t_SUBw: case t_CMPw: case t_INCw: case t_DECw: case t_TESTw: case t_SHLw: case t_SHRw: case t_SARw: case t_DSHLw: case t_DSHRw: case t_NEGw: return (lf_resw&0x8000); case t_ADDd: case t_ORd: case t_ADCd: case t_SBBd: case t_ANDd: case t_XORd: case t_SUBd: case t_CMPd: case t_INCd: case t_DECd: case t_TESTd: case t_SHLd: case t_SHRd: case t_SARd: case t_DSHLd: case t_DSHRd: case t_NEGd: return (lf_resd&0x80000000); case t_DIV: case t_MUL: return false; /* Unkown */ default: LOG(LOG_CPU,LOG_ERROR)("get_SF Unkown %d",lflags.type); } return false; } Bitu get_OF(void) { Bitu type=lflags.type; switch (type) { case t_UNKNOWN: case t_MUL: return GETFLAG(OF); case t_ADDb: case t_ADCb: return ((lf_var1b ^ lf_var2b ^ 0x80) & (lf_resb ^ lf_var2b)) & 0x80; case t_ADDw: case t_ADCw: return ((lf_var1w ^ lf_var2w ^ 0x8000) & (lf_resw ^ lf_var2w)) & 0x8000; case t_ADDd: case t_ADCd: return ((lf_var1d ^ lf_var2d ^ 0x80000000) & (lf_resd ^ lf_var2d)) & 0x80000000; case t_SBBb: case t_SUBb: case t_CMPb: return ((lf_var1b ^ lf_var2b) & (lf_var1b ^ lf_resb)) & 0x80; case t_SBBw: case t_SUBw: case t_CMPw: return ((lf_var1w ^ lf_var2w) & (lf_var1w ^ lf_resw)) & 0x8000; case t_SBBd: case t_SUBd: case t_CMPd: return ((lf_var1d ^ lf_var2d) & (lf_var1d ^ lf_resd)) & 0x80000000; case t_INCb: return (lf_resb == 0x80); case t_INCw: return (lf_resw == 0x8000); case t_INCd: return (lf_resd == 0x80000000); case t_DECb: return (lf_resb == 0x7f); case t_DECw: return (lf_resw == 0x7fff); case t_DECd: return (lf_resd == 0x7fffffff); case t_NEGb: return (lf_var1b == 0x80); case t_NEGw: return (lf_var1w == 0x8000); case t_NEGd: return (lf_var1d == 0x80000000); case t_ROLb: case t_RORb: case t_RCLb: case t_RCRb: case t_SHLb: case t_SHRb: return (lf_resb ^ lf_var1b) & 0x80; case t_ROLw: case t_RORw: case t_RCLw: case t_RCRw: case t_SHLw: case t_SHRw: case t_DSHRw: case t_DSHLw: return (lf_resw ^ lf_var1w) & 0x8000; case t_ROLd: case t_RORd: case t_RCLd: case t_RCRd: case t_SHLd: case t_SHRd: case t_DSHRd: case t_DSHLd: return (lf_resd ^ lf_var1d) & 0x80000000; case t_ORb: case t_ORw: case t_ORd: case t_ANDb: case t_ANDw: case t_ANDd: case t_XORb: case t_XORw: case t_XORd: case t_TESTb: case t_TESTw: case t_TESTd: case t_SARb: case t_SARw: case t_SARd: return false; /* Return false */ case t_DIV: return false; /* Unkown */ default: LOG(LOG_CPU,LOG_ERROR)("get_OF Unkown %d",lflags.type); } return false; } Bit16u parity_lookup[256] = { FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF, 0, FLAG_PF, FLAG_PF, 0, 0, FLAG_PF, FLAG_PF, 0, FLAG_PF, 0, 0, FLAG_PF }; Bitu get_PF(void) { switch (lflags.type) { case t_UNKNOWN: return GETFLAG(PF); default: return (parity_lookup[lf_resb]);; }; return false; } #if 0 Bitu FillFlags(void) { // if (lflags.type==t_UNKNOWN) return reg_flags; Bitu new_word=(reg_flags & ~FLAG_MASK); if (get_CF()) new_word|=FLAG_CF; if (get_PF()) new_word|=FLAG_PF; if (get_AF()) new_word|=FLAG_AF; if (get_ZF()) new_word|=FLAG_ZF; if (get_SF()) new_word|=FLAG_SF; if (get_OF()) new_word|=FLAG_OF; reg_flags=new_word; lflags.type=t_UNKNOWN; return reg_flags; } #else #define DOFLAG_PF reg_flags=(reg_flags & ~FLAG_PF) | parity_lookup[lf_resb]; #define DOFLAG_AF reg_flags=(reg_flags & ~FLAG_AF) | (((lf_var1b ^ lf_var2b) ^ lf_resb) & 0x10); #define DOFLAG_ZFb SETFLAGBIT(ZF,lf_resb==0); #define DOFLAG_ZFw SETFLAGBIT(ZF,lf_resw==0); #define DOFLAG_ZFd SETFLAGBIT(ZF,lf_resd==0); #define DOFLAG_SFb reg_flags=(reg_flags & ~FLAG_SF) | ((lf_resb & 0x80) >> 0); #define DOFLAG_SFw reg_flags=(reg_flags & ~FLAG_SF) | ((lf_resw & 0x8000) >> 8); #define DOFLAG_SFd reg_flags=(reg_flags & ~FLAG_SF) | ((lf_resd & 0x80000000) >> 24); #define SETCF(NEWBIT) reg_flags=(reg_flags & ~FLAG_CF)|(NEWBIT); #define SET_FLAG SETFLAGBIT Bitu FillFlags(void) { switch (lflags.type) { case t_UNKNOWN: break; case t_ADDb: SET_FLAG(CF,(lf_resb8) SET_FLAG(CF,false); else SET_FLAG(CF,(lf_var1b >> (8-lf_var2b)) & 1); DOFLAG_ZFb; DOFLAG_SFb; SET_FLAG(OF,(lf_resb ^ lf_var1b) & 0x80); DOFLAG_PF; break; case t_SHLw: if (lf_var2b>16) SET_FLAG(CF,false); else SET_FLAG(CF,(lf_var1w >> (16-lf_var2b)) & 1); DOFLAG_ZFw; DOFLAG_SFw; SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000); DOFLAG_PF; break; case t_SHLd: SET_FLAG(CF,(lf_var1d >> (32 - lf_var2b)) & 1); DOFLAG_ZFd; DOFLAG_SFd; SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000); DOFLAG_PF; break; case t_DSHLw: SET_FLAG(CF,(lf_var1d >> (32 - lf_var2b)) & 1); DOFLAG_ZFw; DOFLAG_SFw; SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000); DOFLAG_PF; break; case t_DSHLd: SET_FLAG(CF,(lf_var1d >> (32 - lf_var2b)) & 1); DOFLAG_ZFd; DOFLAG_SFd; SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000); DOFLAG_PF; break; case t_SHRb: SET_FLAG(CF,(lf_var1b >> (lf_var2b - 1)) & 1); DOFLAG_ZFb; DOFLAG_SFb; SET_FLAG(OF,(lf_resb ^ lf_var1b) & 0x80); DOFLAG_PF; break; case t_SHRw: SET_FLAG(CF,(lf_var1w >> (lf_var2b - 1)) & 1); DOFLAG_ZFw; DOFLAG_SFw; SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000); DOFLAG_PF; break; case t_SHRd: SET_FLAG(CF,(lf_var1d >> (lf_var2b - 1)) & 1); DOFLAG_ZFd; DOFLAG_SFd; SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000); DOFLAG_PF; break; case t_DSHRw: /* Hmm this is not correct for shift higher than 16 */ SET_FLAG(CF,(lf_var1d >> (lf_var2b - 1)) & 1); DOFLAG_ZFw; DOFLAG_SFw; SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000); DOFLAG_PF; break; case t_DSHRd: SET_FLAG(CF,(lf_var1d >> (lf_var2b - 1)) & 1); DOFLAG_ZFd; DOFLAG_SFd; SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000); DOFLAG_PF; break; case t_SARb: SET_FLAG(CF,(((Bit8s) lf_var1b) >> (lf_var2b - 1)) & 1); DOFLAG_ZFb; DOFLAG_SFb; SET_FLAG(OF,false); DOFLAG_PF; break; case t_SARw: SET_FLAG(CF,(((Bit16s) lf_var1w) >> (lf_var2b - 1)) & 1); DOFLAG_ZFw; DOFLAG_SFw; SET_FLAG(OF,false); DOFLAG_PF; break; case t_SARd: SET_FLAG(CF,(((Bit32s) lf_var1d) >> (lf_var2b - 1)) & 1); DOFLAG_ZFd; DOFLAG_SFd; SET_FLAG(OF,false); DOFLAG_PF; break; case t_ROLb: SET_FLAG(CF,lf_resb & 1); SET_FLAG(OF,(lf_resb ^ lf_var1b) & 0x80); break; case t_ROLw: SET_FLAG(CF,lf_resw & 1); SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000); break; case t_ROLd: SET_FLAG(CF,lf_resd & 1); SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000); break; case t_RORb: SET_FLAG(CF,(lf_resb & 0x80)); SET_FLAG(OF,(lf_resb ^ lf_var1b) & 0x80); break; case t_RORw: SET_FLAG(CF,(lf_resw & 0x8000)); SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000); break; case t_RORd: SET_FLAG(CF,(lf_resd & 0x80000000)); SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000); break; case t_RCLb: SET_FLAG(OF,(lf_resb ^ lf_var1b) & 0x80); break; case t_RCLw: SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000); break; case t_RCLd: SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000); break; case t_RCRb: SET_FLAG(CF,(lf_var1b >> (lf_var2b - 1)) & 1); SET_FLAG(OF,(lf_resb ^ lf_var1b) & 0x80); break; case t_RCRw: SET_FLAG(CF,(lf_var1w >> (lf_var2b - 1)) & 1); SET_FLAG(OF,(lf_resw ^ lf_var1w) & 0x8000); break; case t_RCRd: SET_FLAG(CF,(lf_var1d >> (lf_var2b - 1)) & 1); SET_FLAG(OF,(lf_resd ^ lf_var1d) & 0x80000000); break; case t_INCb: SET_FLAG(AF,(lf_resb & 0x0f) == 0); DOFLAG_ZFb; DOFLAG_SFb; SET_FLAG(OF,(lf_resb == 0x80)); DOFLAG_PF; break; case t_INCw: SET_FLAG(AF,(lf_resw & 0x0f) == 0); DOFLAG_ZFw; DOFLAG_SFw; SET_FLAG(OF,(lf_resw == 0x8000)); DOFLAG_PF; break; case t_INCd: SET_FLAG(AF,(lf_resd & 0x0f) == 0); DOFLAG_ZFd; DOFLAG_SFd; SET_FLAG(OF,(lf_resd == 0x80000000)); DOFLAG_PF; break; case t_DECb: SET_FLAG(AF,(lf_resb & 0x0f) == 0); DOFLAG_ZFb; DOFLAG_SFb; SET_FLAG(OF,(lf_resb == 0x7f)); DOFLAG_PF; break; case t_DECw: SET_FLAG(AF,(lf_resw & 0x0f) == 0); DOFLAG_ZFw; DOFLAG_SFw; SET_FLAG(OF,(lf_resw == 0x7fff)); DOFLAG_PF; break; case t_DECd: SET_FLAG(AF,(lf_resd & 0x0f) == 0); DOFLAG_ZFd; DOFLAG_SFd; SET_FLAG(OF,(lf_resd == 0x7fffffff)); DOFLAG_PF; break; case t_NEGb: SET_FLAG(CF,(lf_var1b!=0)); SET_FLAG(AF,(lf_resb & 0x0f) == 0); DOFLAG_ZFb; DOFLAG_SFb; SET_FLAG(OF,(lf_var1b == 0x80)); DOFLAG_PF; break; case t_NEGw: SET_FLAG(CF,(lf_var1w!=0)); SET_FLAG(AF,(lf_resw & 0x0f) == 0); DOFLAG_ZFw; DOFLAG_SFw; SET_FLAG(OF,(lf_var1w == 0x8000)); DOFLAG_PF; break; case t_NEGd: SET_FLAG(CF,(lf_var1d!=0)); SET_FLAG(AF,(lf_resd & 0x0f) == 0); DOFLAG_ZFd; DOFLAG_SFd; SET_FLAG(OF,(lf_var1d == 0x80000000)); DOFLAG_PF; break; case t_DIV: case t_MUL: break; default: LOG(LOG_CPU,LOG_ERROR)("Unhandled flag type %d",lflags.type); return 0; } lflags.type=t_UNKNOWN; return reg_flags; } #endif