DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/cpu/core_normal/string.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 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