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];
|
|
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
chan->base_address=val;
|
|
|
|
} else {
|
|
|
|
chan->base_address|=(val<<8);
|
|
|
|
}
|
|
|
|
cont->flipflop=!cont->flipflop;
|
|
|
|
chan->addr_changed=true;
|
|
|
|
break;
|
|
|
|
case 0x01:case 0x03:case 0x05:case 0x07:
|
|
|
|
if (cont->flipflop) {
|
|
|
|
chan->base_count=val;
|
|
|
|
} else {
|
|
|
|
chan->base_count|=(val<<8);
|
|
|
|
}
|
|
|
|
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 */
|
|
|
|
} 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;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
static Bit8u channelindex[7] = {2, 3, 1, 0, 0, 0, 0};
|
|
|
|
void write_dma_page(Bit32u port,Bit8u val) {
|
|
|
|
|
|
|
|
switch (port) {
|
|
|
|
case 0x81: /* dma0 page register, channel 2 */
|
|
|
|
case 0x82: /* dma0 page register, channel 3 */
|
|
|
|
case 0x83: /* dma0 page register, channel 1 */
|
|
|
|
case 0x87: /* dma0 page register, channel 0 */
|
|
|
|
Bitu channel = channelindex[port - 0x81];
|
|
|
|
dma[0].chan[channel].page=val;
|
|
|
|
dma[0].chan[channel].addr_changed=true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
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:03:37 +02:00
|
|
|
buffer+=chan->current_count;
|
|
|
|
count-=(Bit16u)chan->current_count;
|
|
|
|
/* Autoinit reset the dma channel */
|
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;
|
|
|
|
/* Copy the rest of the buffer */
|
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
|
|
|
}
|
|
|
|
};
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void DMA_Init(void) {
|
|
|
|
for (Bit32u i=0;i<0x10;i++) {
|
|
|
|
IO_RegisterWriteHandler(i,write_dma,"DMA1");
|
|
|
|
}
|
|
|
|
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");
|
|
|
|
}
|