DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/cpu/core_prefetch_8086.cpp
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 #include <stdio.h>
00021 #include <string.h>
00022 
00023 #include "dosbox.h"
00024 #include "mem.h"
00025 #include "cpu.h"
00026 #include "lazyflags.h"
00027 #include "inout.h"
00028 #include "callback.h"
00029 #include "pic.h"
00030 #include "fpu.h"
00031 #include "paging.h"
00032 #include "mmx.h"
00033 
00034 using namespace std;
00035 
00036 #include <algorithm>
00037 
00038 #define CPU_CORE CPU_ARCHTYPE_8086
00039 #define CPU_Core_Prefetch_Trap_Run CPU_Core8086_Prefetch_Trap_Run
00040 
00041 #define DoString DoString_Prefetch8086
00042 
00043 extern bool ignore_opcode_63;
00044 
00045 #if C_DEBUG
00046 #include "debug.h"
00047 #endif
00048 
00049 static Bit16u last_ea86_offset;
00050 
00051 /* NTS: we special case writes to seg:ffff to emulate 8086 behavior where word read/write wraps around the 64KB segment */
00052 #if (!C_CORE_INLINE)
00053 
00054 #define LoadMb(off) mem_readb(off)
00055 
00056 static inline Bit16u LoadMw(Bitu off) {
00057         if (last_ea86_offset == 0xffff)
00058                 return (mem_readb(off) | (mem_readb(off-0xffff) << 8));
00059 
00060         return mem_readw(off);  
00061 }
00062 
00063 #define LoadMd(off) mem_readd(off)
00064 
00065 #define SaveMb(off,val) mem_writeb(off,val)
00066 
00067 static void SaveMw(Bitu off,Bitu val) {
00068         if (last_ea86_offset == 0xffff) {
00069                 mem_writeb(off,val);
00070                 mem_writeb(off-0xffff,val>>8);
00071         }
00072         else {
00073                 mem_writew(off,val);
00074         }
00075 }
00076 
00077 #define SaveMd(off,val) mem_writed(off,val)
00078 
00079 #else 
00080 
00081 #include "paging.h"
00082 
00083 #define LoadMb(off) mem_readb_inline(off)
00084 
00085 static inline Bit16u LoadMw(Bitu off) {
00086         if (last_ea86_offset == 0xffff)
00087                 return (mem_readb_inline((PhysPt)off) | (mem_readb_inline((PhysPt)(off-0xffff)) << 8));
00088 
00089         return mem_readw_inline((PhysPt)off);
00090 }
00091 
00092 #define LoadMd(off) mem_readd_inline(off)
00093 
00094 #define SaveMb(off,val) mem_writeb_inline(off,val)
00095 
00096 static void SaveMw(Bitu off,Bitu val) {
00097         if (last_ea86_offset == 0xffff) {
00098                 mem_writeb_inline((PhysPt)off,(Bit8u)val);
00099                 mem_writeb_inline((PhysPt)(off-0xffff),(Bit8u)(val>>8));
00100         }
00101         else {
00102                 mem_writew_inline((PhysPt)off,(Bit16u)val);
00103         }
00104 }
00105 
00106 #define SaveMd(off,val) mem_writed_inline(off,val)
00107 
00108 #endif
00109 
00110 extern Bitu cycle_count;
00111 
00112 #if C_FPU
00113 #define CPU_FPU 1u                                              //Enable FPU escape instructions
00114 #endif
00115 
00116 #define CPU_PIC_CHECK 1u
00117 #define CPU_TRAP_CHECK 1u
00118 
00119 #define OPCODE_NONE                     0x000u
00120 #define OPCODE_0F                       0x100u
00121 
00122 #define OPCODE_SIZE                     0u          //DISABLED
00123 
00124 #define PREFIX_ADDR                     0u                      //DISABLED
00125 
00126 #define PREFIX_REP                      0x2u
00127 
00128 #define TEST_PREFIX_ADDR        (0u)
00129 #define TEST_PREFIX_REP         (core.prefixes & PREFIX_REP)
00130 
00131 #define DO_PREFIX_SEG(_SEG)                                     \
00132     if (GETFLAG(IF) && CPU_Cycles <= 0) goto prefix_out; \
00133         BaseDS=SegBase(_SEG);                                   \
00134         BaseSS=SegBase(_SEG);                                   \
00135         core.base_val_ds=_SEG;                                  \
00136         goto restart_opcode;
00137 
00138 // it's the core's job not to decode 0x66-0x67 when compiled for 8086
00139 #define DO_PREFIX_ADDR()                                                                \
00140     abort();
00141 
00142 #define DO_PREFIX_REP(_ZERO)                            \
00143     if (GETFLAG(IF) && CPU_Cycles <= 0) goto prefix_out; \
00144         core.prefixes|=PREFIX_REP;                              \
00145         core.rep_zero=_ZERO;                                    \
00146         goto restart_opcode;
00147 
00148 typedef PhysPt (*GetEAHandler)(void);
00149 
00150 static const Bit32u AddrMaskTable[2]={0x0000ffffu,0x0000ffffu};
00151 
00152 static struct {
00153         Bitu opcode_index;
00154         PhysPt cseip;
00155         PhysPt base_ds,base_ss;
00156         SegNames base_val_ds;
00157         bool rep_zero;
00158         Bitu prefixes;
00159         GetEAHandler * ea_table;
00160 } core;
00161 
00162 #define GETIP           (core.cseip-SegBase(cs))
00163 #define SAVEIP          reg_eip=GETIP;
00164 #define LOADIP          core.cseip=(SegBase(cs)+reg_eip);
00165 
00166 #define SAVEIP_PREFIX           reg_eip=GETIP-1;
00167 
00168 #define SegBase(c)      SegPhys(c)
00169 #define BaseDS          core.base_ds
00170 #define BaseSS          core.base_ss
00171 
00172 //#define PREFETCH_DEBUG
00173 
00174 #define MAX_PQ_SIZE 32
00175 static Bit8u prefetch_buffer[MAX_PQ_SIZE];
00176 static bool pq_valid=false;
00177 static Bitu pq_start;
00178 static Bitu pq_fill;
00179 static Bitu pq_limit;
00180 static Bitu pq_reload;
00181 #ifdef PREFETCH_DEBUG
00182 static double pq_next_dbg=0;
00183 static unsigned int pq_hit=0,pq_miss=0;
00184 #endif
00185 
00186 /* MUST BE POWER OF 2 */
00187 #define prefetch_unit       (2ul)
00188 
00189 #include "core_prefetch_buf.h"
00190 
00191 static INLINE void FetchDiscardb() {
00192         FetchDiscard<uint8_t>();
00193 }
00194 
00195 static INLINE Bit8u FetchPeekb() {
00196         return FetchPeek<uint8_t>();
00197 }
00198 
00199 static Bit8u Fetchb() {
00200         return Fetch<uint8_t>();
00201 }
00202 
00203 static Bit16u Fetchw() {
00204         return Fetch<uint16_t>();
00205 }
00206 
00207 static Bit32u Fetchd() {
00208         return Fetch<uint32_t>();
00209 }
00210 
00211 #define Push_16 CPU_Push16
00212 #define Pop_16 CPU_Pop16
00213 
00214 #include "instructions.h"
00215 #include "core_normal/support.h"
00216 #include "core_normal/string.h"
00217 
00218 
00219 #define EALookupTable (core.ea_table)
00220 
00221 void CPU_Core8086_Prefetch_reset(void) {
00222     pq_valid=false;
00223     prefetch_init(0);
00224 #ifdef PREFETCH_DEBUG
00225     pq_next_dbg=0;
00226 #endif
00227 }
00228 
00229 Bits CPU_Core8086_Prefetch_Run(void) {
00230         bool invalidate_pq=false;
00231 
00232     if (CPU_Cycles <= 0)
00233             return CBRET_NONE;
00234 
00235     pq_limit = (max(CPU_PrefetchQueueSize,(unsigned int)(4ul + prefetch_unit)) + prefetch_unit - 1ul) & (~(prefetch_unit-1ul));
00236     pq_reload = min(pq_limit,(Bitu)8u);
00237 
00238         while (CPU_Cycles-->0) {
00239                 if (invalidate_pq) {
00240                         pq_valid=false;
00241                         invalidate_pq=false;
00242                 }
00243                 LOADIP;
00244                 core.prefixes=0;
00245                 core.opcode_index=0;
00246                 last_ea86_offset=0;
00247                 core.ea_table=&EATable[0];
00248                 BaseDS=SegBase(ds);
00249                 BaseSS=SegBase(ss);
00250                 core.base_val_ds=ds;
00251 #if C_DEBUG
00252 #if C_HEAVY_DEBUG
00253                 if (DEBUG_HeavyIsBreakpoint()) {
00254                         FillFlags();
00255                         return (Bits)debugCallback;
00256                 }
00257 #endif
00258                 cycle_count++;
00259 #endif
00260 restart_opcode:
00261                 Bit8u next_opcode=Fetchb();
00262                 invalidate_pq=false;
00263                 if (core.opcode_index&OPCODE_0F) invalidate_pq=true;
00264                 else switch (next_opcode) {
00265                         case 0x70:      case 0x71:      case 0x72:      case 0x73:
00266                         case 0x74:      case 0x75:      case 0x76:      case 0x77:
00267                         case 0x78:      case 0x79:      case 0x7a:      case 0x7b:
00268                         case 0x7c:      case 0x7d:      case 0x7e:      case 0x7f:      // jcc
00269                         case 0x9a:      // call
00270                         case 0xc2:      case 0xc3:      // retn
00271                         case 0xc8:      // enter
00272                         case 0xc9:      // leave
00273                         case 0xca:      case 0xcb:      // retf
00274                         case 0xcc:      // int3
00275                         case 0xcd:      // int
00276                         case 0xce:      // into
00277                         case 0xcf:      // iret
00278                         case 0xe0:      // loopnz
00279                         case 0xe1:      // loopz
00280                         case 0xe2:      // loop
00281                         case 0xe3:      // jcxz
00282                         case 0xe8:      // call
00283                         case 0xe9:      case 0xea:      case 0xeb:      // jmp
00284                         case 0xff:
00285                                 invalidate_pq=true;
00286                                 break;
00287                         default:
00288                                 break;
00289                 }
00290                 switch (core.opcode_index+next_opcode) {
00291                 #include "core_normal/prefix_none.h"
00292                 default:
00293                 illegal_opcode:
00294 #if C_DEBUG
00295                         {
00296                                 bool ignore=false;
00297                                 Bitu len=(GETIP-reg_eip);
00298                                 LOADIP;
00299                                 if (len>16) len=16;
00300                                 char tempcode[16*2+1];char * writecode=tempcode;
00301                                 if (ignore_opcode_63 && mem_readb(core.cseip) == 0x63)
00302                                         ignore = true;
00303                                 for (;len>0;len--) {
00304                                         sprintf(writecode,"%02X",mem_readb(core.cseip++));
00305                                         writecode+=2;
00306                                 }
00307                                 if (!ignore)
00308                                         LOG(LOG_CPU,LOG_NORMAL)("Illegal/Unhandled opcode %s",tempcode);
00309                         }
00310 #endif
00311                         /* there is no invalid opcode instruction. */
00312                         reg_eip += 2;
00313                         continue;
00314                 }
00315                 SAVEIP;
00316         }
00317 
00318 #ifdef PREFETCH_DEBUG
00319     if (PIC_FullIndex() > pq_next_dbg) {
00320         LOG_MSG("Prefetch core debug: prefetch cache hit=%u miss=%u",pq_hit,pq_miss);
00321         pq_next_dbg += 500.0;
00322     }
00323 #endif
00324 
00325         FillFlags();
00326         return CBRET_NONE;
00327 /* 8086/286 multiple prefix interrupt bug emulation.
00328  * If an instruction is interrupted, only the last prefix is restarted.
00329  * See also [https://www.pcjs.org/pubs/pc/reference/intel/8086/] and [https://www.youtube.com/watch?v=6FC-tcwMBnU] */ 
00330 prefix_out:
00331         SAVEIP_PREFIX;
00332         FillFlags();
00333         return CBRET_NONE;
00334 decode_end:
00335         SAVEIP;
00336         FillFlags();
00337         return CBRET_NONE;
00338 }
00339 
00340 Bits CPU_Core_Prefetch_Trap_Run(void) {
00341         Bits oldCycles = CPU_Cycles;
00342         CPU_Cycles = 1;
00343         cpu.trap_skip = false;
00344 
00345         Bits ret=CPU_Core_Prefetch_Run();
00346         if (!cpu.trap_skip) CPU_HW_Interrupt(1);
00347         CPU_Cycles = oldCycles-1;
00348         cpudecoder = &CPU_Core_Prefetch_Run;
00349 
00350         return ret;
00351 }
00352