2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: subsystems/mvdm/ntvdm/hardware/dma.c
5 * PURPOSE: ISA DMA - Direct Memory Access Controller emulation -
6 * i8237A compatible with 74LS612 Memory Mapper extension
7 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
10 /* INCLUDES *******************************************************************/
23 /* PRIVATE VARIABLES **********************************************************/
26 * DMA Controller 0 (Channels 0..3): Slave controller
27 * DMA Controller 1 (Channels 4..7): Master controller
29 static DMA_CONTROLLER DmaControllers
[DMA_CONTROLLERS
];
31 /* External page registers for each channel of the two DMA controllers */
32 static DMA_PAGE_REGISTER DmaPageRegisters
[DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
];
34 /* PRIVATE FUNCTIONS **********************************************************/
36 #define READ_ADDR(CtrlIndex, ChanIndex, Data) \
39 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrAddress + \
40 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
41 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
44 #define READ_CNT(CtrlIndex, ChanIndex, Data) \
47 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
48 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
49 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
52 static BYTE WINAPI
DmaReadPort(USHORT Port
)
54 BYTE ReadValue
= 0xFF;
56 DPRINT1("DmaReadPort(Port = 0x%04X)\n", Port
);
60 /* Current Address Registers */
63 READ_ADDR(0, 0, ReadValue
);
66 READ_ADDR(0, 1, ReadValue
);
69 READ_ADDR(0, 2, ReadValue
);
72 READ_ADDR(0, 3, ReadValue
);
75 READ_ADDR(1, 0, ReadValue
);
78 READ_ADDR(1, 1, ReadValue
);
81 READ_ADDR(1, 2, ReadValue
);
84 READ_ADDR(1, 3, ReadValue
);
88 /* Current Count Registers */
91 READ_CNT(0, 0, ReadValue
);
94 READ_CNT(0, 1, ReadValue
);
97 READ_CNT(0, 2, ReadValue
);
100 READ_CNT(0, 3, ReadValue
);
103 READ_CNT(1, 0, ReadValue
);
106 READ_CNT(1, 1, ReadValue
);
109 READ_CNT(1, 2, ReadValue
);
112 READ_CNT(1, 3, ReadValue
);
116 /* Status Registers */
119 return DmaControllers
[0].Status
;
121 return DmaControllers
[1].Status
;
124 /* DMA Intermediate (Temporary) Registers */
127 return DmaControllers
[0].TempReg
;
129 return DmaControllers
[1].TempReg
;
132 /* Multi-Channel Mask Registers */
135 return DmaControllers
[0].Mask
;
137 return DmaControllers
[1].Mask
;
144 #define WRITE_ADDR(CtrlIndex, ChanIndex, Data) \
146 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseAddress + \
147 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
148 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrAddress + \
149 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
150 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
153 #define WRITE_CNT(CtrlIndex, ChanIndex, Data) \
155 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseElemCnt + \
156 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
157 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
158 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
159 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
162 static VOID WINAPI
DmaWritePort(USHORT Port
, BYTE Data
)
164 DPRINT1("DmaWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port
, Data
);
168 /* Start Address Registers */
171 WRITE_ADDR(0, 0, Data
);
174 WRITE_ADDR(0, 1, Data
);
177 WRITE_ADDR(0, 2, Data
);
180 WRITE_ADDR(0, 3, Data
);
183 WRITE_ADDR(1, 0, Data
);
186 WRITE_ADDR(1, 1, Data
);
189 WRITE_ADDR(1, 2, Data
);
192 WRITE_ADDR(1, 3, Data
);
196 /* Base Count Registers */
199 WRITE_CNT(0, 0, Data
);
202 WRITE_CNT(0, 1, Data
);
205 WRITE_CNT(0, 2, Data
);
208 WRITE_CNT(0, 3, Data
);
211 WRITE_CNT(1, 0, Data
);
214 WRITE_CNT(1, 1, Data
);
217 WRITE_CNT(1, 2, Data
);
220 WRITE_CNT(1, 3, Data
);
224 /* Command Registers */
227 DmaControllers
[0].Command
= Data
;
230 DmaControllers
[1].Command
= Data
;
237 DmaControllers
[0].DmaChannel
[Data
& 0x03].Mode
= (Data
& ~0x03);
240 DmaControllers
[1].DmaChannel
[Data
& 0x03].Mode
= (Data
& ~0x03);
244 /* Request Registers */
247 DmaControllers
[0].Request
= Data
;
250 DmaControllers
[1].Request
= Data
;
254 /* Single Channel Mask Registers */
258 DmaControllers
[0].Mask
|= (1 << (Data
& 0x03));
260 DmaControllers
[0].Mask
&= ~(1 << (Data
& 0x03));
264 DmaControllers
[1].Mask
|= (1 << (Data
& 0x03));
266 DmaControllers
[1].Mask
&= ~(1 << (Data
& 0x03));
270 /* Multi-Channel Mask Registers */
273 DmaControllers
[0].Mask
= (Data
& 0x0F);
276 DmaControllers
[1].Mask
= (Data
& 0x0F);
280 /* Flip-Flop Reset */
283 DmaControllers
[0].FlipFlop
= 0;
286 DmaControllers
[1].FlipFlop
= 0;
290 /* DMA Master Reset Registers */
293 DmaControllers
[0].Command
= 0x00;
294 DmaControllers
[0].Status
= 0x00;
295 DmaControllers
[0].Request
= 0x00;
296 DmaControllers
[0].TempReg
= 0x00;
297 DmaControllers
[0].FlipFlop
= 0;
298 DmaControllers
[0].Mask
= 0x0F;
301 DmaControllers
[1].Command
= 0x00;
302 DmaControllers
[1].Status
= 0x00;
303 DmaControllers
[1].Request
= 0x00;
304 DmaControllers
[1].TempReg
= 0x00;
305 DmaControllers
[1].FlipFlop
= 0;
306 DmaControllers
[1].Mask
= 0x0F;
310 /* Mask Reset Registers */
313 DmaControllers
[0].Mask
= 0x00;
316 DmaControllers
[1].Mask
= 0x00;
322 /* Page Address Registers */
324 static BYTE WINAPI
DmaPageReadPort(USHORT Port
)
326 DPRINT1("DmaPageReadPort(Port = 0x%04X)\n", Port
);
331 return DmaPageRegisters
[0].Page
;
333 return DmaPageRegisters
[1].Page
;
335 return DmaPageRegisters
[2].Page
;
337 return DmaPageRegisters
[3].Page
;
339 return DmaPageRegisters
[4].Page
;
341 return DmaPageRegisters
[5].Page
;
343 return DmaPageRegisters
[6].Page
;
345 return DmaPageRegisters
[7].Page
;
351 static VOID WINAPI
DmaPageWritePort(USHORT Port
, BYTE Data
)
353 DPRINT1("DmaPageWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port
, Data
);
358 DmaPageRegisters
[0].Page
= Data
;
361 DmaPageRegisters
[1].Page
= Data
;
364 DmaPageRegisters
[2].Page
= Data
;
367 DmaPageRegisters
[3].Page
= Data
;
370 DmaPageRegisters
[4].Page
= Data
;
373 DmaPageRegisters
[5].Page
= Data
;
376 DmaPageRegisters
[6].Page
= Data
;
379 DmaPageRegisters
[7].Page
= Data
;
384 /* PUBLIC FUNCTIONS ***********************************************************/
386 DWORD
DmaRequest(IN WORD iChannel
,
391 * NOTE: This function is adapted from Wine's krnl386.exe,
392 * DMA emulation by Christian Costa.
394 PDMA_CONTROLLER pDcp
;
397 DWORD i
, Size
, ret
= 0;
398 BYTE RegMode
, OpMode
, Increment
, Autoinit
, TrMode
;
399 PBYTE dmabuf
= Buffer
;
403 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
405 SetLastError(ERROR_INVALID_ADDRESS
);
409 pDcp
= &DmaControllers
[iChannel
/ DMA_CONTROLLER_CHANNELS
];
410 Channel
= iChannel
% DMA_CONTROLLER_CHANNELS
; // == (iChannel & 0x03)
412 RegMode
= pDcp
->DmaChannel
[Channel
].Mode
;
414 DPRINT1("DMA_Command = %x length=%d\n", RegMode
, length
);
416 /* Exit if the controller is disabled or the channel is masked */
417 if ((pDcp
->Command
& 0x04) || (pDcp
->Mask
& (1 << Channel
)))
420 OpMode
= (RegMode
& 0xC0) >> 6;
421 Increment
= !(RegMode
& 0x20);
422 Autoinit
= RegMode
& 0x10;
423 TrMode
= (RegMode
& 0x0C) >> 2;
425 /* Process operating mode */
430 DPRINT1("Request Mode - Not Implemented\n");
437 DPRINT1("Block Mode - Not Implemented\n");
441 DPRINT1("Cascade Mode should not be used by regular apps\n");
445 /* Perform one the 4 transfer modes */
449 DPRINT1("DMA Transfer Type Illegal\n");
453 /* Transfer size : 8 bits for channels 0..3, 16 bits for channels 4..7 */
454 Size
= (iChannel
< 4) ? sizeof(BYTE
) : sizeof(WORD
);
456 // FIXME: Handle wrapping?
457 /* Get the number of elements to transfer */
458 ret
= min(pDcp
->DmaChannel
[Channel
].CurrElemCnt
, length
/ Size
);
461 /* 16-bit mode addressing, see: http://wiki.osdev.org/ISA_DMA#16_bit_issues */
462 CurrAddress
= (iChannel
< 4) ? (DmaPageRegisters
[iChannel
].Page
<< 16) | ((pDcp
->DmaChannel
[Channel
].CurrAddress
<< 0) & 0xFFFF)
463 : (DmaPageRegisters
[iChannel
].Page
<< 16) | ((pDcp
->DmaChannel
[Channel
].CurrAddress
<< 1) & 0xFFFF);
467 /* Verification (no real transfer) */
470 DPRINT1("Verification DMA operation\n");
477 DPRINT1("Perform Write transfer of %d elements (%d bytes) at 0x%x %s with count %x\n",
478 ret
, length
, CurrAddress
, Increment
? "up" : "down", pDcp
->DmaChannel
[Channel
].CurrElemCnt
);
482 EmulatorWriteMemory(&EmulatorContext
, CurrAddress
, dmabuf
, length
);
486 for (i
= 0; i
< length
; i
++)
488 EmulatorWriteMemory(&EmulatorContext
, CurrAddress
- i
, dmabuf
+ i
, sizeof(BYTE
));
498 DPRINT1("Perform Read transfer of %d elements (%d bytes) at 0x%x %s with count %x\n",
499 ret
, length
, CurrAddress
, Increment
? "up" : "down", pDcp
->DmaChannel
[Channel
].CurrElemCnt
);
503 EmulatorReadMemory(&EmulatorContext
, CurrAddress
, dmabuf
, length
);
507 for (i
= 0; i
< length
; i
++)
509 EmulatorReadMemory(&EmulatorContext
, CurrAddress
- i
, dmabuf
+ i
, sizeof(BYTE
));
517 /* Update DMA registers */
518 pDcp
->DmaChannel
[Channel
].CurrElemCnt
-= ret
;
520 pDcp
->DmaChannel
[Channel
].CurrAddress
+= ret
;
522 pDcp
->DmaChannel
[Channel
].CurrAddress
-= ret
;
524 /* Check for end of transfer */
525 if (pDcp
->DmaChannel
[Channel
].CurrElemCnt
== 0)
527 DPRINT1("DMA buffer empty\n");
529 /* Update status register of the DMA chip corresponding to the channel */
530 pDcp
->Status
|= 1 << Channel
; /* Mark transfer as finished */
531 pDcp
->Status
&= ~(1 << (Channel
+ 4)); /* Reset soft request if any */
535 /* Reload Current* registers to their initial values */
536 pDcp
->DmaChannel
[Channel
].CurrAddress
= pDcp
->DmaChannel
[Channel
].BaseAddress
;
537 pDcp
->DmaChannel
[Channel
].CurrElemCnt
= pDcp
->DmaChannel
[Channel
].BaseElemCnt
;
541 /* Set the mask bit for the channel */
542 pDcp
->Mask
|= (1 << Channel
);
549 VOID
DmaInitialize(VOID
)
551 /* Register the I/O Ports */
553 /* Channels 0(Reserved)..3 */
554 RegisterIoPort(0x00, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 0 (Reserved) */
555 RegisterIoPort(0x01, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 0 (Reserved) */
556 RegisterIoPort(0x02, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 1 */
557 RegisterIoPort(0x03, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 1 */
558 RegisterIoPort(0x04, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 2 */
559 RegisterIoPort(0x05, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 2 */
560 RegisterIoPort(0x06, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 3 */
561 RegisterIoPort(0x07, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 3 */
563 RegisterIoPort(0x08, DmaReadPort
, DmaWritePort
); /* Status (Read) / Command (Write) Registers */
564 RegisterIoPort(0x09, NULL
, DmaWritePort
); /* Request Register */
565 RegisterIoPort(0x0A, NULL
, DmaWritePort
); /* Single Channel Mask Register */
566 RegisterIoPort(0x0B, NULL
, DmaWritePort
); /* Mode Register */
567 RegisterIoPort(0x0C, NULL
, DmaWritePort
); /* Flip-Flop Reset Register */
568 RegisterIoPort(0x0D, DmaReadPort
, DmaWritePort
); /* Intermediate (Read) / Master Reset (Write) Registers */
569 RegisterIoPort(0x0E, NULL
, DmaWritePort
); /* Mask Reset Register */
570 RegisterIoPort(0x0F, DmaReadPort
, DmaWritePort
); /* Multi-Channel Mask Register */
573 /* Channels 4(Reserved)..7 */
574 RegisterIoPort(0xC0, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 4 (Reserved) */
575 RegisterIoPort(0xC2, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 4 (Reserved) */
576 RegisterIoPort(0xC4, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 5 */
577 RegisterIoPort(0xC6, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 5 */
578 RegisterIoPort(0xC8, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 6 */
579 RegisterIoPort(0xCA, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 6 */
580 RegisterIoPort(0xCC, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 7 */
581 RegisterIoPort(0xCE, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 7 */
583 RegisterIoPort(0xD0, DmaReadPort
, DmaWritePort
); /* Status (Read) / Command (Write) Registers */
584 RegisterIoPort(0xD2, NULL
, DmaWritePort
); /* Request Register */
585 RegisterIoPort(0xD4, NULL
, DmaWritePort
); /* Single Channel Mask Register */
586 RegisterIoPort(0xD6, NULL
, DmaWritePort
); /* Mode Register */
587 RegisterIoPort(0xD8, NULL
, DmaWritePort
); /* Flip-Flop Reset Register */
588 RegisterIoPort(0xDA, DmaReadPort
, DmaWritePort
); /* Intermediate (Read) / Master Reset (Write) Registers */
589 RegisterIoPort(0xDC, NULL
, DmaWritePort
); /* Mask Reset Register */
590 RegisterIoPort(0xDE, DmaReadPort
, DmaWritePort
); /* Multi-Channel Mask Register */
593 /* Channels Page Address Registers */
594 RegisterIoPort(0x87, DmaPageReadPort
, DmaPageWritePort
); /* Channel 0 (Reserved) */
595 RegisterIoPort(0x83, DmaPageReadPort
, DmaPageWritePort
); /* Channel 1 */
596 RegisterIoPort(0x81, DmaPageReadPort
, DmaPageWritePort
); /* Channel 2 */
597 RegisterIoPort(0x82, DmaPageReadPort
, DmaPageWritePort
); /* Channel 3 */
598 RegisterIoPort(0x8F, DmaPageReadPort
, DmaPageWritePort
); /* Channel 4 (Reserved) */
599 RegisterIoPort(0x8B, DmaPageReadPort
, DmaPageWritePort
); /* Channel 5 */
600 RegisterIoPort(0x89, DmaPageReadPort
, DmaPageWritePort
); /* Channel 6 */
601 RegisterIoPort(0x8A, DmaPageReadPort
, DmaPageWritePort
); /* Channel 7 */
608 VDDRequestDMA(IN HANDLE hVdd
,
613 UNREFERENCED_PARAMETER(hVdd
);
615 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
617 SetLastError(ERROR_INVALID_ADDRESS
);
622 * We assume success first. If something fails,
623 * DmaRequest sets an adequate last error.
625 SetLastError(ERROR_SUCCESS
);
627 return DmaRequest(iChannel
, Buffer
, length
);
632 VDDQueryDMA(IN HANDLE hVdd
,
634 IN PVDD_DMA_INFO pDmaInfo
)
636 PDMA_CONTROLLER pDcp
;
639 UNREFERENCED_PARAMETER(hVdd
);
641 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
643 SetLastError(ERROR_INVALID_ADDRESS
);
647 pDcp
= &DmaControllers
[iChannel
/ DMA_CONTROLLER_CHANNELS
];
648 Channel
= iChannel
% DMA_CONTROLLER_CHANNELS
;
650 pDmaInfo
->addr
= pDcp
->DmaChannel
[Channel
].CurrAddress
;
651 pDmaInfo
->count
= pDcp
->DmaChannel
[Channel
].CurrElemCnt
;
653 pDmaInfo
->page
= DmaPageRegisters
[iChannel
].Page
;
654 pDmaInfo
->status
= pDcp
->Status
;
655 pDmaInfo
->mode
= pDcp
->DmaChannel
[Channel
].Mode
;
656 pDmaInfo
->mask
= pDcp
->Mask
;
663 VDDSetDMA(IN HANDLE hVdd
,
666 IN PVDD_DMA_INFO pDmaInfo
)
668 PDMA_CONTROLLER pDcp
;
671 UNREFERENCED_PARAMETER(hVdd
);
673 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
675 SetLastError(ERROR_INVALID_ADDRESS
);
679 pDcp
= &DmaControllers
[iChannel
/ DMA_CONTROLLER_CHANNELS
];
680 Channel
= iChannel
% DMA_CONTROLLER_CHANNELS
;
682 if (fDMA
& VDD_DMA_ADDR
)
683 pDcp
->DmaChannel
[Channel
].CurrAddress
= pDmaInfo
->addr
;
685 if (fDMA
& VDD_DMA_COUNT
)
686 pDcp
->DmaChannel
[Channel
].CurrElemCnt
= pDmaInfo
->count
;
688 if (fDMA
& VDD_DMA_PAGE
)
689 DmaPageRegisters
[iChannel
].Page
= pDmaInfo
->page
;
691 if (fDMA
& VDD_DMA_STATUS
)
692 pDcp
->Status
= pDmaInfo
->status
;