* COPYRIGHT: GPL - See COPYING in the top level directory
* PROJECT: ReactOS Virtual DOS Machine
* FILE: dma.c
- * PURPOSE: Direct Memory Access Controller emulation -
+ * PURPOSE: ISA DMA - Direct Memory Access Controller emulation -
* i8237A compatible with 74LS612 Memory Mapper extension
* PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
*/
#include "dma.h"
#include "io.h"
+#include "memory.h"
/* PRIVATE VARIABLES **********************************************************/
#define READ_CNT(CtrlIndex, ChanIndex, Data) \
do { \
(Data) = \
- *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrWordCnt + \
+ *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
(DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
} while(0)
{
BYTE ReadValue = 0xFF;
+ DPRINT1("DmaReadPort(Port = 0x%04X)\n", Port);
+
switch (Port)
{
- /* Start Address Registers */
+ /* Current Address Registers */
{
case 0x00:
READ_ADDR(0, 0, ReadValue);
return ReadValue;
}
- /* Count Address Registers */
+ /* Current Count Registers */
{
case 0x01:
READ_CNT(0, 0, ReadValue);
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_CNT(CtrlIndex, ChanIndex, Data) \
do { \
- *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseWordCnt + \
+ *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseElemCnt + \
(DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
- *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrWordCnt + \
+ *((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 */
break;
}
- /* Count Address Registers */
+ /* Base Count Registers */
{
case 0x01:
WRITE_CNT(0, 0, 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:
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:
break;
}
- /* DMA Master Reset */
+ /* DMA Master Reset Registers */
{
case 0x0D:
DmaControllers[0].Command = 0x00;
DmaControllers[1].Mask = 0x0F;
break;
}
+
+ /* Mask Reset Registers */
+ {
+ case 0x0E:
+ DmaControllers[0].Mask = 0x00;
+ break;
+ case 0xDC:
+ DmaControllers[1].Mask = 0x00;
+ break;
+ }
}
}
static BYTE WINAPI DmaPageReadPort(USHORT Port)
{
+ DPRINT1("DmaPageReadPort(Port = 0x%04X)\n", Port);
+
switch (Port)
{
case 0x87:
static VOID WINAPI DmaPageWritePort(USHORT Port, BYTE Data)
{
+ DPRINT1("DmaPageWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port, Data);
+
switch (Port)
{
case 0x87:
/* 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, NULL, DmaWritePort); /* Start Address Register 0 (Reserved) */
- RegisterIoPort(0x01, NULL, DmaWritePort); /* Count Address Register 0 (Reserved) */
- RegisterIoPort(0x02, NULL, DmaWritePort); /* Start Address Register 1 */
- RegisterIoPort(0x03, NULL, DmaWritePort); /* Count Address Register 1 */
- RegisterIoPort(0x04, NULL, DmaWritePort); /* Start Address Register 2 */
- RegisterIoPort(0x05, NULL, DmaWritePort); /* Count Address Register 2 */
- RegisterIoPort(0x06, NULL, DmaWritePort); /* Start Address Register 3 */
- RegisterIoPort(0x07, NULL, DmaWritePort); /* Count Address Register 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(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 Reset Register */
+ RegisterIoPort(0x0F, DmaReadPort, DmaWritePort); /* Multi-Channel Mask Register */
/* Channels 4(Reserved)..7 */
- RegisterIoPort(0xC0, NULL, DmaWritePort); /* Start Address Register 4 (Reserved) */
- RegisterIoPort(0xC2, NULL, DmaWritePort); /* Count Address Register 4 (Reserved) */
- RegisterIoPort(0xC4, NULL, DmaWritePort); /* Start Address Register 5 */
- RegisterIoPort(0xC6, NULL, DmaWritePort); /* Count Address Register 5 */
- RegisterIoPort(0xC8, NULL, DmaWritePort); /* Start Address Register 6 */
- RegisterIoPort(0xCA, NULL, DmaWritePort); /* Count Address Register 6 */
- RegisterIoPort(0xCC, NULL, DmaWritePort); /* Start Address Register 7 */
- RegisterIoPort(0xCE, NULL, DmaWritePort); /* Count Address Register 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(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 Reset Register */
+ RegisterIoPort(0xDE, DmaReadPort, DmaWritePort); /* Multi-Channel Mask Register */
/* Channels Page Address Registers */
return FALSE;
}
- UNIMPLEMENTED;
- return 0;
+ /*
+ * We assume success first. If something fails,
+ * DmaRequest sets an adequate last error.
+ */
+ SetLastError(ERROR_SUCCESS);
+
+ return DmaRequest(iChannel, Buffer, length);
}
BOOL
Channel = iChannel % DMA_CONTROLLER_CHANNELS;
pDmaInfo->addr = pDcp->DmaChannel[Channel].CurrAddress;
- pDmaInfo->count = pDcp->DmaChannel[Channel].CurrWordCnt;
+ pDmaInfo->count = pDcp->DmaChannel[Channel].CurrElemCnt;
- // pDmaInfo->page = DmaPageRegisters[iChannel].Page;
+ pDmaInfo->page = DmaPageRegisters[iChannel].Page;
pDmaInfo->status = pDcp->Status;
pDmaInfo->mode = pDcp->DmaChannel[Channel].Mode;
pDmaInfo->mask = pDcp->Mask;
pDcp->DmaChannel[Channel].CurrAddress = pDmaInfo->addr;
if (fDMA & VDD_DMA_COUNT)
- pDcp->DmaChannel[Channel].CurrWordCnt = pDmaInfo->count;
+ pDcp->DmaChannel[Channel].CurrElemCnt = pDmaInfo->count;
- // if (fDMA & VDD_DMA_PAGE)
- // DmaPageRegisters[iChannel].Page = pDmaInfo->page;
+ if (fDMA & VDD_DMA_PAGE)
+ DmaPageRegisters[iChannel].Page = pDmaInfo->page;
if (fDMA & VDD_DMA_STATUS)
pDcp->Status = pDmaInfo->status;