DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/dma.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 "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 #ifdef _MSC_VER
00031 # define MIN(a,b) ((a) < (b) ? (a) : (b))
00032 # define MAX(a,b) ((a) > (b) ? (a) : (b))
00033 #else
00034 # define MIN(a,b) std::min(a,b)
00035 # define MAX(a,b) std::max(a,b)
00036 #endif
00037 
00038 bool has_pcibus_enable(void);
00039 
00040 DmaController *DmaControllers[2]={NULL};
00041 unsigned char dma_extra_page_registers[16]={0}; /* 0x80-0x8F */
00042 bool enable_dma_extra_page_registers = true;
00043 bool dma_page_register_writeonly = false;
00044 
00045 #define EMM_PAGEFRAME4K ((0xE000*16)/4096)
00046 Bit32u ems_board_mapping[LINK_START];
00047 
00048 static Bit32u dma_wrapping = 0xffff;
00049 
00050 bool enable_1st_dma = true;
00051 bool enable_2nd_dma = true;
00052 bool allow_decrement_mode = true;
00053 int isadma128k = -1;
00054 
00055 static void UpdateEMSMapping(void) {
00056         /* if EMS is not present, this will result in a 1:1 mapping */
00057         Bitu i;
00058         for (i=0;i<0x10;i++) {
00059                 ems_board_mapping[EMM_PAGEFRAME4K+i]=paging.firstmb[EMM_PAGEFRAME4K+i];
00060         }
00061 }
00062 
00063 enum {
00064     DMA_INCREMENT,
00065     DMA_DECREMENT
00066 };
00067 
00068 /* DMA block anti-copypasta common code */
00069 template <const unsigned int dma_mode> static inline void DMA_BlockReadCommonSetup(
00070     /*output*/PhysPt &o_xfer,unsigned int &o_size,
00071      /*input*/PhysPt const spage,PhysPt offset,Bitu size,const Bit8u dma16,const Bit32u DMA16_ADDRMASK) {
00072     assert(size != 0u);
00073 
00074         const Bitu highpart_addr_page = spage>>12;
00075         size <<= dma16;
00076         offset <<= dma16;
00077         const Bit32u dma_wrap = (((0xfffful << dma16) + dma16)&DMA16_ADDRMASK) | dma_wrapping;
00078     offset &= dma_wrap;
00079     Bitu page = highpart_addr_page+(offset >> 12); /* page */
00080     offset &= 0xFFFu; /* 4KB offset in page */
00081     /* care for EMS pageframe etc. */
00082     if (page < EMM_PAGEFRAME4K) page = paging.firstmb[page];
00083     else if (page < EMM_PAGEFRAME4K+0x10) page = ems_board_mapping[page];
00084     else if (page < LINK_START) page = paging.firstmb[page];
00085     /* check our work, should not cross 4KB, then transfer linearly. Do not remove {} curly braces, assert() may compile to nothing. */
00086     if (dma_mode == DMA_INCREMENT)
00087         { assert((offset + size - ((Bitu)1u << dma16)) < 4096); } /* should stop with offset at or before first byte of next page */
00088     else if (dma_mode == DMA_DECREMENT)
00089         { assert(offset >= (size - ((Bitu)1u << dma16))); } /* should stop with offset at or after the last byte of the previous page */
00090     /* result */
00091     o_xfer = (PhysPt)((page * 4096u) + offset);
00092     o_size = (unsigned int)size;
00093 }
00094 
00095 /* read a block from physical memory.
00096  * This code assumes new DMA read code that transfers 4KB at a time (4KB byte or 2KB word) therefore it is safe
00097  * to compute the page and offset once and transfer in a tight loop. */
00098 template <const unsigned int dma_mode> static void DMA_BlockRead4KB(const PhysPt spage,const PhysPt offset,void * data,const Bitu size,const Bit8u dma16,const Bit32u DMA16_ADDRMASK) {
00099         Bit8u *write = (Bit8u*)data;
00100     unsigned int o_size;
00101     PhysPt xfer;
00102 
00103     DMA_BlockReadCommonSetup<dma_mode>(/*&*/xfer,/*&*/o_size,spage,offset,size,dma16,DMA16_ADDRMASK);
00104     if (!dma16) { // 8-bit
00105         for ( ; o_size ; o_size--, (dma_mode == DMA_DECREMENT ? (xfer--) : (xfer++)) ) *write++ = phys_readb(xfer);
00106     }
00107     else { // 16-bit
00108         assert((o_size & 1u) == 0);
00109         assert((xfer   & 1u) == 0);
00110         for ( ; o_size ; o_size -= 2, (dma_mode == DMA_DECREMENT ? (xfer -= 2) : (xfer += 2)), write += 2 ) host_writew(write,phys_readw(xfer));
00111     }
00112 }
00113 
00114 /* write a block into physical memory.
00115  * assume caller has already clipped the transfer to stay within a 4KB page. */
00116 template <const unsigned int dma_mode> static void DMA_BlockWrite4KB(PhysPt spage,PhysPt offset,const void * data,Bitu size,Bit8u dma16,const Bit32u DMA16_ADDRMASK) {
00117     const Bit8u *read = (const Bit8u*)data;
00118     unsigned int o_size;
00119     PhysPt xfer;
00120 
00121     DMA_BlockReadCommonSetup<dma_mode>(/*&*/xfer,/*&*/o_size,spage,offset,size,dma16,DMA16_ADDRMASK);
00122     if (!dma16) { // 8-bit
00123         for ( ; o_size ; o_size--, (dma_mode == DMA_DECREMENT ? (xfer--) : (xfer++)) ) phys_writeb(xfer,*read++);
00124     }
00125     else { // 16-bit
00126         assert((o_size & 1u) == 0);
00127         assert((xfer   & 1u) == 0);
00128         for ( ; o_size ; o_size -= 2, (dma_mode == DMA_DECREMENT ? (xfer -= 2) : (xfer += 2)), read += 2 ) phys_writew(xfer,host_readw(read));
00129     }
00130 }
00131 
00132 DmaChannel * GetDMAChannel(Bit8u chan) {
00133         if (chan<4) {
00134                 /* channel on first DMA controller */
00135                 if (DmaControllers[0]) return DmaControllers[0]->GetChannel(chan);
00136         } else if (chan<8) {
00137                 /* channel on second DMA controller */
00138                 if (DmaControllers[1]) return DmaControllers[1]->GetChannel(chan-4);
00139         }
00140         return NULL;
00141 }
00142 
00143 /* remove the second DMA controller (ports are removed automatically) */
00144 void CloseSecondDMAController(void) {
00145         if (DmaControllers[1]) {
00146                 delete DmaControllers[1];
00147                 DmaControllers[1]=NULL;
00148         }
00149 }
00150 
00151 /* check availability of second DMA controller, needed for SB16 */
00152 bool SecondDMAControllerAvailable(void) {
00153         if (DmaControllers[1]) return true;
00154         else return false;
00155 }
00156 
00157 static Bit8u pc98_port_29h = 0;
00158 
00159 static void DMA_Write_Port(Bitu port,Bitu val,Bitu /*iolen*/) {
00160     if (IS_PC98_ARCH) {
00161         // I/O port translation
00162         if (port < 0x20u)
00163             port >>= 1u;
00164         else if (port < 0x28) {/* "bank" registers at 21h, 23h, 25h, 27h */
00165             switch ((port>>1u)&3u) {
00166                 case 0:/* 21h DMA channel 1 */  port=0x83; break;
00167                 case 1:/* 23h DMA channel 2 */  port=0x81; break;
00168                 case 2:/* 25h DMA channel 3 */  port=0x82; break;
00169                 case 3:/* 27h DMA channel 0 */  port=0x87; break;
00170                 default: abort(); break;
00171             }
00172         }
00173         else if (port == 0x29) { /* auto bank increment */
00174             pc98_port_29h = (Bit8u)val;
00175             DmaControllers[0]->GetChannel(val & 3)->page_bank_increment_wraparound =
00176                 ((val & 0x08) ? 0xF0 : 0x00) +
00177                 ((val & 0x04) ? 0x0F : 0x00);
00178 #if 0
00179             LOG_MSG("DMA channel %u page auto increment mask %x",
00180                 (unsigned int)(val&3u),
00181                 DmaControllers[0]->GetChannel(val & 3)->page_bank_increment_wraparound);
00182 #endif
00183         }
00184         else {
00185             abort();
00186         }
00187     }
00188 
00189         if (port<0x10) {
00190                 /* write to the first DMA controller (channels 0-3) */
00191                 DmaControllers[0]->WriteControllerReg(port,val,1);
00192         } else if (port>=0xc0 && port <=0xdf) {
00193                 /* write to the second DMA controller (channels 4-7) */
00194                 DmaControllers[1]->WriteControllerReg((port-0xc0) >> 1,val,1);
00195         } else {
00196                 UpdateEMSMapping();
00197                 dma_extra_page_registers[port&0xF] = (unsigned char)val;
00198                 switch (port) {
00199                         /* write DMA page register */
00200                         case 0x81:GetDMAChannel(2)->SetPage((Bit8u)val);break;
00201                         case 0x82:GetDMAChannel(3)->SetPage((Bit8u)val);break;
00202                         case 0x83:GetDMAChannel(1)->SetPage((Bit8u)val);break;
00203                         case 0x87:GetDMAChannel(0)->SetPage((Bit8u)val);break;
00204                         case 0x89:GetDMAChannel(6)->SetPage((Bit8u)val);break;
00205                         case 0x8a:GetDMAChannel(7)->SetPage((Bit8u)val);break;
00206                         case 0x8b:GetDMAChannel(5)->SetPage((Bit8u)val);break;
00207                         case 0x8f:GetDMAChannel(4)->SetPage((Bit8u)val);break;
00208                         default:
00209                                   if (!enable_dma_extra_page_registers)
00210                                           LOG(LOG_DMACONTROL,LOG_NORMAL)("Trying to write undefined DMA page register %x",(int)port);
00211                                   break;
00212                 }
00213         }
00214 }
00215 
00216 static Bitu DMA_Read_Port(Bitu port,Bitu iolen) {
00217     if (IS_PC98_ARCH) {
00218         // I/O port translation
00219         if (port < 0x20u)
00220             port >>= 1u;
00221         else if (port < 0x28) {/* "bank" registers at 21h, 23h, 25h, 27h */
00222             switch ((port>>1u)&3u) {
00223                 case 0:/* 21h DMA channel 1 */  port=0x83; break;
00224                 case 1:/* 23h DMA channel 2 */  port=0x81; break;
00225                 case 2:/* 25h DMA channel 3 */  port=0x82; break;
00226                 case 3:/* 27h DMA channel 0 */  port=0x87; break;
00227                 default: abort(); break;
00228             }
00229         }
00230         else if (port == 0x29) { /* auto bank increment */
00231             return pc98_port_29h;
00232         }
00233         else {
00234             abort();
00235         }
00236     }
00237 
00238         if (port<0x10) {
00239                 /* read from the first DMA controller (channels 0-3) */
00240                 return DmaControllers[0]->ReadControllerReg(port,iolen);
00241         } else if (port>=0xc0 && port <=0xdf) {
00242                 /* read from the second DMA controller (channels 4-7) */
00243                 return DmaControllers[1]->ReadControllerReg((port-0xc0) >> 1,iolen);
00244         } else {
00245                 /* if we're emulating PC/XT DMA controller behavior, then the page registers
00246                  * are write-only and cannot be read */
00247                 if (dma_page_register_writeonly)
00248                         return ~0UL;
00249 
00250                 switch (port) {
00251                         /* read DMA page register */
00252                         case 0x81:return GetDMAChannel(2)->pagenum;
00253                         case 0x82:return GetDMAChannel(3)->pagenum;
00254                         case 0x83:return GetDMAChannel(1)->pagenum;
00255                         case 0x87:return GetDMAChannel(0)->pagenum;
00256                         case 0x89:return GetDMAChannel(6)->pagenum;
00257                         case 0x8a:return GetDMAChannel(7)->pagenum;
00258                         case 0x8b:return GetDMAChannel(5)->pagenum;
00259                         case 0x8f:return GetDMAChannel(4)->pagenum;
00260                         default:
00261                                   if (enable_dma_extra_page_registers)
00262                                         return dma_extra_page_registers[port&0xF];
00263  
00264                                   LOG(LOG_DMACONTROL,LOG_NORMAL)("Trying to read undefined DMA page register %x",(int)port);
00265                                   break;
00266                 }
00267         }
00268 
00269         return ~0UL;
00270 }
00271 
00272 void DmaController::WriteControllerReg(Bitu reg,Bitu val,Bitu /*len*/) {
00273         DmaChannel * chan;
00274         switch (reg) {
00275         /* set base address of DMA transfer (1st byte low part, 2nd byte high part) */
00276         case 0x0:case 0x2:case 0x4:case 0x6:
00277                 UpdateEMSMapping();
00278                 chan=GetChannel((Bit8u)(reg >> 1));
00279                 flipflop=!flipflop;
00280                 if (flipflop) {
00281                         chan->baseaddr=(Bit16u)((chan->baseaddr&0xff00)|val);
00282                         chan->curraddr=(Bit32u)((chan->curraddr&0xff00)|val);
00283                 } else {
00284                         chan->baseaddr=(Bit16u)((chan->baseaddr&0x00ff)|(val << 8));
00285                         chan->curraddr=(Bit32u)((chan->curraddr&0x00ff)|(val << 8));
00286                 }
00287                 break;
00288         /* set DMA transfer count (1st byte low part, 2nd byte high part) */
00289         case 0x1:case 0x3:case 0x5:case 0x7:
00290                 UpdateEMSMapping();
00291                 chan=GetChannel((Bit8u)(reg >> 1));
00292                 flipflop=!flipflop;
00293                 if (flipflop) {
00294                         chan->basecnt=(Bit16u)((chan->basecnt&0xff00)|val);
00295                         chan->currcnt=(Bit16u)((chan->currcnt&0xff00)|val);
00296                 } else {
00297                         chan->basecnt=(Bit16u)((chan->basecnt&0x00ff)|(val << 8));
00298                         chan->currcnt=(Bit16u)((chan->currcnt&0x00ff)|(val << 8));
00299                 }
00300                 break;
00301         case 0x8:               /* Comand reg not used */
00302                 break;
00303         case 0x9:               /* Request registers, memory to memory */
00304                 //TODO Warning?
00305                 break;
00306         case 0xa:               /* Mask Register */
00307                 if ((val & 0x4)==0) UpdateEMSMapping();
00308                 chan=GetChannel(val & 3);
00309                 chan->SetMask((val & 0x4)>0);
00310                 break;
00311         case 0xb:               /* Mode Register */
00312                 UpdateEMSMapping();
00313                 chan=GetChannel(val & 3);
00314                 chan->autoinit=(val & 0x10) > 0;
00315                 chan->increment=(!allow_decrement_mode) || ((val & 0x20) == 0); /* 0=increment 1=decrement */
00316         chan->transfer_mode=((val >> 2) & 3);
00317                 //TODO Maybe other bits? Like bits 6-7 to select demand/single/block/cascade mode? */
00318                 break;
00319         case 0xc:               /* Clear Flip/Flip */
00320                 flipflop=false;
00321                 break;
00322         case 0xd:               /* Master Clear/Reset */
00323                 for (Bit8u ct=0;ct<4;ct++) {
00324                         chan=GetChannel(ct);
00325                         chan->SetMask(true);
00326                         chan->tcount=false;
00327                 }
00328                 flipflop=false;
00329                 break;
00330         case 0xe:               /* Clear Mask register */               
00331                 UpdateEMSMapping();
00332                 for (Bit8u ct=0;ct<4;ct++) {
00333                         chan=GetChannel(ct);
00334                         chan->SetMask(false);
00335                 }
00336                 break;
00337         case 0xf:               /* Multiple Mask register */
00338                 UpdateEMSMapping();
00339                 for (Bit8u ct=0;ct<4;ct++) {
00340                         chan=GetChannel(ct);
00341                         chan->SetMask(val & 1);
00342                         val>>=1;
00343                 }
00344                 break;
00345         }
00346 }
00347 
00348 Bitu DmaController::ReadControllerReg(Bitu reg,Bitu /*len*/) {
00349         DmaChannel * chan;Bitu ret;
00350         switch (reg) {
00351         /* read base address of DMA transfer (1st byte low part, 2nd byte high part) */
00352         case 0x0:case 0x2:case 0x4:case 0x6:
00353                 chan=GetChannel((Bit8u)(reg >> 1));
00354                 flipflop=!flipflop;
00355                 if (flipflop) {
00356                         return chan->curraddr & 0xff;
00357                 } else {
00358                         return (chan->curraddr >> 8) & 0xff;
00359                 }
00360         /* read DMA transfer count (1st byte low part, 2nd byte high part) */
00361         case 0x1:case 0x3:case 0x5:case 0x7:
00362                 chan=GetChannel((Bit8u)(reg >> 1));
00363                 flipflop=!flipflop;
00364                 if (flipflop) {
00365                         return chan->currcnt & 0xff;
00366                 } else {
00367                         return (chan->currcnt >> 8) & 0xff;
00368                 }
00369         case 0x8:               /* Status Register */
00370                 ret=0;
00371                 for (Bit8u ct=0;ct<4;ct++) {
00372                         chan=GetChannel(ct);
00373                         if (chan->tcount) ret |= (Bitu)1U << ct;
00374                         chan->tcount=false;
00375                         if (chan->request) ret |= (Bitu)1U << (4U + ct);
00376                 }
00377                 return ret;
00378         case 0xc:               /* Clear Flip/Flip (apparently most motherboards will treat read OR write as reset) */
00379                 flipflop=false;
00380                 break;
00381         default:
00382                 LOG(LOG_DMACONTROL,LOG_NORMAL)("Trying to read undefined DMA port %x",(int)reg);
00383                 break;
00384         }
00385         return 0xffffffff;
00386 }
00387 
00388 DmaChannel::DmaChannel(Bit8u num, bool dma16) {
00389         masked = true;
00390         callback = NULL;
00391         channum = num;
00392         DMA16 = dma16 ? 0x1 : 0x0;
00393     transfer_mode = 0;
00394 
00395     if (isadma128k >= 0)
00396         Set128KMode(isadma128k > 0); // user's choice
00397     else
00398         Set128KMode(true); // most hardware seems to implement the 128K case
00399 
00400     LOG(LOG_DMACONTROL,LOG_DEBUG)("DMA channel %u. DMA16_PAGESHIFT=%u DMA16_ADDRMASK=0x%lx",
00401         (unsigned int)channum,(unsigned int)DMA16_PAGESHIFT,(unsigned long)DMA16_ADDRMASK);
00402     pagenum = 0;
00403         pagebase = 0;
00404         baseaddr = 0;
00405         curraddr = 0;
00406         basecnt = 0;
00407         currcnt = 0;
00408         increment = true;
00409         autoinit = false;
00410         tcount = false;
00411         request = false;
00412 }
00413 
00414 Bitu DmaChannel::Read(Bitu want, Bit8u * buffer) {
00415         Bitu done=0;
00416         curraddr &= dma_wrapping;
00417 
00418         /* ISA devices cannot cycle DMA if the controller has masked the channel! Fix your code! */
00419         if (masked) {
00420                 LOG(LOG_DMACONTROL,LOG_WARN)("BUG: Attempted DMA channel read while channel masked");
00421                 return 0;
00422         }
00423     /* You cannot read a DMA channel configured for writing (to memory) */
00424     if (transfer_mode != DMAT_READ) {
00425         LOG(LOG_DMACONTROL,LOG_WARN)("BUG: Attempted DMA channel read when DMA channel is configured by guest for writing (to memory)");
00426         return 0;
00427     }
00428 
00429     /* WARNING: "want" is expressed in DMA transfer units.
00430      *          For 8-bit DMA, want is in bytes.
00431      *          For 16-bit DMA, want is in 16-bit WORDs.
00432      *          Keep that in mind when writing code to call this function!
00433      *          Perhaps a future modification could provide an alternate
00434      *          Read function that expresses the count in bytes so the caller
00435      *          cannot accidentally cause buffer overrun issues that cause
00436      *          mystery crashes. */
00437 
00438     const Bit32u addrmask = 0xFFFu >> DMA16; /* 16-bit ISA style DMA needs 0x7FFF, else 0xFFFF. Use 0x7FF/0xFFF (4KB) for simplicity reasons. */
00439     while (want > 0) {
00440         const Bit32u addr =
00441             curraddr & addrmask;
00442         const Bitu wrapdo =
00443             increment ?
00444                 /*inc*/((addrmask + 1u) - addr) :   /* how many transfer units until (end of 4KB page) + 1 */
00445                 /*dec*/(addr + 1u);                 /* how many transfer units until (start of 4KB page) - 1 */
00446         const Bitu cando =
00447             MIN(MIN(want,Bitu(currcnt+1u)),wrapdo);
00448         assert(wrapdo != (Bitu)0);
00449         assert(cando != (Bitu)0);
00450         assert(cando <= want);
00451         assert(cando <= (addrmask + 1u));
00452 
00453         if (increment) {
00454             assert((curraddr & (~addrmask)) == ((curraddr + ((Bit32u)cando - 1u)) & (~addrmask)));//check our work, must not cross a 4KB boundary
00455             DMA_BlockRead4KB<DMA_INCREMENT>(pagebase,curraddr,buffer,cando,DMA16,DMA16_ADDRMASK);
00456             curraddr += (Bit32u)cando;
00457         }
00458         else {
00459             assert((curraddr & (~addrmask)) == ((curraddr - ((Bit32u)cando - 1u)) & (~addrmask)));//check our work, must not cross a 4KB boundary
00460             DMA_BlockRead4KB<DMA_DECREMENT>(pagebase,curraddr,buffer,cando,DMA16,DMA16_ADDRMASK);
00461             curraddr -= (Bit32u)cando;
00462         }
00463 
00464         curraddr &= dma_wrapping;
00465         buffer += cando << DMA16;
00466         currcnt -= (Bit16u)cando;
00467         want -= cando;
00468         done += cando;
00469 
00470         if (IS_PC98_ARCH) {
00471             /* check wraparound, to emulate auto bank increment.
00472              * do not check DMA16 because PC-98 does not have 16-bit DMA channels.
00473              *
00474              * The PC-98 port of Sim City 2000 needs this to properly play digitized speech,
00475              * especially "reticulating splines". */
00476             if ((( increment) && (curraddr & 0xFFFFu) == 0u) ||
00477                 ((!increment) && (curraddr & 0xFFFFu) == 0xFFFFu)) {
00478                 page_bank_increment();
00479             }
00480         }
00481 
00482         if (currcnt == 0xFFFF) {
00483             ReachedTC();
00484             if (autoinit) {
00485                 currcnt = basecnt;
00486                 curraddr = baseaddr;
00487                 UpdateEMSMapping();
00488             } else {
00489                 masked = true;
00490                 UpdateEMSMapping();
00491                 DoCallBack(DMA_TRANSFEREND);
00492                 break;
00493             }
00494         }
00495     }
00496 
00497         return done;
00498 }
00499 
00500 Bitu DmaChannel::Write(Bitu want, Bit8u * buffer) {
00501         Bitu done=0;
00502         curraddr &= dma_wrapping;
00503 
00504         /* ISA devices cannot cycle DMA if the controller has masked the channel! Fix your code! */
00505         if (masked) {
00506                 LOG(LOG_DMACONTROL,LOG_WARN)("BUG: Attempted DMA channel write while channel masked");
00507                 return 0;
00508         }
00509     /* You cannot write a DMA channel configured for reading (from memory) */
00510     if (transfer_mode != DMAT_WRITE) {
00511         LOG(LOG_DMACONTROL,LOG_WARN)("BUG: Attempted DMA channel write when DMA channel is configured by guest for reading (from memory)");
00512         return 0;
00513     }
00514 
00515     /* WARNING: "want" is expressed in DMA transfer units.
00516      *          For 8-bit DMA, want is in bytes.
00517      *          For 16-bit DMA, want is in 16-bit WORDs.
00518      *          Keep that in mind when writing code to call this function!
00519      *          Perhaps a future modification could provide an alternate
00520      *          Read function that expresses the count in bytes so the caller
00521      *          cannot accidentally cause buffer overrun issues that cause
00522      *          mystery crashes. */
00523 
00524     const Bit32u addrmask = 0xFFFu >> DMA16; /* 16-bit ISA style DMA needs 0x7FFF, else 0xFFFF. Use 0x7FF/0xFFF (4KB) for simplicity reasons. */
00525     while (want > 0) {
00526         const Bit32u addr =
00527             curraddr & addrmask;
00528         const Bitu wrapdo =
00529             increment ?
00530                 /*inc*/((addrmask + 1u) - addr) :   /* how many transfer units until (end of 4KB page) + 1 */
00531                 /*dec*/(addr + 1u);                 /* how many transfer units until (start of 4KB page) - 1 */
00532         const Bitu cando =
00533             MIN(MIN(want,Bitu(currcnt+1u)),wrapdo);
00534         assert(wrapdo != (Bitu)0);
00535         assert(cando != (Bitu)0);
00536         assert(cando <= want);
00537         assert(cando <= (addrmask + 1u));
00538 
00539         if (increment) {
00540             assert((curraddr & (~addrmask)) == ((curraddr + ((Bit32u)cando - 1u)) & (~addrmask)));//check our work, must not cross a 4KB boundary
00541             DMA_BlockWrite4KB<DMA_INCREMENT>(pagebase,curraddr,buffer,cando,DMA16,DMA16_ADDRMASK);
00542             curraddr += (Bit32u)cando;
00543         }
00544         else {
00545             assert((curraddr & (~addrmask)) == ((curraddr - ((Bit32u)cando - 1u)) & (~addrmask)));//check our work, must not cross a 4KB boundary
00546             DMA_BlockWrite4KB<DMA_DECREMENT>(pagebase,curraddr,buffer,cando,DMA16,DMA16_ADDRMASK);
00547             curraddr -= (Bit32u)cando;
00548         }
00549 
00550         curraddr &= dma_wrapping;
00551         buffer += cando << DMA16;
00552         currcnt -= (Bit16u)cando;
00553         want -= cando;
00554         done += cando;
00555 
00556         if (IS_PC98_ARCH) {
00557             /* check wraparound, to emulate auto bank increment.
00558              * do not check DMA16 because PC-98 does not have 16-bit DMA channels.
00559              *
00560              * The PC-98 port of Sim City 2000 needs this to properly play digitized speech,
00561              * especially "reticulating splines". */
00562             if ((( increment) && (curraddr & 0xFFFFu) == 0u) ||
00563                 ((!increment) && (curraddr & 0xFFFFu) == 0xFFFFu)) {
00564                 page_bank_increment();
00565             }
00566         }
00567 
00568         if (currcnt == 0xFFFF) {
00569             ReachedTC();
00570             if (autoinit) {
00571                 currcnt = basecnt;
00572                 curraddr = baseaddr;
00573                 UpdateEMSMapping();
00574             } else {
00575                 masked = true;
00576                 UpdateEMSMapping();
00577                 DoCallBack(DMA_TRANSFEREND);
00578                 break;
00579             }
00580         }
00581     }
00582 
00583         return done;
00584 }
00585 
00586 void DMA_SetWrapping(Bit32u wrap) {
00587         dma_wrapping = wrap;
00588 }
00589 
00590 void DMA_FreeControllers() {
00591         if (DmaControllers[0]) {
00592                 delete DmaControllers[0];
00593                 DmaControllers[0]=NULL;
00594         }
00595         if (DmaControllers[1]) {
00596                 delete DmaControllers[1];
00597                 DmaControllers[1]=NULL;
00598         }
00599 }
00600 
00601 void DMA_Destroy(Section* /*sec*/) {
00602         DMA_FreeControllers();
00603 }
00604 
00605 void DMA_Reset(Section* /*sec*/) {
00606         Bit16u i;
00607 
00608         DMA_FreeControllers();
00609 
00610         // LOG
00611         LOG(LOG_MISC,LOG_DEBUG)("DMA_Reset(): reinitializing DMA controller(s)");
00612 
00613         Section_prop * section=static_cast<Section_prop *>(control->GetSection("dosbox"));
00614         assert(section != NULL);
00615 
00616         DMA_SetWrapping(0xffff);
00617 
00618         /* NTS: parsing on reset means a reboot of the VM (and possibly other conditions) can permit
00619          *      the user to change DMA emulation settings and have them take effect on VM reboot. */
00620         enable_2nd_dma = section->Get_bool("enable 2nd dma controller");
00621         enable_1st_dma = enable_2nd_dma || section->Get_bool("enable 1st dma controller");
00622         enable_dma_extra_page_registers = section->Get_bool("enable dma extra page registers");
00623         dma_page_register_writeonly = section->Get_bool("dma page registers write-only");
00624         allow_decrement_mode = section->Get_bool("allow dma address decrement");
00625 
00626     if (IS_PC98_ARCH) // DMA 4-7 do not exist on PC-98
00627         enable_2nd_dma = false;
00628 
00629     if (machine == MCH_PCJR) {
00630         LOG(LOG_MISC,LOG_DEBUG)("DMA is disabled in PCjr mode");
00631         enable_1st_dma = false;
00632         enable_2nd_dma = false;
00633         return;
00634     }
00635 
00636     {
00637         std::string s = section->Get_string("enable 128k capable 16-bit dma");
00638 
00639         if (s == "true" || s == "1")
00640             isadma128k = 1;
00641         else if (s == "false" || s == "0")
00642             isadma128k = 0;
00643         else
00644             isadma128k = -1;
00645     }
00646 
00647         if (enable_1st_dma)
00648                 DmaControllers[0] = new DmaController(0);
00649         else
00650                 DmaControllers[0] = NULL;
00651 
00652         if (enable_2nd_dma)
00653                 DmaControllers[1] = new DmaController(1);
00654         else
00655                 DmaControllers[1] = NULL;
00656 
00657         for (i=0;i<0x10;i++) {
00658                 Bitu mask = IO_MB;
00659                 if (i < 8) mask |= IO_MW;
00660 
00661                 if (enable_1st_dma) {
00662                         /* install handler for first DMA controller ports */
00663                         DmaControllers[0]->DMA_WriteHandler[i].Install(IS_PC98_ARCH ? ((i * 2u) + 1u) : i,DMA_Write_Port,mask);
00664                         DmaControllers[0]->DMA_ReadHandler[i].Install(IS_PC98_ARCH ? ((i * 2u) + 1u) : i,DMA_Read_Port,mask);
00665                 }
00666                 if (enable_2nd_dma) {
00667             assert(!IS_PC98_ARCH);
00668                         /* install handler for second DMA controller ports */
00669                         DmaControllers[1]->DMA_WriteHandler[i].Install(0xc0u+i*2u,DMA_Write_Port,mask);
00670                         DmaControllers[1]->DMA_ReadHandler[i].Install(0xc0u+i*2u,DMA_Read_Port,mask);
00671                 }
00672         }
00673 
00674         if (enable_1st_dma) {
00675         if (IS_PC98_ARCH) {
00676             /* install handlers for ports 0x21-0x29 odd */
00677             for (i=0;i < 5;i++) {
00678                 DmaControllers[0]->DMA_WriteHandler[0x10+i].Install(0x21+(i*2u),DMA_Write_Port,IO_MB,1);
00679                 DmaControllers[0]->DMA_ReadHandler[0x10+i].Install(0x21+(i*2u),DMA_Read_Port,IO_MB,1);
00680             }
00681         }
00682         else {
00683             /* install handlers for ports 0x81-0x83 (on the first DMA controller) */
00684             DmaControllers[0]->DMA_WriteHandler[0x10].Install(0x80,DMA_Write_Port,IO_MB,8);
00685             DmaControllers[0]->DMA_ReadHandler[0x10].Install(0x80,DMA_Read_Port,IO_MB,8);
00686         }
00687         }
00688 
00689         if (enable_2nd_dma) {
00690         assert(!IS_PC98_ARCH);
00691         /* install handlers for ports 0x81-0x83 (on the second DMA controller) */
00692                 DmaControllers[1]->DMA_WriteHandler[0x10].Install(0x88,DMA_Write_Port,IO_MB,8);
00693                 DmaControllers[1]->DMA_ReadHandler[0x10].Install(0x88,DMA_Read_Port,IO_MB,8);
00694         }
00695 
00696         /* FIXME: This should be in a separate EMS board init */
00697         for (i=0;i < LINK_START;i++) ems_board_mapping[i] = i;
00698 }
00699 
00700 void Init_DMA() {
00701         LOG(LOG_MISC,LOG_DEBUG)("Initializing DMA controller emulation");
00702 
00703         AddExitFunction(AddExitFunctionFuncPair(DMA_Destroy));
00704         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(DMA_Reset));
00705 }
00706 
00707 //save state support
00708 extern void *GUS_DMA_Callback_Func;
00709 extern void *SB_DSP_DMA_CallBack_Func;
00710 extern void *SB_DSP_ADC_CallBack_Func;
00711 extern void *SB_DSP_E2_DMA_CallBack_Func;
00712 extern void *TandyDAC_DMA_CallBack_Func;
00713 
00714 
00715 const void *dma_state_callback_table[] = {
00716         NULL,
00717         GUS_DMA_Callback_Func,
00718         SB_DSP_DMA_CallBack_Func,
00719         SB_DSP_ADC_CallBack_Func,
00720         SB_DSP_E2_DMA_CallBack_Func,
00721         TandyDAC_DMA_CallBack_Func
00722 };
00723 
00724 
00725 Bit8u POD_State_Find_DMA_Callback( Bitu addr )
00726 {
00727         Bit8u size;
00728 
00729         size = sizeof(dma_state_callback_table) / sizeof(void *);
00730         for( int lcv=0; lcv<size; lcv++ ) {
00731                 if( (Bitu) dma_state_callback_table[lcv] == addr ) return lcv;
00732         }
00733 
00734 
00735         // ERROR! Set debug breakpoint
00736         return 0xff;
00737 }
00738 
00739 
00740 Bitu POD_State_Index_DMA_Callback( Bit8u index )
00741 {
00742         return (Bitu) dma_state_callback_table[index];
00743 }
00744 
00745 void DmaChannel::SaveState( std::ostream& stream )
00746 {
00747         Bit8u dma_callback;
00748 
00749 
00750         dma_callback = POD_State_Find_DMA_Callback( (Bitu) (callback) );
00751 
00752         //******************************************
00753         //******************************************
00754         //******************************************
00755 
00756         // - pure data
00757         WRITE_POD( &pagebase, pagebase );
00758         WRITE_POD( &baseaddr, baseaddr );
00759         WRITE_POD( &curraddr, curraddr );
00760         WRITE_POD( &basecnt, basecnt );
00761         WRITE_POD( &currcnt, currcnt );
00762         WRITE_POD( &channum, channum );
00763         WRITE_POD( &pagenum, pagenum );
00764         WRITE_POD( &DMA16, DMA16 );
00765         WRITE_POD( &increment, increment );
00766         WRITE_POD( &autoinit, autoinit );
00767         //WRITE_POD( &trantype, trantype );
00768         WRITE_POD( &masked, masked );
00769         WRITE_POD( &tcount, tcount );
00770         WRITE_POD( &request, request );
00771 
00772         //******************************************
00773         //******************************************
00774         //******************************************
00775 
00776         // - reloc ptr (!!!)
00777         WRITE_POD( &dma_callback, dma_callback );
00778 }
00779 
00780 void DmaChannel::LoadState( std::istream& stream )
00781 {
00782         Bit8u dma_callback;
00783 
00784 
00785         dma_callback = POD_State_Find_DMA_Callback( (Bitu) (callback) );
00786 
00787         //******************************************
00788         //******************************************
00789         //******************************************
00790 
00791         // - pure data
00792         READ_POD( &pagebase, pagebase );
00793         READ_POD( &baseaddr, baseaddr );
00794         READ_POD( &curraddr, curraddr );
00795         READ_POD( &basecnt, basecnt );
00796         READ_POD( &currcnt, currcnt );
00797         READ_POD( &channum, channum );
00798         READ_POD( &pagenum, pagenum );
00799         READ_POD( &DMA16, DMA16 );
00800         READ_POD( &increment, increment );
00801         READ_POD( &autoinit, autoinit );
00802         //READ_POD( &trantype, trantype );
00803         READ_POD( &masked, masked );
00804         READ_POD( &tcount, tcount );
00805         READ_POD( &request, request );
00806 
00807         //********************************
00808         //********************************
00809         //********************************
00810 
00811         // - reloc func ptr
00812         READ_POD( &dma_callback, dma_callback );
00813 
00814 
00815         callback = (DMA_CallBack) POD_State_Index_DMA_Callback( dma_callback );
00816 }
00817 
00818 
00819 void DmaController::SaveState( std::ostream& stream )
00820 {
00821         // - pure data
00822         WRITE_POD( &ctrlnum, ctrlnum );
00823         WRITE_POD( &flipflop, flipflop );
00824 
00825         for( int lcv=0; lcv<4; lcv++ ) {
00826                 DmaChannels[lcv]->SaveState(stream);
00827         }
00828 }
00829 
00830 
00831 void DmaController::LoadState( std::istream& stream )
00832 {
00833         // - pure data
00834         READ_POD( &ctrlnum, ctrlnum );
00835         READ_POD( &flipflop, flipflop );
00836 
00837         for( int lcv=0; lcv<4; lcv++ ) {
00838                 DmaChannels[lcv]->LoadState(stream);
00839         }
00840 }
00841 
00842 
00843 namespace
00844 {
00845         class SerializeDMA : public SerializeGlobalPOD
00846         {
00847         public:
00848                 SerializeDMA() : SerializeGlobalPOD("DMA")
00849                 {}
00850 
00851         private:
00852                 virtual void getBytes(std::ostream& stream)
00853                 {
00854                         SerializeGlobalPOD::getBytes(stream);
00855 
00856 
00857                         // - pure data
00858                         WRITE_POD( &dma_wrapping, dma_wrapping );
00859 
00860 
00861                         for( int lcv=0; lcv<2; lcv++ ) {
00862                                 // cga, tandy, pcjr = no 2nd controller
00863                                 if( !DmaControllers[lcv] ) continue;
00864 
00865                                 DmaControllers[lcv]->SaveState(stream);
00866                         }
00867 
00868 
00869                         // - pure data
00870                         WRITE_POD( &ems_board_mapping, ems_board_mapping );
00871                 }
00872 
00873                 virtual void setBytes(std::istream& stream)
00874                 {
00875                         SerializeGlobalPOD::setBytes(stream);
00876 
00877 
00878                         // - pure data
00879                         READ_POD( &dma_wrapping, dma_wrapping );
00880 
00881 
00882                         for( int lcv=0; lcv<2; lcv++ ) {
00883                                 // cga, tandy, pcjr = no 2nd controller
00884                                 if( !DmaControllers[lcv] ) continue;
00885 
00886                                 DmaControllers[lcv]->LoadState(stream);
00887                         }
00888 
00889 
00890                         // - pure data
00891                         READ_POD( &ems_board_mapping, ems_board_mapping );
00892                 }
00893         } dummy;
00894 }