Author: hbelusca Date: Sun May 10 18:02:45 2015 New Revision: 67629
URL: http://svn.reactos.org/svn/reactos?rev=67629&view=rev Log: [NTVDM]: Implement support for DMA transfers, single-mode only for now, and fix its support for VDDs.
Modified: trunk/reactos/subsystems/mvdm/ntvdm/hardware/dma.c trunk/reactos/subsystems/mvdm/ntvdm/hardware/dma.h
Modified: trunk/reactos/subsystems/mvdm/ntvdm/hardware/dma.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/mvdm/ntvdm/hardw... ============================================================================== --- trunk/reactos/subsystems/mvdm/ntvdm/hardware/dma.c [iso-8859-1] (original) +++ trunk/reactos/subsystems/mvdm/ntvdm/hardware/dma.c [iso-8859-1] Sun May 10 18:02:45 2015 @@ -2,7 +2,7 @@ * 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) */ @@ -16,6 +16,7 @@ #include "dma.h"
#include "io.h" +#include "memory.h"
/* PRIVATE VARIABLES **********************************************************/
@@ -41,7 +42,7 @@ #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) @@ -49,6 +50,8 @@ static BYTE WINAPI DmaReadPort(USHORT Port) { BYTE ReadValue = 0xFF; + + DPRINT1("DmaReadPort(Port = 0x%04X)\n", Port);
switch (Port) { @@ -122,6 +125,14 @@ 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; } }
@@ -139,15 +150,17 @@
#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 */ @@ -216,6 +229,16 @@ 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: @@ -226,6 +249,32 @@ 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: @@ -236,7 +285,7 @@ break; }
- /* DMA Master Reset */ + /* DMA Master Reset Registers */ { case 0x0D: DmaControllers[0].Command = 0x00; @@ -255,6 +304,16 @@ DmaControllers[1].Mask = 0x0F; break; } + + /* Mask Reset Registers */ + { + case 0x0E: + DmaControllers[0].Mask = 0x00; + break; + case 0xDC: + DmaControllers[1].Mask = 0x00; + break; + } } }
@@ -262,6 +321,8 @@
static BYTE WINAPI DmaPageReadPort(USHORT Port) { + DPRINT1("DmaPageReadPort(Port = 0x%04X)\n", Port); + switch (Port) { case 0x87: @@ -287,6 +348,8 @@
static VOID WINAPI DmaPageWritePort(USHORT Port, BYTE Data) { + DPRINT1("DmaPageWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port, Data); + switch (Port) { case 0x87: @@ -317,6 +380,169 @@ }
/* 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) + : (DmaPageRegisters[iChannel].Page << 16) | (pDcp->DmaChannel[Channel].CurrAddress << 1); + + 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) + { + MemWrite(CurrAddress, dmabuf, length); + } + else + { + for (i = 0; i < length; i++) + { + MemWrite(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) + { + MemRead(CurrAddress, dmabuf, length); + } + else + { + for (i = 0; i < length; i++) + { + MemRead(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) { @@ -339,7 +565,7 @@ 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 */ @@ -359,7 +585,7 @@ 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 */ @@ -390,8 +616,13 @@ 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 @@ -415,9 +646,9 @@ Channel = iChannel % DMA_CONTROLLER_CHANNELS;
pDmaInfo->addr = pDcp->DmaChannel[Channel].CurrAddress; - pDmaInfo->count = pDcp->DmaChannel[Channel].CurrWordCnt; - - // pDmaInfo->page = DmaPageRegisters[iChannel].Page; + 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; @@ -450,10 +681,10 @@ pDcp->DmaChannel[Channel].CurrAddress = pDmaInfo->addr;
if (fDMA & VDD_DMA_COUNT) - pDcp->DmaChannel[Channel].CurrWordCnt = pDmaInfo->count; - - // if (fDMA & VDD_DMA_PAGE) - // DmaPageRegisters[iChannel].Page = pDmaInfo->page; + 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;
Modified: trunk/reactos/subsystems/mvdm/ntvdm/hardware/dma.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/subsystems/mvdm/ntvdm/hardw... ============================================================================== --- trunk/reactos/subsystems/mvdm/ntvdm/hardware/dma.h [iso-8859-1] (original) +++ trunk/reactos/subsystems/mvdm/ntvdm/hardware/dma.h [iso-8859-1] Sun May 10 18:02:45 2015 @@ -2,7 +2,7 @@ * COPYRIGHT: GPL - See COPYING in the top level directory * PROJECT: ReactOS Virtual DOS Machine * FILE: dma.h - * 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) */ @@ -18,9 +18,9 @@ typedef struct _DMA_CHANNEL { WORD BaseAddress; - WORD BaseWordCnt; + WORD BaseElemCnt; WORD CurrAddress; - WORD CurrWordCnt; + WORD CurrElemCnt; BYTE Mode; } DMA_CHANNEL, *PDMA_CHANNEL;
@@ -29,7 +29,7 @@ DMA_CHANNEL DmaChannel[DMA_CONTROLLER_CHANNELS];
WORD TempAddress; - WORD TempWordCnt; + WORD TempElemCnt;
BYTE TempReg;
@@ -49,9 +49,13 @@ } DMA_PAGE_REGISTER, *PDMA_PAGE_REGISTER;
// The 74LS612 contains 16 bytes, each of them being a page register. -// They are accessible via ports 0x80 through 0x8F . +// They are accessible via ports 0x80 through 0x8F.
/* FUNCTIONS ******************************************************************/ + +DWORD DmaRequest(IN WORD iChannel, + IN OUT PVOID Buffer, + IN DWORD length);
VOID DmaInitialize(VOID);