reactos/subsystems/mvdm/ntvdm/hardware/dma.c

698 lines
22 KiB
C

/*
* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: subsystems/mvdm/ntvdm/hardware/dma.c
* PURPOSE: ISA DMA - Direct Memory Access Controller emulation -
* i8237A compatible with 74LS612 Memory Mapper extension
* PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
/* INCLUDES *******************************************************************/
#include "ntvdm.h"
#define NDEBUG
#include <debug.h>
#include "emulator.h"
#include "dma.h"
#include "io.h"
#include "memory.h"
/* PRIVATE VARIABLES **********************************************************/
/*
* DMA Controller 0 (Channels 0..3): Slave controller
* DMA Controller 1 (Channels 4..7): Master controller
*/
static DMA_CONTROLLER DmaControllers[DMA_CONTROLLERS];
/* External page registers for each channel of the two DMA controllers */
static DMA_PAGE_REGISTER DmaPageRegisters[DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS];
/* PRIVATE FUNCTIONS **********************************************************/
#define READ_ADDR(CtrlIndex, ChanIndex, Data) \
do { \
(Data) = \
*((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrAddress + \
(DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
} while(0)
#define READ_CNT(CtrlIndex, ChanIndex, Data) \
do { \
(Data) = \
*((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
(DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
} while(0)
static BYTE WINAPI DmaReadPort(USHORT Port)
{
BYTE ReadValue = 0xFF;
DPRINT1("DmaReadPort(Port = 0x%04X)\n", Port);
switch (Port)
{
/* Current Address Registers */
{
case 0x00:
READ_ADDR(0, 0, ReadValue);
return ReadValue;
case 0x02:
READ_ADDR(0, 1, ReadValue);
return ReadValue;
case 0x04:
READ_ADDR(0, 2, ReadValue);
return ReadValue;
case 0x06:
READ_ADDR(0, 3, ReadValue);
return ReadValue;
case 0xC0:
READ_ADDR(1, 0, ReadValue);
return ReadValue;
case 0xC4:
READ_ADDR(1, 1, ReadValue);
return ReadValue;
case 0xC8:
READ_ADDR(1, 2, ReadValue);
return ReadValue;
case 0xCC:
READ_ADDR(1, 3, ReadValue);
return ReadValue;
}
/* Current Count Registers */
{
case 0x01:
READ_CNT(0, 0, ReadValue);
return ReadValue;
case 0x03:
READ_CNT(0, 1, ReadValue);
return ReadValue;
case 0x05:
READ_CNT(0, 2, ReadValue);
return ReadValue;
case 0x07:
READ_CNT(0, 3, ReadValue);
return ReadValue;
case 0xC2:
READ_CNT(1, 0, ReadValue);
return ReadValue;
case 0xC6:
READ_CNT(1, 1, ReadValue);
return ReadValue;
case 0xCA:
READ_CNT(1, 2, ReadValue);
return ReadValue;
case 0xCE:
READ_CNT(1, 3, ReadValue);
return ReadValue;
}
/* Status Registers */
{
case 0x08:
return DmaControllers[0].Status;
case 0xD0:
return DmaControllers[1].Status;
}
/* DMA Intermediate (Temporary) Registers */
{
case 0x0D:
return DmaControllers[0].TempReg;
case 0xDA:
return DmaControllers[1].TempReg;
}
/* Multi-Channel Mask Registers */
{
case 0x0F:
return DmaControllers[0].Mask;
case 0xDE:
return DmaControllers[1].Mask;
}
}
return 0x00;
}
#define WRITE_ADDR(CtrlIndex, ChanIndex, Data) \
do { \
*((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseAddress + \
(DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
*((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrAddress + \
(DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
} while(0)
#define WRITE_CNT(CtrlIndex, ChanIndex, Data) \
do { \
*((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseElemCnt + \
(DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
*((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
(DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
} while(0)
static VOID WINAPI DmaWritePort(USHORT Port, BYTE Data)
{
DPRINT1("DmaWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port, Data);
switch (Port)
{
/* Start Address Registers */
{
case 0x00:
WRITE_ADDR(0, 0, Data);
break;
case 0x02:
WRITE_ADDR(0, 1, Data);
break;
case 0x04:
WRITE_ADDR(0, 2, Data);
break;
case 0x06:
WRITE_ADDR(0, 3, Data);
break;
case 0xC0:
WRITE_ADDR(1, 0, Data);
break;
case 0xC4:
WRITE_ADDR(1, 1, Data);
break;
case 0xC8:
WRITE_ADDR(1, 2, Data);
break;
case 0xCC:
WRITE_ADDR(1, 3, Data);
break;
}
/* Base Count Registers */
{
case 0x01:
WRITE_CNT(0, 0, Data);
break;
case 0x03:
WRITE_CNT(0, 1, Data);
break;
case 0x05:
WRITE_CNT(0, 2, Data);
break;
case 0x07:
WRITE_CNT(0, 3, Data);
break;
case 0xC2:
WRITE_CNT(1, 0, Data);
break;
case 0xC6:
WRITE_CNT(1, 1, Data);
break;
case 0xCA:
WRITE_CNT(1, 2, Data);
break;
case 0xCE:
WRITE_CNT(1, 3, Data);
break;
}
/* Command Registers */
{
case 0x08:
DmaControllers[0].Command = Data;
break;
case 0xD0:
DmaControllers[1].Command = Data;
break;
}
/* Mode Registers */
{
case 0x0B:
DmaControllers[0].DmaChannel[Data & 0x03].Mode = (Data & ~0x03);
break;
case 0xD6:
DmaControllers[1].DmaChannel[Data & 0x03].Mode = (Data & ~0x03);
break;
}
/* Request Registers */
{
case 0x09:
DmaControllers[0].Request = Data;
break;
case 0xD2:
DmaControllers[1].Request = Data;
break;
}
/* Single Channel Mask Registers */
{
case 0x0A:
if (Data & 0x04)
DmaControllers[0].Mask |= (1 << (Data & 0x03));
else
DmaControllers[0].Mask &= ~(1 << (Data & 0x03));
break;
case 0xD4:
if (Data & 0x04)
DmaControllers[1].Mask |= (1 << (Data & 0x03));
else
DmaControllers[1].Mask &= ~(1 << (Data & 0x03));
break;
}
/* Multi-Channel Mask Registers */
{
case 0x0F:
DmaControllers[0].Mask = (Data & 0x0F);
break;
case 0xDE:
DmaControllers[1].Mask = (Data & 0x0F);
break;
}
/* Flip-Flop Reset */
{
case 0x0C:
DmaControllers[0].FlipFlop = 0;
break;
case 0xD8:
DmaControllers[1].FlipFlop = 0;
break;
}
/* DMA Master Reset Registers */
{
case 0x0D:
DmaControllers[0].Command = 0x00;
DmaControllers[0].Status = 0x00;
DmaControllers[0].Request = 0x00;
DmaControllers[0].TempReg = 0x00;
DmaControllers[0].FlipFlop = 0;
DmaControllers[0].Mask = 0x0F;
break;
case 0xDA:
DmaControllers[1].Command = 0x00;
DmaControllers[1].Status = 0x00;
DmaControllers[1].Request = 0x00;
DmaControllers[1].TempReg = 0x00;
DmaControllers[1].FlipFlop = 0;
DmaControllers[1].Mask = 0x0F;
break;
}
/* Mask Reset Registers */
{
case 0x0E:
DmaControllers[0].Mask = 0x00;
break;
case 0xDC:
DmaControllers[1].Mask = 0x00;
break;
}
}
}
/* Page Address Registers */
static BYTE WINAPI DmaPageReadPort(USHORT Port)
{
DPRINT1("DmaPageReadPort(Port = 0x%04X)\n", Port);
switch (Port)
{
case 0x87:
return DmaPageRegisters[0].Page;
case 0x83:
return DmaPageRegisters[1].Page;
case 0x81:
return DmaPageRegisters[2].Page;
case 0x82:
return DmaPageRegisters[3].Page;
case 0x8F:
return DmaPageRegisters[4].Page;
case 0x8B:
return DmaPageRegisters[5].Page;
case 0x89:
return DmaPageRegisters[6].Page;
case 0x8A:
return DmaPageRegisters[7].Page;
}
return 0x00;
}
static VOID WINAPI DmaPageWritePort(USHORT Port, BYTE Data)
{
DPRINT1("DmaPageWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port, Data);
switch (Port)
{
case 0x87:
DmaPageRegisters[0].Page = Data;
break;
case 0x83:
DmaPageRegisters[1].Page = Data;
break;
case 0x81:
DmaPageRegisters[2].Page = Data;
break;
case 0x82:
DmaPageRegisters[3].Page = Data;
break;
case 0x8F:
DmaPageRegisters[4].Page = Data;
break;
case 0x8B:
DmaPageRegisters[5].Page = Data;
break;
case 0x89:
DmaPageRegisters[6].Page = Data;
break;
case 0x8A:
DmaPageRegisters[7].Page = Data;
break;
}
}
/* PUBLIC FUNCTIONS ***********************************************************/
DWORD DmaRequest(IN WORD iChannel,
IN OUT PVOID Buffer,
IN DWORD length)
{
/*
* NOTE: This function is adapted from Wine's krnl386.exe,
* DMA emulation by Christian Costa.
*/
PDMA_CONTROLLER pDcp;
WORD Channel;
DWORD i, Size, ret = 0;
BYTE RegMode, OpMode, Increment, Autoinit, TrMode;
PBYTE dmabuf = Buffer;
ULONG CurrAddress;
if (iChannel >= DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS)
{
SetLastError(ERROR_INVALID_ADDRESS);
return 0;
}
pDcp = &DmaControllers[iChannel / DMA_CONTROLLER_CHANNELS];
Channel = iChannel % DMA_CONTROLLER_CHANNELS; // == (iChannel & 0x03)
RegMode = pDcp->DmaChannel[Channel].Mode;
DPRINT1("DMA_Command = %x length=%d\n", RegMode, length);
/* Exit if the controller is disabled or the channel is masked */
if ((pDcp->Command & 0x04) || (pDcp->Mask & (1 << Channel)))
return 0;
OpMode = (RegMode & 0xC0) >> 6;
Increment = !(RegMode & 0x20);
Autoinit = RegMode & 0x10;
TrMode = (RegMode & 0x0C) >> 2;
/* Process operating mode */
switch (OpMode)
{
case 0:
/* Request mode */
DPRINT1("Request Mode - Not Implemented\n");
return 0;
case 1:
/* Single Mode */
break;
case 2:
/* Request mode */
DPRINT1("Block Mode - Not Implemented\n");
return 0;
case 3:
/* Cascade Mode */
DPRINT1("Cascade Mode should not be used by regular apps\n");
return 0;
}
/* Perform one the 4 transfer modes */
if (TrMode == 4)
{
/* Illegal */
DPRINT1("DMA Transfer Type Illegal\n");
return 0;
}
/* Transfer size : 8 bits for channels 0..3, 16 bits for channels 4..7 */
Size = (iChannel < 4) ? sizeof(BYTE) : sizeof(WORD);
// FIXME: Handle wrapping?
/* Get the number of elements to transfer */
ret = min(pDcp->DmaChannel[Channel].CurrElemCnt, length / Size);
length = ret * Size;
/* 16-bit mode addressing, see: http://wiki.osdev.org/ISA_DMA#16_bit_issues */
CurrAddress = (iChannel < 4) ? (DmaPageRegisters[iChannel].Page << 16) | ((pDcp->DmaChannel[Channel].CurrAddress << 0) & 0xFFFF)
: (DmaPageRegisters[iChannel].Page << 16) | ((pDcp->DmaChannel[Channel].CurrAddress << 1) & 0xFFFF);
switch (TrMode)
{
/* Verification (no real transfer) */
case 0:
{
DPRINT1("Verification DMA operation\n");
break;
}
/* Write */
case 1:
{
DPRINT1("Perform Write transfer of %d elements (%d bytes) at 0x%x %s with count %x\n",
ret, length, CurrAddress, Increment ? "up" : "down", pDcp->DmaChannel[Channel].CurrElemCnt);
if (Increment)
{
EmulatorWriteMemory(&EmulatorContext, CurrAddress, dmabuf, length);
}
else
{
for (i = 0; i < length; i++)
{
EmulatorWriteMemory(&EmulatorContext, CurrAddress - i, dmabuf + i, sizeof(BYTE));
}
}
break;
}
/* Read */
case 2:
{
DPRINT1("Perform Read transfer of %d elements (%d bytes) at 0x%x %s with count %x\n",
ret, length, CurrAddress, Increment ? "up" : "down", pDcp->DmaChannel[Channel].CurrElemCnt);
if (Increment)
{
EmulatorReadMemory(&EmulatorContext, CurrAddress, dmabuf, length);
}
else
{
for (i = 0; i < length; i++)
{
EmulatorReadMemory(&EmulatorContext, CurrAddress - i, dmabuf + i, sizeof(BYTE));
}
}
break;
}
}
/* Update DMA registers */
pDcp->DmaChannel[Channel].CurrElemCnt -= ret;
if (Increment)
pDcp->DmaChannel[Channel].CurrAddress += ret;
else
pDcp->DmaChannel[Channel].CurrAddress -= ret;
/* Check for end of transfer */
if (pDcp->DmaChannel[Channel].CurrElemCnt == 0)
{
DPRINT1("DMA buffer empty\n");
/* Update status register of the DMA chip corresponding to the channel */
pDcp->Status |= 1 << Channel; /* Mark transfer as finished */
pDcp->Status &= ~(1 << (Channel + 4)); /* Reset soft request if any */
if (Autoinit)
{
/* Reload Current* registers to their initial values */
pDcp->DmaChannel[Channel].CurrAddress = pDcp->DmaChannel[Channel].BaseAddress;
pDcp->DmaChannel[Channel].CurrElemCnt = pDcp->DmaChannel[Channel].BaseElemCnt;
}
else
{
/* Set the mask bit for the channel */
pDcp->Mask |= (1 << Channel);
}
}
return length;
}
VOID DmaInitialize(VOID)
{
/* Register the I/O Ports */
/* Channels 0(Reserved)..3 */
RegisterIoPort(0x00, DmaReadPort, DmaWritePort); /* Current(R) / Start(W) Address Register 0 (Reserved) */
RegisterIoPort(0x01, DmaReadPort, DmaWritePort); /* Current(R) / Base (W) Count Register 0 (Reserved) */
RegisterIoPort(0x02, DmaReadPort, DmaWritePort); /* Current(R) / Start(W) Address Register 1 */
RegisterIoPort(0x03, DmaReadPort, DmaWritePort); /* Current(R) / Base (W) Count Register 1 */
RegisterIoPort(0x04, DmaReadPort, DmaWritePort); /* Current(R) / Start(W) Address Register 2 */
RegisterIoPort(0x05, DmaReadPort, DmaWritePort); /* Current(R) / Base (W) Count Register 2 */
RegisterIoPort(0x06, DmaReadPort, DmaWritePort); /* Current(R) / Start(W) Address Register 3 */
RegisterIoPort(0x07, DmaReadPort, DmaWritePort); /* Current(R) / Base (W) Count Register 3 */
RegisterIoPort(0x08, DmaReadPort, DmaWritePort); /* Status (Read) / Command (Write) Registers */
RegisterIoPort(0x09, NULL, DmaWritePort); /* Request Register */
RegisterIoPort(0x0A, NULL, DmaWritePort); /* Single Channel Mask Register */
RegisterIoPort(0x0B, NULL, DmaWritePort); /* Mode Register */
RegisterIoPort(0x0C, NULL, DmaWritePort); /* Flip-Flop Reset Register */
RegisterIoPort(0x0D, DmaReadPort, DmaWritePort); /* Intermediate (Read) / Master Reset (Write) Registers */
RegisterIoPort(0x0E, NULL, DmaWritePort); /* Mask Reset Register */
RegisterIoPort(0x0F, DmaReadPort, DmaWritePort); /* Multi-Channel Mask Register */
/* Channels 4(Reserved)..7 */
RegisterIoPort(0xC0, DmaReadPort, DmaWritePort); /* Current(R) / Start(W) Address Register 4 (Reserved) */
RegisterIoPort(0xC2, DmaReadPort, DmaWritePort); /* Current(R) / Base (W) Count Register 4 (Reserved) */
RegisterIoPort(0xC4, DmaReadPort, DmaWritePort); /* Current(R) / Start(W) Address Register 5 */
RegisterIoPort(0xC6, DmaReadPort, DmaWritePort); /* Current(R) / Base (W) Count Register 5 */
RegisterIoPort(0xC8, DmaReadPort, DmaWritePort); /* Current(R) / Start(W) Address Register 6 */
RegisterIoPort(0xCA, DmaReadPort, DmaWritePort); /* Current(R) / Base (W) Count Register 6 */
RegisterIoPort(0xCC, DmaReadPort, DmaWritePort); /* Current(R) / Start(W) Address Register 7 */
RegisterIoPort(0xCE, DmaReadPort, DmaWritePort); /* Current(R) / Base (W) Count Register 7 */
RegisterIoPort(0xD0, DmaReadPort, DmaWritePort); /* Status (Read) / Command (Write) Registers */
RegisterIoPort(0xD2, NULL, DmaWritePort); /* Request Register */
RegisterIoPort(0xD4, NULL, DmaWritePort); /* Single Channel Mask Register */
RegisterIoPort(0xD6, NULL, DmaWritePort); /* Mode Register */
RegisterIoPort(0xD8, NULL, DmaWritePort); /* Flip-Flop Reset Register */
RegisterIoPort(0xDA, DmaReadPort, DmaWritePort); /* Intermediate (Read) / Master Reset (Write) Registers */
RegisterIoPort(0xDC, NULL, DmaWritePort); /* Mask Reset Register */
RegisterIoPort(0xDE, DmaReadPort, DmaWritePort); /* Multi-Channel Mask Register */
/* Channels Page Address Registers */
RegisterIoPort(0x87, DmaPageReadPort, DmaPageWritePort); /* Channel 0 (Reserved) */
RegisterIoPort(0x83, DmaPageReadPort, DmaPageWritePort); /* Channel 1 */
RegisterIoPort(0x81, DmaPageReadPort, DmaPageWritePort); /* Channel 2 */
RegisterIoPort(0x82, DmaPageReadPort, DmaPageWritePort); /* Channel 3 */
RegisterIoPort(0x8F, DmaPageReadPort, DmaPageWritePort); /* Channel 4 (Reserved) */
RegisterIoPort(0x8B, DmaPageReadPort, DmaPageWritePort); /* Channel 5 */
RegisterIoPort(0x89, DmaPageReadPort, DmaPageWritePort); /* Channel 6 */
RegisterIoPort(0x8A, DmaPageReadPort, DmaPageWritePort); /* Channel 7 */
}
DWORD
WINAPI
VDDRequestDMA(IN HANDLE hVdd,
IN WORD iChannel,
IN OUT PVOID Buffer,
IN DWORD length)
{
UNREFERENCED_PARAMETER(hVdd);
if (iChannel >= DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS)
{
SetLastError(ERROR_INVALID_ADDRESS);
return FALSE;
}
/*
* We assume success first. If something fails,
* DmaRequest sets an adequate last error.
*/
SetLastError(ERROR_SUCCESS);
return DmaRequest(iChannel, Buffer, length);
}
BOOL
WINAPI
VDDQueryDMA(IN HANDLE hVdd,
IN WORD iChannel,
IN PVDD_DMA_INFO pDmaInfo)
{
PDMA_CONTROLLER pDcp;
WORD Channel;
UNREFERENCED_PARAMETER(hVdd);
if (iChannel >= DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS)
{
SetLastError(ERROR_INVALID_ADDRESS);
return FALSE;
}
pDcp = &DmaControllers[iChannel / DMA_CONTROLLER_CHANNELS];
Channel = iChannel % DMA_CONTROLLER_CHANNELS;
pDmaInfo->addr = pDcp->DmaChannel[Channel].CurrAddress;
pDmaInfo->count = pDcp->DmaChannel[Channel].CurrElemCnt;
pDmaInfo->page = DmaPageRegisters[iChannel].Page;
pDmaInfo->status = pDcp->Status;
pDmaInfo->mode = pDcp->DmaChannel[Channel].Mode;
pDmaInfo->mask = pDcp->Mask;
return TRUE;
}
BOOL
WINAPI
VDDSetDMA(IN HANDLE hVdd,
IN WORD iChannel,
IN WORD fDMA,
IN PVDD_DMA_INFO pDmaInfo)
{
PDMA_CONTROLLER pDcp;
WORD Channel;
UNREFERENCED_PARAMETER(hVdd);
if (iChannel >= DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS)
{
SetLastError(ERROR_INVALID_ADDRESS);
return FALSE;
}
pDcp = &DmaControllers[iChannel / DMA_CONTROLLER_CHANNELS];
Channel = iChannel % DMA_CONTROLLER_CHANNELS;
if (fDMA & VDD_DMA_ADDR)
pDcp->DmaChannel[Channel].CurrAddress = pDmaInfo->addr;
if (fDMA & VDD_DMA_COUNT)
pDcp->DmaChannel[Channel].CurrElemCnt = pDmaInfo->count;
if (fDMA & VDD_DMA_PAGE)
DmaPageRegisters[iChannel].Page = pDmaInfo->page;
if (fDMA & VDD_DMA_STATUS)
pDcp->Status = pDmaInfo->status;
return TRUE;
}
/* EOF */