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