DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/iohandler.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <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 }