dosbox-wii/src/hardware/dma.cpp

310 lines
7.8 KiB
C++
Raw Normal View History

2009-05-02 23:03:37 +02:00
/*
2009-05-02 23:35:44 +02:00
* Copyright (C) 2002-2003 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
* GNU Library 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.
*/
/*
Based the port handling from the bochs dma code.
*/
/*
2009-05-02 23:35:44 +02:00
TODO
Implement 16-bit dma
2009-05-02 23:03:37 +02:00
*/
#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:12:18 +02:00
#if DEBUG_DMA
2009-05-02 23:03:37 +02:00
#define DMA_DEBUG LOG_DEBUG
#else
#define DMA_DEBUG
#endif
#define DMA_MODE_DEMAND 0
#define DMA_MODE_SINGLE 1
#define DMA_MODE_BLOCK 2
#define DMA_MODE_CASCADE 3
struct DMA_CHANNEL {
struct {
Bit8u mode_type;
2009-05-02 23:35:44 +02:00
bool address_decrement;
bool autoinit_enable;
2009-05-02 23:03:37 +02:00
Bit8u transfer_type;
} mode;
Bit16u base_address;
Bit16u base_count;
Bit16u current_address;
Bit32u current_count;
Bit8u page;
bool masked;
2009-05-02 23:12:18 +02:00
PhysPt address;
2009-05-02 23:03:37 +02:00
bool addr_changed;
2009-05-02 23:35:44 +02:00
DMA_EnableCallBack enable_callback;
2009-05-02 23:03:37 +02:00
};
struct DMA_CONTROLLER {
bool flipflop;
Bit8u status_reg;
Bit8u command_reg;
DMA_CHANNEL chan[4];
};
static DMA_CONTROLLER dma[2];
2009-05-02 23:20:05 +02:00
static Bit8u read_dma(Bit32u port) {
/* only use first dma for now */
DMA_CONTROLLER * cont=&dma[0];
DMA_CHANNEL * chan=&cont->chan[port>>1];
Bit8u ret;
switch (port) {
case 0x00:case 0x02:case 0x04:case 0x06:
if (cont->flipflop) {
ret=chan->current_address & 0xff;
} else {
ret=(chan->current_address>>8)&0xff;
}
cont->flipflop=!cont->flipflop;
2009-05-02 23:27:47 +02:00
break;
2009-05-02 23:20:05 +02:00
case 0x01:case 0x03:case 0x05:case 0x07:
if (cont->flipflop) {
2009-05-02 23:27:47 +02:00
ret=(Bit8u)((chan->current_count-1) & 0xff);
2009-05-02 23:20:05 +02:00
} else {
2009-05-02 23:27:47 +02:00
ret=(Bit8u)(((chan->current_count-1)>>8)&0xff);
2009-05-02 23:20:05 +02:00
}
cont->flipflop=!cont->flipflop;
break;
2009-05-02 23:27:47 +02:00
case 0x08: /* Read Status */
ret=cont->status_reg;
cont->status_reg&=0xf; /* Clear lower 4 bits on read */
break;
2009-05-02 23:35:44 +02:00
case 0x0a:
case 0x0e:
/* Seem to return 0 on a real controller */
ret=0x0;
break;
2009-05-02 23:20:05 +02:00
default:
2009-05-02 23:35:44 +02:00
LOG(LOG_ERROR,"DMA:Unhandled read from %X",port);
2009-05-02 23:20:05 +02:00
}
return ret;
}
2009-05-02 23:03:37 +02:00
static void write_dma(Bit32u port,Bit8u val) {
/* only use first dma for now */
DMA_CONTROLLER * cont=&dma[0];
2009-05-02 23:35:44 +02:00
DMA_CHANNEL * chan;
2009-05-02 23:03:37 +02:00
switch (port) {
case 0x00:case 0x02:case 0x04:case 0x06:
2009-05-02 23:35:44 +02:00
chan=&cont->chan[port>>1];
2009-05-02 23:03:37 +02:00
if (cont->flipflop) {
2009-05-02 23:27:47 +02:00
chan->base_address=(chan->base_address & 0xff00) | val;
2009-05-02 23:03:37 +02:00
} else {
2009-05-02 23:27:47 +02:00
chan->base_address=(chan->base_address & 0x00ff) | (val<<8);
2009-05-02 23:03:37 +02:00
}
cont->flipflop=!cont->flipflop;
chan->addr_changed=true;
break;
case 0x01:case 0x03:case 0x05:case 0x07:
2009-05-02 23:35:44 +02:00
chan=&cont->chan[port>>1];
2009-05-02 23:03:37 +02:00
if (cont->flipflop) {
2009-05-02 23:27:47 +02:00
chan->base_count=(chan->base_count & 0xff00) | val;
2009-05-02 23:03:37 +02:00
} else {
2009-05-02 23:27:47 +02:00
chan->base_count=(chan->base_count & 0x00ff) | (val<<8);
2009-05-02 23:03:37 +02:00
}
cont->flipflop=!cont->flipflop;
chan->addr_changed=true;
break;
case 0x08: /* Command Register */
2009-05-02 23:35:44 +02:00
if (val != 4) LOG(LOG_ERROR,"DMA1:Illegal command %2X",val);
2009-05-02 23:03:37 +02:00
cont->command_reg=val;
break;
case 0x09: /* Request Register */
if (val&4) {
/* Set Request bit */
2009-05-02 23:27:47 +02:00
Bitu channel = val & 0x03;
cont->status_reg |= (1 << (channel+4));
2009-05-02 23:03:37 +02:00
} else {
Bitu channel = val & 0x03;
cont->status_reg &= ~(1 << (channel+4));
}
break;
case 0x0a: /* single mask bit register */
2009-05-02 23:35:44 +02:00
chan=&cont->chan[val & 0x3];
2009-05-02 23:03:37 +02:00
chan->masked=(val & 4)>0;
2009-05-02 23:35:44 +02:00
if (chan->enable_callback) chan->enable_callback(!chan->masked);
2009-05-02 23:03:37 +02:00
break;
case 0x0b: /* mode register */
2009-05-02 23:35:44 +02:00
chan=&cont->chan[val & 0x3];
2009-05-02 23:03:37 +02:00
chan->mode.mode_type = (val >> 6) & 0x03;
2009-05-02 23:35:44 +02:00
chan->mode.address_decrement = (val & 0x20) > 0;
chan->mode.autoinit_enable = (val & 0x10) > 0;
2009-05-02 23:03:37 +02:00
chan->mode.transfer_type = (val >> 2) & 0x03;
if (chan->mode.address_decrement) {
2009-05-02 23:35:44 +02:00
LOG(LOG_ERROR,"DMA:Address Decrease not supported yet");
2009-05-02 23:03:37 +02:00
}
break;
case 0x0c: /* Clear Flip/Flip */
cont->flipflop=true;
break;
2009-05-02 23:35:44 +02:00
default:
LOG(LOG_ERROR,"DMA:Unhandled write %X to %X",val,port);
};
2009-05-02 23:03:37 +02:00
};
void write_dma_page(Bit32u port,Bit8u val) {
2009-05-02 23:27:47 +02:00
Bitu channel;
2009-05-02 23:03:37 +02:00
switch (port) {
case 0x81: /* dma0 page register, channel 2 */
2009-05-02 23:27:47 +02:00
channel=2;break;
2009-05-02 23:03:37 +02:00
case 0x82: /* dma0 page register, channel 3 */
2009-05-02 23:27:47 +02:00
channel=3;break;
2009-05-02 23:03:37 +02:00
case 0x83: /* dma0 page register, channel 1 */
2009-05-02 23:27:47 +02:00
channel=1;break;
2009-05-02 23:03:37 +02:00
case 0x87: /* dma0 page register, channel 0 */
2009-05-02 23:27:47 +02:00
channel=0;break;
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:27:47 +02:00
dma[0].chan[channel].page=val;
dma[0].chan[channel].addr_changed=true;
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:35:44 +02:00
INLINE void ResetDMA8(DMA_CHANNEL * chan) {
chan->addr_changed=false;
chan->address=(chan->page << 16)+chan->base_address;
chan->current_count=chan->base_count+1;
chan->current_address=chan->base_address;
}
Bitu DMA_8_Read(Bitu dmachan,Bit8u * buffer,Bitu count) {
DMA_CHANNEL * chan=&dma[0].chan[dmachan];
if (chan->masked || !count) return 0;
if (chan->addr_changed) ResetDMA8(chan);
2009-05-02 23:27:47 +02:00
if (chan->current_count>count) {
2009-05-02 23:12:18 +02:00
MEM_BlockRead(chan->address,buffer,count);
chan->address+=count;
2009-05-02 23:03:37 +02:00
chan->current_address+=count;
chan->current_count-=count;
2009-05-02 23:12:18 +02:00
return count;
2009-05-02 23:03:37 +02:00
} else {
/* Copy remaining piece of first buffer */
2009-05-02 23:12:18 +02:00
MEM_BlockRead(chan->address,buffer,chan->current_count);
2009-05-02 23:27:47 +02:00
if (!chan->mode.autoinit_enable) {
/* Set the end of counter bit */
dma[0].status_reg|=(1 << dmachan);
2009-05-02 23:35:44 +02:00
count=chan->current_count;
2009-05-02 23:27:47 +02:00
chan->current_address+=count;;
chan->current_count=0;
return count;
} else {
buffer+=chan->current_count;
2009-05-02 23:35:44 +02:00
Bitu left=count-(Bit16u)chan->current_count;
2009-05-02 23:27:47 +02:00
/* Autoinit reset the dma channel */
2009-05-02 23:35:44 +02:00
ResetDMA8(chan);
2009-05-02 23:27:47 +02:00
/* Copy the rest of the buffer */
MEM_BlockRead(chan->address,buffer,left);
chan->address+=left;
chan->current_address+=left;
chan->current_count-=left;
return count;
}
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:35:44 +02:00
}
2009-05-02 23:03:37 +02:00
2009-05-02 23:35:44 +02:00
Bitu DMA_8_Write(Bitu dmachan,Bit8u * buffer,Bitu count) {
DMA_CHANNEL * chan=&dma[0].chan[dmachan];
if (chan->masked || !count) return 0;
if (chan->addr_changed) ResetDMA8(chan);
if (chan->current_count>count) {
MEM_BlockWrite(chan->address,buffer,count);
chan->address+=count;
chan->current_address+=count;
chan->current_count-=count;
return count;
} else {
/* Copy remaining piece of first buffer */
MEM_BlockWrite(chan->address,buffer,chan->current_count);
if (!chan->mode.autoinit_enable) {
/* Set the end of counter bit */
dma[0].status_reg|=(1 << dmachan);
count=chan->current_count;
chan->current_address+=count;;
chan->current_count=0;
return count;
} else {
buffer+=chan->current_count;
Bitu left=count-(Bit16u)chan->current_count;
/* Autoinit reset the dma channel */
ResetDMA8(chan);
/* Copy the rest of the buffer */
MEM_BlockWrite(chan->address,buffer,left);
chan->address+=left;
chan->current_address+=left;
chan->current_count-=left;
return count;
}
}
}
2009-05-02 23:03:37 +02:00
2009-05-02 23:35:44 +02:00
Bitu DMA_16_Read(Bitu dmachan,Bit8u * buffer,Bitu count) {
2009-05-02 23:03:37 +02:00
2009-05-02 23:12:18 +02:00
return 0;
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:35:44 +02:00
Bitu DMA_16_Write(Bitu dmachan,Bit8u * buffer,Bitu count) {
2009-05-02 23:12:18 +02:00
2009-05-02 23:03:37 +02:00
2009-05-02 23:12:18 +02:00
return 0;
2009-05-02 23:03:37 +02:00
}
2009-05-02 23:35:44 +02:00
void DMA_SetEnableCallBack(Bitu channel,DMA_EnableCallBack callback) {
if (channel<4) {
dma[0].chan[channel].enable_callback=callback;
if (callback) callback(!dma[0].chan[channel].masked);
return;
}
if (channel<8) {
dma[1].chan[channel-4].enable_callback=callback;
if (callback) callback(!dma[1].chan[channel-4].masked);
}
}
2009-05-02 23:20:05 +02:00
void DMA_Init(Section* sec) {
2009-05-02 23:03:37 +02:00
for (Bit32u i=0;i<0x10;i++) {
IO_RegisterWriteHandler(i,write_dma,"DMA1");
2009-05-02 23:20:05 +02:00
IO_RegisterReadHandler(i,read_dma,"DMA1");
2009-05-02 23:03:37 +02:00
}
IO_RegisterWriteHandler(0x81,write_dma_page,"DMA Pages");
IO_RegisterWriteHandler(0x82,write_dma_page,"DMA Pages");
IO_RegisterWriteHandler(0x83,write_dma_page,"DMA Pages");
IO_RegisterWriteHandler(0x87,write_dma_page,"DMA Pages");
}