dosbox-wii/src/hardware/dma.cpp
2009-05-02 22:02:15 +00:00

264 lines
6.8 KiB
C++

/*
* Copyright (C) 2002-2004 The DOSBox Team
*
* 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
* GNU General Public License for more details.
*
* 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"
#include "pic.h"
DmaChannel *DmaChannels[8];
DmaController *DmaControllers[2];
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);
}
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;
} else {
chan->basecnt=(chan->basecnt&0x00ff)|(val << 8);
chan->currcnt=(chan->currcnt&0x00ff)|(val << 8);
}
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;
}
cont->flipflop=false;
break;
case 0xe: /* Clear Mask register */
for (i=0;i<4;i++) {
DmaChannels[base+i]->SetMask(false);
}
break;
case 0xf: /* Multiple Mask register */
for (i=0;i<4;i++) {
DmaChannels[base+i]->SetMask(val & 1);
val>>=1;
}
break;
}
}
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;
} else {
return (chan->curraddr >> 8) & 0xff;
}
case 0x1:case 0x3:case 0x5:case 0x7:
chan=DmaChannels[base+(reg >> 1)];
cont->flipflop=!cont->flipflop;
if (cont->flipflop) {
return chan->currcnt & 0xff;
} else {
return (chan->currcnt >> 8) & 0xff;
}
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);
}
return ret;
default:
LOG_MSG("Trying to read undefined DMA Red %x",reg);
}
return 0xffffffff;
}
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;
}
}
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;
}
return 0;
}
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;
}
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;
} else {
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;
} else {
curraddr+=left;
currcnt=0xffff;
masked=true;
DoCallBack(DMA_TRANSFEREND);
}
}
return done;
}
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;
} else {
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;
} else {
curraddr+=left;
currcnt=0xffff;
masked=true;
DoCallBack(DMA_TRANSFEREND);
}
}
return done;
}
void DMA_Init(Section* sec) {
Bitu i;
DmaControllers[0] = new DmaController(0);
DmaControllers[1] = new DmaController(1);
for(i=0;i<8;i++) {
DmaChannels[i] = new DmaChannel(i,i>=4);
}
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);
}
}
IO_RegisterWriteHandler(0x81,DMA_Write_Port,IO_MB,3);
IO_RegisterWriteHandler(0x89,DMA_Write_Port,IO_MB,3);
IO_RegisterReadHandler(0x81,DMA_Read_Port,IO_MB,3);
IO_RegisterReadHandler(0x89,DMA_Read_Port,IO_MB,3);
}