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");
|
|
|
|
}
|