[NTVDM]: Implement support for DMA transfers, single-mode only for now, and fix its...
[reactos.git] / reactos / subsystems / mvdm / ntvdm / hardware / dma.c
1 /*
2 * COPYRIGHT: GPL - See COPYING in the top level directory
3 * PROJECT: ReactOS Virtual DOS Machine
4 * FILE: 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)
8 */
9
10 /* INCLUDES *******************************************************************/
11
12 #define NDEBUG
13
14 #include "ntvdm.h"
15 #include "emulator.h"
16 #include "dma.h"
17
18 #include "io.h"
19 #include "memory.h"
20
21 /* PRIVATE VARIABLES **********************************************************/
22
23 /*
24 * DMA Controller 0 (Channels 0..3): Slave controller
25 * DMA Controller 1 (Channels 4..7): Master controller
26 */
27 static DMA_CONTROLLER DmaControllers[DMA_CONTROLLERS];
28
29 /* External page registers for each channel of the two DMA controllers */
30 static DMA_PAGE_REGISTER DmaPageRegisters[DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS];
31
32 /* PRIVATE FUNCTIONS **********************************************************/
33
34 #define READ_ADDR(CtrlIndex, ChanIndex, Data) \
35 do { \
36 (Data) = \
37 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrAddress + \
38 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
39 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
40 } while(0)
41
42 #define READ_CNT(CtrlIndex, ChanIndex, Data) \
43 do { \
44 (Data) = \
45 *((PBYTE)&DmaControllers[(CtrlIndex)].DmaChannel[(ChanIndex)].CurrElemCnt + \
46 (DmaControllers[(CtrlIndex)].FlipFlop & 0x01)); \
47 DmaControllers[(CtrlIndex)].FlipFlop ^= 1; \
48 } while(0)
49
50 static BYTE WINAPI DmaReadPort(USHORT Port)
51 {
52 BYTE ReadValue = 0xFF;
53
54 DPRINT1("DmaReadPort(Port = 0x%04X)\n", Port);
55
56 switch (Port)
57 {
58 /* Start Address Registers */
59 {
60 case 0x00:
61 READ_ADDR(0, 0, ReadValue);
62 return ReadValue;
63 case 0x02:
64 READ_ADDR(0, 1, ReadValue);
65 return ReadValue;
66 case 0x04:
67 READ_ADDR(0, 2, ReadValue);
68 return ReadValue;
69 case 0x06:
70 READ_ADDR(0, 3, ReadValue);
71 return ReadValue;
72 case 0xC0:
73 READ_ADDR(1, 0, ReadValue);
74 return ReadValue;
75 case 0xC4:
76 READ_ADDR(1, 1, ReadValue);
77 return ReadValue;
78 case 0xC8:
79 READ_ADDR(1, 2, ReadValue);
80 return ReadValue;
81 case 0xCC:
82 READ_ADDR(1, 3, ReadValue);
83 return ReadValue;
84 }
85
86 /* Count Address Registers */
87 {
88 case 0x01:
89 READ_CNT(0, 0, ReadValue);
90 return ReadValue;
91 case 0x03:
92 READ_CNT(0, 1, ReadValue);
93 return ReadValue;
94 case 0x05:
95 READ_CNT(0, 2, ReadValue);
96 return ReadValue;
97 case 0x07:
98 READ_CNT(0, 3, ReadValue);
99 return ReadValue;
100 case 0xC2:
101 READ_CNT(1, 0, ReadValue);
102 return ReadValue;
103 case 0xC6:
104 READ_CNT(1, 1, ReadValue);
105 return ReadValue;
106 case 0xCA:
107 READ_CNT(1, 2, ReadValue);
108 return ReadValue;
109 case 0xCE:
110 READ_CNT(1, 3, ReadValue);
111 return ReadValue;
112 }
113
114 /* Status Registers */
115 {
116 case 0x08:
117 return DmaControllers[0].Status;
118 case 0xD0:
119 return DmaControllers[1].Status;
120 }
121
122 /* DMA Intermediate (Temporary) Registers */
123 {
124 case 0x0D:
125 return DmaControllers[0].TempReg;
126 case 0xDA:
127 return DmaControllers[1].TempReg;
128 }
129
130 /* Multi-Channel Mask Registers */
131 {
132 case 0x0F:
133 return DmaControllers[0].Mask;
134 case 0xDE:
135 return DmaControllers[1].Mask;
136 }
137 }
138
139 return 0x00;
140 }
141
142 #define WRITE_ADDR(CtrlIndex, ChanIndex, Data) \
143 do { \
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; \
149 } while(0)
150
151 #define WRITE_CNT(CtrlIndex, ChanIndex, Data) \
152 do { \
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; \
158 } while(0)
159
160 static VOID WINAPI DmaWritePort(USHORT Port, BYTE Data)
161 {
162 DPRINT1("DmaWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port, Data);
163
164 switch (Port)
165 {
166 /* Start Address Registers */
167 {
168 case 0x00:
169 WRITE_ADDR(0, 0, Data);
170 break;
171 case 0x02:
172 WRITE_ADDR(0, 1, Data);
173 break;
174 case 0x04:
175 WRITE_ADDR(0, 2, Data);
176 break;
177 case 0x06:
178 WRITE_ADDR(0, 3, Data);
179 break;
180 case 0xC0:
181 WRITE_ADDR(1, 0, Data);
182 break;
183 case 0xC4:
184 WRITE_ADDR(1, 1, Data);
185 break;
186 case 0xC8:
187 WRITE_ADDR(1, 2, Data);
188 break;
189 case 0xCC:
190 WRITE_ADDR(1, 3, Data);
191 break;
192 }
193
194 /* Count Address Registers */
195 {
196 case 0x01:
197 WRITE_CNT(0, 0, Data);
198 break;
199 case 0x03:
200 WRITE_CNT(0, 1, Data);
201 break;
202 case 0x05:
203 WRITE_CNT(0, 2, Data);
204 break;
205 case 0x07:
206 WRITE_CNT(0, 3, Data);
207 break;
208 case 0xC2:
209 WRITE_CNT(1, 0, Data);
210 break;
211 case 0xC6:
212 WRITE_CNT(1, 1, Data);
213 break;
214 case 0xCA:
215 WRITE_CNT(1, 2, Data);
216 break;
217 case 0xCE:
218 WRITE_CNT(1, 3, Data);
219 break;
220 }
221
222 /* Command Registers */
223 {
224 case 0x08:
225 DmaControllers[0].Command = Data;
226 break;
227 case 0xD0:
228 DmaControllers[1].Command = Data;
229 break;
230 }
231
232 /* Mode Registers */
233 {
234 case 0x0B:
235 DmaControllers[0].DmaChannel[Data & 0x03].Mode = (Data & ~0x03);
236 break;
237 case 0xD6:
238 DmaControllers[1].DmaChannel[Data & 0x03].Mode = (Data & ~0x03);
239 break;
240 }
241
242 /* Request Registers */
243 {
244 case 0x09:
245 DmaControllers[0].Request = Data;
246 break;
247 case 0xD2:
248 DmaControllers[1].Request = Data;
249 break;
250 }
251
252 /* Single Channel Mask Registers */
253 {
254 case 0x0A:
255 if (Data & 0x04)
256 DmaControllers[0].Mask |= (1 << (Data & 0x03));
257 else
258 DmaControllers[0].Mask &= ~(1 << (Data & 0x03));
259 break;
260 case 0xD4:
261 if (Data & 0x04)
262 DmaControllers[1].Mask |= (1 << (Data & 0x03));
263 else
264 DmaControllers[1].Mask &= ~(1 << (Data & 0x03));
265 break;
266 }
267
268 /* Multi-Channel Mask Registers */
269 {
270 case 0x0F:
271 DmaControllers[0].Mask = (Data & 0x0F);
272 break;
273 case 0xDE:
274 DmaControllers[1].Mask = (Data & 0x0F);
275 break;
276 }
277
278 /* Flip-Flop Reset */
279 {
280 case 0x0C:
281 DmaControllers[0].FlipFlop = 0;
282 break;
283 case 0xD8:
284 DmaControllers[1].FlipFlop = 0;
285 break;
286 }
287
288 /* DMA Master Reset Registers */
289 {
290 case 0x0D:
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;
297 break;
298 case 0xDA:
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;
305 break;
306 }
307
308 /* Mask Reset Registers */
309 {
310 case 0x0E:
311 DmaControllers[0].Mask = 0x00;
312 break;
313 case 0xDC:
314 DmaControllers[1].Mask = 0x00;
315 break;
316 }
317 }
318 }
319
320 /* Page Address Registers */
321
322 static BYTE WINAPI DmaPageReadPort(USHORT Port)
323 {
324 DPRINT1("DmaPageReadPort(Port = 0x%04X)\n", Port);
325
326 switch (Port)
327 {
328 case 0x87:
329 return DmaPageRegisters[0].Page;
330 case 0x83:
331 return DmaPageRegisters[1].Page;
332 case 0x81:
333 return DmaPageRegisters[2].Page;
334 case 0x82:
335 return DmaPageRegisters[3].Page;
336 case 0x8F:
337 return DmaPageRegisters[4].Page;
338 case 0x8B:
339 return DmaPageRegisters[5].Page;
340 case 0x89:
341 return DmaPageRegisters[6].Page;
342 case 0x8A:
343 return DmaPageRegisters[7].Page;
344 }
345
346 return 0x00;
347 }
348
349 static VOID WINAPI DmaPageWritePort(USHORT Port, BYTE Data)
350 {
351 DPRINT1("DmaPageWritePort(Port = 0x%04X, Data = 0x%02X)\n", Port, Data);
352
353 switch (Port)
354 {
355 case 0x87:
356 DmaPageRegisters[0].Page = Data;
357 break;
358 case 0x83:
359 DmaPageRegisters[1].Page = Data;
360 break;
361 case 0x81:
362 DmaPageRegisters[2].Page = Data;
363 break;
364 case 0x82:
365 DmaPageRegisters[3].Page = Data;
366 break;
367 case 0x8F:
368 DmaPageRegisters[4].Page = Data;
369 break;
370 case 0x8B:
371 DmaPageRegisters[5].Page = Data;
372 break;
373 case 0x89:
374 DmaPageRegisters[6].Page = Data;
375 break;
376 case 0x8A:
377 DmaPageRegisters[7].Page = Data;
378 break;
379 }
380 }
381
382 /* PUBLIC FUNCTIONS ***********************************************************/
383
384 DWORD DmaRequest(IN WORD iChannel,
385 IN OUT PVOID Buffer,
386 IN DWORD length)
387 {
388 /*
389 * NOTE: This function is adapted from Wine's krnl386.exe,
390 * DMA emulation by Christian Costa.
391 */
392 PDMA_CONTROLLER pDcp;
393 WORD Channel;
394
395 DWORD i, Size, ret = 0;
396 BYTE RegMode, OpMode, Increment, Autoinit, TrMode;
397 PBYTE dmabuf = Buffer;
398
399 ULONG CurrAddress;
400
401 if (iChannel >= DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS)
402 {
403 SetLastError(ERROR_INVALID_ADDRESS);
404 return 0;
405 }
406
407 pDcp = &DmaControllers[iChannel / DMA_CONTROLLER_CHANNELS];
408 Channel = iChannel % DMA_CONTROLLER_CHANNELS; // == (iChannel & 0x03)
409
410 RegMode = pDcp->DmaChannel[Channel].Mode;
411
412 DPRINT1("DMA_Command = %x length=%d\n", RegMode, length);
413
414 /* Exit if the controller is disabled or the channel is masked */
415 if ((pDcp->Command & 0x04) || (pDcp->Mask & (1 << Channel)))
416 return 0;
417
418 OpMode = (RegMode & 0xC0) >> 6;
419 Increment = !(RegMode & 0x20);
420 Autoinit = RegMode & 0x10;
421 TrMode = (RegMode & 0x0C) >> 2;
422
423 /* Process operating mode */
424 switch (OpMode)
425 {
426 case 0:
427 /* Request mode */
428 DPRINT1("Request Mode - Not Implemented\n");
429 return 0;
430 case 1:
431 /* Single Mode */
432 break;
433 case 2:
434 /* Request mode */
435 DPRINT1("Block Mode - Not Implemented\n");
436 return 0;
437 case 3:
438 /* Cascade Mode */
439 DPRINT1("Cascade Mode should not be used by regular apps\n");
440 return 0;
441 }
442
443 /* Perform one the 4 transfer modes */
444 if (TrMode == 4)
445 {
446 /* Illegal */
447 DPRINT1("DMA Transfer Type Illegal\n");
448 return 0;
449 }
450
451 /* Transfer size : 8 bits for channels 0..3, 16 bits for channels 4..7 */
452 Size = (iChannel < 4) ? sizeof(BYTE) : sizeof(WORD);
453
454 // FIXME: Handle wrapping?
455 /* Get the number of elements to transfer */
456 ret = min(pDcp->DmaChannel[Channel].CurrElemCnt, length / Size);
457 length = ret * Size;
458
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);
462
463 switch (TrMode)
464 {
465 /* Verification (no real transfer) */
466 case 0:
467 {
468 DPRINT1("Verification DMA operation\n");
469 break;
470 }
471
472 /* Write */
473 case 1:
474 {
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);
477
478 if (Increment)
479 {
480 MemWrite(CurrAddress, dmabuf, length);
481 }
482 else
483 {
484 for (i = 0; i < length; i++)
485 {
486 MemWrite(CurrAddress - i, dmabuf + i, sizeof(BYTE));
487 }
488 }
489
490 break;
491 }
492
493 /* Read */
494 case 2:
495 {
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);
498
499 if (Increment)
500 {
501 MemRead(CurrAddress, dmabuf, length);
502 }
503 else
504 {
505 for (i = 0; i < length; i++)
506 {
507 MemRead(CurrAddress - i, dmabuf + i, sizeof(BYTE));
508 }
509 }
510
511 break;
512 }
513 }
514
515 /* Update DMA registers */
516 pDcp->DmaChannel[Channel].CurrElemCnt -= ret;
517 if (Increment)
518 pDcp->DmaChannel[Channel].CurrAddress += ret;
519 else
520 pDcp->DmaChannel[Channel].CurrAddress -= ret;
521
522 /* Check for end of transfer */
523 if (pDcp->DmaChannel[Channel].CurrElemCnt == 0)
524 {
525 DPRINT1("DMA buffer empty\n");
526
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 */
530
531 if (Autoinit)
532 {
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;
536 }
537 else
538 {
539 /* Set the mask bit for the channel */
540 pDcp->Mask |= (1 << Channel);
541 }
542 }
543
544 return length;
545 }
546
547 VOID DmaInitialize(VOID)
548 {
549 /* Register the I/O Ports */
550
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 */
560
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 */
569
570
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 */
580
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 */
589
590
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 */
600 }
601
602
603
604 DWORD
605 WINAPI
606 VDDRequestDMA(IN HANDLE hVdd,
607 IN WORD iChannel,
608 IN OUT PVOID Buffer,
609 IN DWORD length)
610 {
611 UNREFERENCED_PARAMETER(hVdd);
612
613 if (iChannel >= DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS)
614 {
615 SetLastError(ERROR_INVALID_ADDRESS);
616 return FALSE;
617 }
618
619 /*
620 * We assume success first. If something fails,
621 * DmaRequest sets an adequate last error.
622 */
623 SetLastError(ERROR_SUCCESS);
624
625 return DmaRequest(iChannel, Buffer, length);
626 }
627
628 BOOL
629 WINAPI
630 VDDQueryDMA(IN HANDLE hVdd,
631 IN WORD iChannel,
632 IN PVDD_DMA_INFO pDmaInfo)
633 {
634 PDMA_CONTROLLER pDcp;
635 WORD Channel;
636
637 UNREFERENCED_PARAMETER(hVdd);
638
639 if (iChannel >= DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS)
640 {
641 SetLastError(ERROR_INVALID_ADDRESS);
642 return FALSE;
643 }
644
645 pDcp = &DmaControllers[iChannel / DMA_CONTROLLER_CHANNELS];
646 Channel = iChannel % DMA_CONTROLLER_CHANNELS;
647
648 pDmaInfo->addr = pDcp->DmaChannel[Channel].CurrAddress;
649 pDmaInfo->count = pDcp->DmaChannel[Channel].CurrElemCnt;
650
651 pDmaInfo->page = DmaPageRegisters[iChannel].Page;
652 pDmaInfo->status = pDcp->Status;
653 pDmaInfo->mode = pDcp->DmaChannel[Channel].Mode;
654 pDmaInfo->mask = pDcp->Mask;
655
656 return TRUE;
657 }
658
659 BOOL
660 WINAPI
661 VDDSetDMA(IN HANDLE hVdd,
662 IN WORD iChannel,
663 IN WORD fDMA,
664 IN PVDD_DMA_INFO pDmaInfo)
665 {
666 PDMA_CONTROLLER pDcp;
667 WORD Channel;
668
669 UNREFERENCED_PARAMETER(hVdd);
670
671 if (iChannel >= DMA_CONTROLLERS * DMA_CONTROLLER_CHANNELS)
672 {
673 SetLastError(ERROR_INVALID_ADDRESS);
674 return FALSE;
675 }
676
677 pDcp = &DmaControllers[iChannel / DMA_CONTROLLER_CHANNELS];
678 Channel = iChannel % DMA_CONTROLLER_CHANNELS;
679
680 if (fDMA & VDD_DMA_ADDR)
681 pDcp->DmaChannel[Channel].CurrAddress = pDmaInfo->addr;
682
683 if (fDMA & VDD_DMA_COUNT)
684 pDcp->DmaChannel[Channel].CurrElemCnt = pDmaInfo->count;
685
686 if (fDMA & VDD_DMA_PAGE)
687 DmaPageRegisters[iChannel].Page = pDmaInfo->page;
688
689 if (fDMA & VDD_DMA_STATUS)
690 pDcp->Status = pDmaInfo->status;
691
692 return TRUE;
693 }
694
695 /* EOF */