2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
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 *******************************************************************/
21 /* PRIVATE VARIABLES **********************************************************/
24 * DMA Controller 0 (Channels 0..3): Slave controller
25 * DMA Controller 1 (Channels 4..7): Master controller
27 static DMA_CONTROLLER DmaControllers
[DMA_CONTROLLERS
];
29 /* External page registers for each channel of the two DMA controllers */
30 static DMA_PAGE_REGISTER DmaPageRegisters
[DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
];
32 /* PRIVATE FUNCTIONS **********************************************************/
34 #define READ_ADDR(CtrlIndex, ChanIndex, Data) \
37 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrAddress + \
38 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
39 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
42 #define READ_CNT(CtrlIndex, ChanIndex, Data) \
45 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
46 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
47 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
50 static BYTE WINAPI
DmaReadPort(USHORT Port
)
52 BYTE ReadValue
= 0xFF;
54 DPRINT1("DmaReadPort(Port = 0x%04X)\n", Port
);
58 /* Start Address Registers */
61 READ_ADDR(0, 0, ReadValue
);
64 READ_ADDR(0, 1, ReadValue
);
67 READ_ADDR(0, 2, ReadValue
);
70 READ_ADDR(0, 3, ReadValue
);
73 READ_ADDR(1, 0, ReadValue
);
76 READ_ADDR(1, 1, ReadValue
);
79 READ_ADDR(1, 2, ReadValue
);
82 READ_ADDR(1, 3, ReadValue
);
86 /* Count Address Registers */
89 READ_CNT(0, 0, ReadValue
);
92 READ_CNT(0, 1, ReadValue
);
95 READ_CNT(0, 2, ReadValue
);
98 READ_CNT(0, 3, ReadValue
);
101 READ_CNT(1, 0, ReadValue
);
104 READ_CNT(1, 1, ReadValue
);
107 READ_CNT(1, 2, ReadValue
);
110 READ_CNT(1, 3, ReadValue
);
114 /* Status Registers */
117 return DmaControllers
[0].Status
;
119 return DmaControllers
[1].Status
;
122 /* DMA Intermediate (Temporary) Registers */
125 return DmaControllers
[0].TempReg
;
127 return DmaControllers
[1].TempReg
;
130 /* Multi-Channel Mask Registers */
133 return DmaControllers
[0].Mask
;
135 return DmaControllers
[1].Mask
;
142 #define WRITE_ADDR(CtrlIndex, ChanIndex, Data) \
144 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseAddress + \
145 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
146 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrAddress + \
147 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
148 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
151 #define WRITE_CNT(CtrlIndex, ChanIndex, Data) \
153 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseElemCnt + \
154 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
155 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
156 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
157 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
160 static VOID WINAPI
DmaWritePort(USHORT Port
, BYTE Data
)
162 DPRINT1("DmaWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port
, Data
);
166 /* Start Address Registers */
169 WRITE_ADDR(0, 0, Data
);
172 WRITE_ADDR(0, 1, Data
);
175 WRITE_ADDR(0, 2, Data
);
178 WRITE_ADDR(0, 3, Data
);
181 WRITE_ADDR(1, 0, Data
);
184 WRITE_ADDR(1, 1, Data
);
187 WRITE_ADDR(1, 2, Data
);
190 WRITE_ADDR(1, 3, Data
);
194 /* Count Address Registers */
197 WRITE_CNT(0, 0, Data
);
200 WRITE_CNT(0, 1, Data
);
203 WRITE_CNT(0, 2, Data
);
206 WRITE_CNT(0, 3, Data
);
209 WRITE_CNT(1, 0, Data
);
212 WRITE_CNT(1, 1, Data
);
215 WRITE_CNT(1, 2, Data
);
218 WRITE_CNT(1, 3, Data
);
222 /* Command Registers */
225 DmaControllers
[0].Command
= Data
;
228 DmaControllers
[1].Command
= Data
;
235 DmaControllers
[0].DmaChannel
[Data
& 0x03].Mode
= (Data
& ~0x03);
238 DmaControllers
[1].DmaChannel
[Data
& 0x03].Mode
= (Data
& ~0x03);
242 /* Request Registers */
245 DmaControllers
[0].Request
= Data
;
248 DmaControllers
[1].Request
= Data
;
252 /* Single Channel Mask Registers */
256 DmaControllers
[0].Mask
|= (1 << (Data
& 0x03));
258 DmaControllers
[0].Mask
&= ~(1 << (Data
& 0x03));
262 DmaControllers
[1].Mask
|= (1 << (Data
& 0x03));
264 DmaControllers
[1].Mask
&= ~(1 << (Data
& 0x03));
268 /* Multi-Channel Mask Registers */
271 DmaControllers
[0].Mask
= (Data
& 0x0F);
274 DmaControllers
[1].Mask
= (Data
& 0x0F);
278 /* Flip-Flop Reset */
281 DmaControllers
[0].FlipFlop
= 0;
284 DmaControllers
[1].FlipFlop
= 0;
288 /* DMA Master Reset Registers */
291 DmaControllers
[0].Command
= 0x00;
292 DmaControllers
[0].Status
= 0x00;
293 DmaControllers
[0].Request
= 0x00;
294 DmaControllers
[0].TempReg
= 0x00;
295 DmaControllers
[0].FlipFlop
= 0;
296 DmaControllers
[0].Mask
= 0x0F;
299 DmaControllers
[1].Command
= 0x00;
300 DmaControllers
[1].Status
= 0x00;
301 DmaControllers
[1].Request
= 0x00;
302 DmaControllers
[1].TempReg
= 0x00;
303 DmaControllers
[1].FlipFlop
= 0;
304 DmaControllers
[1].Mask
= 0x0F;
308 /* Mask Reset Registers */
311 DmaControllers
[0].Mask
= 0x00;
314 DmaControllers
[1].Mask
= 0x00;
320 /* Page Address Registers */
322 static BYTE WINAPI
DmaPageReadPort(USHORT Port
)
324 DPRINT1("DmaPageReadPort(Port = 0x%04X)\n", Port
);
329 return DmaPageRegisters
[0].Page
;
331 return DmaPageRegisters
[1].Page
;
333 return DmaPageRegisters
[2].Page
;
335 return DmaPageRegisters
[3].Page
;
337 return DmaPageRegisters
[4].Page
;
339 return DmaPageRegisters
[5].Page
;
341 return DmaPageRegisters
[6].Page
;
343 return DmaPageRegisters
[7].Page
;
349 static VOID WINAPI
DmaPageWritePort(USHORT Port
, BYTE Data
)
351 DPRINT1("DmaPageWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port
, Data
);
356 DmaPageRegisters
[0].Page
= Data
;
359 DmaPageRegisters
[1].Page
= Data
;
362 DmaPageRegisters
[2].Page
= Data
;
365 DmaPageRegisters
[3].Page
= Data
;
368 DmaPageRegisters
[4].Page
= Data
;
371 DmaPageRegisters
[5].Page
= Data
;
374 DmaPageRegisters
[6].Page
= Data
;
377 DmaPageRegisters
[7].Page
= Data
;
382 /* PUBLIC FUNCTIONS ***********************************************************/
384 DWORD
DmaRequest(IN WORD iChannel
,
389 * NOTE: This function is adapted from Wine's krnl386.exe,
390 * DMA emulation by Christian Costa.
392 PDMA_CONTROLLER pDcp
;
395 DWORD i
, Size
, ret
= 0;
396 BYTE RegMode
, OpMode
, Increment
, Autoinit
, TrMode
;
397 PBYTE dmabuf
= Buffer
;
401 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
403 SetLastError(ERROR_INVALID_ADDRESS
);
407 pDcp
= &DmaControllers
[iChannel
/ DMA_CONTROLLER_CHANNELS
];
408 Channel
= iChannel
% DMA_CONTROLLER_CHANNELS
; // == (iChannel & 0x03)
410 RegMode
= pDcp
->DmaChannel
[Channel
].Mode
;
412 DPRINT1("DMA_Command = %x length=%d\n", RegMode
, length
);
414 /* Exit if the controller is disabled or the channel is masked */
415 if ((pDcp
->Command
& 0x04) || (pDcp
->Mask
& (1 << Channel
)))
418 OpMode
= (RegMode
& 0xC0) >> 6;
419 Increment
= !(RegMode
& 0x20);
420 Autoinit
= RegMode
& 0x10;
421 TrMode
= (RegMode
& 0x0C) >> 2;
423 /* Process operating mode */
428 DPRINT1("Request Mode - Not Implemented\n");
435 DPRINT1("Block Mode - Not Implemented\n");
439 DPRINT1("Cascade Mode should not be used by regular apps\n");
443 /* Perform one the 4 transfer modes */
447 DPRINT1("DMA Transfer Type Illegal\n");
451 /* Transfer size : 8 bits for channels 0..3, 16 bits for channels 4..7 */
452 Size
= (iChannel
< 4) ? sizeof(BYTE
) : sizeof(WORD
);
454 // FIXME: Handle wrapping?
455 /* Get the number of elements to transfer */
456 ret
= min(pDcp
->DmaChannel
[Channel
].CurrElemCnt
, length
/ Size
);
459 /* 16-bit mode addressing, see: http://wiki.osdev.org/ISA_DMA#16_bit_issues */
460 CurrAddress
= (iChannel
< 4) ? (DmaPageRegisters
[iChannel
].Page
<< 16) | (pDcp
->DmaChannel
[Channel
].CurrAddress
<< 0)
461 : (DmaPageRegisters
[iChannel
].Page
<< 16) | (pDcp
->DmaChannel
[Channel
].CurrAddress
<< 1);
465 /* Verification (no real transfer) */
468 DPRINT1("Verification DMA operation\n");
475 DPRINT1("Perform Write transfer of %d elements (%d bytes) at 0x%x %s with count %x\n",
476 ret
, length
, CurrAddress
, Increment
? "up" : "down", pDcp
->DmaChannel
[Channel
].CurrElemCnt
);
480 MemWrite(CurrAddress
, dmabuf
, length
);
484 for (i
= 0; i
< length
; i
++)
486 MemWrite(CurrAddress
- i
, dmabuf
+ i
, sizeof(BYTE
));
496 DPRINT1("Perform Read transfer of %d elements (%d bytes) at 0x%x %s with count %x\n",
497 ret
, length
, CurrAddress
, Increment
? "up" : "down", pDcp
->DmaChannel
[Channel
].CurrElemCnt
);
501 MemRead(CurrAddress
, dmabuf
, length
);
505 for (i
= 0; i
< length
; i
++)
507 MemRead(CurrAddress
- i
, dmabuf
+ i
, sizeof(BYTE
));
515 /* Update DMA registers */
516 pDcp
->DmaChannel
[Channel
].CurrElemCnt
-= ret
;
518 pDcp
->DmaChannel
[Channel
].CurrAddress
+= ret
;
520 pDcp
->DmaChannel
[Channel
].CurrAddress
-= ret
;
522 /* Check for end of transfer */
523 if (pDcp
->DmaChannel
[Channel
].CurrElemCnt
== 0)
525 DPRINT1("DMA buffer empty\n");
527 /* Update status register of the DMA chip corresponding to the channel */
528 pDcp
->Status
|= 1 << Channel
; /* Mark transfer as finished */
529 pDcp
->Status
&= ~(1 << (Channel
+ 4)); /* Reset soft request if any */
533 /* Reload Current* registers to their initial values */
534 pDcp
->DmaChannel
[Channel
].CurrAddress
= pDcp
->DmaChannel
[Channel
].BaseAddress
;
535 pDcp
->DmaChannel
[Channel
].CurrElemCnt
= pDcp
->DmaChannel
[Channel
].BaseElemCnt
;
539 /* Set the mask bit for the channel */
540 pDcp
->Mask
|= (1 << Channel
);
547 VOID
DmaInitialize(VOID
)
549 /* Register the I/O Ports */
551 /* Channels 0(Reserved)..3 */
552 RegisterIoPort(0x00, NULL
, DmaWritePort
); /* Start Address Register 0 (Reserved) */
553 RegisterIoPort(0x01, NULL
, DmaWritePort
); /* Count Address Register 0 (Reserved) */
554 RegisterIoPort(0x02, NULL
, DmaWritePort
); /* Start Address Register 1 */
555 RegisterIoPort(0x03, NULL
, DmaWritePort
); /* Count Address Register 1 */
556 RegisterIoPort(0x04, NULL
, DmaWritePort
); /* Start Address Register 2 */
557 RegisterIoPort(0x05, NULL
, DmaWritePort
); /* Count Address Register 2 */
558 RegisterIoPort(0x06, NULL
, DmaWritePort
); /* Start Address Register 3 */
559 RegisterIoPort(0x07, NULL
, DmaWritePort
); /* Count Address Register 3 */
561 RegisterIoPort(0x08, DmaReadPort
, DmaWritePort
); /* Status (Read) / Command (Write) Registers */
562 RegisterIoPort(0x09, NULL
, DmaWritePort
); /* Request Register */
563 RegisterIoPort(0x0A, NULL
, DmaWritePort
); /* Single Channel Mask Register */
564 RegisterIoPort(0x0B, NULL
, DmaWritePort
); /* Mode Register */
565 RegisterIoPort(0x0C, NULL
, DmaWritePort
); /* Flip-Flop Reset Register */
566 RegisterIoPort(0x0D, DmaReadPort
, DmaWritePort
); /* Intermediate (Read) / Master Reset (Write) Registers */
567 RegisterIoPort(0x0E, NULL
, DmaWritePort
); /* Mask Reset Register */
568 RegisterIoPort(0x0F, DmaReadPort
, DmaWritePort
); /* Multi-Channel Mask Register */
571 /* Channels 4(Reserved)..7 */
572 RegisterIoPort(0xC0, NULL
, DmaWritePort
); /* Start Address Register 4 (Reserved) */
573 RegisterIoPort(0xC2, NULL
, DmaWritePort
); /* Count Address Register 4 (Reserved) */
574 RegisterIoPort(0xC4, NULL
, DmaWritePort
); /* Start Address Register 5 */
575 RegisterIoPort(0xC6, NULL
, DmaWritePort
); /* Count Address Register 5 */
576 RegisterIoPort(0xC8, NULL
, DmaWritePort
); /* Start Address Register 6 */
577 RegisterIoPort(0xCA, NULL
, DmaWritePort
); /* Count Address Register 6 */
578 RegisterIoPort(0xCC, NULL
, DmaWritePort
); /* Start Address Register 7 */
579 RegisterIoPort(0xCE, NULL
, DmaWritePort
); /* Count Address Register 7 */
581 RegisterIoPort(0xD0, DmaReadPort
, DmaWritePort
); /* Status (Read) / Command (Write) Registers */
582 RegisterIoPort(0xD2, NULL
, DmaWritePort
); /* Request Register */
583 RegisterIoPort(0xD4, NULL
, DmaWritePort
); /* Single Channel Mask Register */
584 RegisterIoPort(0xD6, NULL
, DmaWritePort
); /* Mode Register */
585 RegisterIoPort(0xD8, NULL
, DmaWritePort
); /* Flip-Flop Reset Register */
586 RegisterIoPort(0xDA, DmaReadPort
, DmaWritePort
); /* Intermediate (Read) / Master Reset (Write) Registers */
587 RegisterIoPort(0xDC, NULL
, DmaWritePort
); /* Mask Reset Register */
588 RegisterIoPort(0xDE, DmaReadPort
, DmaWritePort
); /* Multi-Channel Mask Register */
591 /* Channels Page Address Registers */
592 RegisterIoPort(0x87, DmaPageReadPort
, DmaPageWritePort
); /* Channel 0 (Reserved) */
593 RegisterIoPort(0x83, DmaPageReadPort
, DmaPageWritePort
); /* Channel 1 */
594 RegisterIoPort(0x81, DmaPageReadPort
, DmaPageWritePort
); /* Channel 2 */
595 RegisterIoPort(0x82, DmaPageReadPort
, DmaPageWritePort
); /* Channel 3 */
596 RegisterIoPort(0x8F, DmaPageReadPort
, DmaPageWritePort
); /* Channel 4 (Reserved) */
597 RegisterIoPort(0x8B, DmaPageReadPort
, DmaPageWritePort
); /* Channel 5 */
598 RegisterIoPort(0x89, DmaPageReadPort
, DmaPageWritePort
); /* Channel 6 */
599 RegisterIoPort(0x8A, DmaPageReadPort
, DmaPageWritePort
); /* Channel 7 */
606 VDDRequestDMA(IN HANDLE hVdd
,
611 UNREFERENCED_PARAMETER(hVdd
);
613 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
615 SetLastError(ERROR_INVALID_ADDRESS
);
620 * We assume success first. If something fails,
621 * DmaRequest sets an adequate last error.
623 SetLastError(ERROR_SUCCESS
);
625 return DmaRequest(iChannel
, Buffer
, length
);
630 VDDQueryDMA(IN HANDLE hVdd
,
632 IN PVDD_DMA_INFO pDmaInfo
)
634 PDMA_CONTROLLER pDcp
;
637 UNREFERENCED_PARAMETER(hVdd
);
639 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
641 SetLastError(ERROR_INVALID_ADDRESS
);
645 pDcp
= &DmaControllers
[iChannel
/ DMA_CONTROLLER_CHANNELS
];
646 Channel
= iChannel
% DMA_CONTROLLER_CHANNELS
;
648 pDmaInfo
->addr
= pDcp
->DmaChannel
[Channel
].CurrAddress
;
649 pDmaInfo
->count
= pDcp
->DmaChannel
[Channel
].CurrElemCnt
;
651 pDmaInfo
->page
= DmaPageRegisters
[iChannel
].Page
;
652 pDmaInfo
->status
= pDcp
->Status
;
653 pDmaInfo
->mode
= pDcp
->DmaChannel
[Channel
].Mode
;
654 pDmaInfo
->mask
= pDcp
->Mask
;
661 VDDSetDMA(IN HANDLE hVdd
,
664 IN PVDD_DMA_INFO pDmaInfo
)
666 PDMA_CONTROLLER pDcp
;
669 UNREFERENCED_PARAMETER(hVdd
);
671 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
673 SetLastError(ERROR_INVALID_ADDRESS
);
677 pDcp
= &DmaControllers
[iChannel
/ DMA_CONTROLLER_CHANNELS
];
678 Channel
= iChannel
% DMA_CONTROLLER_CHANNELS
;
680 if (fDMA
& VDD_DMA_ADDR
)
681 pDcp
->DmaChannel
[Channel
].CurrAddress
= pDmaInfo
->addr
;
683 if (fDMA
& VDD_DMA_COUNT
)
684 pDcp
->DmaChannel
[Channel
].CurrElemCnt
= pDmaInfo
->count
;
686 if (fDMA
& VDD_DMA_PAGE
)
687 DmaPageRegisters
[iChannel
].Page
= pDmaInfo
->page
;
689 if (fDMA
& VDD_DMA_STATUS
)
690 pDcp
->Status
= pDmaInfo
->status
;