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 #include <string.h> 00021 #include "control.h" 00022 #include "dosbox.h" 00023 #include "inout.h" 00024 #include "setup.h" 00025 #include "cpu.h" 00026 #include "../src/cpu/lazyflags.h" 00027 #include "callback.h" 00028 00029 //#define ENABLE_PORTLOG 00030 00031 #include <vector> 00032 00033 extern bool pcibus_enable; 00034 00035 #define IO_callouts_max (IO_TYPE_MAX - IO_TYPE_MIN) 00036 #define IO_callouts_index(t) (t - IO_TYPE_MIN) 00037 00038 class IO_callout_vector : public std::vector<IO_CalloutObject> { 00039 public: 00040 IO_callout_vector() : std::vector<IO_CalloutObject>(), getcounter(0), alloc_from(0) { }; 00041 public: 00042 unsigned int getcounter; 00043 unsigned int alloc_from; 00044 }; 00045 00046 IO_WriteHandler * io_writehandlers[3][IO_MAX]; 00047 IO_ReadHandler * io_readhandlers[3][IO_MAX]; 00048 00049 static IO_callout_vector IO_callouts[IO_callouts_max]; 00050 00051 #if C_DEBUG 00052 void DEBUG_EnableDebugger(void); 00053 #endif 00054 00055 static Bitu IO_ReadBlocked(Bitu /*port*/,Bitu /*iolen*/) { 00056 return ~0ul; 00057 } 00058 00059 static void IO_WriteBlocked(Bitu /*port*/,Bitu /*val*/,Bitu /*iolen*/) { 00060 #if C_DEBUG && 0 00061 DEBUG_EnableDebugger(); 00062 #endif 00063 } 00064 00065 static Bitu IO_ReadDefault(Bitu port,Bitu iolen) { 00066 switch (iolen) { 00067 case 1: 00068 LOG(LOG_IO,LOG_WARN)("Read from port %04X",(int)port); 00069 io_readhandlers[0][port]=IO_ReadBlocked; 00070 return 0xff; 00071 case 2: 00072 return 00073 (io_readhandlers[0][port+0](port+0,1) << 0) | 00074 (io_readhandlers[0][port+1](port+1,1) << 8); 00075 case 4: 00076 return 00077 (io_readhandlers[1][port+0](port+0,2) << 0) | 00078 (io_readhandlers[1][port+2](port+2,2) << 16); 00079 } 00080 return ~0ul; 00081 } 00082 00083 void IO_WriteDefault(Bitu port,Bitu val,Bitu iolen) { 00084 switch (iolen) { 00085 case 1: 00086 LOG(LOG_IO,LOG_WARN)("Writing %02X to port %04X",(int)val,(int)port); 00087 io_writehandlers[0][port]=IO_WriteBlocked; 00088 break; 00089 case 2: 00090 io_writehandlers[0][port+0](port+0,(val >> 0) & 0xff,1); 00091 io_writehandlers[0][port+1](port+1,(val >> 8) & 0xff,1); 00092 break; 00093 case 4: 00094 io_writehandlers[1][port+0](port+0,(val >> 0 ) & 0xffff,2); 00095 io_writehandlers[1][port+2](port+2,(val >> 16) & 0xffff,2); 00096 break; 00097 } 00098 } 00099 00100 template <enum IO_Type_t iotype> static unsigned int IO_Gen_Callout_Read(Bitu &ret,IO_ReadHandler* &f,Bitu port,Bitu iolen) { 00101 int actual = iotype - IO_TYPE_MIN; 00102 IO_callout_vector &vec = IO_callouts[actual]; 00103 unsigned int match = 0; 00104 IO_ReadHandler *t_f; 00105 size_t scan = 0; 00106 00107 while (scan < vec.size()) { 00108 IO_CalloutObject &obj = vec[scan++]; 00109 if (!obj.isInstalled()) continue; 00110 if (obj.m_r_handler == NULL) continue; 00111 if (!obj.MatchPort((Bit16u)port)) continue; 00112 00113 t_f = obj.m_r_handler(obj,port,iolen); 00114 if (t_f != NULL) { 00115 if (match != 0) { 00116 if (iotype == IO_TYPE_ISA) 00117 ret &= t_f(port,iolen); /* ISA pullup resisters vs ISA devices pulling data lines down (two conflicting devices) */ 00118 } 00119 else { 00120 ret = (/*assign and call*/f=t_f)(port,iolen); 00121 } 00122 00123 match++; 00124 if (iotype == IO_TYPE_PCI) /* PCI bus only one device can respond, no conflicts */ 00125 break; 00126 } 00127 } 00128 00129 return match; 00130 } 00131 00132 template <enum IO_Type_t iotype> static unsigned int IO_Gen_Callout_Write(IO_WriteHandler* &f,Bitu port,Bitu val,Bitu iolen) { 00133 int actual = iotype - IO_TYPE_MIN; 00134 IO_callout_vector &vec = IO_callouts[actual]; 00135 unsigned int match = 0; 00136 IO_WriteHandler *t_f; 00137 size_t scan = 0; 00138 00139 while (scan < vec.size()) { 00140 IO_CalloutObject &obj = vec[scan++]; 00141 if (!obj.isInstalled()) continue; 00142 if (obj.m_w_handler == NULL) continue; 00143 if (!obj.MatchPort((Bit16u)port)) continue; 00144 00145 t_f = obj.m_w_handler(obj,port,iolen); 00146 if (t_f != NULL) { 00147 t_f(port,val,iolen); 00148 if (match == 0) f = t_f; 00149 match++; 00150 } 00151 } 00152 00153 return match; 00154 } 00155 00156 static unsigned int IO_Motherboard_Callout_Read(Bitu &ret,IO_ReadHandler* &f,Bitu port,Bitu iolen) { 00157 return IO_Gen_Callout_Read<IO_TYPE_MB>(ret,f,port,iolen); 00158 } 00159 00160 static unsigned int IO_PCI_Callout_Read(Bitu &ret,IO_ReadHandler* &f,Bitu port,Bitu iolen) { 00161 return IO_Gen_Callout_Read<IO_TYPE_PCI>(ret,f,port,iolen); 00162 } 00163 00164 static unsigned int IO_ISA_Callout_Read(Bitu &ret,IO_ReadHandler* &f,Bitu port,Bitu iolen) { 00165 return IO_Gen_Callout_Read<IO_TYPE_ISA>(ret,f,port,iolen); 00166 } 00167 00168 static unsigned int IO_Motherboard_Callout_Write(IO_WriteHandler* &f,Bitu port,Bitu val,Bitu iolen) { 00169 return IO_Gen_Callout_Write<IO_TYPE_MB>(f,port,val,iolen); 00170 } 00171 00172 static unsigned int IO_PCI_Callout_Write(IO_WriteHandler* &f,Bitu port,Bitu val,Bitu iolen) { 00173 return IO_Gen_Callout_Write<IO_TYPE_PCI>(f,port,val,iolen); 00174 } 00175 00176 static unsigned int IO_ISA_Callout_Write(IO_WriteHandler* &f,Bitu port,Bitu val,Bitu iolen) { 00177 return IO_Gen_Callout_Write<IO_TYPE_ISA>(f,port,val,iolen); 00178 } 00179 00180 static Bitu IO_ReadSlowPath(Bitu port,Bitu iolen) { 00181 IO_ReadHandler *f = iolen > 1 ? IO_ReadDefault : IO_ReadBlocked; 00182 unsigned int match = 0; 00183 unsigned int porti; 00184 Bitu ret = ~0ul; 00185 00186 /* check motherboard devices */ 00187 if ((port & 0xFF00) == 0x0000 || IS_PC98_ARCH) /* motherboard-level I/O */ 00188 match = IO_Motherboard_Callout_Read(/*&*/ret,/*&*/f,port,iolen); 00189 00190 if (match == 0) { 00191 /* first PCI bus device, then ISA. 00192 * 00193 * Note that calling out ISA devices that conflict with PCI 00194 * is based on experience with an old Pentium system of mine in the late 1990s 00195 * when I had a Sound Blaster Live PCI! and a Sound Blaster clone both listening 00196 * to ports 220h-22Fh in Windows 98, in which both cards responded to a DOS 00197 * game in the DOS box even though read-wise only the Live PCI!'s data was returned. 00198 * Both cards responded to I/O writes. 00199 * 00200 * Based on that experience, my guess is that to keep ISA from going too slow Intel 00201 * designed the PIIX PCI/ISA bridge to listen for I/O and start an ISA I/O cycle 00202 * even while another PCI device is preparing to respond to it. Then if nobody takes 00203 * it, the PCI/ISA bridge completes the ISA bus cycle and takes up the PCI bus 00204 * transaction. If another PCI device took the I/O write, then the cycle completes 00205 * quietly and the result is discarded. 00206 * 00207 * That's what I think happened, anyway. 00208 * 00209 * I wish I had tools to watch I/O transactions on the ISA bus to verify this. --J.C. */ 00210 if (pcibus_enable) { 00211 /* PCI and PCI/ISA bridge emulation */ 00212 match = IO_PCI_Callout_Read(/*&*/ret,/*&*/f,port,iolen); 00213 00214 if (match == 0) { 00215 /* PCI didn't take it, ask ISA bus */ 00216 match = IO_ISA_Callout_Read(/*&*/ret,/*&*/f,port,iolen); 00217 } 00218 else { 00219 Bitu dummy; 00220 00221 /* PCI did match. Based on behavior noted above, probe ISA bus anyway and discard data. */ 00222 match += IO_ISA_Callout_Read(/*&*/dummy,/*&*/f,port,iolen); 00223 } 00224 } 00225 else { 00226 /* Pure ISA emulation */ 00227 match = IO_ISA_Callout_Read(/*&*/ret,/*&*/f,port,iolen); 00228 } 00229 } 00230 00231 /* if nothing matched, assign default handler to IO handler slot. 00232 * if one device responded, assign it's handler to the IO handler slot. 00233 * if more than one responded, then do not update the IO handler slot. */ 00234 assert(iolen >= 1 && iolen <= 4); 00235 porti = (iolen >= 4) ? 2 : (unsigned int)(iolen - 1); /* 1 2 x 4 -> 0 1 1 2 */ 00236 LOG(LOG_MISC,LOG_DEBUG)("IO read slow path port=%x iolen=%u: device matches=%u",(unsigned int)port,(unsigned int)iolen,(unsigned int)match); 00237 if (match == 0) ret = f(port,iolen); /* if nobody responded, then call the default */ 00238 if (match <= 1) io_readhandlers[porti][port] = f; 00239 00240 return ret; 00241 } 00242 00243 void IO_WriteSlowPath(Bitu port,Bitu val,Bitu iolen) { 00244 IO_WriteHandler *f = iolen > 1 ? IO_WriteDefault : IO_WriteBlocked; 00245 unsigned int match = 0; 00246 unsigned int porti; 00247 00248 /* check motherboard devices */ 00249 if ((port & 0xFF00) == 0x0000 || IS_PC98_ARCH) /* motherboard-level I/O */ 00250 match = IO_Motherboard_Callout_Write(/*&*/f,port,val,iolen); 00251 00252 if (match == 0) { 00253 /* first PCI bus device, then ISA. 00254 * 00255 * Note that calling out ISA devices that conflict with PCI 00256 * is based on experience with an old Pentium system of mine in the late 1990s 00257 * when I had a Sound Blaster Live PCI! and a Sound Blaster clone both listening 00258 * to ports 220h-22Fh in Windows 98, in which both cards responded to a DOS 00259 * game in the DOS box even though read-wise only the Live PCI!'s data was returned. 00260 * Both cards responded to I/O writes. 00261 * 00262 * Based on that experience, my guess is that to keep ISA from going too slow Intel 00263 * designed the PIIX PCI/ISA bridge to listen for I/O and start an ISA I/O cycle 00264 * even while another PCI device is preparing to respond to it. Then if nobody takes 00265 * it, the PCI/ISA bridge completes the ISA bus cycle and takes up the PCI bus 00266 * transaction. If another PCI device took the I/O write, then the cycle completes 00267 * quietly and the result is discarded. 00268 * 00269 * That's what I think happened, anyway. 00270 * 00271 * I wish I had tools to watch I/O transactions on the ISA bus to verify this. --J.C. */ 00272 if (pcibus_enable) { 00273 /* PCI and PCI/ISA bridge emulation */ 00274 match = IO_PCI_Callout_Write(/*&*/f,port,val,iolen); 00275 00276 if (match == 0) { 00277 /* PCI didn't take it, ask ISA bus */ 00278 match = IO_ISA_Callout_Write(/*&*/f,port,val,iolen); 00279 } 00280 else { 00281 /* PCI did match. Based on behavior noted above, probe ISA bus anyway and discard data. */ 00282 match += IO_ISA_Callout_Write(/*&*/f,port,val,iolen); 00283 } 00284 } 00285 else { 00286 /* Pure ISA emulation */ 00287 match = IO_ISA_Callout_Write(/*&*/f,port,val,iolen); 00288 } 00289 } 00290 00291 /* if nothing matched, assign default handler to IO handler slot. 00292 * if one device responded, assign it's handler to the IO handler slot. 00293 * if more than one responded, then do not update the IO handler slot. */ 00294 assert(iolen >= 1 && iolen <= 4); 00295 porti = (iolen >= 4) ? 2 : (unsigned int)(iolen - 1); /* 1 2 x 4 -> 0 1 1 2 */ 00296 LOG(LOG_MISC,LOG_DEBUG)("IO write slow path port=%x data=%x iolen=%u: device matches=%u",(unsigned int)port,(unsigned int)val,(unsigned int)iolen,(unsigned int)match); 00297 if (match == 0) f(port,val,iolen); /* if nobody responded, then call the default */ 00298 if (match <= 1) io_writehandlers[porti][port] = f; 00299 00300 #if C_DEBUG && 0 00301 if (match == 0) DEBUG_EnableDebugger(); 00302 #endif 00303 } 00304 00305 void IO_RegisterReadHandler(Bitu port,IO_ReadHandler * handler,Bitu mask,Bitu range) { 00306 assert((port+range) <= IO_MAX); 00307 while (range--) { 00308 if (mask&IO_MB) io_readhandlers[0][port]=handler; 00309 if (mask&IO_MW) io_readhandlers[1][port]=handler; 00310 if (mask&IO_MD) io_readhandlers[2][port]=handler; 00311 port++; 00312 } 00313 } 00314 00315 void IO_RegisterWriteHandler(Bitu port,IO_WriteHandler * handler,Bitu mask,Bitu range) { 00316 assert((port+range) <= IO_MAX); 00317 while (range--) { 00318 if (mask&IO_MB) io_writehandlers[0][port]=handler; 00319 if (mask&IO_MW) io_writehandlers[1][port]=handler; 00320 if (mask&IO_MD) io_writehandlers[2][port]=handler; 00321 port++; 00322 } 00323 } 00324 00325 void IO_FreeReadHandler(Bitu port,Bitu mask,Bitu range) { 00326 assert((port+range) <= IO_MAX); 00327 while (range--) { 00328 if (mask&IO_MB) io_readhandlers[0][port]=IO_ReadSlowPath; 00329 if (mask&IO_MW) io_readhandlers[1][port]=IO_ReadSlowPath; 00330 if (mask&IO_MD) io_readhandlers[2][port]=IO_ReadSlowPath; 00331 port++; 00332 } 00333 } 00334 00335 void IO_FreeWriteHandler(Bitu port,Bitu mask,Bitu range) { 00336 assert((port+range) <= IO_MAX); 00337 while (range--) { 00338 if (mask&IO_MB) io_writehandlers[0][port]=IO_WriteSlowPath; 00339 if (mask&IO_MW) io_writehandlers[1][port]=IO_WriteSlowPath; 00340 if (mask&IO_MD) io_writehandlers[2][port]=IO_WriteSlowPath; 00341 port++; 00342 } 00343 } 00344 00345 void IO_InvalidateCachedHandler(Bitu port,Bitu range) { 00346 assert((port+range) <= IO_MAX); 00347 for (Bitu mb=0;mb <= 2;mb++) { 00348 Bitu p = port; 00349 Bitu r = range; 00350 while (r--) { 00351 io_writehandlers[mb][p]=IO_WriteSlowPath; 00352 io_readhandlers[mb][p]=IO_ReadSlowPath; 00353 p++; 00354 } 00355 } 00356 } 00357 00358 void IO_ReadHandleObject::Install(Bitu port,IO_ReadHandler * handler,Bitu mask,Bitu range) { 00359 if(!installed) { 00360 installed=true; 00361 m_port=port; 00362 m_mask=mask; 00363 m_range=range; 00364 IO_RegisterReadHandler(port,handler,mask,range); 00365 } else E_Exit("IO_readHandler already installed port %x",(int)port); 00366 } 00367 00368 void IO_ReadHandleObject::Uninstall(){ 00369 if(!installed) return; 00370 IO_FreeReadHandler(m_port,m_mask,m_range); 00371 installed=false; 00372 } 00373 00374 IO_ReadHandleObject::~IO_ReadHandleObject(){ 00375 Uninstall(); 00376 } 00377 00378 void IO_WriteHandleObject::Install(Bitu port,IO_WriteHandler * handler,Bitu mask,Bitu range) { 00379 if(!installed) { 00380 installed=true; 00381 m_port=port; 00382 m_mask=mask; 00383 m_range=range; 00384 IO_RegisterWriteHandler(port,handler,mask,range); 00385 } else E_Exit("IO_writeHandler already installed port %x",(int)port); 00386 } 00387 00388 void IO_WriteHandleObject::Uninstall() { 00389 if(!installed) return; 00390 IO_FreeWriteHandler(m_port,m_mask,m_range); 00391 installed=false; 00392 } 00393 00394 IO_WriteHandleObject::~IO_WriteHandleObject(){ 00395 Uninstall(); 00396 //LOG_MSG("FreeWritehandler called with port %X",m_port); 00397 } 00398 00399 00400 /* Some code to make io operations take some virtual time. Helps certain 00401 * games with their timing of certain operations 00402 */ 00403 00404 /* how much delay to add to I/O in nanoseconds */ 00405 int io_delay_ns[3] = {-1,-1,-1}; 00406 00407 /* nonzero if we're in a callback */ 00408 extern unsigned int last_callback; 00409 00410 inline void IO_USEC_read_delay(const unsigned int szidx) { 00411 if (io_delay_ns[szidx] > 0 && last_callback == 0/*NOT running within a callback function*/) { 00412 Bits delaycyc = (CPU_CycleMax * io_delay_ns[szidx]) / 1000000; 00413 CPU_Cycles -= delaycyc; 00414 CPU_IODelayRemoved += delaycyc; 00415 } 00416 } 00417 00418 inline void IO_USEC_write_delay(const unsigned int szidx) { 00419 if (io_delay_ns[szidx] > 0 && last_callback == 0/*NOT running within a callback function*/) { 00420 Bits delaycyc = (CPU_CycleMax * io_delay_ns[szidx] * 3) / (1000000 * 4); 00421 CPU_Cycles -= delaycyc; 00422 CPU_IODelayRemoved += delaycyc; 00423 } 00424 } 00425 00426 #ifdef ENABLE_PORTLOG 00427 static Bit8u crtc_index = 0; 00428 const char* const len_type[] = {" 8","16","32"}; 00429 void log_io(Bitu width, bool write, Bitu port, Bitu val) { 00430 switch(width) { 00431 case 0: 00432 val&=0xff; 00433 break; 00434 case 1: 00435 val&=0xffff; 00436 break; 00437 } 00438 if (write) { 00439 // skip the video cursor position spam 00440 if (port==0x3d4) { 00441 if (width==0) crtc_index = (Bit8u)val; 00442 else if(width==1) crtc_index = (Bit8u)(val>>8); 00443 } 00444 if (crtc_index==0xe || crtc_index==0xf) { 00445 if((width==0 && (port==0x3d4 || port==0x3d5))||(width==1 && port==0x3d4)) 00446 return; 00447 } 00448 00449 switch(port) { 00450 //case 0x020: // interrupt command 00451 //case 0x040: // timer 0 00452 //case 0x042: // timer 2 00453 //case 0x043: // timer control 00454 //case 0x061: // speaker control 00455 case 0x3c8: // VGA palette 00456 case 0x3c9: // VGA palette 00457 // case 0x3d4: // VGA crtc 00458 // case 0x3d5: // VGA crtc 00459 // case 0x3c4: // VGA seq 00460 // case 0x3c5: // VGA seq 00461 break; 00462 default: 00463 LOG_MSG("iow%s % 4x % 4x, cs:ip %04x:%04x", len_type[width], 00464 (int)port, (int)val, (int)SegValue(cs), (int)reg_eip); 00465 break; 00466 } 00467 } else { 00468 switch(port) { 00469 //case 0x021: // interrupt status 00470 //case 0x040: // timer 0 00471 //case 0x042: // timer 2 00472 //case 0x061: // speaker control 00473 case 0x201: // joystick status 00474 case 0x3c9: // VGA palette 00475 // case 0x3d4: // VGA crtc index 00476 // case 0x3d5: // VGA crtc 00477 case 0x3da: // display status - a real spammer 00478 // don't log for the above cases 00479 break; 00480 default: 00481 LOG_MSG("ior%s % 4x % 4x,\t\tcs:ip %04x:%04x", len_type[width], 00482 (int)port, (int)val, (int)SegValue(cs), (int)reg_eip); 00483 break; 00484 } 00485 } 00486 } 00487 #else 00488 #define log_io(W, X, Y, Z) 00489 #endif 00490 00491 00492 void IO_WriteB(Bitu port,Bit8u val) { 00493 log_io(0, true, port, val); 00494 if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,1)))) { 00495 CPU_ForceV86FakeIO_Out(port,val,1); 00496 } 00497 else { 00498 IO_USEC_write_delay(0); 00499 io_writehandlers[0][port](port,val,1); 00500 } 00501 } 00502 00503 void IO_WriteW(Bitu port,Bit16u val) { 00504 log_io(1, true, port, val); 00505 if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,2)))) { 00506 CPU_ForceV86FakeIO_Out(port,val,2); 00507 } 00508 else { 00509 IO_USEC_write_delay(1); 00510 io_writehandlers[1][port](port,val,2); 00511 } 00512 } 00513 00514 void IO_WriteD(Bitu port,Bit32u val) { 00515 log_io(2, true, port, val); 00516 if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,4)))) { 00517 CPU_ForceV86FakeIO_Out(port,val,4); 00518 } 00519 else { 00520 IO_USEC_write_delay(2); 00521 io_writehandlers[2][port](port,val,4); 00522 } 00523 } 00524 00525 Bit8u IO_ReadB(Bitu port) { 00526 Bit8u retval; 00527 if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,1)))) { 00528 return (Bit8u)CPU_ForceV86FakeIO_In(port,1); 00529 } 00530 else { 00531 IO_USEC_read_delay(0); 00532 retval = (Bit8u)io_readhandlers[0][port](port,1); 00533 } 00534 log_io(0, false, port, retval); 00535 return retval; 00536 } 00537 00538 Bit16u IO_ReadW(Bitu port) { 00539 Bit16u retval; 00540 if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,2)))) { 00541 return (Bit16u)CPU_ForceV86FakeIO_In(port,2); 00542 } 00543 else { 00544 IO_USEC_read_delay(1); 00545 retval = (Bit16u)io_readhandlers[1][port](port,2); 00546 } 00547 log_io(1, false, port, retval); 00548 return retval; 00549 } 00550 00551 Bit32u IO_ReadD(Bitu port) { 00552 Bit32u retval; 00553 if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,4)))) { 00554 return (Bit32u)CPU_ForceV86FakeIO_In(port,4); 00555 } 00556 else { 00557 IO_USEC_read_delay(2); 00558 retval = (Bit32u)io_readhandlers[2][port](port,4); 00559 } 00560 log_io(2, false, port, retval); 00561 return retval; 00562 } 00563 00564 void IO_Reset(Section * /*sec*/) { // Reset or power on 00565 Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox")); 00566 00567 io_delay_ns[0] = section->Get_int("iodelay"); 00568 if (io_delay_ns[0] < 0) { // 8-bit transfers are said to have a transfer cycle with 4 wait states 00569 // TODO: How can we emulate Intel 440FX chipsets that allow 8-bit PIO with 1 wait state, or zero wait state devices? 00570 double t = (1000000000.0 * clockdom_ISA_BCLK.freq_div * 8.5) / clockdom_ISA_BCLK.freq; 00571 io_delay_ns[0] = (int)floor(t); 00572 } 00573 00574 io_delay_ns[1] = section->Get_int("iodelay16"); 00575 if (io_delay_ns[1] < 0) { // 16-bit transfers are said to have a transfer cycle with 1 wait state 00576 // TODO: How can we emulate ISA devices that support zero wait states? 00577 double t = (1000000000.0 * clockdom_ISA_BCLK.freq_div * 5.5) / clockdom_ISA_BCLK.freq; 00578 io_delay_ns[1] = (int)floor(t); 00579 } 00580 00581 io_delay_ns[2] = section->Get_int("iodelay32"); 00582 if (io_delay_ns[2] < 0) { // assume ISA bus details that turn 32-bit PIO into two 16-bit I/O transfers 00583 // TODO: If the device is a PCI device, then 32-bit PIO should take the same amount of time as 8/16-bit PIO 00584 double t = (1000000000.0 * clockdom_ISA_BCLK.freq_div * (5.5 + 5.5)) / clockdom_ISA_BCLK.freq; 00585 io_delay_ns[2] = (int)floor(t); 00586 } 00587 00588 LOG(LOG_IO,LOG_DEBUG)("I/O 8-bit delay %uns",io_delay_ns[0]); 00589 LOG(LOG_IO,LOG_DEBUG)("I/O 16-bit delay %uns",io_delay_ns[1]); 00590 LOG(LOG_IO,LOG_DEBUG)("I/O 32-bit delay %uns",io_delay_ns[2]); 00591 } 00592 00593 void IO_Init() { 00594 LOG(LOG_MISC,LOG_DEBUG)("Initializing I/O port handler system"); 00595 00596 /* init the ports, rather than risk I/O jumping to random code */ 00597 IO_FreeReadHandler(0,IO_MA,IO_MAX); 00598 IO_FreeWriteHandler(0,IO_MA,IO_MAX); 00599 00600 /* please call our reset function on power-on and reset */ 00601 AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(IO_Reset)); 00602 00603 /* prepare callouts */ 00604 IO_InitCallouts(); 00605 } 00606 00607 void IO_CalloutObject::InvalidateCachedHandlers(void) { 00608 Bitu p; 00609 00610 /* NTS: Worst case scenario might be a 10-bit ISA device with 16 I/O ports, 00611 * which would then require resetting 1024 entries of the array. 00612 * 00613 * A 10-bit decode ignores the upper 6 bits = 1 << 6 = 64 00614 * 00615 * 64 * 16 = 1024 I/O ports to be reset. 00616 * 00617 * Not too bad, really. */ 00618 00619 /* for both the base I/O, as well as it's aliases, revert the I/O ports back to "slow path" */ 00620 for (p=m_port;p < 0x10000ul;p += alias_mask+1ul) 00621 IO_InvalidateCachedHandler(p,range_mask+1ul); 00622 } 00623 00624 void IO_CalloutObject::Install(Bitu port,Bitu portmask/*IOMASK_ISA_10BIT, etc.*/,IO_ReadCalloutHandler *r_handler,IO_WriteCalloutHandler *w_handler) { 00625 if(!installed) { 00626 if (portmask == 0 || (portmask & ~0xFFFFU)) { 00627 LOG(LOG_MISC,LOG_ERROR)("IO_CalloutObject::Install: Port mask %x is invalid",(unsigned int)portmask); 00628 return; 00629 } 00630 00631 /* we need a mask for the distance between aliases of the port, and the range of I/O ports. */ 00632 /* only the low part of the mask where bits are zero, not the upper. 00633 * This loop is the reason portmask cannot be ~0 else it would become an infinite loop. 00634 * This also serves to check that the mask is a proper combination of ISA masking and 00635 * I/O port range (example: IOMASK_ISA_10BIT & (~0x3) == 0x3FF & 0xFFFFFFFC = 0x3FC for a device with 4 I/O ports). 00636 * A proper mask has (from MSB to LSB): 00637 * - zero or more 0 bits from MSB 00638 * - 1 or more 1 bits in the middle 00639 * - zero or more 0 bits to LSB */ 00640 { 00641 Bitu m = 1; 00642 Bitu test; 00643 00644 /* compute range mask from zero bits at LSB */ 00645 range_mask = 0; 00646 test = portmask ^ 0xFFFFU; 00647 while ((test & m) == m) { 00648 range_mask = (Bit16u)m; 00649 m = (m << 1) + 1; 00650 } 00651 00652 /* DEBUG */ 00653 if ((portmask & range_mask) != 0 || 00654 ((range_mask + 1) & range_mask) != 0/* should be a mask, therefore AND by itself + 1 should be zero (think (0xF + 1) & 0xF = 0x10 & 0xF = 0x0) */) { 00655 LOG(LOG_MISC,LOG_ERROR)("IO_CalloutObject::Install: portmask(%x) & range_mask(%x) != 0 (%x). You found a corner case that broke this code, fix it.", 00656 (unsigned int)portmask, 00657 (unsigned int)range_mask, 00658 (unsigned int)(portmask & range_mask)); 00659 return; 00660 } 00661 00662 /* compute alias mask from middle 1 bits */ 00663 alias_mask = range_mask; 00664 test = portmask + range_mask; /* will break if portmask & range_mask != 0 */ 00665 while ((test & m) == m) { 00666 alias_mask = (Bit16u)m; 00667 m = (m << 1) + 1; 00668 } 00669 00670 /* any bits after that should be zero. */ 00671 /* confirm this by XORing portmask by (alias_mask ^ range_mask). */ 00672 /* we already confirmed that portmask & range_mask == 0. */ 00673 /* Example: 00674 * 00675 * Sound Blaster at port 220-22Fh with 10-bit ISA decode would be 0x03F0 therefore: 00676 * portmask = 0x03F0 00677 * range_mask = 0x000F 00678 * alias_mask = 0x03FF 00679 * 00680 * portmask ^ range_mask = 0x3FF 00681 * portmask ^ range_mask ^ alias_mask = 0x0000 00682 * 00683 * Example of invalid portmask 0x13F0: 00684 * portmask = 0x13F0 00685 * range_mask = 0x000F 00686 * alias_mask = 0x03FF 00687 * 00688 * portmask ^ range_mask = 0x13FF 00689 * portmask ^ range_mask ^ alias_mask = 0x1000 */ 00690 if ((portmask ^ range_mask ^ alias_mask) != 0 || 00691 ((alias_mask + 1) & alias_mask) != 0/* should be a mask, therefore AND by itself + 1 should be zero */) { 00692 LOG(LOG_MISC,LOG_ERROR)("IO_CalloutObject::Install: portmask(%x) ^ range_mask(%x) ^ alias_mask(%x) != 0 (%x). Invalid portmask.", 00693 (unsigned int)portmask, 00694 (unsigned int)range_mask, 00695 (unsigned int)alias_mask, 00696 (unsigned int)(portmask ^ range_mask ^ alias_mask)); 00697 return; 00698 } 00699 00700 if (port & range_mask) { 00701 LOG(LOG_MISC,LOG_ERROR)("IO_CalloutObject::Install: port %x and port mask %x not aligned (range_mask %x)", 00702 (unsigned int)port,(unsigned int)portmask,(unsigned int)range_mask); 00703 return; 00704 } 00705 } 00706 00707 installed=true; 00708 m_port=port; 00709 m_mask=0; /* not used */ 00710 m_range=0; /* not used */ 00711 io_mask=(Bit16u)portmask; 00712 m_r_handler=r_handler; 00713 m_w_handler=w_handler; 00714 00715 /* add this object to the callout array. 00716 * don't register any I/O handlers. those will be registered during the "slow path" 00717 * callout process when the CPU goes to access them. to encourage that to happen, 00718 * we invalidate the I/O ranges */ 00719 LOG(LOG_MISC,LOG_DEBUG)("IO_CalloutObject::Install added device with port=0x%x io_mask=0x%x rangemask=0x%x aliasmask=0x%x", 00720 (unsigned int)port,(unsigned int)io_mask,(unsigned int)range_mask,(unsigned int)alias_mask); 00721 00722 InvalidateCachedHandlers(); 00723 } 00724 } 00725 00726 void IO_CalloutObject::Uninstall() { 00727 if(!installed) return; 00728 InvalidateCachedHandlers(); 00729 installed=false; 00730 } 00731 00732 void IO_InitCallouts(void) { 00733 /* make sure each vector has enough for a typical load */ 00734 IO_callouts[IO_callouts_index(IO_TYPE_ISA)].resize(64); 00735 IO_callouts[IO_callouts_index(IO_TYPE_PCI)].resize(64); 00736 IO_callouts[IO_callouts_index(IO_TYPE_MB)].resize(64); 00737 } 00738 00739 /* callers maintain a handle to it. 00740 * if they need to touch it, they get a pointer, which they then have to put back. 00741 * The way DOSBox/DOSbox-X code handles IO callbacks it's common to declare an IO object, 00742 * call the install, but then never touch it again, so this should work fine. 00743 * 00744 * this allows us to maintain ready-made IO callout objects to return quickly rather 00745 * than write more complicated code where the caller has to make an IO_CalloutObject 00746 * and then call install and we have to add it's pointer to a list/vector/whatever. 00747 * It also avoids problems where if we have to resize the vector, the pointers become 00748 * invalid, because callers have only handles and they have to put all the pointers 00749 * back in order for us to resize the vector. */ 00750 IO_Callout_t IO_AllocateCallout(IO_Type_t t) { 00751 if (t < IO_TYPE_MIN || t >= IO_TYPE_MAX) 00752 return IO_Callout_t_none; 00753 00754 IO_callout_vector &vec = IO_callouts[t - IO_TYPE_MIN]; 00755 00756 try_again: 00757 while (vec.alloc_from < vec.size()) { 00758 IO_CalloutObject &obj = vec[vec.alloc_from]; 00759 00760 if (!obj.alloc) { 00761 obj.alloc = true; 00762 assert(obj.isInstalled() == false); 00763 return IO_Callout_t_comb(t,vec.alloc_from++); /* make combination, then increment alloc_from */ 00764 } 00765 00766 vec.alloc_from++; 00767 } 00768 00769 /* okay, double the size of the vector within reason. 00770 * if anyone has pointers out to our elements, then we cannot resize. vector::resize() may invalidate them. */ 00771 if (vec.size() < 4096 && vec.getcounter == 0) { 00772 size_t nsz = vec.size() * 2; 00773 00774 LOG(LOG_MISC,LOG_WARN)("IO_AllocateCallout type %u expanding array to %u",(unsigned int)t,(unsigned int)nsz); 00775 vec.alloc_from = (unsigned int)vec.size(); /* allocate from end of old vector size */ 00776 vec.resize(nsz); 00777 goto try_again; 00778 } 00779 00780 LOG(LOG_MISC,LOG_WARN)("IO_AllocateCallout type %u no free entries",(unsigned int)t); 00781 return IO_Callout_t_none; 00782 } 00783 00784 void IO_FreeCallout(IO_Callout_t c) { 00785 enum IO_Type_t t = IO_Callout_t_type(c); 00786 00787 if (t < IO_TYPE_MIN || t >= IO_TYPE_MAX) 00788 return; 00789 00790 IO_callout_vector &vec = IO_callouts[t - IO_TYPE_MIN]; 00791 uint32_t idx = IO_Callout_t_index(c); 00792 00793 if (idx >= vec.size()) 00794 return; 00795 00796 IO_CalloutObject &obj = vec[idx]; 00797 if (!obj.alloc) return; 00798 00799 if (obj.isInstalled()) 00800 obj.Uninstall(); 00801 00802 obj.alloc = false; 00803 if (vec.alloc_from > idx) 00804 vec.alloc_from = idx; /* an empty slot just opened up, you can alloc from there */ 00805 } 00806 00807 IO_CalloutObject *IO_GetCallout(IO_Callout_t c) { 00808 enum IO_Type_t t = IO_Callout_t_type(c); 00809 00810 if (t < IO_TYPE_MIN || t >= IO_TYPE_MAX) 00811 return NULL; 00812 00813 IO_callout_vector &vec = IO_callouts[t - IO_TYPE_MIN]; 00814 uint32_t idx = IO_Callout_t_index(c); 00815 00816 if (idx >= vec.size()) 00817 return NULL; 00818 00819 IO_CalloutObject &obj = vec[idx]; 00820 if (!obj.alloc) return NULL; 00821 obj.getcounter++; 00822 00823 return &obj; 00824 } 00825 00826 void IO_PutCallout(IO_CalloutObject *obj) { 00827 if (obj == NULL) return; 00828 if (obj->getcounter == 0) return; 00829 obj->getcounter--; 00830 } 00831 00832 //save state support 00833 namespace 00834 { 00835 class SerializeIO : public SerializeGlobalPOD 00836 { 00837 public: 00838 SerializeIO() : SerializeGlobalPOD("IO handler") 00839 { 00840 //io_writehandlers -> quasi constant 00841 //io_readhandlers -> quasi constant 00842 00843 //registerPOD(iof_queue.used); registerPOD(iof_queue.entries); 00844 } 00845 } dummy; 00846 }