385 lines
12 KiB
C
385 lines
12 KiB
C
/*
|
|
* DMA Emulation
|
|
*
|
|
* Copyright 2002 Christian Costa
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "dosexe.h"
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(dma);
|
|
|
|
/* Internal registers of the 2 DMA chips wich control 8 DMA channels */
|
|
static DWORD DMA_BaseAddress[8];
|
|
static WORD DMA_ByteCount[8];
|
|
static DWORD DMA_CurrentBaseAddress[8];
|
|
static WORD DMA_CurrentByteCount[8];
|
|
static BYTE DMA_Command[8];
|
|
static BYTE DMA_Mask[2]={0x0F,0x0F};
|
|
static BYTE DMA_Status[2]={0x00,0x00};
|
|
static BOOL DMA_Toggle[2]={FALSE,FALSE};
|
|
|
|
/*
|
|
* DMA_Transfer : Try to perform a transfer of reqlen elements (8 or 16 bits)
|
|
* on the specified channel and return the elements transferred
|
|
*/
|
|
int DMA_Transfer(int channel,int reqlen,void* buffer)
|
|
{
|
|
int i,size,ret=0;
|
|
int opmode,increment,autoinit,trmode,dmachip;
|
|
int regmode = DMA_Command[channel];
|
|
char *p,*dmabuf;
|
|
|
|
dmabuf = buffer;
|
|
dmachip = (channel<4) ? 0 : 1;
|
|
|
|
TRACE("DMA_Command = %x reqlen=%d\n",regmode,reqlen);
|
|
|
|
/* Exit if channel is masked */
|
|
if (DMA_Mask[dmachip]&(1<<(channel&3)))
|
|
return 0;
|
|
|
|
opmode = (regmode & 0xC0) >> 6;
|
|
increment = !(regmode & 0x20);
|
|
autoinit = regmode & 0x10;
|
|
trmode = (regmode & 0x0C) >> 2;
|
|
|
|
/* Transfer size : 8 bits for channels 0..3, 16 bits for channels 4..7 */
|
|
size = (channel<4) ? 1 : 2;
|
|
|
|
/* Process operating mode */
|
|
switch(opmode)
|
|
{
|
|
case 0:
|
|
/* Request mode */
|
|
FIXME("Request Mode - Not Implemented\n");
|
|
return 0;
|
|
case 1:
|
|
/* Single Mode */
|
|
break;
|
|
case 2:
|
|
/* Request mode */
|
|
FIXME("Block Mode - Not Implemented\n");
|
|
return 0;
|
|
case 3:
|
|
/* Cascade Mode */
|
|
ERR("Cascade Mode should not be used by regular apps\n");
|
|
return 0;
|
|
}
|
|
|
|
/* Perform one the 4 transfer modes */
|
|
if (trmode == 4) {
|
|
/* Illegal */
|
|
ERR("DMA Transfer Type Illegal\n");
|
|
return 0;
|
|
}
|
|
|
|
ret = min(DMA_CurrentByteCount[channel],reqlen);
|
|
|
|
/* Update DMA registers */
|
|
DMA_CurrentByteCount[channel]-=ret;
|
|
if (increment)
|
|
DMA_CurrentBaseAddress[channel] += ret * size;
|
|
else
|
|
DMA_CurrentBaseAddress[channel] -= ret * size;
|
|
|
|
switch(trmode)
|
|
{
|
|
case 0:
|
|
/* Verification (no real transfer)*/
|
|
TRACE("Verification DMA operation\n");
|
|
break;
|
|
case 1:
|
|
/* Write */
|
|
TRACE("Perform Write transfer of %d bytes at %lx with count %x\n",ret,
|
|
DMA_CurrentBaseAddress[channel],DMA_CurrentByteCount[channel]);
|
|
if (increment)
|
|
memcpy((void*)DMA_CurrentBaseAddress[channel],dmabuf,ret*size);
|
|
else
|
|
for(i=0,p=(char*)DMA_CurrentBaseAddress[channel];i<ret*size;i++)
|
|
/* FIXME: possible endianness issue for 16 bits DMA */
|
|
*(p-i) = dmabuf[i];
|
|
break;
|
|
case 2:
|
|
/* Read */
|
|
TRACE("Perform Read transfer of %d bytes at %lx with count %x\n",ret,
|
|
DMA_CurrentBaseAddress[channel],DMA_CurrentByteCount[channel]);
|
|
if (increment)
|
|
memcpy(dmabuf,(void*)DMA_CurrentBaseAddress[channel],ret*size);
|
|
else
|
|
for(i=0,p=(char*)DMA_CurrentBaseAddress[channel];i<ret*size;i++)
|
|
/* FIXME: possible endianness issue for 16 bits DMA */
|
|
dmabuf[i] = *(p-i);
|
|
break;
|
|
}
|
|
|
|
/* Check for end of transfer */
|
|
if (DMA_CurrentByteCount[channel]==0) {
|
|
TRACE("DMA buffer empty\n");
|
|
|
|
/* Update status register of the DMA chip corresponding to the channel */
|
|
DMA_Status[dmachip] |= 1 << (channel & 0x3); /* Mark transfer as finished */
|
|
DMA_Status[dmachip] &= ~(1 << ((channel & 0x3) + 4)); /* Reset soft request if any */
|
|
|
|
if (autoinit) {
|
|
/* Reload Current* register to their initial values */
|
|
DMA_CurrentBaseAddress[channel] = DMA_BaseAddress[channel];
|
|
DMA_CurrentByteCount[channel] = DMA_ByteCount[channel];
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
void DMA_ioport_out( WORD port, BYTE val )
|
|
{
|
|
int channel,dmachip;
|
|
|
|
switch(port)
|
|
{
|
|
case 0x00:
|
|
case 0x02:
|
|
case 0x04:
|
|
case 0x06:
|
|
case 0xC0:
|
|
case 0xC4:
|
|
case 0xC8:
|
|
case 0xCC:
|
|
/* Base Address*/
|
|
channel = (port&0xC0)?((port-0xC0)>>2):(port>>1);
|
|
dmachip = (channel<4) ? 0 : 1;
|
|
if (!DMA_Toggle[dmachip])
|
|
DMA_BaseAddress[channel]=(DMA_BaseAddress[channel] & ~0xFF)|(val & 0xFF);
|
|
else {
|
|
DMA_BaseAddress[channel]=(DMA_BaseAddress[channel] & (~(0xFF << 8)))|((val & 0xFF) << 8);
|
|
DMA_CurrentBaseAddress[channel] = DMA_BaseAddress[channel];
|
|
TRACE("Write Base Address = %lx\n",DMA_BaseAddress[channel]);
|
|
}
|
|
DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
|
|
break;
|
|
|
|
case 0x01:
|
|
case 0x03:
|
|
case 0x05:
|
|
case 0x07:
|
|
case 0xC2:
|
|
case 0xC6:
|
|
case 0xCA:
|
|
case 0xCE:
|
|
/* Count*/
|
|
channel = ((port-1)&0xC0)?(((port-1)-0xC0)>>2):(port>>1);
|
|
dmachip = (channel<4) ? 0 : 1;
|
|
if (!DMA_Toggle[dmachip])
|
|
DMA_ByteCount[channel]=(DMA_ByteCount[channel] & ~0xFF)|((val+1) & 0xFF);
|
|
else {
|
|
DMA_ByteCount[channel]=(DMA_ByteCount[channel] & (~(0xFF << 8)))|(((val+1) & 0xFF) << 8);
|
|
DMA_CurrentByteCount[channel] = DMA_ByteCount[channel];
|
|
TRACE("Write Count = %x.\n",DMA_ByteCount[channel]);
|
|
}
|
|
DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
|
|
break;
|
|
|
|
/* Low Page Base Address */
|
|
case 0x87: DMA_BaseAddress[0]=(DMA_BaseAddress[0] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
|
case 0x83: DMA_BaseAddress[1]=(DMA_BaseAddress[1] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
|
case 0x81: DMA_BaseAddress[2]=(DMA_BaseAddress[2] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
|
case 0x82: DMA_BaseAddress[3]=(DMA_BaseAddress[3] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
|
case 0x8B: DMA_BaseAddress[5]=(DMA_BaseAddress[5] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
|
case 0x89: DMA_BaseAddress[6]=(DMA_BaseAddress[6] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
|
case 0x8A: DMA_BaseAddress[7]=(DMA_BaseAddress[7] & (~0xFF << 16))|((val & 0xFF) << 16); break;
|
|
|
|
/* Low Page Base Address (only 4 lower bits are significant) */
|
|
case 0x487: DMA_BaseAddress[0]=(DMA_BaseAddress[0] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
|
case 0x483: DMA_BaseAddress[1]=(DMA_BaseAddress[1] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
|
case 0x481: DMA_BaseAddress[2]=(DMA_BaseAddress[2] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
|
case 0x482: DMA_BaseAddress[3]=(DMA_BaseAddress[3] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
|
case 0x48B: DMA_BaseAddress[5]=(DMA_BaseAddress[5] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
|
case 0x489: DMA_BaseAddress[6]=(DMA_BaseAddress[6] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
|
case 0x48A: DMA_BaseAddress[7]=(DMA_BaseAddress[7] & (~0xFF << 24))|((val & 0x0F) << 24); break;
|
|
|
|
case 0x08:
|
|
case 0xD0:
|
|
/* Command */
|
|
FIXME("Write Command (%x) - Not Implemented\n",val);
|
|
break;
|
|
|
|
case 0x0B:
|
|
case 0xD6:
|
|
/* Mode */
|
|
TRACE("Write Mode (%x)\n",val);
|
|
DMA_Command[((port==0xD6)?4:0)+(val&0x3)]=val;
|
|
switch(val>>6)
|
|
{
|
|
case 0:
|
|
/* Request mode */
|
|
FIXME("Request Mode - Not Implemented\n");
|
|
break;
|
|
case 1:
|
|
/* Single Mode */
|
|
break;
|
|
case 2:
|
|
/* Block mode */
|
|
FIXME("Block Mode - Not Implemented\n");
|
|
break;
|
|
case 3:
|
|
/* Cascade Mode */
|
|
ERR("Cascade Mode should not be used by regular apps\n");
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case 0x0A:
|
|
case 0xD4:
|
|
/* Write Single Mask Bit */
|
|
TRACE("Write Single Mask Bit (%x)\n",val);
|
|
dmachip = (port==0x0A) ? 0 : 1;
|
|
if (val&4)
|
|
DMA_Mask[dmachip] |= 1<<(val&3);
|
|
else
|
|
DMA_Mask[dmachip] &= ~(1<<(val&3));
|
|
break;
|
|
|
|
case 0x0F:
|
|
case 0xDE:
|
|
/* Write All Mask Bits (only 4 lower bits are significant */
|
|
FIXME("Write All Mask Bits (%x)\n",val);
|
|
dmachip = (port==0x0F) ? 0 : 1;
|
|
DMA_Mask[dmachip] = val & 0x0F;
|
|
break;
|
|
|
|
case 0x09:
|
|
case 0xD2:
|
|
/* Software DRQx Request */
|
|
FIXME("Software DRQx Request (%x) - Not Implemented\n",val);
|
|
break;
|
|
|
|
case 0x0C:
|
|
case 0xD8:
|
|
/* Reset DMA Pointer Flip-Flop */
|
|
TRACE("Reset Flip-Flop\n");
|
|
DMA_Toggle[port==0xD8]=FALSE;
|
|
break;
|
|
|
|
case 0x0D:
|
|
case 0xDA:
|
|
/* Master Reset */
|
|
TRACE("Master Reset\n");
|
|
dmachip = (port==0x0D) ? 0 : 1;
|
|
/* Reset DMA Pointer Flip-Flop */
|
|
DMA_Toggle[dmachip]=FALSE;
|
|
/* Mask all channels */
|
|
DMA_Mask[dmachip] = 0x0F;
|
|
break;
|
|
|
|
case 0x0E:
|
|
case 0xDC:
|
|
/* Reset Mask Register */
|
|
FIXME("Reset Mask Register\n");
|
|
dmachip = (port==0x0E) ? 0 : 1;
|
|
/* Unmask all channels */
|
|
DMA_Mask[dmachip] = 0x00;
|
|
break;
|
|
}
|
|
}
|
|
|
|
BYTE DMA_ioport_in( WORD port )
|
|
{
|
|
int channel,dmachip;
|
|
BYTE res = 0;
|
|
|
|
switch(port)
|
|
{
|
|
case 0x00:
|
|
case 0x02:
|
|
case 0x04:
|
|
case 0x06:
|
|
case 0xC0:
|
|
case 0xC4:
|
|
case 0xC8:
|
|
case 0xCC:
|
|
/* Base Address*/
|
|
channel = (port&0xC0)?((port-0xC0)>>2):(port>>1);
|
|
dmachip = (channel<4) ? 0 : 1;
|
|
if (!DMA_Toggle[dmachip])
|
|
res = DMA_CurrentBaseAddress[channel] & 0xFF;
|
|
else {
|
|
res = (DMA_CurrentBaseAddress[channel] & (0xFF << 8))>>8;
|
|
TRACE("Read Current Base Address = %lx\n",DMA_CurrentBaseAddress[channel]);
|
|
}
|
|
DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
|
|
break;
|
|
|
|
case 0x01:
|
|
case 0x03:
|
|
case 0x05:
|
|
case 0x07:
|
|
case 0xC2:
|
|
case 0xC6:
|
|
case 0xCA:
|
|
case 0xCE:
|
|
/* Count*/
|
|
channel = ((port-1)&0xC0)?(((port-1)-0xC0)>>2):(port>>1);
|
|
dmachip = (channel<4) ? 0 : 1;
|
|
if (!DMA_Toggle[dmachip])
|
|
res = DMA_CurrentByteCount[channel] & 0xFF;
|
|
else {
|
|
res = (DMA_CurrentByteCount[channel] & (0xFF << 8))>>8;
|
|
TRACE("Read Current Count = %x.\n",DMA_CurrentByteCount[channel]);
|
|
}
|
|
DMA_Toggle[dmachip] = !DMA_Toggle[dmachip];
|
|
break;
|
|
|
|
/* Low Page Base Address */
|
|
case 0x87: res = (DMA_BaseAddress[0]&(0xFF<<16))>>16; break;
|
|
case 0x83: res = (DMA_BaseAddress[1]&(0xFF<<16))>>16; break;
|
|
case 0x81: res = (DMA_BaseAddress[2]&(0xFF<<16))>>16; break;
|
|
case 0x82: res = (DMA_BaseAddress[3]&(0xFF<<16))>>16; break;
|
|
case 0x8B: res = (DMA_BaseAddress[5]&(0xFF<<16))>>16; break;
|
|
case 0x89: res = (DMA_BaseAddress[6]&(0xFF<<16))>>16; break;
|
|
case 0x8A: res = (DMA_BaseAddress[7]&(0xFF<<16))>>16; break;
|
|
|
|
/* High Page Base Address */
|
|
case 0x487: res = (DMA_BaseAddress[0]&(0xFF<<24))>>24; break;
|
|
case 0x483: res = (DMA_BaseAddress[1]&(0xFF<<24))>>24; break;
|
|
case 0x481: res = (DMA_BaseAddress[2]&(0xFF<<24))>>24; break;
|
|
case 0x482: res = (DMA_BaseAddress[3]&(0xFF<<24))>>24; break;
|
|
case 0x48B: res = (DMA_BaseAddress[5]&(0xFF<<24))>>24; break;
|
|
case 0x489: res = (DMA_BaseAddress[6]&(0xFF<<24))>>24; break;
|
|
case 0x48A: res = (DMA_BaseAddress[7]&(0xFF<<24))>>24; break;
|
|
|
|
case 0x08:
|
|
case 0xD0:
|
|
/* Status */
|
|
TRACE("Status Register Read\n");
|
|
res = DMA_Status[(port==0x08)?0:1];
|
|
|
|
case 0x0D:
|
|
case 0xDA:
|
|
/* Temporary */
|
|
FIXME("Temporary Register Read- Not Implemented\n");
|
|
break;
|
|
}
|
|
return res;
|
|
}
|