2009-05-02 23:03:37 +02:00
|
|
|
/*
|
2009-05-02 23:53:27 +02:00
|
|
|
* Copyright (C) 2002-2004 The DOSBox Team
|
2009-05-02 23:03:37 +02:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2009-05-03 00:02:15 +02:00
|
|
|
* GNU General Public License for more details.
|
2009-05-02 23:03:37 +02:00
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include "dosbox.h"
|
|
|
|
#include "mem.h"
|
|
|
|
#include "inout.h"
|
|
|
|
#include "dma.h"
|
2009-05-02 23:35:44 +02:00
|
|
|
#include "pic.h"
|
2009-05-02 23:03:37 +02:00
|
|
|
|
2009-05-02 23:53:27 +02:00
|
|
|
DmaChannel *DmaChannels[8];
|
|
|
|
DmaController *DmaControllers[2];
|
|
|
|
|
2009-05-03 00:02:15 +02:00
|
|
|
static void DMA_WriteControllerReg(DmaController * cont,Bitu reg,Bitu val,Bitu len) {
|
|
|
|
DmaChannel * chan;Bitu i;
|
|
|
|
Bitu base=cont->chanbase;
|
|
|
|
switch (reg) {
|
|
|
|
case 0x0:case 0x2:case 0x4:case 0x6:
|
|
|
|
chan=DmaChannels[base+(reg >> 1)];
|
|
|
|
cont->flipflop=!cont->flipflop;
|
|
|
|
if (cont->flipflop) {
|
|
|
|
chan->baseaddr=(chan->baseaddr&0xff00)|val;
|
|
|
|
chan->curraddr=(chan->curraddr&0xff00)|val;
|
|
|
|
} else {
|
|
|
|
chan->baseaddr=(chan->baseaddr&0x00ff)|(val << 8);
|
|
|
|
chan->curraddr=(chan->curraddr&0x00ff)|(val << 8);
|
2009-05-02 23:20:05 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
break;
|
|
|
|
case 0x1:case 0x3:case 0x5:case 0x7:
|
|
|
|
chan=DmaChannels[base+(reg >> 1)];
|
|
|
|
cont->flipflop=!cont->flipflop;
|
|
|
|
if (cont->flipflop) {
|
|
|
|
chan->basecnt=(chan->basecnt&0xff00)|val;
|
|
|
|
chan->currcnt=(chan->currcnt&0xff00)|val;
|
2009-05-02 23:20:05 +02:00
|
|
|
} else {
|
2009-05-03 00:02:15 +02:00
|
|
|
chan->basecnt=(chan->basecnt&0x00ff)|(val << 8);
|
|
|
|
chan->currcnt=(chan->currcnt&0x00ff)|(val << 8);
|
2009-05-02 23:20:05 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
break;
|
|
|
|
case 0x8: /* Comand reg not used */
|
|
|
|
break;
|
|
|
|
case 0x9: /* Request registers, memory to memory */
|
|
|
|
//TODO Warning?
|
|
|
|
break;
|
|
|
|
case 0xa: /* Mask Register */
|
|
|
|
chan=DmaChannels[base+(val & 3 )];
|
|
|
|
chan->SetMask((val & 0x4)>0);
|
|
|
|
break;
|
|
|
|
case 0xb: /* Mode Register */
|
|
|
|
chan=DmaChannels[base+(val & 3 )];
|
|
|
|
chan->autoinit=(val & 0x10) > 0;
|
|
|
|
chan->increment=(val & 0x20) > 0;
|
|
|
|
//TODO Maybe other bits?
|
|
|
|
break;
|
|
|
|
case 0xc: /* Clear Flip/Flip */
|
|
|
|
cont->flipflop=false;
|
|
|
|
break;
|
|
|
|
case 0xd: /* Master Clear/Reset */
|
|
|
|
for (i=0;i<4;i++) {
|
|
|
|
DmaChannels[base+i]->SetMask(true);
|
|
|
|
DmaChannels[base+i]->tcount=false;
|
2009-05-02 23:53:27 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
cont->flipflop=false;
|
|
|
|
break;
|
|
|
|
case 0xe: /* Clear Mask register */
|
|
|
|
for (i=0;i<4;i++) {
|
|
|
|
DmaChannels[base+i]->SetMask(false);
|
2009-05-02 23:53:27 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
break;
|
|
|
|
case 0xf: /* Multiple Mask register */
|
|
|
|
for (i=0;i<4;i++) {
|
|
|
|
DmaChannels[base+i]->SetMask(val & 1);
|
|
|
|
val>>=1;
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
break;
|
2009-05-02 23:53:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-03 00:02:15 +02:00
|
|
|
static Bitu DMA_ReadControllerReg(DmaController * cont,Bitu reg,Bitu len) {
|
|
|
|
DmaChannel * chan;Bitu i,ret;
|
|
|
|
Bitu base=cont->chanbase;
|
|
|
|
switch (reg) {
|
|
|
|
case 0x0:case 0x2:case 0x4:case 0x6:
|
|
|
|
chan=DmaChannels[base+(reg >> 1)];
|
|
|
|
cont->flipflop=!cont->flipflop;
|
|
|
|
if (cont->flipflop) {
|
|
|
|
return chan->curraddr & 0xff;
|
2009-05-02 23:03:37 +02:00
|
|
|
} else {
|
2009-05-03 00:02:15 +02:00
|
|
|
return (chan->curraddr >> 8) & 0xff;
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
case 0x1:case 0x3:case 0x5:case 0x7:
|
|
|
|
chan=DmaChannels[base+(reg >> 1)];
|
|
|
|
cont->flipflop=!cont->flipflop;
|
|
|
|
if (cont->flipflop) {
|
|
|
|
return chan->currcnt & 0xff;
|
2009-05-02 23:43:00 +02:00
|
|
|
} else {
|
2009-05-03 00:02:15 +02:00
|
|
|
return (chan->currcnt >> 8) & 0xff;
|
2009-05-02 23:43:00 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
case 0x8:
|
|
|
|
ret=0;
|
|
|
|
for (i=0;i<4;i++) {
|
|
|
|
chan=DmaChannels[base+i];
|
|
|
|
if (chan->tcount) ret|=1 << i;
|
|
|
|
chan->tcount=false;
|
|
|
|
if (chan->callback) ret|=1 << (i+4);
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
return ret;
|
|
|
|
default:
|
|
|
|
LOG_MSG("Trying to read undefined DMA Red %x",reg);
|
2009-05-02 23:53:27 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
return 0xffffffff;
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
|
|
|
|
2009-05-02 23:53:27 +02:00
|
|
|
|
2009-05-03 00:02:15 +02:00
|
|
|
static void DMA_Write_Port(Bitu port,Bitu val,Bitu iolen) {
|
|
|
|
if (port<0x10) {
|
|
|
|
DMA_WriteControllerReg(DmaControllers[0],port,val,1);
|
|
|
|
} else if (port>=0xc0 && port <=0xdf) {
|
|
|
|
DMA_WriteControllerReg(DmaControllers[1],(port-0xc0) >> 1,val,1);
|
|
|
|
} else switch (port) {
|
|
|
|
case 0x81:DmaChannels[2]->SetPage(val);break;
|
|
|
|
case 0x82:DmaChannels[3]->SetPage(val);break;
|
|
|
|
case 0x83:DmaChannels[1]->SetPage(val);break;
|
|
|
|
case 0x89:DmaChannels[6]->SetPage(val);break;
|
|
|
|
case 0x8a:DmaChannels[7]->SetPage(val);break;
|
|
|
|
case 0x8b:DmaChannels[5]->SetPage(val);break;
|
2009-05-02 23:43:00 +02:00
|
|
|
}
|
|
|
|
}
|
2009-05-02 23:03:37 +02:00
|
|
|
|
2009-05-03 00:02:15 +02:00
|
|
|
static Bitu DMA_Read_Port(Bitu port,Bitu iolen) {
|
|
|
|
if (port<0x10) {
|
|
|
|
return DMA_ReadControllerReg(DmaControllers[0],port,iolen);
|
|
|
|
} else if (port>=0xc0 && port <=0xdf) {
|
|
|
|
return DMA_ReadControllerReg(DmaControllers[1],(port-0xc0) >> 1,iolen);
|
|
|
|
} else switch (port) {
|
|
|
|
case 0x81:return DmaChannels[2]->pagenum;
|
|
|
|
case 0x82:return DmaChannels[3]->pagenum;
|
|
|
|
case 0x83:return DmaChannels[1]->pagenum;
|
|
|
|
case 0x89:return DmaChannels[6]->pagenum;
|
|
|
|
case 0x8a:return DmaChannels[7]->pagenum;
|
|
|
|
case 0x8b:return DmaChannels[5]->pagenum;
|
2009-05-02 23:53:27 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
return 0;
|
2009-05-02 23:35:44 +02:00
|
|
|
}
|
|
|
|
|
2009-05-03 00:02:15 +02:00
|
|
|
DmaChannel::DmaChannel(Bit8u num, bool dma16) {
|
|
|
|
masked = true;
|
|
|
|
callback = NULL;
|
|
|
|
if(num == 4) return;
|
|
|
|
channum = num;
|
|
|
|
DMA16 = dma16 ? 0x1 : 0x0;
|
|
|
|
pagenum = 0;
|
|
|
|
pagebase = 0;
|
|
|
|
baseaddr = 0;
|
|
|
|
curraddr = 0;
|
|
|
|
basecnt = 0;
|
|
|
|
currcnt = 0;
|
|
|
|
increment = true;
|
|
|
|
autoinit = false;
|
|
|
|
tcount = false;
|
2009-05-02 23:53:27 +02:00
|
|
|
}
|
|
|
|
|
2009-05-03 00:02:15 +02:00
|
|
|
Bitu DmaChannel::Read(Bitu want, Bit8u * buffer) {
|
|
|
|
Bitu done=0;
|
|
|
|
again:
|
|
|
|
Bitu left=(currcnt+1);
|
|
|
|
if (want<left) {
|
|
|
|
MEM_BlockRead(pagebase+(curraddr << DMA16),buffer,want << DMA16);
|
|
|
|
done+=want;
|
|
|
|
curraddr+=want;
|
|
|
|
currcnt-=want;
|
2009-05-02 23:03:37 +02:00
|
|
|
} else {
|
2009-05-03 00:02:15 +02:00
|
|
|
MEM_BlockRead(pagebase+(curraddr << DMA16),buffer,left << DMA16);
|
|
|
|
buffer+=left << DMA16;
|
|
|
|
want-=left;
|
|
|
|
done+=left;
|
|
|
|
ReachedTC();
|
|
|
|
if (autoinit) {
|
|
|
|
currcnt=basecnt;
|
|
|
|
curraddr=baseaddr;
|
|
|
|
if (want) goto again;
|
2009-05-02 23:27:47 +02:00
|
|
|
} else {
|
2009-05-03 00:02:15 +02:00
|
|
|
curraddr+=left;
|
|
|
|
currcnt=0xffff;
|
|
|
|
masked=true;
|
|
|
|
DoCallBack(DMA_TRANSFEREND);
|
|
|
|
}
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
return done;
|
2009-05-02 23:35:44 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
Bitu DmaChannel::Write(Bitu want, Bit8u * buffer) {
|
|
|
|
Bitu done=0;
|
|
|
|
again:
|
|
|
|
Bitu left=(currcnt+1);
|
|
|
|
if (want<left) {
|
|
|
|
MEM_BlockWrite(pagebase+(curraddr << DMA16),buffer,want << DMA16);
|
|
|
|
done+=want;
|
|
|
|
curraddr+=want;
|
|
|
|
currcnt-=want;
|
2009-05-02 23:35:44 +02:00
|
|
|
} else {
|
2009-05-03 00:02:15 +02:00
|
|
|
MEM_BlockWrite(pagebase+(curraddr << DMA16),buffer,left << DMA16);
|
|
|
|
buffer+=left << DMA16;
|
|
|
|
want-=left;
|
|
|
|
done+=left;
|
|
|
|
ReachedTC();
|
|
|
|
if (autoinit) {
|
|
|
|
currcnt=basecnt;
|
|
|
|
curraddr=baseaddr;
|
|
|
|
if (want) goto again;
|
2009-05-02 23:35:44 +02:00
|
|
|
} else {
|
2009-05-03 00:02:15 +02:00
|
|
|
curraddr+=left;
|
|
|
|
currcnt=0xffff;
|
|
|
|
masked=true;
|
|
|
|
DoCallBack(DMA_TRANSFEREND);
|
|
|
|
}
|
2009-05-02 23:43:00 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
return done;
|
2009-05-02 23:43:00 +02:00
|
|
|
}
|
2009-05-02 23:03:37 +02:00
|
|
|
|
|
|
|
|
2009-05-02 23:53:27 +02:00
|
|
|
|
2009-05-02 23:20:05 +02:00
|
|
|
void DMA_Init(Section* sec) {
|
2009-05-02 23:43:00 +02:00
|
|
|
Bitu i;
|
2009-05-02 23:53:27 +02:00
|
|
|
DmaControllers[0] = new DmaController(0);
|
|
|
|
DmaControllers[1] = new DmaController(1);
|
|
|
|
|
2009-05-03 00:02:15 +02:00
|
|
|
for(i=0;i<8;i++) {
|
|
|
|
DmaChannels[i] = new DmaChannel(i,i>=4);
|
2009-05-02 23:53:27 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
|
|
|
|
for (i=0;i<0x10;i++) {
|
|
|
|
Bitu mask=IO_MB;
|
|
|
|
if (i<8) mask|=IO_MW;
|
|
|
|
IO_RegisterWriteHandler(i,DMA_Write_Port,mask);
|
|
|
|
IO_RegisterReadHandler(i,DMA_Read_Port,mask);
|
|
|
|
if (machine==MCH_VGA) {
|
|
|
|
IO_RegisterWriteHandler(0xc0+i*2,DMA_Write_Port,mask);
|
|
|
|
IO_RegisterReadHandler(0xc0+i*2,DMA_Read_Port,mask);
|
|
|
|
}
|
2009-05-02 23:53:27 +02:00
|
|
|
}
|
2009-05-03 00:02:15 +02:00
|
|
|
IO_RegisterWriteHandler(0x81,DMA_Write_Port,IO_MB,3);
|
|
|
|
IO_RegisterWriteHandler(0x89,DMA_Write_Port,IO_MB,3);
|
2009-05-02 23:43:00 +02:00
|
|
|
|
2009-05-03 00:02:15 +02:00
|
|
|
IO_RegisterReadHandler(0x81,DMA_Read_Port,IO_MB,3);
|
|
|
|
IO_RegisterReadHandler(0x89,DMA_Read_Port,IO_MB,3);
|
2009-05-02 23:03:37 +02:00
|
|
|
}
|
2009-05-02 23:53:27 +02:00
|
|
|
|
|
|
|
|