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 enum STRING_OP { 00020 // simple string ops 00021 R_OUTSB,R_OUTSW,R_OUTSD, 00022 R_INSB, R_INSW, R_INSD, 00023 R_MOVSB,R_MOVSW,R_MOVSD, 00024 R_LODSB,R_LODSW,R_LODSD, 00025 R_STOSB,R_STOSW,R_STOSD, 00026 // string ops that will stop if a comparison fails 00027 R_SCASB,R_SCASW,R_SCASD, 00028 R_CMPSB,R_CMPSW,R_CMPSD 00029 }; 00030 00031 #define LoadD(_BLAH) _BLAH 00032 00033 extern int cpu_rep_max; 00034 00035 void DoString(STRING_OP type) { 00036 static PhysPt si_base,di_base; 00037 static Bit32u si_index,di_index; 00038 static Bit32u add_mask; 00039 static Bitu count,count_left; 00040 static Bits add_index; 00041 00042 count_left=0; 00043 si_base=BaseDS; 00044 di_base=SegBase(es); 00045 add_mask=AddrMaskTable[core.prefixes & PREFIX_ADDR]; 00046 si_index=reg_esi & add_mask; 00047 di_index=reg_edi & add_mask; 00048 count=reg_ecx & add_mask; 00049 add_index=cpu.direction; 00050 00051 if (!TEST_PREFIX_REP) { 00052 count=1; 00053 } 00054 else { 00055 /* we allow the user to cap our count as a way of making REP string operations interruptable (and at what granularity) */ 00056 /* NTS: This condition is less important now that the loops themselves break when CPU_Cycles <= 0. when this code was 00057 * initially implemented the string ops stubbornly counted through the bytes regardless of pending interrupts and 00058 * it caused problems with code that needed fine timing i.e. demos that played music through the LPT DAC while 00059 * using REP OUTSB to the VGA palette suffered from audio quality problems. at this phase of implementation the 00060 * "interruptible string ops" parameter is now merely a testing parameter that can be used to verify this code 00061 * breaks and restarts string ops correctly. */ 00062 if (cpu_rep_max > 0 && count > (unsigned int)cpu_rep_max) { 00063 count_left+=count-(unsigned int)cpu_rep_max; 00064 count=(unsigned int)cpu_rep_max; 00065 } 00066 } 00067 00068 #if defined(PREFETCH_CORE) 00069 if (pq_valid && pq_limit >= (2 * prefetch_unit)) { 00070 // a REP MOVSB might be a good time for the prefetch queue to empty out old code. 00071 // Hack for self-erasing exit code in demoscene program [mirrors/hornet/demos/1993/s/stereo.zip] 00072 // which erases itself with: 00073 // 00074 // REP STOSW ; blows away own code segment, CX = 0xE000+ or some large value, AL = 0x00 00075 // MOV AH,4C ; remains in prefetch queue 00076 // INT 21h ; remains in prefetch queue 00077 // ; anything past this point doesn't matter 00078 // 00079 // While the REP STOSW + MOV AH,4C + INT 21h is small enough to easily fit into the prefetch queue, 00080 // this fix ensures that it will be in the prefetch queue so the demo part can blow itself away and 00081 // terminate from prefetch properly without crashing. 00082 // 00083 // Another possible fix of course is to NOP out the REP STOSW in the executable. 00084 Bitu stopat = /*LOADIP without setting core.cseip*/(SegBase(cs)+reg_eip) & (~(prefetch_unit-1ul))/*round down*/; 00085 for (unsigned int i=0;pq_start < stopat/* do NOT flush out the REP in REP STOSW*/ && i < ((count/4u)+4u);i++) { 00086 prefetch_lazyflush(core.cseip+pq_limit); 00087 if ((pq_fill - pq_start) < pq_limit) 00088 prefetch_filldword(); 00089 else 00090 break; 00091 } 00092 } 00093 #endif 00094 00095 if (count != 0) { 00096 try { 00097 switch (type) { 00098 case R_OUTSB: 00099 do { 00100 IO_WriteB(reg_dx,LoadMb(si_base+si_index)); 00101 si_index=(si_index+(Bitu)add_index) & add_mask; 00102 count--; 00103 00104 if ((--CPU_Cycles) <= 0) break; 00105 } while (count != 0); break; 00106 case R_OUTSW: 00107 add_index<<=1; 00108 do { 00109 IO_WriteW(reg_dx,LoadMw(si_base+si_index)); 00110 si_index=(si_index+(Bitu)add_index) & add_mask; 00111 count--; 00112 00113 if ((--CPU_Cycles) <= 0) break; 00114 } while (count != 0); break; 00115 case R_OUTSD: 00116 add_index<<=2; 00117 do { 00118 IO_WriteD(reg_dx,LoadMd(si_base+si_index)); 00119 si_index=(si_index+(Bitu)add_index) & add_mask; 00120 count--; 00121 00122 if ((--CPU_Cycles) <= 0) break; 00123 } while (count != 0); break; 00124 00125 case R_INSB: 00126 do { 00127 SaveMb(di_base+di_index,IO_ReadB(reg_dx)); 00128 di_index=(di_index+(Bitu)add_index) & add_mask; 00129 count--; 00130 00131 if ((--CPU_Cycles) <= 0) break; 00132 } while (count != 0); break; 00133 case R_INSW: 00134 add_index<<=1; 00135 do { 00136 SaveMw(di_base+di_index,IO_ReadW(reg_dx)); 00137 di_index=(di_index+(Bitu)add_index) & add_mask; 00138 count--; 00139 00140 if ((--CPU_Cycles) <= 0) break; 00141 } while (count != 0); break; 00142 case R_INSD: 00143 add_index<<=2; 00144 do { 00145 SaveMd(di_base+di_index,IO_ReadD(reg_dx)); 00146 di_index=(di_index+(Bitu)add_index) & add_mask; 00147 count--; 00148 00149 if ((--CPU_Cycles) <= 0) break; 00150 } while (count != 0); break; 00151 00152 case R_STOSB: 00153 do { 00154 SaveMb(di_base+di_index,reg_al); 00155 di_index=(di_index+(Bitu)add_index) & add_mask; 00156 count--; 00157 00158 if ((--CPU_Cycles) <= 0) break; 00159 } while (count != 0); break; 00160 case R_STOSW: 00161 add_index<<=1; 00162 do { 00163 SaveMw(di_base+di_index,reg_ax); 00164 di_index=(di_index+(Bitu)add_index) & add_mask; 00165 count--; 00166 00167 if ((--CPU_Cycles) <= 0) break; 00168 } while (count != 0); break; 00169 case R_STOSD: 00170 add_index<<=2; 00171 do { 00172 SaveMd(di_base+di_index,reg_eax); 00173 di_index=(di_index+(Bitu)add_index) & add_mask; 00174 count--; 00175 00176 if ((--CPU_Cycles) <= 0) break; 00177 } while (count != 0); break; 00178 00179 case R_MOVSB: 00180 do { 00181 SaveMb(di_base+di_index,LoadMb(si_base+si_index)); 00182 di_index=(di_index+(Bitu)add_index) & add_mask; 00183 si_index=(si_index+(Bitu)add_index) & add_mask; 00184 count--; 00185 00186 if ((--CPU_Cycles) <= 0) break; 00187 } while (count != 0); break; 00188 case R_MOVSW: 00189 add_index<<=1; 00190 do { 00191 SaveMw(di_base+di_index,LoadMw(si_base+si_index)); 00192 di_index=(di_index+(Bitu)add_index) & add_mask; 00193 si_index=(si_index+(Bitu)add_index) & add_mask; 00194 count--; 00195 00196 if ((--CPU_Cycles) <= 0) break; 00197 } while (count != 0); break; 00198 case R_MOVSD: 00199 add_index<<=2; 00200 do { 00201 SaveMd(di_base+di_index,LoadMd(si_base+si_index)); 00202 di_index=(di_index+(Bitu)add_index) & add_mask; 00203 si_index=(si_index+(Bitu)add_index) & add_mask; 00204 count--; 00205 00206 if ((--CPU_Cycles) <= 0) break; 00207 } while (count != 0); break; 00208 00209 case R_LODSB: 00210 do { 00211 reg_al=LoadMb(si_base+si_index); 00212 si_index=(si_index+(Bitu)add_index) & add_mask; 00213 count--; 00214 00215 if ((--CPU_Cycles) <= 0) break; 00216 } while (count != 0); break; 00217 case R_LODSW: 00218 add_index<<=1; 00219 do { 00220 reg_ax=LoadMw(si_base+si_index); 00221 si_index=(si_index+(Bitu)add_index) & add_mask; 00222 count--; 00223 00224 if ((--CPU_Cycles) <= 0) break; 00225 } while (count != 0); break; 00226 case R_LODSD: 00227 add_index<<=2; 00228 do { 00229 reg_eax=LoadMd(si_base+si_index); 00230 si_index=(si_index+(Bitu)add_index) & add_mask; 00231 count--; 00232 00233 if ((--CPU_Cycles) <= 0) break; 00234 } while (count != 0); break; 00235 00236 case R_SCASB: 00237 { 00238 Bit8u val2; 00239 do { 00240 val2=LoadMb(di_base+di_index); 00241 di_index=(di_index+(Bitu)add_index) & add_mask; 00242 count--; 00243 00244 if ((--CPU_Cycles) <= 0) break; 00245 if ((reg_al==val2)!=core.rep_zero) break; 00246 } while (count != 0); 00247 CMPB(reg_al,val2,LoadD,0); 00248 } 00249 break; 00250 case R_SCASW: 00251 add_index<<=1; 00252 { 00253 Bit16u val2; 00254 do { 00255 val2=LoadMw(di_base+di_index); 00256 di_index=(di_index+(Bitu)add_index) & add_mask; 00257 count--; 00258 00259 if ((--CPU_Cycles) <= 0) break; 00260 if ((reg_ax==val2)!=core.rep_zero) break; 00261 } while (count != 0); 00262 CMPW(reg_ax,val2,LoadD,0); 00263 } 00264 break; 00265 case R_SCASD: 00266 add_index<<=2; 00267 { 00268 Bit32u val2; 00269 do { 00270 val2=LoadMd(di_base+di_index); 00271 di_index=(di_index+(Bitu)add_index) & add_mask; 00272 count--; 00273 00274 if ((--CPU_Cycles) <= 0) break; 00275 if ((reg_eax==val2)!=core.rep_zero) break; 00276 } while (count != 0); 00277 CMPD(reg_eax,val2,LoadD,0); 00278 } 00279 break; 00280 00281 case R_CMPSB: 00282 { 00283 Bit8u val1,val2; 00284 do { 00285 val1=LoadMb(si_base+si_index); 00286 val2=LoadMb(di_base+di_index); 00287 si_index=(si_index+(Bitu)add_index) & add_mask; 00288 di_index=(di_index+(Bitu)add_index) & add_mask; 00289 count--; 00290 00291 if ((--CPU_Cycles) <= 0) break; 00292 if ((val1==val2)!=core.rep_zero) break; 00293 } while (count != 0); 00294 CMPB(val1,val2,LoadD,0); 00295 } 00296 break; 00297 case R_CMPSW: 00298 add_index<<=1; 00299 { 00300 Bit16u val1,val2; 00301 do { 00302 val1=LoadMw(si_base+si_index); 00303 val2=LoadMw(di_base+di_index); 00304 si_index=(si_index+(Bitu)add_index) & add_mask; 00305 di_index=(di_index+(Bitu)add_index) & add_mask; 00306 count--; 00307 00308 if ((--CPU_Cycles) <= 0) break; 00309 if ((val1==val2)!=core.rep_zero) break; 00310 } while (count != 0); 00311 CMPW(val1,val2,LoadD,0); 00312 } 00313 break; 00314 case R_CMPSD: 00315 add_index<<=2; 00316 { 00317 Bit32u val1,val2; 00318 do { 00319 val1=LoadMd(si_base+si_index); 00320 val2=LoadMd(di_base+di_index); 00321 si_index=(si_index+(Bitu)add_index) & add_mask; 00322 di_index=(di_index+(Bitu)add_index) & add_mask; 00323 count--; 00324 00325 if ((--CPU_Cycles) <= 0) break; 00326 if ((val1==val2)!=core.rep_zero) break; 00327 } while (count != 0); 00328 CMPD(val1,val2,LoadD,0); 00329 } 00330 break; 00331 00332 default: 00333 LOG(LOG_CPU,LOG_ERROR)("Unhandled string op %d",type); 00334 } 00335 00336 /* Clean up after certain amount of instructions */ 00337 reg_esi&=(~add_mask); 00338 reg_esi|=(si_index & add_mask); 00339 reg_edi&=(~add_mask); 00340 reg_edi|=(di_index & add_mask); 00341 if (TEST_PREFIX_REP) { 00342 count+=count_left; 00343 reg_ecx&=(~add_mask); 00344 reg_ecx|=(count & add_mask); 00345 00346 /* if the count is still nonzero, then there is still work to do and the 00347 * instruction has not finished executing---it needs to be restarted. 00348 * if the string op was REP CMPSB or REP SCASB then it also matters 00349 * whether the ZF flag matches the REP condition on whether or not we 00350 * restart the instruction. */ 00351 if (count != 0) { 00352 if (type < R_SCASB) { 00353 /* if count != 0 then restart the instruction */ 00354 LOADIP; 00355 } 00356 else { 00357 /* if ZF matches the REP condition, restart the instruction */ 00358 if ((get_ZF()?1:0) == (core.rep_zero?1:0)) { 00359 LOADIP; 00360 } 00361 } 00362 } 00363 } 00364 } 00365 catch (GuestPageFaultException &pf) { 00366 (void)pf; 00367 /* Clean up after certain amount of instructions */ 00368 reg_esi&=(~add_mask); 00369 reg_esi|=(si_index & add_mask); 00370 reg_edi&=(~add_mask); 00371 reg_edi|=(di_index & add_mask); 00372 if (TEST_PREFIX_REP) { 00373 count+=count_left; 00374 reg_ecx&=(~add_mask); 00375 reg_ecx|=(count & add_mask); 00376 } 00377 00378 /* rethrow the exception. 00379 * NOTE: this means the normal core has no chance to execute SAVEIP, therefore 00380 * when the guest OS has finished handling the page fault the instruction 00381 * pointer will come right back to the string op that caused the fault 00382 * and the string op will restart where it left off. */ 00383 throw; 00384 } 00385 } 00386 } 00387