DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/hardware/dma.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 "dosbox.h"
00022 #include "mem.h"
00023 #include "inout.h"
00024 #include "dma.h"
00025 #include "pic.h"
00026 #include "paging.h"
00027 #include "setup.h"
00028 #include "control.h"
00029 
00030 bool has_pcibus_enable(void);
00031 
00032 DmaController *DmaControllers[2]={NULL};
00033 unsigned char dma_extra_page_registers[16]={0}; /* 0x80-0x8F */
00034 bool enable_dma_extra_page_registers = true;
00035 bool dma_page_register_writeonly = false;
00036 
00037 #define EMM_PAGEFRAME4K ((0xE000*16)/4096)
00038 Bit32u ems_board_mapping[LINK_START];
00039 
00040 static Bit32u dma_wrapping = 0xffff;
00041 
00042 bool enable_1st_dma = true;
00043 bool enable_2nd_dma = true;
00044 bool allow_decrement_mode = true;
00045 int isadma128k = -1;
00046 
00047 static void UpdateEMSMapping(void) {
00048         /* if EMS is not present, this will result in a 1:1 mapping */
00049         Bitu i;
00050         for (i=0;i<0x10;i++) {
00051                 ems_board_mapping[EMM_PAGEFRAME4K+i]=paging.firstmb[EMM_PAGEFRAME4K+i];
00052         }
00053 }
00054 
00055 /* read a block from physical memory */
00056 static void DMA_BlockRead(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16,const Bit32u DMA16_ADDRMASK) {
00057         Bit8u * write=(Bit8u *) data;
00058         Bitu highpart_addr_page = spage>>12;
00059         size <<= dma16;
00060         offset <<= dma16;
00061         Bit32u dma_wrap = (((0xfffful << dma16) + dma16)&DMA16_ADDRMASK) | dma_wrapping;
00062         for ( ; size ; size--, offset++) {
00063                 offset &= dma_wrap;
00064                 Bitu page = highpart_addr_page+(offset >> 12);
00065                 /* care for EMS pageframe etc. */
00066                 if (page < EMM_PAGEFRAME4K) page = paging.firstmb[page];
00067                 else if (page < EMM_PAGEFRAME4K+0x10) page = ems_board_mapping[page];
00068                 else if (page < LINK_START) page = paging.firstmb[page];
00069                 *write++=phys_readb(page*4096 + (offset & 4095));
00070         }
00071 }
00072 
00073 /* decrement mode. Needed for EMF Internal Damage and other weird demo programs that like to transfer
00074  * audio data backwards to the sound card.
00075  *
00076  * NTS: Don't forget, from 8237 datasheet: The DMA chip transfers a byte (or word if 16-bit) of data,
00077  *      and THEN increments or decrements the address. So in decrement mode, "address" is still the
00078  *      first byte before decrementing. */
00079 static void DMA_BlockReadBackwards(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16,const Bit32u DMA16_ADDRMASK) {
00080         Bit8u * write=(Bit8u *) data;
00081         Bitu highpart_addr_page = spage>>12;
00082 
00083         size <<= dma16;
00084         offset <<= dma16;
00085         Bit32u dma_wrap = (((0xfffful << dma16) + dma16)&DMA16_ADDRMASK) | dma_wrapping;
00086 
00087         if (dma16) {
00088                 /* I'm going to assume by how ISA DMA works that you can't just copy bytes backwards,
00089                  * because 16-bit DMA means one 16-bit WORD transferred per DMA memory cycle on the ISA Bus.
00090                  *
00091                  * I have yet to see a DOS program use this mode of ISA DMA, so this remains unimplemented.
00092                  *
00093                  * Data to transfer from the device:
00094                  *
00095                  * 0x1234 0x5678 0x9ABC 0xDEF0
00096                  *
00097                  * Becomes stored to memory by DMA like this (one 16-bit WORD at a time):
00098                  *
00099                  * 0xDEF0 0x9ABC 0x5678 0x1234
00100                  *
00101                  * it does NOT become:
00102                  *
00103                  * 0xF0DE 0xBC9A 0x7856 0x3412 */
00104                 LOG(LOG_DMACONTROL,LOG_WARN)("16-bit decrementing DMA not implemented");
00105         }
00106         else {
00107                 for ( ; size ; size--, offset--) {
00108                         offset &= dma_wrap;
00109                         Bitu page = highpart_addr_page+(offset >> 12);
00110                         /* care for EMS pageframe etc. */
00111                         if (page < EMM_PAGEFRAME4K) page = paging.firstmb[page];
00112                         else if (page < EMM_PAGEFRAME4K+0x10) page = ems_board_mapping[page];
00113                         else if (page < LINK_START) page = paging.firstmb[page];
00114                         *write++=phys_readb(page*4096 + (offset & 4095));
00115                 }
00116         }
00117 }
00118 
00119 /* write a block into physical memory */
00120 static void DMA_BlockWrite(PhysPt spage,PhysPt offset,void * data,Bitu size,Bit8u dma16,const Bit32u DMA16_ADDRMASK) {
00121         Bit8u * read=(Bit8u *) data;
00122         Bitu highpart_addr_page = spage>>12;
00123         size <<= dma16;
00124         offset <<= dma16;
00125         Bit32u dma_wrap = (((0xfffful << dma16) + dma16) & DMA16_ADDRMASK) | dma_wrapping;
00126         for ( ; size ; size--, offset++) {
00127                 if (offset>(dma_wrapping<<dma16)) {
00128                         LOG_MSG("DMA segbound wrapping (write): %x:%x size %x [%x] wrap %x",(int)spage,(int)offset,(int)size,dma16,(int)dma_wrapping);
00129                 }
00130                 offset &= dma_wrap;
00131                 Bitu page = highpart_addr_page+(offset >> 12);
00132                 /* care for EMS pageframe etc. */
00133                 if (page < EMM_PAGEFRAME4K) page = paging.firstmb[page];
00134                 else if (page < EMM_PAGEFRAME4K+0x10) page = ems_board_mapping[page];
00135                 else if (page < LINK_START) page = paging.firstmb[page];
00136                 phys_writeb(page*4096 + (offset & 4095), *read++);
00137         }
00138 }
00139 
00140 DmaChannel * GetDMAChannel(Bit8u chan) {
00141         if (chan<4) {
00142                 /* channel on first DMA controller */
00143                 if (DmaControllers[0]) return DmaControllers[0]->GetChannel(chan);
00144         } else if (chan<8) {
00145                 /* channel on second DMA controller */
00146                 if (DmaControllers[1]) return DmaControllers[1]->GetChannel(chan-4);
00147         }
00148         return NULL;
00149 }
00150 
00151 /* remove the second DMA controller (ports are removed automatically) */
00152 void CloseSecondDMAController(void) {
00153         if (DmaControllers[1]) {
00154                 delete DmaControllers[1];
00155                 DmaControllers[1]=NULL;
00156         }
00157 }
00158 
00159 /* check availability of second DMA controller, needed for SB16 */
00160 bool SecondDMAControllerAvailable(void) {
00161         if (DmaControllers[1]) return true;
00162         else return false;
00163 }
00164 
00165 static void DMA_Write_Port(Bitu port,Bitu val,Bitu /*iolen*/) {
00166         if (port<0x10) {
00167                 /* write to the first DMA controller (channels 0-3) */
00168                 DmaControllers[0]->WriteControllerReg(port,val,1);
00169         } else if (port>=0xc0 && port <=0xdf) {
00170                 /* write to the second DMA controller (channels 4-7) */
00171                 DmaControllers[1]->WriteControllerReg((port-0xc0) >> 1,val,1);
00172         } else {
00173                 UpdateEMSMapping();
00174                 dma_extra_page_registers[port&0xF] = val;
00175                 switch (port) {
00176                         /* write DMA page register */
00177                         case 0x81:GetDMAChannel(2)->SetPage((Bit8u)val);break;
00178                         case 0x82:GetDMAChannel(3)->SetPage((Bit8u)val);break;
00179                         case 0x83:GetDMAChannel(1)->SetPage((Bit8u)val);break;
00180                         case 0x87:GetDMAChannel(0)->SetPage((Bit8u)val);break;
00181                         case 0x89:GetDMAChannel(6)->SetPage((Bit8u)val);break;
00182                         case 0x8a:GetDMAChannel(7)->SetPage((Bit8u)val);break;
00183                         case 0x8b:GetDMAChannel(5)->SetPage((Bit8u)val);break;
00184                         case 0x8f:GetDMAChannel(4)->SetPage((Bit8u)val);break;
00185                         default:
00186                                   if (!enable_dma_extra_page_registers)
00187                                           LOG(LOG_DMACONTROL,LOG_NORMAL)("Trying to write undefined DMA page register %x",(int)port);
00188                                   break;
00189                 }
00190         }
00191 }
00192 
00193 static Bitu DMA_Read_Port(Bitu port,Bitu iolen) {
00194         if (port<0x10) {
00195                 /* read from the first DMA controller (channels 0-3) */
00196                 return DmaControllers[0]->ReadControllerReg(port,iolen);
00197         } else if (port>=0xc0 && port <=0xdf) {
00198                 /* read from the second DMA controller (channels 4-7) */
00199                 return DmaControllers[1]->ReadControllerReg((port-0xc0) >> 1,iolen);
00200         } else {
00201                 /* if we're emulating PC/XT DMA controller behavior, then the page registers
00202                  * are write-only and cannot be read */
00203                 if (dma_page_register_writeonly)
00204                         return ~0UL;
00205 
00206                 switch (port) {
00207                         /* read DMA page register */
00208                         case 0x81:return GetDMAChannel(2)->pagenum;
00209                         case 0x82:return GetDMAChannel(3)->pagenum;
00210                         case 0x83:return GetDMAChannel(1)->pagenum;
00211                         case 0x87:return GetDMAChannel(0)->pagenum;
00212                         case 0x89:return GetDMAChannel(6)->pagenum;
00213                         case 0x8a:return GetDMAChannel(7)->pagenum;
00214                         case 0x8b:return GetDMAChannel(5)->pagenum;
00215                         case 0x8f:return GetDMAChannel(4)->pagenum;
00216                         default:
00217                                   if (enable_dma_extra_page_registers)
00218                                         return dma_extra_page_registers[port&0xF];
00219  
00220                                   LOG(LOG_DMACONTROL,LOG_NORMAL)("Trying to read undefined DMA page register %x",(int)port);
00221                                   break;
00222                 }
00223         }
00224 
00225         return ~0UL;
00226 }
00227 
00228 void DmaController::WriteControllerReg(Bitu reg,Bitu val,Bitu /*len*/) {
00229         DmaChannel * chan;
00230         switch (reg) {
00231         /* set base address of DMA transfer (1st byte low part, 2nd byte high part) */
00232         case 0x0:case 0x2:case 0x4:case 0x6:
00233                 UpdateEMSMapping();
00234                 chan=GetChannel((Bit8u)(reg >> 1));
00235                 flipflop=!flipflop;
00236                 if (flipflop) {
00237                         chan->baseaddr=(chan->baseaddr&0xff00)|val;
00238                         chan->curraddr=(chan->curraddr&0xff00)|val;
00239                 } else {
00240                         chan->baseaddr=(chan->baseaddr&0x00ff)|(val << 8);
00241                         chan->curraddr=(chan->curraddr&0x00ff)|(val << 8);
00242                 }
00243                 break;
00244         /* set DMA transfer count (1st byte low part, 2nd byte high part) */
00245         case 0x1:case 0x3:case 0x5:case 0x7:
00246                 UpdateEMSMapping();
00247                 chan=GetChannel((Bit8u)(reg >> 1));
00248                 flipflop=!flipflop;
00249                 if (flipflop) {
00250                         chan->basecnt=(chan->basecnt&0xff00)|val;
00251                         chan->currcnt=(chan->currcnt&0xff00)|val;
00252                 } else {
00253                         chan->basecnt=(chan->basecnt&0x00ff)|(val << 8);
00254                         chan->currcnt=(chan->currcnt&0x00ff)|(val << 8);
00255                 }
00256                 break;
00257         case 0x8:               /* Comand reg not used */
00258                 break;
00259         case 0x9:               /* Request registers, memory to memory */
00260                 //TODO Warning?
00261                 break;
00262         case 0xa:               /* Mask Register */
00263                 if ((val & 0x4)==0) UpdateEMSMapping();
00264                 chan=GetChannel(val & 3);
00265                 chan->SetMask((val & 0x4)>0);
00266                 break;
00267         case 0xb:               /* Mode Register */
00268                 UpdateEMSMapping();
00269                 chan=GetChannel(val & 3);
00270                 chan->autoinit=(val & 0x10) > 0;
00271                 chan->increment=(!allow_decrement_mode) || ((val & 0x20) == 0); /* 0=increment 1=decrement */
00272                 //TODO Maybe other bits? Like bits 6-7 to select demand/single/block/cascade mode? */
00273                 break;
00274         case 0xc:               /* Clear Flip/Flip */
00275                 flipflop=false;
00276                 break;
00277         case 0xd:               /* Master Clear/Reset */
00278                 for (Bit8u ct=0;ct<4;ct++) {
00279                         chan=GetChannel(ct);
00280                         chan->SetMask(true);
00281                         chan->tcount=false;
00282                 }
00283                 flipflop=false;
00284                 break;
00285         case 0xe:               /* Clear Mask register */               
00286                 UpdateEMSMapping();
00287                 for (Bit8u ct=0;ct<4;ct++) {
00288                         chan=GetChannel(ct);
00289                         chan->SetMask(false);
00290                 }
00291                 break;
00292         case 0xf:               /* Multiple Mask register */
00293                 UpdateEMSMapping();
00294                 for (Bit8u ct=0;ct<4;ct++) {
00295                         chan=GetChannel(ct);
00296                         chan->SetMask(val & 1);
00297                         val>>=1;
00298                 }
00299                 break;
00300         }
00301 }
00302 
00303 Bitu DmaController::ReadControllerReg(Bitu reg,Bitu /*len*/) {
00304         DmaChannel * chan;Bitu ret;
00305         switch (reg) {
00306         /* read base address of DMA transfer (1st byte low part, 2nd byte high part) */
00307         case 0x0:case 0x2:case 0x4:case 0x6:
00308                 chan=GetChannel((Bit8u)(reg >> 1));
00309                 flipflop=!flipflop;
00310                 if (flipflop) {
00311                         return chan->curraddr & 0xff;
00312                 } else {
00313                         return (chan->curraddr >> 8) & 0xff;
00314                 }
00315         /* read DMA transfer count (1st byte low part, 2nd byte high part) */
00316         case 0x1:case 0x3:case 0x5:case 0x7:
00317                 chan=GetChannel((Bit8u)(reg >> 1));
00318                 flipflop=!flipflop;
00319                 if (flipflop) {
00320                         return chan->currcnt & 0xff;
00321                 } else {
00322                         return (chan->currcnt >> 8) & 0xff;
00323                 }
00324         case 0x8:               /* Status Register */
00325                 ret=0;
00326                 for (Bit8u ct=0;ct<4;ct++) {
00327                         chan=GetChannel(ct);
00328                         if (chan->tcount) ret |= 1U << ct;
00329                         chan->tcount=false;
00330                         if (chan->request) ret |= 1U << (4U + ct);
00331                 }
00332                 return ret;
00333         case 0xc:               /* Clear Flip/Flip (apparently most motherboards will treat read OR write as reset) */
00334                 flipflop=false;
00335                 break;
00336         default:
00337                 LOG(LOG_DMACONTROL,LOG_NORMAL)("Trying to read undefined DMA port %x",(int)reg);
00338                 break;
00339         }
00340         return 0xffffffff;
00341 }
00342 
00343 DmaChannel::DmaChannel(Bit8u num, bool dma16) {
00344         masked = true;
00345         callback = NULL;
00346         channum = num;
00347         DMA16 = dma16 ? 0x1 : 0x0;
00348 
00349     if (isadma128k >= 0)
00350         Set128KMode(isadma128k > 0); // user's choice
00351     else
00352         Set128KMode(true); // most hardware seems to implement the 128K case
00353 
00354     LOG(LOG_DMACONTROL,LOG_DEBUG)("DMA channel %u. DMA16_PAGESHIFT=%u DMA16_ADDRMASK=0x%lx",
00355         (unsigned int)channum,(unsigned int)DMA16_PAGESHIFT,(unsigned long)DMA16_ADDRMASK);
00356     pagenum = 0;
00357         pagebase = 0;
00358         baseaddr = 0;
00359         curraddr = 0;
00360         basecnt = 0;
00361         currcnt = 0;
00362         increment = true;
00363         autoinit = false;
00364         tcount = false;
00365         request = false;
00366 }
00367 
00368 Bitu DmaChannel::Read(Bitu want, Bit8u * buffer) {
00369         Bitu done=0;
00370         curraddr &= dma_wrapping;
00371 
00372         /* ISA devices cannot cycle DMA if the controller has masked the channel! Fix your code! */
00373         if (masked) {
00374                 LOG(LOG_DMACONTROL,LOG_WARN)("BUG: Attempted DMA channel read while channel masked");
00375                 return 0;
00376         }
00377 
00378 again:
00379         Bitu left=(currcnt+1UL);
00380         if (want<left) {
00381                 if (increment) {
00382                         DMA_BlockRead(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
00383                         curraddr+=want;
00384                 }
00385                 else {
00386                         DMA_BlockReadBackwards(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
00387                         curraddr-=want;
00388                 }
00389 
00390                 currcnt-=want;
00391                 done+=want;
00392         } else {
00393                 if (increment)
00394                         DMA_BlockRead(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
00395                 else
00396                         DMA_BlockReadBackwards(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
00397 
00398                 buffer+=left << DMA16;
00399                 want-=left;
00400                 done+=left;
00401                 ReachedTC();
00402                 if (autoinit) {
00403                         currcnt=basecnt;
00404                         curraddr=baseaddr;
00405                         if (want) goto again;
00406                         UpdateEMSMapping();
00407                 } else {
00408                         if (increment) curraddr+=left;
00409                         else curraddr-=left;
00410                         currcnt=0xffff;
00411                         masked=true;
00412                         UpdateEMSMapping();
00413                         DoCallBack(DMA_TRANSFEREND);
00414                 }
00415         }
00416         return done;
00417 }
00418 
00419 Bitu DmaChannel::Write(Bitu want, Bit8u * buffer) {
00420         Bitu done=0;
00421         curraddr &= dma_wrapping;
00422 
00423         /* ISA devices cannot cycle DMA if the controller has masked the channel! Fix your code! */
00424         if (masked) {
00425                 LOG(LOG_DMACONTROL,LOG_WARN)("BUG: Attempted DMA channel write while channel masked");
00426                 return 0;
00427         }
00428 
00429         /* TODO: Implement DMA_BlockWriteBackwards() if you find a DOS program, any program, that
00430          *       transfers data backwards into system memory */
00431         if (!increment) {
00432                 LOG(LOG_DMACONTROL,LOG_WARN)("DMA decrement mode (writing) not implemented");
00433                 return 0;
00434         }
00435 
00436 again:
00437         Bitu left=(currcnt+1UL);
00438         if (want<left) {
00439                 DMA_BlockWrite(pagebase,curraddr,buffer,want,DMA16,DMA16_ADDRMASK);
00440                 done+=want;
00441                 curraddr+=want;
00442                 currcnt-=want;
00443         } else {
00444                 DMA_BlockWrite(pagebase,curraddr,buffer,left,DMA16,DMA16_ADDRMASK);
00445                 buffer+=left << DMA16;
00446                 want-=left;
00447                 done+=left;
00448                 ReachedTC();
00449                 if (autoinit) {
00450                         currcnt=basecnt;
00451                         curraddr=baseaddr;
00452                         if (want) goto again;
00453                         UpdateEMSMapping();
00454                 } else {
00455                         curraddr+=left;
00456                         currcnt=0xffff;
00457                         masked=true;
00458                         UpdateEMSMapping();
00459                         DoCallBack(DMA_TRANSFEREND);
00460                 }
00461         }
00462         return done;
00463 }
00464 
00465 void DMA_SetWrapping(Bitu wrap) {
00466         dma_wrapping = wrap;
00467 }
00468 
00469 void DMA_FreeControllers() {
00470         if (DmaControllers[0]) {
00471                 delete DmaControllers[0];
00472                 DmaControllers[0]=NULL;
00473         }
00474         if (DmaControllers[1]) {
00475                 delete DmaControllers[1];
00476                 DmaControllers[1]=NULL;
00477         }
00478 }
00479 
00480 void DMA_Destroy(Section* /*sec*/) {
00481         DMA_FreeControllers();
00482 }
00483 
00484 void DMA_Reset(Section* /*sec*/) {
00485         Bitu i;
00486 
00487         DMA_FreeControllers();
00488 
00489     // FIXME: For now, disable DMA emulation entirely for PC-98.
00490     //        The 8237 is there, but on entirely different I/O ports, and only one DMA controller (0-3).
00491     //        DMA emulation is less important to port than other things like interrupt handling.
00492     if (IS_PC98_ARCH) return;
00493 
00494         // LOG
00495         LOG(LOG_MISC,LOG_DEBUG)("DMA_Reset(): reinitializing DMA controller(s)");
00496 
00497         Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
00498         assert(section != NULL);
00499 
00500         DMA_SetWrapping(0xffff);
00501 
00502         /* NTS: parsing on reset means a reboot of the VM (and possibly other conditions) can permit
00503          *      the user to change DMA emulation settings and have them take effect on VM reboot. */
00504         enable_2nd_dma = section->Get_bool("enable 2nd dma controller");
00505         enable_1st_dma = enable_2nd_dma || section->Get_bool("enable 1st dma controller");
00506         enable_dma_extra_page_registers = section->Get_bool("enable dma extra page registers");
00507         dma_page_register_writeonly = section->Get_bool("dma page registers write-only");
00508         allow_decrement_mode = section->Get_bool("allow dma address decrement");
00509 
00510     {
00511         std::string s = section->Get_string("enable 128k capable 16-bit dma");
00512 
00513         if (s == "true" || s == "1")
00514             isadma128k = 1;
00515         else if (s == "false" || s == "0")
00516             isadma128k = 0;
00517         else
00518             isadma128k = -1;
00519     }
00520 
00521         if (enable_1st_dma)
00522                 DmaControllers[0] = new DmaController(0);
00523         else
00524                 DmaControllers[0] = NULL;
00525 
00526         if (enable_2nd_dma)
00527                 DmaControllers[1] = new DmaController(1);
00528         else
00529                 DmaControllers[1] = NULL;
00530 
00531         for (i=0;i<0x10;i++) {
00532                 Bitu mask = IO_MB;
00533                 if (i < 8) mask |= IO_MW;
00534 
00535                 if (enable_1st_dma) {
00536                         /* install handler for first DMA controller ports */
00537                         DmaControllers[0]->DMA_WriteHandler[i].Install(i,DMA_Write_Port,mask);
00538                         DmaControllers[0]->DMA_ReadHandler[i].Install(i,DMA_Read_Port,mask);
00539                 }
00540                 if (enable_2nd_dma) {
00541                         /* install handler for second DMA controller ports */
00542                         DmaControllers[1]->DMA_WriteHandler[i].Install(0xc0+i*2,DMA_Write_Port,mask);
00543                         DmaControllers[1]->DMA_ReadHandler[i].Install(0xc0+i*2,DMA_Read_Port,mask);
00544                 }
00545         }
00546 
00547         if (enable_1st_dma) {
00548                 /* install handlers for ports 0x81-0x83 (on the first DMA controller) */
00549                 DmaControllers[0]->DMA_WriteHandler[0x10].Install(0x80,DMA_Write_Port,IO_MB,8);
00550                 DmaControllers[0]->DMA_ReadHandler[0x10].Install(0x80,DMA_Read_Port,IO_MB,8);
00551         }
00552 
00553         if (enable_2nd_dma) {
00554                 /* install handlers for ports 0x81-0x83 (on the second DMA controller) */
00555                 DmaControllers[1]->DMA_WriteHandler[0x10].Install(0x88,DMA_Write_Port,IO_MB,8);
00556                 DmaControllers[1]->DMA_ReadHandler[0x10].Install(0x88,DMA_Read_Port,IO_MB,8);
00557         }
00558 
00559         /* FIXME: This should be in a separate EMS board init */
00560         for (i=0;i < LINK_START;i++) ems_board_mapping[i] = i;
00561 }
00562 
00563 void Init_DMA() {
00564         LOG(LOG_MISC,LOG_DEBUG)("Initializing DMA controller emulation");
00565 
00566         AddExitFunction(AddExitFunctionFuncPair(DMA_Destroy));
00567         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(DMA_Reset));
00568 }
00569