DOSBox-X
|
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 }