dosbox-wii/src/hardware/dma.cpp

260 lines
6.6 KiB
C++
Raw Normal View History

2009-05-02 23:03:37 +02:00
/*
* Copyright (C) 2002 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 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.
*/
/*
Still need to implement reads from dma ports :)
Perhaps sometime also implement dma writes.
DMA transfer that get setup with a size 0 are also done wrong should be 0x10000 probably.
*/
#include <string.h>
#include "dosbox.h"
#include "mem.h"
#include "inout.h"
#include "dma.h"
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;
Bit8u address_decrement;
Bit8u autoinit_enable;
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;
};
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:20:05 +02:00
default:
LOG_WARN("DMA:Unhandled read from %d",port);
}
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];
DMA_CHANNEL * chan=&cont->chan[port>>1];
switch (port) {
case 0x00:case 0x02:case 0x04:case 0x06:
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:
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 */
if (val != 4) LOG_WARN("DMA1:Illegal command %2X",val);
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 */
chan->masked=(val & 4)>0;
break;
case 0x0b: /* mode register */
chan->mode.mode_type = (val >> 6) & 0x03;
chan->mode.address_decrement = (val >> 5) & 0x01;
chan->mode.autoinit_enable = (val >> 4) & 0x01;
chan->mode.transfer_type = (val >> 2) & 0x03;
if (chan->mode.address_decrement) {
LOG_WARN("DMA:Address Decrease not supported yet");
}
break;
case 0x0c: /* Clear Flip/Flip */
cont->flipflop=true;
break;
};
};
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:12:18 +02:00
Bit16u DMA_8_Read(Bit32u dmachan,Bit8u * buffer,Bit16u count) {
2009-05-02 23:03:37 +02:00
DMA_CHANNEL * chan=&dma[0].chan[dmachan];
2009-05-02 23:12:18 +02:00
if (chan->masked) return 0;
if (!count) return 0;
2009-05-02 23:03:37 +02:00
/* DMA always does autoinit should work under normal situations */
if (chan->addr_changed) {
/* Calculate the new starting position for dma read*/
chan->addr_changed=false;
2009-05-02 23:12:18 +02:00
chan->address=(chan->page << 16)+chan->base_address;
2009-05-02 23:03:37 +02:00
chan->current_count=chan->base_count+1;
chan->current_address=chan->base_address;
DMA_DEBUG("DMA:Transfer from %d size %d",(chan->page << 16)+chan->base_address,chan->current_count);
}
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);
count=(Bit16u)chan->current_count;
chan->current_address+=count;;
chan->current_count=0;
return count;
} else {
buffer+=chan->current_count;
Bit16u left=count-(Bit16u)chan->current_count;
/* Autoinit reset the dma channel */
chan->address=(chan->page << 16)+chan->base_address;
chan->current_count=chan->base_count+1;
chan->current_address=chan->base_address;
/* 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:12:18 +02:00
Bit16u DMA_8_Write(Bit32u dmachan,Bit8u * buffer,Bit16u 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:12:18 +02:00
Bit16u DMA_16_Read(Bit32u dmachan,Bit8u * buffer,Bit16u 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:12:18 +02:00
Bit16u DMA_16_Write(Bit32u dmachan,Bit8u * buffer,Bit16u 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: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");
}