DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/cpu/core_dynrec/risc_x86.h
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) { }