DOSBox-X
|
00001 /* 00002 * Copyright (C) 2002-2020 The DOSBox Team 00003 * 00004 * This program is free software; you can redistribute it and/or modify 00005 * it under the terms of the GNU General Public License as published by 00006 * the Free Software Foundation; either version 2 of the License, or 00007 * (at your option) any later version. 00008 * 00009 * This program is distributed in the hope that it will be useful, 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 * GNU General Public License for more details. 00013 * 00014 * You should have received a copy of the GNU General Public License along 00015 * with this program; if not, write to the Free Software Foundation, Inc., 00016 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00017 */ 00018 00019 00020 00021 // some configuring defines that specify the capabilities of this architecture 00022 // or aspects of the recompiling 00023 00024 // protect FC_ADDR over function calls if necessaray 00025 // #define DRC_PROTECT_ADDR_REG 00026 00027 // try to use non-flags generating functions if possible 00028 #define DRC_FLAGS_INVALIDATION 00029 // try to replace _simple functions by code 00030 #define DRC_FLAGS_INVALIDATION_DCODE 00031 00032 // type with the same size as a pointer 00033 #define DRC_PTR_SIZE_IM Bit32u 00034 00035 // calling convention modifier 00036 #if defined (WIN32) 00037 #define DRC_CALL_CONV _fastcall 00038 #define DRC_FC /* nothing */ 00039 #else 00040 #define DRC_CALL_CONV /* nothing */ 00041 #define DRC_FC GCC_ATTRIBUTE(fastcall) 00042 #endif 00043 00044 00045 // register mapping 00046 enum HostReg { 00047 HOST_EAX=0, 00048 HOST_ECX, 00049 HOST_EDX, 00050 HOST_EBX, 00051 HOST_ESP, 00052 HOST_EBP, 00053 HOST_ESI, 00054 HOST_EDI 00055 }; 00056 00057 00058 // register that holds function return values 00059 #define FC_RETOP HOST_EAX 00060 00061 // register used for address calculations, if the ABI does not 00062 // state that this register is preserved across function calls 00063 // then define DRC_PROTECT_ADDR_REG above 00064 #define FC_ADDR HOST_EBX 00065 00066 // register that holds the first parameter 00067 #define FC_OP1 HOST_ECX 00068 00069 // register that holds the second parameter 00070 #define FC_OP2 HOST_EDX 00071 00072 // special register that holds the third parameter for _R3 calls (byte accessible) 00073 #define FC_OP3 HOST_EAX 00074 00075 // register that holds byte-accessible temporary values 00076 #define FC_TMP_BA1 HOST_ECX 00077 00078 // register that holds byte-accessible temporary values 00079 #define FC_TMP_BA2 HOST_EDX 00080 00081 00082 // temporary register for LEA 00083 #define TEMP_REG_DRC HOST_ESI 00084 00085 00086 // move a full register from reg_src to reg_dst 00087 static void gen_mov_regs(HostReg reg_dst,HostReg reg_src) { 00088 cache_addb(0x8b); // mov reg_dst,reg_src 00089 cache_addb(0xc0+(reg_dst<<3)+reg_src); 00090 } 00091 00092 // move a 32bit (dword==true) or 16bit (dword==false) value from memory into dest_reg 00093 // 16bit moves may destroy the upper 16bit of the destination register 00094 static void gen_mov_word_to_reg(HostReg dest_reg,void* data,bool dword) { 00095 if (!dword) cache_addb(0x66); 00096 cache_addw(0x058b+(dest_reg<<11)); // mov reg,[data] 00097 cache_addd((Bit32u)data); 00098 } 00099 00100 // move a 16bit constant value into dest_reg 00101 // the upper 16bit of the destination register may be destroyed 00102 static void gen_mov_word_to_reg_imm(HostReg dest_reg,Bit16u imm) { 00103 cache_addb(0x66); 00104 cache_addb(0xb8+dest_reg); // mov reg,imm 00105 cache_addw(imm); 00106 } 00107 00108 // move a 32bit constant value into dest_reg 00109 static void gen_mov_dword_to_reg_imm(HostReg dest_reg,Bit32u imm) { 00110 cache_addb(0xb8+dest_reg); // mov reg,imm 00111 cache_addd(imm); 00112 } 00113 00114 // move 32bit (dword==true) or 16bit (dword==false) of a register into memory 00115 static void gen_mov_word_from_reg(HostReg src_reg,void* dest,bool dword) { 00116 if (!dword) cache_addb(0x66); 00117 cache_addw(0x0589+(src_reg<<11)); // mov [data],reg 00118 cache_addd((Bit32u)dest); 00119 } 00120 00121 // move an 8bit value from memory into dest_reg 00122 // the upper 24bit of the destination register can be destroyed 00123 // this function does not use FC_OP1/FC_OP2 as dest_reg as these 00124 // registers might not be directly byte-accessible on some architectures 00125 static void gen_mov_byte_to_reg_low(HostReg dest_reg,void* data) { 00126 cache_addw(0x058a+(dest_reg<<11)); // mov reg,[data] 00127 cache_addd((Bit32u)data); 00128 } 00129 00130 // move an 8bit value from memory into dest_reg 00131 // the upper 24bit of the destination register can be destroyed 00132 // this function can use FC_OP1/FC_OP2 as dest_reg which are 00133 // not directly byte-accessible on some architectures 00134 static void gen_mov_byte_to_reg_low_canuseword(HostReg dest_reg,void* data) { 00135 cache_addb(0x66); 00136 cache_addw(0x058b+(dest_reg<<11)); // mov reg,[data] 00137 cache_addd((Bit32u)data); 00138 } 00139 00140 // move an 8bit constant value into dest_reg 00141 // the upper 24bit of the destination register can be destroyed 00142 // this function does not use FC_OP1/FC_OP2 as dest_reg as these 00143 // registers might not be directly byte-accessible on some architectures 00144 static void gen_mov_byte_to_reg_low_imm(HostReg dest_reg,Bit8u imm) { 00145 cache_addb(0xb0+dest_reg); // mov reg,imm 00146 cache_addb(imm); 00147 } 00148 00149 // move an 8bit constant value into dest_reg 00150 // the upper 24bit of the destination register can be destroyed 00151 // this function can use FC_OP1/FC_OP2 as dest_reg which are 00152 // not directly byte-accessible on some architectures 00153 static void gen_mov_byte_to_reg_low_imm_canuseword(HostReg dest_reg,Bit8u imm) { 00154 cache_addb(0x66); 00155 cache_addb(0xb8+dest_reg); // mov reg,imm 00156 cache_addw(imm); 00157 } 00158 00159 // move the lowest 8bit of a register into memory 00160 static void gen_mov_byte_from_reg_low(HostReg src_reg,void* dest) { 00161 cache_addw(0x0588+(src_reg<<11)); // mov [data],reg 00162 cache_addd((Bit32u)dest); 00163 } 00164 00165 00166 00167 // convert an 8bit word to a 32bit dword 00168 // the register is zero-extended (sign==false) or sign-extended (sign==true) 00169 static void gen_extend_byte(bool sign,HostReg reg) { 00170 cache_addw(0xb60f+(sign?0x800:0)); // movsx/movzx 00171 cache_addb(0xc0+(reg<<3)+reg); 00172 } 00173 00174 // convert a 16bit word to a 32bit dword 00175 // the register is zero-extended (sign==false) or sign-extended (sign==true) 00176 static void gen_extend_word(bool sign,HostReg reg) { 00177 cache_addw(0xb70f+(sign?0x800:0)); // movsx/movzx 00178 cache_addb(0xc0+(reg<<3)+reg); 00179 } 00180 00181 00182 00183 // add a 32bit value from memory to a full register 00184 static void gen_add(HostReg reg,void* op) { 00185 cache_addw(0x0503+(reg<<11)); // add reg,[data] 00186 cache_addd((Bit32u)op); 00187 } 00188 00189 // add a 32bit constant value to a full register 00190 static void gen_add_imm(HostReg reg,Bit32u imm) { 00191 cache_addw(0xc081+(reg<<8)); // add reg,imm 00192 cache_addd(imm); 00193 } 00194 00195 // and a 32bit constant value with a full register 00196 static void gen_and_imm(HostReg reg,Bit32u imm) { 00197 cache_addw(0xe081+(reg<<8)); // and reg,imm 00198 cache_addd(imm); 00199 } 00200 00201 00202 00203 // move a 32bit constant value into memory 00204 static void gen_mov_direct_dword(void* dest,Bit32u imm) { 00205 cache_addw(0x05c7); // mov [data],imm 00206 cache_addd((Bit32u)dest); 00207 cache_addd(imm); 00208 } 00209 00210 // move an address into memory 00211 static void INLINE gen_mov_direct_ptr(void* dest,DRC_PTR_SIZE_IM imm) { 00212 gen_mov_direct_dword(dest,(Bit32u)imm); 00213 } 00214 00215 00216 // add an 8bit constant value to a memory value 00217 static void gen_add_direct_byte(void* dest,Bit8s imm) { 00218 cache_addw(0x0583); // add [data],imm 00219 cache_addd((Bit32u)dest); 00220 cache_addb(imm); 00221 } 00222 00223 // add a 32bit (dword==true) or 16bit (dword==false) constant value to a memory value 00224 static void gen_add_direct_word(void* dest,Bit32u imm,bool dword) { 00225 if ((imm<128) && dword) { 00226 gen_add_direct_byte(dest,(Bit8s)imm); 00227 return; 00228 } 00229 if (!dword) cache_addb(0x66); 00230 cache_addw(0x0581); // add [data],imm 00231 cache_addd((Bit32u)dest); 00232 if (dword) cache_addd((Bit32u)imm); 00233 else cache_addw((Bit16u)imm); 00234 } 00235 00236 // subtract an 8bit constant value from a memory value 00237 static void gen_sub_direct_byte(void* dest,Bit8s imm) { 00238 cache_addw(0x2d83); // sub [data],imm 00239 cache_addd((Bit32u)dest); 00240 cache_addb(imm); 00241 } 00242 00243 // subtract a 32bit (dword==true) or 16bit (dword==false) constant value from a memory value 00244 static void gen_sub_direct_word(void* dest,Bit32u imm,bool dword) { 00245 if ((imm<128) && dword) { 00246 gen_sub_direct_byte(dest,(Bit8s)imm); 00247 return; 00248 } 00249 if (!dword) cache_addb(0x66); 00250 cache_addw(0x2d81); // sub [data],imm 00251 cache_addd((Bit32u)dest); 00252 if (dword) cache_addd((Bit32u)imm); 00253 else cache_addw((Bit16u)imm); 00254 } 00255 00256 00257 00258 // effective address calculation, destination is dest_reg 00259 // scale_reg is scaled by scale (scale_reg*(2^scale)) and 00260 // added to dest_reg, then the immediate value is added 00261 static INLINE void gen_lea(HostReg dest_reg,HostReg scale_reg,Bitu scale,Bits imm) { 00262 Bit8u rm_base; 00263 Bitu imm_size; 00264 if (!imm) { 00265 imm_size=0; rm_base=0x0; //no imm 00266 } else if ((imm>=-128 && imm<=127)) { 00267 imm_size=1; rm_base=0x40; //Signed byte imm 00268 } else { 00269 imm_size=4; rm_base=0x80; //Signed dword imm 00270 } 00271 00272 // ea_reg := ea_reg+scale_reg*(2^scale)+imm 00273 cache_addb(0x8d); //LEA 00274 cache_addb(0x04+(dest_reg << 3)+rm_base); //The sib indicator 00275 cache_addb(dest_reg+(scale_reg<<3)+(scale<<6)); 00276 00277 switch (imm_size) { 00278 case 0: break; 00279 case 1:cache_addb(imm);break; 00280 case 4:cache_addd(imm);break; 00281 } 00282 } 00283 00284 // effective address calculation, destination is dest_reg 00285 // dest_reg is scaled by scale (dest_reg*(2^scale)), 00286 // then the immediate value is added 00287 static INLINE void gen_lea(HostReg dest_reg,Bitu scale,Bits imm) { 00288 // ea_reg := ea_reg*(2^scale)+imm 00289 // ea_reg := op2 *(2^scale)+imm 00290 cache_addb(0x8d); //LEA 00291 cache_addb(0x04+(dest_reg<<3)); 00292 cache_addb(0x05+(dest_reg<<3)+(scale<<6)); 00293 00294 cache_addd(imm); // always add dword immediate 00295 } 00296 00297 00298 00299 // generate a call to a parameterless function 00300 template <typename T> static void INLINE gen_call_function_raw(const T func) { 00301 cache_addb(0xe8); 00302 cache_addd((Bit32u)func - (Bit32u)cache.pos-4); 00303 } 00304 00305 // generate a call to a function with paramcount parameters 00306 // note: the parameters are loaded in the architecture specific way 00307 // using the gen_load_param_ functions below 00308 template <typename T> static Bit32u INLINE gen_call_function_setup(const T func,Bitu paramcount,bool fastcall=false) { 00309 Bit32u proc_addr=(Bit32u)cache.pos; 00310 // Do the actual call to the procedure 00311 cache_addb(0xe8); 00312 cache_addd((Bit32u)func - (Bit32u)cache.pos-4); 00313 00314 // Restore the params of the stack 00315 if (paramcount) { 00316 cache_addw(0xc483); //add ESP,imm byte 00317 cache_addb((!fastcall)?paramcount*4:0); 00318 } 00319 return proc_addr; 00320 } 00321 00322 00323 // load an immediate value as param'th function parameter 00324 static void INLINE gen_load_param_imm(Bitu imm,Bitu param) { 00325 (void)param; 00326 cache_addb(0x68); // push immediate 00327 cache_addd(imm); 00328 } 00329 00330 // load an address as param'th function parameter 00331 static void INLINE gen_load_param_addr(Bitu addr,Bitu param) { 00332 (void)param; 00333 cache_addb(0x68); // push immediate (address) 00334 cache_addd(addr); 00335 } 00336 00337 // load a host-register as param'th function parameter 00338 static void INLINE gen_load_param_reg(Bitu reg,Bitu param) { 00339 (void)param; 00340 cache_addb(0x50+(reg&7)); // push reg 00341 } 00342 00343 // load a value from memory as param'th function parameter 00344 static void INLINE gen_load_param_mem(Bitu mem,Bitu param) { 00345 (void)param; 00346 cache_addw(0x35ff); // push [] 00347 cache_addd(mem); 00348 } 00349 00350 00351 00352 // jump to an address pointed at by ptr, offset is in imm 00353 static void gen_jmp_ptr(void * ptr,Bits imm=0) { 00354 gen_mov_word_to_reg(HOST_EAX,ptr,true); 00355 cache_addb(0xff); // jmp [eax+imm] 00356 if (!imm) { 00357 cache_addb(0x20); 00358 } else if ((imm>=-128 && imm<=127)) { 00359 cache_addb(0x60); 00360 cache_addb(imm); 00361 } else { 00362 cache_addb(0xa0); 00363 cache_addd(imm); 00364 } 00365 } 00366 00367 00368 // short conditional jump (+-127 bytes) if register is zero 00369 // the destination is set by gen_fill_branch() later 00370 static Bit32u gen_create_branch_on_zero(HostReg reg,bool dword) { 00371 if (!dword) cache_addb(0x66); 00372 cache_addb(0x0b); // or reg,reg 00373 cache_addb(0xc0+reg+(reg<<3)); 00374 00375 cache_addw(0x0074); // jz addr 00376 return ((Bit32u)cache.pos-1); 00377 } 00378 00379 // short conditional jump (+-127 bytes) if register is nonzero 00380 // the destination is set by gen_fill_branch() later 00381 static Bit32u gen_create_branch_on_nonzero(HostReg reg,bool dword) { 00382 if (!dword) cache_addb(0x66); 00383 cache_addb(0x0b); // or reg,reg 00384 cache_addb(0xc0+reg+(reg<<3)); 00385 00386 cache_addw(0x0075); // jnz addr 00387 return ((Bit32u)cache.pos-1); 00388 } 00389 00390 // calculate relative offset and fill it into the location pointed to by data 00391 static void gen_fill_branch(DRC_PTR_SIZE_IM data) { 00392 #if C_DEBUG 00393 Bits len=(Bit32u)cache.pos-data; 00394 if (len<0) len=-len; 00395 if (len>126) LOG_MSG("Big jump %d",len); 00396 #endif 00397 *(Bit8u*)data=(Bit8u)((Bit32u)cache.pos-data-1); 00398 } 00399 00400 // conditional jump if register is nonzero 00401 // for isdword==true the 32bit of the register are tested 00402 // for isdword==false the lowest 8bit of the register are tested 00403 static Bit32u gen_create_branch_long_nonzero(HostReg reg,bool isdword) { 00404 // isdword: cmp reg32,0 00405 // not isdword: cmp reg8,0 00406 cache_addb(0x0a+(isdword?1:0)); // or reg,reg 00407 cache_addb(0xc0+reg+(reg<<3)); 00408 00409 cache_addw(0x850f); // jnz 00410 cache_addd(0); 00411 return ((Bit32u)cache.pos-4); 00412 } 00413 00414 // compare 32bit-register against zero and jump if value less/equal than zero 00415 static Bit32u gen_create_branch_long_leqzero(HostReg reg) { 00416 cache_addw(0xf883+(reg<<8)); 00417 cache_addb(0x00); // cmp reg,0 00418 00419 cache_addw(0x8e0f); // jle 00420 cache_addd(0); 00421 return ((Bit32u)cache.pos-4); 00422 } 00423 00424 // calculate long relative offset and fill it into the location pointed to by data 00425 static void gen_fill_branch_long(Bit32u data) { 00426 *(Bit32u*)data=((Bit32u)cache.pos-data-4); 00427 } 00428 00429 00430 static void gen_run_code(void) { 00431 cache_addd(0x0424448b); // mov eax,[esp+4] 00432 cache_addb(0x53); // push ebx 00433 cache_addb(0x56); // push esi 00434 cache_addw(0xd0ff); // call eax 00435 cache_addb(0x5e); // pop esi 00436 cache_addb(0x5b); // pop ebx 00437 } 00438 00439 // return from a function 00440 static void gen_return_function(void) { 00441 cache_addb(0xc3); // ret 00442 } 00443 00444 #ifdef DRC_FLAGS_INVALIDATION 00445 // called when a call to a function can be replaced by a 00446 // call to a simpler function 00447 static void gen_fill_function_ptr(Bit8u * pos,void* fct_ptr,Bitu flags_type) { 00448 #ifdef DRC_FLAGS_INVALIDATION_DCODE 00449 // try to avoid function calls but rather directly fill in code 00450 switch (flags_type) { 00451 case t_ADDb: 00452 case t_ADDw: 00453 case t_ADDd: 00454 *(Bit32u*)pos=0xc203c18b; // mov eax,ecx; add eax,edx 00455 *(pos+4)=0x90; 00456 break; 00457 case t_ORb: 00458 case t_ORw: 00459 case t_ORd: 00460 *(Bit32u*)pos=0xc20bc18b; // mov eax,ecx; or eax,edx 00461 *(pos+4)=0x90; 00462 break; 00463 case t_ANDb: 00464 case t_ANDw: 00465 case t_ANDd: 00466 *(Bit32u*)pos=0xc223c18b; // mov eax,ecx; and eax,edx 00467 *(pos+4)=0x90; 00468 break; 00469 case t_SUBb: 00470 case t_SUBw: 00471 case t_SUBd: 00472 *(Bit32u*)pos=0xc22bc18b; // mov eax,ecx; sub eax,edx 00473 *(pos+4)=0x90; 00474 break; 00475 case t_XORb: 00476 case t_XORw: 00477 case t_XORd: 00478 *(Bit32u*)pos=0xc233c18b; // mov eax,ecx; xor eax,edx 00479 *(pos+4)=0x90; 00480 break; 00481 case t_CMPb: 00482 case t_CMPw: 00483 case t_CMPd: 00484 case t_TESTb: 00485 case t_TESTw: 00486 case t_TESTd: 00487 *(Bit32u*)pos=0x909003eb; // skip 00488 *(pos+4)=0x90; 00489 break; 00490 case t_INCb: 00491 case t_INCw: 00492 case t_INCd: 00493 *(Bit32u*)pos=0x9040c18b; // mov eax,ecx; inc eax 00494 *(pos+4)=0x90; 00495 break; 00496 case t_DECb: 00497 case t_DECw: 00498 case t_DECd: 00499 *(Bit32u*)pos=0x9048c18b; // mov eax,ecx; dec eax 00500 *(pos+4)=0x90; 00501 break; 00502 case t_NEGb: 00503 case t_NEGw: 00504 case t_NEGd: 00505 *(Bit32u*)pos=0xd8f7c18b; // mov eax,ecx; neg eax 00506 *(pos+4)=0x90; 00507 break; 00508 default: 00509 *(Bit32u*)(pos+1)=(Bit32u)((Bit8u*)fct_ptr - (pos+1+4)); // fill function pointer 00510 break; 00511 } 00512 #else 00513 *(Bit32u*)(pos+1)=(Bit32u)((Bit8u*)fct_ptr - (pos+1+4)); // fill function pointer 00514 #endif 00515 } 00516 #endif 00517 00518 static void cache_block_closing(Bit8u* block_start,Bitu block_size) { (void)block_start; (void)block_size; } 00519 00520 static void cache_block_before_close(void) { }