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)
15 /* PRIVATE VARIABLES **********************************************************/
18 * DMA Controller 0 (Channels 0..3): Slave controller
19 * DMA Controller 1 (Channels 4..7): Master controller
21 static DMA_CONTROLLER DmaControllers
[DMA_CONTROLLERS
];
23 /* External page registers for each channel of the two DMA controllers */
24 static DMA_PAGE_REGISTER DmaPageRegisters
[DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
];
26 /* PRIVATE FUNCTIONS **********************************************************/
28 #define READ_ADDR(CtrlIndex, ChanIndex, Data) \
31 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrAddress + \
32 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
33 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
36 #define READ_CNT(CtrlIndex, ChanIndex, Data) \
39 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
40 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
41 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
44 static BYTE WINAPI
DmaReadPort(USHORT Port
)
46 BYTE ReadValue
= 0xFF;
48 DPRINT1("DmaReadPort(Port = 0x%04X)\n", Port
);
52 /* Current Address Registers */
55 READ_ADDR(0, 0, ReadValue
);
58 READ_ADDR(0, 1, ReadValue
);
61 READ_ADDR(0, 2, ReadValue
);
64 READ_ADDR(0, 3, ReadValue
);
67 READ_ADDR(1, 0, ReadValue
);
70 READ_ADDR(1, 1, ReadValue
);
73 READ_ADDR(1, 2, ReadValue
);
76 READ_ADDR(1, 3, ReadValue
);
80 /* Current Count Registers */
83 READ_CNT(0, 0, ReadValue
);
86 READ_CNT(0, 1, ReadValue
);
89 READ_CNT(0, 2, ReadValue
);
92 READ_CNT(0, 3, ReadValue
);
95 READ_CNT(1, 0, ReadValue
);
98 READ_CNT(1, 1, ReadValue
);
101 READ_CNT(1, 2, ReadValue
);
104 READ_CNT(1, 3, ReadValue
);
108 /* Status Registers */
111 return DmaControllers
[0].Status
;
113 return DmaControllers
[1].Status
;
116 /* DMA Intermediate (Temporary) Registers */
119 return DmaControllers
[0].TempReg
;
121 return DmaControllers
[1].TempReg
;
124 /* Multi-Channel Mask Registers */
127 return DmaControllers
[0].Mask
;
129 return DmaControllers
[1].Mask
;
136 #define WRITE_ADDR(CtrlIndex, ChanIndex, Data) \
138 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseAddress + \
139 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
140 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrAddress + \
141 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
142 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
145 #define WRITE_CNT(CtrlIndex, ChanIndex, Data) \
147 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].BaseElemCnt + \
148 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
149 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
150 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)) = (Data); \
151 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
154 static VOID WINAPI
DmaWritePort(USHORT Port
, BYTE Data
)
156 DPRINT1("DmaWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port
, Data
);
160 /* Start Address Registers */
163 WRITE_ADDR(0, 0, Data
);
166 WRITE_ADDR(0, 1, Data
);
169 WRITE_ADDR(0, 2, Data
);
172 WRITE_ADDR(0, 3, Data
);
175 WRITE_ADDR(1, 0, Data
);
178 WRITE_ADDR(1, 1, Data
);
181 WRITE_ADDR(1, 2, Data
);
184 WRITE_ADDR(1, 3, Data
);
188 /* Base Count Registers */
191 WRITE_CNT(0, 0, Data
);
194 WRITE_CNT(0, 1, Data
);
197 WRITE_CNT(0, 2, Data
);
200 WRITE_CNT(0, 3, Data
);
203 WRITE_CNT(1, 0, Data
);
206 WRITE_CNT(1, 1, Data
);
209 WRITE_CNT(1, 2, Data
);
212 WRITE_CNT(1, 3, Data
);
216 /* Command Registers */
219 DmaControllers
[0].Command
= Data
;
222 DmaControllers
[1].Command
= Data
;
229 DmaControllers
[0].DmaChannel
[Data
& 0x03].Mode
= (Data
& ~0x03);
232 DmaControllers
[1].DmaChannel
[Data
& 0x03].Mode
= (Data
& ~0x03);
236 /* Request Registers */
239 DmaControllers
[0].Request
= Data
;
242 DmaControllers
[1].Request
= Data
;
246 /* Single Channel Mask Registers */
250 DmaControllers
[0].Mask
|= (1 << (Data
& 0x03));
252 DmaControllers
[0].Mask
&= ~(1 << (Data
& 0x03));
256 DmaControllers
[1].Mask
|= (1 << (Data
& 0x03));
258 DmaControllers
[1].Mask
&= ~(1 << (Data
& 0x03));
262 /* Multi-Channel Mask Registers */
265 DmaControllers
[0].Mask
= (Data
& 0x0F);
268 DmaControllers
[1].Mask
= (Data
& 0x0F);
272 /* Flip-Flop Reset */
275 DmaControllers
[0].FlipFlop
= 0;
278 DmaControllers
[1].FlipFlop
= 0;
282 /* DMA Master Reset Registers */
285 DmaControllers
[0].Command
= 0x00;
286 DmaControllers
[0].Status
= 0x00;
287 DmaControllers
[0].Request
= 0x00;
288 DmaControllers
[0].TempReg
= 0x00;
289 DmaControllers
[0].FlipFlop
= 0;
290 DmaControllers
[0].Mask
= 0x0F;
293 DmaControllers
[1].Command
= 0x00;
294 DmaControllers
[1].Status
= 0x00;
295 DmaControllers
[1].Request
= 0x00;
296 DmaControllers
[1].TempReg
= 0x00;
297 DmaControllers
[1].FlipFlop
= 0;
298 DmaControllers
[1].Mask
= 0x0F;
302 /* Mask Reset Registers */
305 DmaControllers
[0].Mask
= 0x00;
308 DmaControllers
[1].Mask
= 0x00;
314 /* Page Address Registers */
316 static BYTE WINAPI
DmaPageReadPort(USHORT Port
)
318 DPRINT1("DmaPageReadPort(Port = 0x%04X)\n", Port
);
323 return DmaPageRegisters
[0].Page
;
325 return DmaPageRegisters
[1].Page
;
327 return DmaPageRegisters
[2].Page
;
329 return DmaPageRegisters
[3].Page
;
331 return DmaPageRegisters
[4].Page
;
333 return DmaPageRegisters
[5].Page
;
335 return DmaPageRegisters
[6].Page
;
337 return DmaPageRegisters
[7].Page
;
343 static VOID WINAPI
DmaPageWritePort(USHORT Port
, BYTE Data
)
345 DPRINT1("DmaPageWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port
, Data
);
350 DmaPageRegisters
[0].Page
= Data
;
353 DmaPageRegisters
[1].Page
= Data
;
356 DmaPageRegisters
[2].Page
= Data
;
359 DmaPageRegisters
[3].Page
= Data
;
362 DmaPageRegisters
[4].Page
= Data
;
365 DmaPageRegisters
[5].Page
= Data
;
368 DmaPageRegisters
[6].Page
= Data
;
371 DmaPageRegisters
[7].Page
= Data
;
376 /* PUBLIC FUNCTIONS ***********************************************************/
378 DWORD
DmaRequest(IN WORD iChannel
,
383 * NOTE: This function is adapted from Wine's krnl386.exe,
384 * DMA emulation by Christian Costa.
386 PDMA_CONTROLLER pDcp
;
389 DWORD i
, Size
, ret
= 0;
390 BYTE RegMode
, OpMode
, Increment
, Autoinit
, TrMode
;
391 PBYTE dmabuf
= Buffer
;
395 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
397 SetLastError(ERROR_INVALID_ADDRESS
);
401 pDcp
= &DmaControllers
[iChannel
/ DMA_CONTROLLER_CHANNELS
];
402 Channel
= iChannel
% DMA_CONTROLLER_CHANNELS
; // == (iChannel & 0x03)
404 RegMode
= pDcp
->DmaChannel
[Channel
].Mode
;
406 DPRINT1("DMA_Command = %x length=%d\n", RegMode
, length
);
408 /* Exit if the controller is disabled or the channel is masked */
409 if ((pDcp
->Command
& 0x04) || (pDcp
->Mask
& (1 << Channel
)))
412 OpMode
= (RegMode
& 0xC0) >> 6;
413 Increment
= !(RegMode
& 0x20);
414 Autoinit
= RegMode
& 0x10;
415 TrMode
= (RegMode
& 0x0C) >> 2;
417 /* Process operating mode */
422 DPRINT1("Request Mode - Not Implemented\n");
429 DPRINT1("Block Mode - Not Implemented\n");
433 DPRINT1("Cascade Mode should not be used by regular apps\n");
437 /* Perform one the 4 transfer modes */
441 DPRINT1("DMA Transfer Type Illegal\n");
445 /* Transfer size : 8 bits for channels 0..3, 16 bits for channels 4..7 */
446 Size
= (iChannel
< 4) ? sizeof(BYTE
) : sizeof(WORD
);
448 // FIXME: Handle wrapping?
449 /* Get the number of elements to transfer */
450 ret
= min(pDcp
->DmaChannel
[Channel
].CurrElemCnt
, length
/ Size
);
453 /* 16-bit mode addressing, see: http://wiki.osdev.org/ISA_DMA#16_bit_issues */
454 CurrAddress
= (iChannel
< 4) ? (DmaPageRegisters
[iChannel
].Page
<< 16) | ((pDcp
->DmaChannel
[Channel
].CurrAddress
<< 0) & 0xFFFF)
455 : (DmaPageRegisters
[iChannel
].Page
<< 16) | ((pDcp
->DmaChannel
[Channel
].CurrAddress
<< 1) & 0xFFFF);
459 /* Verification (no real transfer) */
462 DPRINT1("Verification DMA operation\n");
469 DPRINT1("Perform Write transfer of %d elements (%d bytes) at 0x%x %s with count %x\n",
470 ret
, length
, CurrAddress
, Increment
? "up" : "down", pDcp
->DmaChannel
[Channel
].CurrElemCnt
);
474 EmulatorWriteMemory(&EmulatorContext
, CurrAddress
, dmabuf
, length
);
478 for (i
= 0; i
< length
; i
++)
480 EmulatorWriteMemory(&EmulatorContext
, CurrAddress
- i
, dmabuf
+ i
, sizeof(BYTE
));
490 DPRINT1("Perform Read transfer of %d elements (%d bytes) at 0x%x %s with count %x\n",
491 ret
, length
, CurrAddress
, Increment
? "up" : "down", pDcp
->DmaChannel
[Channel
].CurrElemCnt
);
495 EmulatorReadMemory(&EmulatorContext
, CurrAddress
, dmabuf
, length
);
499 for (i
= 0; i
< length
; i
++)
501 EmulatorReadMemory(&EmulatorContext
, CurrAddress
- i
, dmabuf
+ i
, sizeof(BYTE
));
509 /* Update DMA registers */
510 pDcp
->DmaChannel
[Channel
].CurrElemCnt
-= ret
;
512 pDcp
->DmaChannel
[Channel
].CurrAddress
+= ret
;
514 pDcp
->DmaChannel
[Channel
].CurrAddress
-= ret
;
516 /* Check for end of transfer */
517 if (pDcp
->DmaChannel
[Channel
].CurrElemCnt
== 0)
519 DPRINT1("DMA buffer empty\n");
521 /* Update status register of the DMA chip corresponding to the channel */
522 pDcp
->Status
|= 1 << Channel
; /* Mark transfer as finished */
523 pDcp
->Status
&= ~(1 << (Channel
+ 4)); /* Reset soft request if any */
527 /* Reload Current* registers to their initial values */
528 pDcp
->DmaChannel
[Channel
].CurrAddress
= pDcp
->DmaChannel
[Channel
].BaseAddress
;
529 pDcp
->DmaChannel
[Channel
].CurrElemCnt
= pDcp
->DmaChannel
[Channel
].BaseElemCnt
;
533 /* Set the mask bit for the channel */
534 pDcp
->Mask
|= (1 << Channel
);
541 VOID
DmaInitialize(VOID
)
543 /* Register the I/O Ports */
545 /* Channels 0(Reserved)..3 */
546 RegisterIoPort(0x00, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 0 (Reserved) */
547 RegisterIoPort(0x01, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 0 (Reserved) */
548 RegisterIoPort(0x02, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 1 */
549 RegisterIoPort(0x03, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 1 */
550 RegisterIoPort(0x04, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 2 */
551 RegisterIoPort(0x05, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 2 */
552 RegisterIoPort(0x06, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 3 */
553 RegisterIoPort(0x07, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 3 */
555 RegisterIoPort(0x08, DmaReadPort
, DmaWritePort
); /* Status (Read) / Command (Write) Registers */
556 RegisterIoPort(0x09, NULL
, DmaWritePort
); /* Request Register */
557 RegisterIoPort(0x0A, NULL
, DmaWritePort
); /* Single Channel Mask Register */
558 RegisterIoPort(0x0B, NULL
, DmaWritePort
); /* Mode Register */
559 RegisterIoPort(0x0C, NULL
, DmaWritePort
); /* Flip-Flop Reset Register */
560 RegisterIoPort(0x0D, DmaReadPort
, DmaWritePort
); /* Intermediate (Read) / Master Reset (Write) Registers */
561 RegisterIoPort(0x0E, NULL
, DmaWritePort
); /* Mask Reset Register */
562 RegisterIoPort(0x0F, DmaReadPort
, DmaWritePort
); /* Multi-Channel Mask Register */
565 /* Channels 4(Reserved)..7 */
566 RegisterIoPort(0xC0, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 4 (Reserved) */
567 RegisterIoPort(0xC2, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 4 (Reserved) */
568 RegisterIoPort(0xC4, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 5 */
569 RegisterIoPort(0xC6, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 5 */
570 RegisterIoPort(0xC8, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 6 */
571 RegisterIoPort(0xCA, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 6 */
572 RegisterIoPort(0xCC, DmaReadPort
, DmaWritePort
); /* Current(R) / Start(W) Address Register 7 */
573 RegisterIoPort(0xCE, DmaReadPort
, DmaWritePort
); /* Current(R) / Base (W) Count Register 7 */
575 RegisterIoPort(0xD0, DmaReadPort
, DmaWritePort
); /* Status (Read) / Command (Write) Registers */
576 RegisterIoPort(0xD2, NULL
, DmaWritePort
); /* Request Register */
577 RegisterIoPort(0xD4, NULL
, DmaWritePort
); /* Single Channel Mask Register */
578 RegisterIoPort(0xD6, NULL
, DmaWritePort
); /* Mode Register */
579 RegisterIoPort(0xD8, NULL
, DmaWritePort
); /* Flip-Flop Reset Register */
580 RegisterIoPort(0xDA, DmaReadPort
, DmaWritePort
); /* Intermediate (Read) / Master Reset (Write) Registers */
581 RegisterIoPort(0xDC, NULL
, DmaWritePort
); /* Mask Reset Register */
582 RegisterIoPort(0xDE, DmaReadPort
, DmaWritePort
); /* Multi-Channel Mask Register */
585 /* Channels Page Address Registers */
586 RegisterIoPort(0x87, DmaPageReadPort
, DmaPageWritePort
); /* Channel 0 (Reserved) */
587 RegisterIoPort(0x83, DmaPageReadPort
, DmaPageWritePort
); /* Channel 1 */
588 RegisterIoPort(0x81, DmaPageReadPort
, DmaPageWritePort
); /* Channel 2 */
589 RegisterIoPort(0x82, DmaPageReadPort
, DmaPageWritePort
); /* Channel 3 */
590 RegisterIoPort(0x8F, DmaPageReadPort
, DmaPageWritePort
); /* Channel 4 (Reserved) */
591 RegisterIoPort(0x8B, DmaPageReadPort
, DmaPageWritePort
); /* Channel 5 */
592 RegisterIoPort(0x89, DmaPageReadPort
, DmaPageWritePort
); /* Channel 6 */
593 RegisterIoPort(0x8A, DmaPageReadPort
, DmaPageWritePort
); /* Channel 7 */
600 VDDRequestDMA(IN HANDLE hVdd
,
605 UNREFERENCED_PARAMETER(hVdd
);
607 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
609 SetLastError(ERROR_INVALID_ADDRESS
);
614 * We assume success first. If something fails,
615 * DmaRequest sets an adequate last error.
617 SetLastError(ERROR_SUCCESS
);
619 return DmaRequest(iChannel
, Buffer
, length
);
624 VDDQueryDMA(IN HANDLE hVdd
,
626 IN PVDD_DMA_INFO pDmaInfo
)
628 PDMA_CONTROLLER pDcp
;
631 UNREFERENCED_PARAMETER(hVdd
);
633 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
635 SetLastError(ERROR_INVALID_ADDRESS
);
639 pDcp
= &DmaControllers
[iChannel
/ DMA_CONTROLLER_CHANNELS
];
640 Channel
= iChannel
% DMA_CONTROLLER_CHANNELS
;
642 pDmaInfo
->addr
= pDcp
->DmaChannel
[Channel
].CurrAddress
;
643 pDmaInfo
->count
= pDcp
->DmaChannel
[Channel
].CurrElemCnt
;
645 pDmaInfo
->page
= DmaPageRegisters
[iChannel
].Page
;
646 pDmaInfo
->status
= pDcp
->Status
;
647 pDmaInfo
->mode
= pDcp
->DmaChannel
[Channel
].Mode
;
648 pDmaInfo
->mask
= pDcp
->Mask
;
655 VDDSetDMA(IN HANDLE hVdd
,
658 IN PVDD_DMA_INFO pDmaInfo
)
660 PDMA_CONTROLLER pDcp
;
663 UNREFERENCED_PARAMETER(hVdd
);
665 if (iChannel
>= DMA_CONTROLLERS
* DMA_CONTROLLER_CHANNELS
)
667 SetLastError(ERROR_INVALID_ADDRESS
);
671 pDcp
= &DmaControllers
[iChannel
/ DMA_CONTROLLER_CHANNELS
];
672 Channel
= iChannel
% DMA_CONTROLLER_CHANNELS
;
674 if (fDMA
& VDD_DMA_ADDR
)
675 pDcp
->DmaChannel
[Channel
].CurrAddress
= pDmaInfo
->addr
;
677 if (fDMA
& VDD_DMA_COUNT
)
678 pDcp
->DmaChannel
[Channel
].CurrElemCnt
= pDmaInfo
->count
;
680 if (fDMA
& VDD_DMA_PAGE
)
681 DmaPageRegisters
[iChannel
].Page
= pDmaInfo
->page
;
683 if (fDMA
& VDD_DMA_STATUS
)
684 pDcp
->Status
= pDmaInfo
->status
;