From 2394f38514e4c57420cb910b1033e73be1a7f7b4 Mon Sep 17 00:00:00 2001 From: Sjoerd van der Berg Date: Sun, 11 Jan 2004 12:24:56 +0000 Subject: [PATCH] Add string operations for MOVS,STOS,LODS Change the way temporary registers are handled with loading and saving Imported-from: https://svn.code.sf.net/p/dosbox/code-0/dosbox/trunk@1560 --- src/cpu/core_dyn_x86.cpp | 7 +- src/cpu/core_dyn_x86/decoder.h | 53 ++++++++-- src/cpu/core_dyn_x86/risc_x86.h | 70 ++++++++++++-- src/cpu/core_dyn_x86/string.h | 165 ++++++++++++++++++++++++++++++++ 4 files changed, 274 insertions(+), 21 deletions(-) diff --git a/src/cpu/core_dyn_x86.cpp b/src/cpu/core_dyn_x86.cpp index c4f96725..e6becba8 100644 --- a/src/cpu/core_dyn_x86.cpp +++ b/src/cpu/core_dyn_x86.cpp @@ -70,7 +70,6 @@ enum DualOps { DOP_AND,DOP_OR, DOP_MOV, DOP_TEST, - DOP_IMUL, DOP_XCHG, }; @@ -108,8 +107,8 @@ enum BlockReturn { #define DYNFLG_HAS8 0x2 //Would like 16-bit host reg support #define DYNFLG_LOAD 0x4 //Load value when accessed #define DYNFLG_SAVE 0x8 //Needs to be saved back at the end of block -#define DYNFLG_CHANGED 0x10 //Load value only once because of save -#define DYNFLG_LOADONCE 0x20 //Load value only once because of save +#define DYNFLG_CHANGED 0x10 //Value is in a register and changed from load +#define DYNFLG_ACTIVE 0x20 //Register has an active value class GenReg; class CodePageHandler; @@ -325,7 +324,7 @@ void CPU_Core_Dyn_X86_Init(void) { DynRegs[G_STACK].data=&extra_regs.stack; DynRegs[G_STACK].flags=0; DynRegs[G_CYCLES].data=&CPU_Cycles; - DynRegs[G_CYCLES].flags=DYNFLG_LOAD|DYNFLG_SAVE;; + DynRegs[G_CYCLES].flags=DYNFLG_LOAD|DYNFLG_SAVE; DynRegs[G_TMPB].data=&extra_regs.tmpb; DynRegs[G_TMPB].flags=DYNFLG_HAS8|DYNFLG_HAS16; DynRegs[G_TMPW].data=&extra_regs.tmpd; diff --git a/src/cpu/core_dyn_x86/decoder.h b/src/cpu/core_dyn_x86/decoder.h index 34318015..cc077ec9 100644 --- a/src/cpu/core_dyn_x86/decoder.h +++ b/src/cpu/core_dyn_x86/decoder.h @@ -1,10 +1,32 @@ +/* + * 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 Library 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. + */ + +enum REP_Type { + REP_NONE=0,REP_NZ,REP_Z +}; + static struct DynDecode { PhysPt code; PhysPt code_start; PhysPt op_start; bool big_op; bool big_addr; - bool rep; + REP_Type rep; Bitu cycles; CacheBlock * block; struct { @@ -16,8 +38,6 @@ static struct DynDecode { DynReg * segprefix; } decode; -#include "helpers.h" - static Bit8u INLINE decode_fetchb(void) { return mem_readb(decode.code++); } @@ -49,6 +69,7 @@ static void dyn_write_word(DynReg * addr,DynReg * val,bool dword) { else gen_call_function((void *)&mem_writew,"%Dd%Dw",addr,val); } + static void dyn_reduce_cycles(void) { if (!decode.cycles) decode.cycles++; gen_lea(DREG(CYCLES),DREG(CYCLES),0,0,-(Bits)decode.cycles); @@ -209,6 +230,9 @@ static void dyn_fill_ea(bool addseg=true) { } } +#include "helpers.h" +#include "string.h" + static void dyn_dop_ebgb(DualOps op) { dyn_get_modrm();DynReg * rm_reg=&DynRegs[decode.modrm.reg&3]; if (decode.modrm.mod<3) { @@ -359,6 +383,7 @@ static void dyn_mov_ev_gb(bool sign) { dyn_read_byte(DREG(EA),DREG(TMPB),false); gen_releasereg(DREG(EA)); gen_extend_byte(sign,decode.big_op,rm_reg,DREG(TMPB),0); + gen_releasereg(DREG(TMPB)); } else { gen_extend_byte(sign,decode.big_op,rm_reg,&DynRegs[decode.modrm.rm&3],decode.modrm.rm&4); } @@ -644,7 +669,7 @@ static void dyn_load_seg_off_ea(SegNames seg) { dyn_fill_ea(); gen_lea(DREG(TMPW),DREG(EA),0,0,decode.big_op ? 4:2); dyn_read_word(DREG(TMPW),DREG(TMPW),false); - dyn_load_seg(seg,DREG(TMPW),false); + dyn_load_seg(seg,DREG(TMPW),false);gen_releasereg(DREG(TMPW)); dyn_read_word(DREG(EA),&DynRegs[decode.modrm.reg],decode.big_op); gen_releasereg(DREG(EA)); } else { @@ -900,7 +925,7 @@ static CacheBlock * CreateCacheBlock(PhysPt start,bool big,Bitu max_opcodes) { decode.block=cache_openblock(); gen_save_host_direct(&cache.block.running,(Bit32u)decode.block); for (i=0;iflags&=~DYNFLG_CHANGED; dynreg->genreg=this; - if (dynreg->flags & (DYNFLG_LOAD|DYNFLG_LOADONCE)) { - dynreg->flags&=~DYNFLG_LOADONCE; + if (dynreg->flags & (DYNFLG_LOAD|DYNFLG_ACTIVE)) { cache_addw(0x058b+(index << (8+3))); //Mov reg,[data] cache_addd((Bit32u)dynreg->data); } + dynreg->flags|=DYNFLG_ACTIVE; } void Save(void) { if (!dynreg) IllegalOption(); @@ -56,13 +72,12 @@ public: if (dynreg->flags&DYNFLG_CHANGED && dynreg->flags&DYNFLG_SAVE) { Save(); } - dynreg->flags&=~(DYNFLG_CHANGED|DYNFLG_LOADONCE); + dynreg->flags&=~(DYNFLG_CHANGED|DYNFLG_ACTIVE); dynreg->genreg=0;dynreg=0; } void Clear(void) { if (!dynreg) return; if (dynreg->flags&DYNFLG_CHANGED) { - dynreg->flags|=DYNFLG_LOADONCE; Save(); } dynreg->genreg=0;dynreg=0; @@ -176,10 +191,15 @@ static GenReg * ForceDynReg(GenReg * genreg,DynReg * dynreg) { genreg->Load(dynreg); return genreg; } + +static void gen_preloadreg(DynReg * dynreg) { + FindDynReg(dynreg); +} + static void gen_releasereg(DynReg * dynreg) { GenReg * genreg=dynreg->genreg; if (genreg) genreg->Release(); - else dynreg->flags&=~(DYNFLG_LOADONCE|DYNFLG_CHANGED); + else dynreg->flags&=~(DYNFLG_ACTIVE|DYNFLG_CHANGED); } static void gen_setupreg(DynReg * dnew,DynReg * dsetup) { @@ -203,12 +223,12 @@ static void gen_synchreg(DynReg * dnew,DynReg * dsynch) { } } /* Always use the loadonce flag from either state */ - dnew->flags|=(dsynch->flags & dnew->flags&DYNFLG_LOADONCE); + dnew->flags|=(dsynch->flags & dnew->flags&DYNFLG_ACTIVE); if ((dnew->flags ^ dsynch->flags) & DYNFLG_CHANGED) { /* Ensure the changed value gets saved */ if (dnew->flags & DYNFLG_CHANGED) { dnew->genreg->Save(); - } + } else dnew->flags|=DYNFLG_CHANGED; } } @@ -498,6 +518,24 @@ static void gen_shift_word(ShiftOps op,DynReg * drecx,bool dword,DynReg * dr1) { dr1->flags|=DYNFLG_CHANGED; } +static void gen_shift_word_imm(ShiftOps op,bool dword,DynReg * dr1,Bit8u imm) { + GenReg * gr1=FindDynReg(dr1); + if (!dword) cache_addb(0x66); + switch (op) { + case SHIFT_ROL:cache_addw(0xc0c1+((gr1->index)<<8));break; + case SHIFT_ROR:cache_addw(0xc8c1+((gr1->index)<<8));break; + case SHIFT_RCL:cache_addw(0xd0c1+((gr1->index)<<8));break; + case SHIFT_RCR:cache_addw(0xd8c1+((gr1->index)<<8));break; + case SHIFT_SHL:cache_addw(0xe0c1+((gr1->index)<<8));break; + case SHIFT_SHR:cache_addw(0xe8c1+((gr1->index)<<8));break; + case SHIFT_SAR:cache_addw(0xf8c1+((gr1->index)<<8));break; + default: + IllegalOption(); + } + cache_addb(imm); + dr1->flags|=DYNFLG_CHANGED; +} + static void gen_cbw(bool dword,DynReg * dyn_ax) { ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax); if (!dword) cache_addb(0x66); @@ -678,10 +716,22 @@ static Bit8u * gen_create_branch(BranchTypes type) { return (cache.pos-1); } -static void gen_fill_branch(Bit8u * data) { - *data=(cache.pos-data-1); +static void gen_fill_branch(Bit8u * data,Bit8u * from=cache.pos) { + *data=(from-data-1); } +static Bit8u * gen_create_jump(Bit8u * to=0) { + /* First free all registers */ + cache_addb(0xe9); + cache_addd(to-(cache.pos+4)); + return (cache.pos-4); +} + +static void gen_fill_jump(Bit8u * data,Bit8u * to=cache.pos) { + *(Bit32u*)data=(to-data-4); +} + + static void gen_jmp_ptr(void * ptr,Bits imm=0) { cache_addb(0xa1); cache_addd((Bit32u)ptr); diff --git a/src/cpu/core_dyn_x86/string.h b/src/cpu/core_dyn_x86/string.h index e69de29b..ba076c99 100644 --- a/src/cpu/core_dyn_x86/string.h +++ b/src/cpu/core_dyn_x86/string.h @@ -0,0 +1,165 @@ +/* + * 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 Library 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. + */ + +enum STRING_OP { + STR_OUTSB=0,STR_OUTSW,STR_OUTSD, + STR_INSB=4,STR_INSW,STR_INSD, + STR_MOVSB=8,STR_MOVSW,STR_MOVSD, + STR_LODSB=12,STR_LODSW,STR_LODSD, + STR_STOSB=16,STR_STOSW,STR_STOSD, + STR_SCASB=20,STR_SCASW,STR_SCASD, + STR_CMPSB=24,STR_CMPSW,STR_CMPSD, +}; + +static void dyn_string(STRING_OP op) { + DynReg * si_base=decode.segprefix ? decode.segprefix : DREG(DS); + DynReg * di_base=DREG(ES); + DynReg * tmp_reg;bool usesi;bool usedi; + gen_storeflags(); + if (decode.rep) { + gen_dop_word_imm(DOP_SUB,true,DREG(CYCLES),decode.cycles); + gen_releasereg(DREG(CYCLES)); + decode.cycles=0; + } + /* Check what each string operation will be using */ + switch (op) { + case STR_MOVSB: case STR_MOVSW: case STR_MOVSD: + case STR_CMPSB: case STR_CMPSW: case STR_CMPSD: + tmp_reg=DREG(TMPB);usesi=true;usedi=true;break; + case STR_LODSB: case STR_LODSW: case STR_LODSD: + tmp_reg=DREG(EAX);usesi=true;usedi=false;break; + case STR_OUTSB: case STR_OUTSW: case STR_OUTSD: + tmp_reg=DREG(TMPB);usesi=true;usedi=false;break; + case STR_SCASB: case STR_SCASW: case STR_SCASD: + case STR_STOSB: case STR_STOSW: case STR_STOSD: + tmp_reg=DREG(EAX);usesi=false;usedi=true;break; + case STR_INSB: case STR_INSW: case STR_INSD: + tmp_reg=DREG(TMPB);usesi=false;usedi=true;break; + default: + IllegalOption(); + } + gen_load_host(&cpu.direction,DREG(TMPW),4); + switch (op & 3) { + case 0:break; + case 1:gen_shift_word_imm(SHIFT_SHL,true,DREG(TMPW),1);break; + case 2:gen_shift_word_imm(SHIFT_SHL,true,DREG(TMPW),2);break; + default: + IllegalOption(); + + } + if (usesi) { + gen_preloadreg(DREG(ESI)); + DynRegs[G_ESI].flags|=DYNFLG_CHANGED; + gen_preloadreg(si_base); + } + if (usedi) { + gen_preloadreg(DREG(EDI)); + DynRegs[G_EDI].flags|=DYNFLG_CHANGED; + gen_preloadreg(di_base); + } + if (decode.rep) { + gen_preloadreg(DREG(ECX)); + DynRegs[G_ECX].flags|=DYNFLG_CHANGED; + } + DynState rep_state; + dyn_savestate(&rep_state); + Bit8u * rep_start=cache.pos; + Bit8u * rep_ecx_jmp; + /* Check if ECX!=zero and decrease it */ + if (decode.rep) { + gen_dop_word(DOP_OR,decode.big_addr,DREG(ECX),DREG(ECX)); + Bit8u * branch_ecx=gen_create_branch(BR_NZ); + rep_ecx_jmp=gen_create_jump(); + gen_fill_branch(branch_ecx); + gen_sop_word(SOP_DEC,decode.big_addr,DREG(ECX)); + } + if (usesi) { + if (!decode.big_addr) { + gen_extend_word(false,DREG(EA),DREG(ESI)); + gen_lea(DREG(EA),si_base,DREG(EA),0,0); + } else { + gen_lea(DREG(EA),si_base,DREG(ESI),0,0); + } + gen_dop_word(DOP_ADD,decode.big_addr,DREG(ESI),DREG(TMPW)); + switch (op&3) { + case 0:dyn_read_byte(DREG(EA),tmp_reg,false);break; + case 1:dyn_read_word(DREG(EA),tmp_reg,false);break; + case 2:dyn_read_word(DREG(EA),tmp_reg,true);break; + } + switch (op) { + case STR_OUTSB: + gen_call_function((void*)&IO_WriteB,"%Id%Dl",DREG(EDX),tmp_reg);break; + case STR_OUTSW: + gen_call_function((void*)&IO_WriteW,"%Id%Dw",DREG(EDX),tmp_reg);break; + case STR_OUTSD: + gen_call_function((void*)&IO_WriteD,"%Id%Dd",DREG(EDX),tmp_reg);break; + } + } + if (usedi) { + if (!decode.big_addr) { + gen_extend_word(false,DREG(EA),DREG(EDI)); + gen_lea(DREG(EA),di_base,DREG(EA),0,0); + } else { + gen_lea(DREG(EA),di_base,DREG(EDI),0,0); + } + gen_dop_word(DOP_ADD,decode.big_addr,DREG(EDI),DREG(TMPW)); + /* Maybe something special to be done to fill the value */ + switch (op) { + case STR_INSB: + gen_call_function((void*)&IO_ReadB,"%Dw%Rl",DREG(EDX),tmp_reg); + case STR_MOVSB: + case STR_STOSB: + dyn_write_byte(DREG(EA),tmp_reg,false); + break; + case STR_INSW: + gen_call_function((void*)&IO_ReadW,"%Dw%Rw",DREG(EDX),tmp_reg); + case STR_MOVSW: + case STR_STOSW: + dyn_write_word(DREG(EA),tmp_reg,false); + break; + case STR_INSD: + gen_call_function((void*)&IO_ReadD,"%Dw%Rd",DREG(EDX),tmp_reg); + case STR_MOVSD: + case STR_STOSD: + dyn_write_word(DREG(EA),tmp_reg,true); + break; + default: + IllegalOption(); + } + } + gen_releasereg(DREG(EA));gen_releasereg(DREG(TMPB)); + if (decode.rep) { + DynState cycle_state; + gen_sop_word(SOP_DEC,true,DREG(CYCLES)); + gen_releasereg(DREG(CYCLES)); + dyn_savestate(&cycle_state); + Bit8u * cycle_branch=gen_create_branch(BR_NLE); + gen_lea(DREG(EIP),DREG(EIP),0,0,decode.op_start-decode.code_start); + dyn_releaseregs(); + gen_restoreflags(true); + gen_return(BR_Cycles); + gen_fill_branch(cycle_branch); + dyn_loadstate(&cycle_state); + dyn_synchstate(&rep_state); + /* Jump back to start of ECX check */ + gen_create_jump(rep_start); + gen_fill_jump(rep_ecx_jmp); + } + gen_releasereg(DREG(TMPW)); + gen_restoreflags(); +} \ No newline at end of file