DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/iohandler.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  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
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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     IO_callout_vector &vec = IO_callouts[iotype - IO_TYPE_MIN];
00102     unsigned int match = 0;
00103     IO_ReadHandler *t_f;
00104     size_t scan = 0;
00105 
00106     while (scan < vec.size()) {
00107         IO_CalloutObject &obj = vec[scan++];
00108         if (!obj.isInstalled()) continue;
00109         if (obj.m_r_handler == NULL) continue;
00110         if (!obj.MatchPort(port)) continue;
00111 
00112         t_f = obj.m_r_handler(obj,port,iolen);
00113         if (t_f != NULL) {
00114             if (match != 0) {
00115                 if (iotype == IO_TYPE_ISA)
00116                     ret &= t_f(port,iolen); /* ISA pullup resisters vs ISA devices pulling data lines down (two conflicting devices) */
00117             }
00118             else {
00119                 ret = (/*assign and call*/f=t_f)(port,iolen);
00120             }
00121 
00122             match++;
00123             if (iotype == IO_TYPE_PCI) /* PCI bus only one device can respond, no conflicts */
00124                 break;
00125         }
00126     }
00127 
00128     return match;
00129 }
00130 
00131 template <enum IO_Type_t iotype> static unsigned int IO_Gen_Callout_Write(IO_WriteHandler* &f,Bitu port,Bitu val,Bitu iolen) {
00132     IO_callout_vector &vec = IO_callouts[iotype - IO_TYPE_MIN];
00133     unsigned int match = 0;
00134     IO_WriteHandler *t_f;
00135     size_t scan = 0;
00136 
00137     while (scan < vec.size()) {
00138         IO_CalloutObject &obj = vec[scan++];
00139         if (!obj.isInstalled()) continue;
00140         if (obj.m_w_handler == NULL) continue;
00141         if (!obj.MatchPort(port)) continue;
00142 
00143         t_f = obj.m_w_handler(obj,port,iolen);
00144         if (t_f != NULL) {
00145             t_f(port,val,iolen);
00146             if (match == 0) f = t_f;
00147             match++;
00148         }
00149     }
00150 
00151     return match;
00152 }
00153 
00154 static unsigned int IO_Motherboard_Callout_Read(Bitu &ret,IO_ReadHandler* &f,Bitu port,Bitu iolen) {
00155     return IO_Gen_Callout_Read<IO_TYPE_MB>(ret,f,port,iolen);
00156 }
00157 
00158 static unsigned int IO_PCI_Callout_Read(Bitu &ret,IO_ReadHandler* &f,Bitu port,Bitu iolen) {
00159     return IO_Gen_Callout_Read<IO_TYPE_PCI>(ret,f,port,iolen);
00160 }
00161 
00162 static unsigned int IO_ISA_Callout_Read(Bitu &ret,IO_ReadHandler* &f,Bitu port,Bitu iolen) {
00163     return IO_Gen_Callout_Read<IO_TYPE_ISA>(ret,f,port,iolen);
00164 }
00165 
00166 static unsigned int IO_Motherboard_Callout_Write(IO_WriteHandler* &f,Bitu port,Bitu val,Bitu iolen) {
00167     return IO_Gen_Callout_Write<IO_TYPE_MB>(f,port,val,iolen);
00168 }
00169 
00170 static unsigned int IO_PCI_Callout_Write(IO_WriteHandler* &f,Bitu port,Bitu val,Bitu iolen) {
00171     return IO_Gen_Callout_Write<IO_TYPE_PCI>(f,port,val,iolen);
00172 }
00173 
00174 static unsigned int IO_ISA_Callout_Write(IO_WriteHandler* &f,Bitu port,Bitu val,Bitu iolen) {
00175     return IO_Gen_Callout_Write<IO_TYPE_ISA>(f,port,val,iolen);
00176 }
00177 
00178 static Bitu IO_ReadSlowPath(Bitu port,Bitu iolen) {
00179     IO_ReadHandler *f = iolen > 1 ? IO_ReadDefault : IO_ReadBlocked;
00180     unsigned int match = 0;
00181     unsigned int porti;
00182     Bitu ret = ~0ul;
00183 
00184     /* check motherboard devices */
00185     if ((port & 0xFF00) == 0x0000) /* motherboard-level I/O */
00186         match = IO_Motherboard_Callout_Read(/*&*/ret,/*&*/f,port,iolen);
00187 
00188     if (match == 0) {
00189         /* first PCI bus device, then ISA.
00190          *
00191          * Note that calling out ISA devices that conflict with PCI
00192          * is based on experience with an old Pentium system of mine in the late 1990s
00193          * when I had a Sound Blaster Live PCI! and a Sound Blaster clone both listening
00194          * to ports 220h-22Fh in Windows 98, in which both cards responded to a DOS
00195          * game in the DOS box even though read-wise only the Live PCI!'s data was returned.
00196          * Both cards responded to I/O writes.
00197          *
00198          * Based on that experience, my guess is that to keep ISA from going too slow Intel
00199          * designed the PIIX PCI/ISA bridge to listen for I/O and start an ISA I/O cycle
00200          * even while another PCI device is preparing to respond to it. Then if nobody takes
00201          * it, the PCI/ISA bridge completes the ISA bus cycle and takes up the PCI bus
00202          * transaction. If another PCI device took the I/O write, then the cycle completes
00203          * quietly and the result is discarded.
00204          *
00205          * That's what I think happened, anyway.
00206          *
00207          * I wish I had tools to watch I/O transactions on the ISA bus to verify this. --J.C. */
00208         if (pcibus_enable) {
00209             /* PCI and PCI/ISA bridge emulation */
00210             match = IO_PCI_Callout_Read(/*&*/ret,/*&*/f,port,iolen);
00211 
00212             if (match == 0) {
00213                 /* PCI didn't take it, ask ISA bus */
00214                 match = IO_ISA_Callout_Read(/*&*/ret,/*&*/f,port,iolen);
00215             }
00216             else {
00217                 Bitu dummy;
00218 
00219                 /* PCI did match. Based on behavior noted above, probe ISA bus anyway and discard data. */
00220                 match += IO_ISA_Callout_Read(/*&*/dummy,/*&*/f,port,iolen);
00221             }
00222         }
00223         else {
00224             /* Pure ISA emulation */
00225             match = IO_ISA_Callout_Read(/*&*/ret,/*&*/f,port,iolen);
00226         }
00227     }
00228 
00229     /* if nothing matched, assign default handler to IO handler slot.
00230      * if one device responded, assign it's handler to the IO handler slot.
00231      * if more than one responded, then do not update the IO handler slot. */
00232     assert(iolen >= 1 && iolen <= 4);
00233     porti = (iolen >= 4) ? 2 : (iolen - 1); /* 1 2 x 4 -> 0 1 1 2 */
00234     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);
00235     if (match == 0) ret = f(port,iolen); /* if nobody responded, then call the default */
00236     if (match <= 1) io_readhandlers[porti][port] = f;
00237 
00238         return ret;
00239 }
00240 
00241 void IO_WriteSlowPath(Bitu port,Bitu val,Bitu iolen) {
00242     IO_WriteHandler *f = iolen > 1 ? IO_WriteDefault : IO_WriteBlocked;
00243     unsigned int match = 0;
00244     unsigned int porti;
00245 
00246     /* check motherboard devices */
00247     if ((port & 0xFF00) == 0x0000) /* motherboard-level I/O */
00248         match = IO_Motherboard_Callout_Write(/*&*/f,port,val,iolen);
00249 
00250     if (match == 0) {
00251         /* first PCI bus device, then ISA.
00252          *
00253          * Note that calling out ISA devices that conflict with PCI
00254          * is based on experience with an old Pentium system of mine in the late 1990s
00255          * when I had a Sound Blaster Live PCI! and a Sound Blaster clone both listening
00256          * to ports 220h-22Fh in Windows 98, in which both cards responded to a DOS
00257          * game in the DOS box even though read-wise only the Live PCI!'s data was returned.
00258          * Both cards responded to I/O writes.
00259          *
00260          * Based on that experience, my guess is that to keep ISA from going too slow Intel
00261          * designed the PIIX PCI/ISA bridge to listen for I/O and start an ISA I/O cycle
00262          * even while another PCI device is preparing to respond to it. Then if nobody takes
00263          * it, the PCI/ISA bridge completes the ISA bus cycle and takes up the PCI bus
00264          * transaction. If another PCI device took the I/O write, then the cycle completes
00265          * quietly and the result is discarded.
00266          *
00267          * That's what I think happened, anyway.
00268          *
00269          * I wish I had tools to watch I/O transactions on the ISA bus to verify this. --J.C. */
00270         if (pcibus_enable) {
00271             /* PCI and PCI/ISA bridge emulation */
00272             match = IO_PCI_Callout_Write(/*&*/f,port,val,iolen);
00273 
00274             if (match == 0) {
00275                 /* PCI didn't take it, ask ISA bus */
00276                 match = IO_ISA_Callout_Write(/*&*/f,port,val,iolen);
00277             }
00278             else {
00279                 /* PCI did match. Based on behavior noted above, probe ISA bus anyway and discard data. */
00280                 match += IO_ISA_Callout_Write(/*&*/f,port,val,iolen);
00281             }
00282         }
00283         else {
00284             /* Pure ISA emulation */
00285             match = IO_ISA_Callout_Write(/*&*/f,port,val,iolen);
00286         }
00287     }
00288 
00289     /* if nothing matched, assign default handler to IO handler slot.
00290      * if one device responded, assign it's handler to the IO handler slot.
00291      * if more than one responded, then do not update the IO handler slot. */
00292     assert(iolen >= 1 && iolen <= 4);
00293     porti = (iolen >= 4) ? 2 : (iolen - 1); /* 1 2 x 4 -> 0 1 1 2 */
00294     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);
00295     if (match == 0) f(port,val,iolen); /* if nobody responded, then call the default */
00296     if (match <= 1) io_writehandlers[porti][port] = f;
00297 
00298 #if C_DEBUG && 0
00299     if (match == 0) DEBUG_EnableDebugger();
00300 #endif
00301 }
00302 
00303 void IO_RegisterReadHandler(Bitu port,IO_ReadHandler * handler,Bitu mask,Bitu range) {
00304     assert((port+range) <= IO_MAX);
00305         while (range--) {
00306                 if (mask&IO_MB) io_readhandlers[0][port]=handler;
00307                 if (mask&IO_MW) io_readhandlers[1][port]=handler;
00308                 if (mask&IO_MD) io_readhandlers[2][port]=handler;
00309                 port++;
00310         }
00311 }
00312 
00313 void IO_RegisterWriteHandler(Bitu port,IO_WriteHandler * handler,Bitu mask,Bitu range) {
00314     assert((port+range) <= IO_MAX);
00315         while (range--) {
00316                 if (mask&IO_MB) io_writehandlers[0][port]=handler;
00317                 if (mask&IO_MW) io_writehandlers[1][port]=handler;
00318                 if (mask&IO_MD) io_writehandlers[2][port]=handler;
00319                 port++;
00320         }
00321 }
00322 
00323 void IO_FreeReadHandler(Bitu port,Bitu mask,Bitu range) {
00324     assert((port+range) <= IO_MAX);
00325         while (range--) {
00326                 if (mask&IO_MB) io_readhandlers[0][port]=IO_ReadSlowPath;
00327                 if (mask&IO_MW) io_readhandlers[1][port]=IO_ReadSlowPath;
00328                 if (mask&IO_MD) io_readhandlers[2][port]=IO_ReadSlowPath;
00329                 port++;
00330         }
00331 }
00332 
00333 void IO_FreeWriteHandler(Bitu port,Bitu mask,Bitu range) {
00334     assert((port+range) <= IO_MAX);
00335         while (range--) {
00336                 if (mask&IO_MB) io_writehandlers[0][port]=IO_WriteSlowPath;
00337                 if (mask&IO_MW) io_writehandlers[1][port]=IO_WriteSlowPath;
00338                 if (mask&IO_MD) io_writehandlers[2][port]=IO_WriteSlowPath;
00339                 port++;
00340         }
00341 }
00342 
00343 void IO_InvalidateCachedHandler(Bitu port,Bitu range) {
00344     Bitu mb,r,p;
00345 
00346     assert((port+range) <= IO_MAX);
00347     for (mb=0;mb <= 2;mb++) {
00348         p = port;
00349         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,Bitu 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,Bitu 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,Bitu 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 Bitu IO_ReadB(Bitu port) {
00526         Bitu retval;
00527         if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,1)))) {
00528                 return CPU_ForceV86FakeIO_In(port,1);
00529         }
00530         else {
00531                 IO_USEC_read_delay(0);
00532                 retval = io_readhandlers[0][port](port,1);
00533         }
00534         log_io(0, false, port, retval);
00535         return retval;
00536 }
00537 
00538 Bitu IO_ReadW(Bitu port) {
00539         Bitu retval;
00540         if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,2)))) {
00541                 return CPU_ForceV86FakeIO_In(port,2);
00542         }
00543         else {
00544                 IO_USEC_read_delay(1);
00545                 retval = io_readhandlers[1][port](port,2);
00546         }
00547         log_io(1, false, port, retval);
00548         return retval;
00549 }
00550 
00551 Bitu IO_ReadD(Bitu port) {
00552         Bitu retval;
00553         if (GCC_UNLIKELY(GETFLAG(VM) && (CPU_IO_Exception(port,4)))) {
00554                 return CPU_ForceV86FakeIO_In(port,4);
00555         }
00556         else {
00557                 IO_USEC_read_delay(2);
00558                 retval = 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 = 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 = 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=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     vec.alloc_from = idx; /* an empty slot just opened up, you can alloc from there */
00804 }
00805 
00806 IO_CalloutObject *IO_GetCallout(IO_Callout_t c) {
00807     enum IO_Type_t t = IO_Callout_t_type(c);
00808 
00809     if (t < IO_TYPE_MIN || t >= IO_TYPE_MAX)
00810         return NULL;
00811 
00812     IO_callout_vector &vec = IO_callouts[t - IO_TYPE_MIN];
00813     uint32_t idx = IO_Callout_t_index(c);
00814 
00815     if (idx >= vec.size())
00816         return NULL;
00817 
00818     IO_CalloutObject &obj = vec[idx];
00819     if (!obj.alloc) return NULL;
00820     obj.getcounter++;
00821 
00822     return &obj;
00823 }
00824 
00825 void IO_PutCallout(IO_CalloutObject *obj) {
00826     if (obj == NULL) return;
00827     if (obj->getcounter == 0) return;
00828     obj->getcounter--;
00829 }
00830