summaryrefslogtreecommitdiff
path: root/v4.0/src/DEV/XMAEM/INDEDMA.ASM
diff options
context:
space:
mode:
Diffstat (limited to 'v4.0/src/DEV/XMAEM/INDEDMA.ASM')
-rw-r--r--v4.0/src/DEV/XMAEM/INDEDMA.ASM660
1 files changed, 660 insertions, 0 deletions
diff --git a/v4.0/src/DEV/XMAEM/INDEDMA.ASM b/v4.0/src/DEV/XMAEM/INDEDMA.ASM
new file mode 100644
index 0000000..84d73dc
--- /dev/null
+++ b/v4.0/src/DEV/XMAEM/INDEDMA.ASM
@@ -0,0 +1,660 @@
1PAGE 60,132
2TITLE INDEDMA - 386 XMA Emulator - DMA Emulation
3
4COMMENT #
5* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6* *
7* MODULE NAME : INDEDMA *
8* *
9* *
10* 5669-196 (C) COPYRIGHT 1988 Microsoft Corporation *
11* *
12* DESCRIPTIVE NAME: DMA handler for the 80386 XMA emulator *
13* *
14* STATUS (LEVEL) : Version (0) Level (1.0) *
15* *
16* FUNCTION : This module intercepts any I/O going to the DMA address *
17* ports. We can't let the DMA requests go to the virtual *
18* addresses. On the real XMA card the addresses on the *
19* bus lines are translated by the card so that it accesses *
20* the correct memory. But with our emulation the addresses *
21* are translated before they hit the bus lines. The DMA *
22* addresses go straight to the bus lines without being *
23* translated. This would result in DMA reading and writing *
24* data to the wrong memory location. Not good. Therefore *
25* we intercept the I/O that is going to the DMA address *
26* ports. We run these addresses through our paging mech- *
27* anism and then write the real addresses to the DMA *
28* address ports. *
29* *
30* MODULE TYPE : ASM *
31* *
32* REGISTER USAGE : 80386 Standard *
33* *
34* RESTRICTIONS : None *
35* *
36* DEPENDENCIES : None *
37* *
38* ENTRY POINTS : DMAIN - Entry point for "IN" instructions *
39* DMAOUT - Entry point for "OUT" instructions *
40* MANPORT - Entry point to issue an OUT to the manufacturing*
41* port to re-IPL the system *
42* *
43* LINKAGE : Jumped to by INDEXMA *
44* *
45* INPUT PARMS : None *
46* *
47* RETURN PARMS : None *
48* *
49* OTHER EFFECTS : None *
50* *
51* EXIT NORMAL : Jump to POPIO to return to the V86 task *
52* *
53* EXIT ERROR : None *
54* *
55* EXTERNAL *
56* REFERENCES : DISPLAY - Entry point in INDEEXC to signal an error *
57* POPIO - Entry point in INDEEMU to return to the V86 *
58* task *
59* PGTBLOFF - Word in INDEI15 that contains the offset of *
60* the page tables *
61* SGTBLOFF - Word in INDEI15 that contains the offset of *
62* the page directory *
63* NORMPAGE - Double Word in INDEI15 that contains the *
64* entry that goes into the first page directory *
65* entry so that it points to the normal page *
66* tables *
67* BUFF_SIZE - Word in INDEI15 that contains the size of the *
68* MOVEBLOCK buffer *
69* MAXMEM - Word in INDEI15 that contains the total *
70* number of K in the box *
71* WORD_FLAG - Byte in INDEXMA that indicates whether the *
72* I/O instruction was for a word or a byte *
73* *
74* SUB-ROUTINES : XLATE - Translate the virtual DMA address to a real DMA *
75* address *
76* *
77* MACROS : DATAOV - Add prefix for the next instruction so that it *
78* accesses data as 32 bits wide *
79* ADDROV - Add prefix for the next instruction so that it *
80* uses addresses that are 32 bits wide *
81* LJB - Long jump if below *
82* LJA - Long jump if above *
83* LJAE - Long jump if above or equal *
84* LJNE - Long jump if not equal *
85* *
86* CONTROL BLOCKS : INDEDAT.INC - System data structures *
87* *
88* CHANGE ACTIVITY : *
89* *
90* $MOD(INDEDMA) COMP(LOAD) PROD(3270PC) : *
91* *
92* $D0=D0004700 410 870101 D : NEW FOR RELEASE 1.1 *
93* $P1=P0000312 410 870804 D : CLEAN UP WARNING MESSAGES *
94* *
95* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
96#
97
98 .286P ; Enable recognition of 286 privileged instructs.
99
100 .XLIST ; Turn off the listing
101 INCLUDE INDEDAT.INC ; Include system data structures and equates
102
103 IF1 ; Only include macros on the first pass
104 INCLUDE INDEOVP.MAC ; Override prefix macros
105 INCLUDE INDEINS.MAC ; 386 instruction macros
106 ENDIF
107 .LIST ; Turn the listing back on
108
109PROG SEGMENT PARA PUBLIC 'PROG'
110
111 ASSUME CS:PROG
112 ASSUME DS:PROG
113 ASSUME ES:NOTHING
114 ASSUME SS:NOTHING
115
116INDEDMA LABEL NEAR
117
118 EXTRN DISPLAY:NEAR ; Entry point in INDEEXC to signal an error
119 EXTRN POPIO:NEAR ; Entry point in INDEEMU to return to the V86
120 ; task
121 EXTRN PGTBLOFF:WORD ; Word in INDEI15 that contains the offset of
122 ; the page tables
123 EXTRN SGTBLOFF:WORD ; Word in INDEI15 that contains the offset of
124 ; the page directory
125 EXTRN NORMPAGE:WORD ; Double Word in INDEI15 that contains the
126 ; entry that goes into the first page direct-
127 ; ory entry so that it points to the normal
128 ; page tables
129 EXTRN BUFF_SIZE:WORD ; Word in INDEI15 that contains the size of the
130 ; MOVEBLOCK buffer
131 EXTRN MAXMEM:WORD ; Word in INDEI15 that contains the total
132 ; number of K in the box
133 EXTRN WORD_FLAG:BYTE ; Byte in INDEXMA that indicates whether the
134 ; I/O instruction was for a word or a byte
135
136; Let these entry points be known to other modules
137
138 PUBLIC INDEDMA
139 PUBLIC DMAIN
140 PUBLIC DMAOUT
141 PUBLIC MANPORT
142
143PAGE
144
145; Define control blocks for each of the DMA channels 0 to 7. The control
146; blocks have information on where the user wanted to do DMA, where we will
147; actually do the DMA, the channel number and the page port. The following
148; is an overlay for the control blocks. After that the actual control
149; blocks are defined.
150
151DMACB STRUC ; DMA control block
152
153DMACHN DB 0 ; Channel number
154DMALSB DB 0 ; Least significant address byte
155DMAMSB DB 0 ; Most significant address byte (16 bits)
156DMAPAGE DB 0 ; Page - Hi-order of 24-bit address
157DMALR DB 0 ; Real Least significant address byte
158DMAMR DB 0 ; Real Most significant address byte (16 bits)
159DMAPR DB 0 ; Real Page - Hi-order of 24-bit address
160DMAPP DB 0 ; Compatability mode page port
161
162DMACB ENDS
163
164DMASTART EQU 0
165DMAENTRYLEN EQU DMAPP+1-DMASTART
166
167; Now, the channel control blocks
168
169DMATABLE DB 0 ; Channel number
170 DB DMAENTRYLEN-2 DUP (0) ; The other stuff
171 DB 87H ; Page port
172
173 DB 1 ; Channel number
174 DB DMAENTRYLEN-2 DUP (0) ; The other stuff
175 DB 83H ; Page port
176
177 DB 2 ; Channel number
178 DB DMAENTRYLEN-2 DUP (0) ; The other stuff
179 DB 81H ; Page port
180
181 DB 3 ; Channel number
182 DB DMAENTRYLEN-2 DUP (0) ; The other stuff
183 DB 82H ; Page port
184
185 DB 4 ; Channel number
186 DB DMAENTRYLEN-2 DUP (0) ; The other stuff
187 DB 8FH ; Page port
188
189 DB 5 ; Channel number
190 DB DMAENTRYLEN-2 DUP (0) ; The other stuff
191 DB 8BH ; Page port
192
193 DB 6 ; Channel number
194 DB DMAENTRYLEN-2 DUP (0) ; The other stuff
195 DB 89H ; Page port
196
197 DB 7 ; Channel number
198 DB DMAENTRYLEN-2 DUP (0) ; The other stuff
199 DB 8AH ; Page port
200
201; And now some more variables
202
203DMACURRENT DB 0 ; Channel we're working on
204DMABYTE DB 0 ; This flag is toggled between 0 and 1
205 ; to indicated whether the least
206 ; significant or most significant byte
207 ; of the DMA address is being written
208DMA_ADV_CHN DB 0 ; Advanced mode channel number
209
210PAGE
211
212; Define the jump table. There are 32 entries in the table that correspond to
213; the first 32 ports, 0 to 20H. The code uses the port number as an index into
214; this table which then gives control to the appropriate routine for that port.
215; The table is initialized so that all entries jump to the code that will pass
216; the I/O back to the real port. Then the entries for the ports we want to
217; handle are set to the corresponding routines for those ports.
218
219OUT_TABLE:
220 .XLIST
221 REPT 32
222 JMP DOOUT
223 ENDM
224 .LIST
225OUT_TABLE_END:
226
227; Set the entries for the ports we want to handle.
228
229 ORG OUT_TABLE+(00H*3)
230 JMP CHN_0
231 ORG OUT_TABLE+(02H*3)
232 JMP CHN_1
233 ORG OUT_TABLE+(04H*3)
234 JMP CHN_2
235 ORG OUT_TABLE+(06H*3)
236 JMP CHN_3
237 ORG OUT_TABLE+(0CH*3)
238 JMP RESET_BYTE_PTR
239 ORG OUT_TABLE+(18H*3)
240 JMP CHK_FUNCTION
241 ORG OUT_TABLE+(1AH*3)
242 JMP CHK_BASE_REG
243
244 ORG OUT_TABLE_END
245
246PAGE
247
248; Control comes here from INDEXMA when it determines that the "IN" instruction
249; is not for one of the XMA ports. On entry, WORD_FLAG is already set for word
250; or byte operation. IP points to next instruction minus 1. DX has the port
251; value in it.
252
253DMAIN PROC NEAR
254 CMP WORD_FLAG,0 ; Is this "IN" for a word?
255 JNE GETWORDREAL ; Yes. Then go get a word.
256
257 IN AL,DX ; Else we'll just get a byte
258 MOV BYTE PTR SS:[BP+BP_AX],AL ; And put it into the user's AL reg.
259 JMP INEXIT ; Go return to the user
260
261GETWORDREAL:
262 IN AX,DX ; Get a word from the port
263 MOV WORD PTR SS:[BP+BP_AX],AX ; Put it into the user's AX register
264 MOV WORD_FLAG,0 ; Reset the word flag
265
266INEXIT:
267 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the instruction, or past
268 ; the port value in the case of I/O
269 ; with an immediate port value
270 JMP POPIO ; Return to the V86 task
271
272PAGE
273
274; Control comes here from INDEXMA when it determines that the "OUT" instruction
275; is not for one of the XMA ports. On entry, WORD_FLAG is already set for word
276; or byte operation. IP points to next instruction minus 1. DX has the port
277; value in it.
278
279DMAOUT:
280
281; Check for DMA page registers in compatibility mode
282
283 MOV AX,SYS_PATCH_DS ; Load DS with the selector for our data
284 MOV DS,AX ; segment
285 CMP WORD_FLAG,0 ; Is this a word operation?
286 LJNE DISPLAY ; No? Sorry. We don't support word DMA
287 ; yet. We'll just have to signal an
288 ; error.
289 LEA BX,DMATABLE ; Point BX to the base of our channel
290 ; control blocks
291 CMP DX,0081H ; Is this out to the channel 2 page port
292 LJB NOT_DMA_PAGE ; If the port number is less than 81H
293 ; then the "OUT" is not to a DMA page
294 JA CHK_CHN_0 ; If the port number is above 81H, then
295 ; go check if it's below 87H, the page
296 ; port for channel 0
297 ADD BX,DMAENTRYLEN*2 ; If it's not below or above, then it
298 ; must be port 81H! Point BX to the
299 ; control block for channel 2
300 JMP CHNCOM ; Continue
301
302CHK_CHN_0:
303 CMP DX,0087H ; Is it the page port for channel 0?
304 JA CHK680 ; Nope. It's above that. Go check for
305 ; the Roundup IPL port
306 JE CHNCOM ; It IS the page port for channel 0.
307 ; BX already points to the control
308 ; block for channel 0.
309 CMP DX,0083H ; Is it the page port for channel 1?
310 JB SET_CHN_3 ; No. It's below that. Then it must
311 ; be the page port for channel 3!
312 LJA DISPLAY ; No. It's above it. We don't know any
313 ; ports between 83H and 87H. Go
314 ; signal an error.
315 ADD BX,DMAENTRYLEN*1 ; Yes. It's the page port for channel 1
316 ; Point BX to the control block for
317 ; channel 1.
318 JMP CHNCOM ; And continue
319
320; The port is greater than 87H
321
322CHK680:
323 CMP DX,680H ; Is it the manufacturing port (for IPL)
324 ; on the Roundup?
325 JE MANPORT ; Yes. Go IPL the system.
326 JMP DOOUT ; No. Pass the "OUT" on to the real
327 ; port.
328
329; The "OUT" is to the page port for channel 3
330
331SET_CHN_3:
332 ADD BX,DMAENTRYLEN*3 ; Point BX to the control block for
333 ; channel 3
334
335; Check to see if the value written to the page port is greater than 0FH.
336; Why? Let me tell you. Addresses are 20 bits, right? The user puts the
337; lower 16 bits into the channel port in two eight bit writes. The value
338; written to the page port is the upper eight bits of the address. But to
339; address 1M you only need 20 bits. Therefore, only the lower four bits are
340; valid if the address is to remain within the 1M address limit. So if the
341; value to be written to the page port is 10H or greater it is invalid, so
342; we will signal an error.
343
344CHNCOM:
345 MOV AL,BYTE PTR SS:[BP+BP_AX] ; Get the value that is to be written
346 ; to the page port
347 CMP AL,10H ; Is it 10H or greater?
348 JB CHNCONT ; Nope. We're still OK.
349
350; Oops. It's an invalid page port value. Time to signal an error. But wait.
351; If we just jump to DISPLAY as usual the code will just return to the V86
352; task. This is not good since we haven't Revised the DMA and it will end
353; up going to the wrong address. What we really want to do is kill the system.
354; To do this we will issue an interrupt 6, invalid instruction. The exception
355; handler checks to see from whence the interrupt came. If it came from the
356; emulator then it assumes something is terribly wrong and issues an NMI.
357; This is what we want. So we'll issue an INT 6 instead of a JMP DISPLAY.
358
359INVALID_PAGE:
360 INT 6 ; Signal and error
361 JMP INVALID_PAGE ; Do it again in case control comes
362 ; back here
363
364; At this point were still OK. BX points to the control block for the channel.
365; Let's translate the address we currently have to its real address and send
366; it out to the DMA channel.
367
368CHNCONT:
369 MOV BYTE PTR [BX+DMAPAGE],AL ; Put the page port value into the
370 ; control block
371 CALL XLATE ; Create the real address entries in
372 ; the control block
373 MOV DL,BYTE PTR [BX+DMACHN] ; Get the channel number from the c.b.
374 SHL DX,1 ; Convert it to a port address
375 MOV AL,BYTE PTR [BX+DMALR] ; Get the LSB of the real address
376 OUT DX,AL ; "OUT" it to the address port
377 JMP $+2 ; Wait a bit
378 MOV AL,BYTE PTR [BX+DMAMR] ; Get the MSB of the real address
379 OUT DX,AL ; "OUT" it to the address port
380 JMP $+2 ; Wait a bit
381 MOV DL,BYTE PTR [BX+DMAPP] ; Get the page port
382 MOV AL,BYTE PTR [BX+DMAPR] ; Get the real page number
383 OUT DX,AL ; Do the "OUT" to the DMA page port
384 JMP OUTEXITDMA ; That's it
385
386PAGE
387
388; This is where we come when we want to simply send the "OUT" to the real port.
389
390DOOUT:
391 CMP WORD_FLAG,0 ; Is this an "OUT" for a word?
392 JNE PUTWORDREAL ; Aye. Go put out a word.
393
394 MOV AL,BYTE PTR SS:[BP+BP_AX] ; Nay. It is for a byte. Get the
395 ; byte from the user's AL register
396 OUT DX,AL ; And send it out to the port
397 JMP OUTEXITDMA ; Time to leave
398
399PUTWORDREAL:
400 MOV AX,WORD PTR SS:[BP+BP_AX] ; Get the word form the user's AX
401 OUT DX,AX ; And thrust it out to the port
402 MOV WORD_FLAG,0 ; Reset the word flag
403
404OUTEXITDMA:
405
406 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the instruction, or past
407 ; the port value in the case of I/O
408 ; with an immediate port value
409 JMP POPIO ; Return to the V86 task
410
411PAGE
412
413; It's not an "OUT" to one of the DMA page ports.
414
415NOT_DMA_PAGE:
416 CMP DX,(OUT_TABLE_END-OUT_TABLE)/3 ; Is the port within the range
417 ; covered by our jump table, i.e.,
418 ; 0 to 20H
419 JAE NOTCOWBOY ; Nope. Let's go check if it's the IPL
420 ; port on the AT
421 MOV AL,DL ; Yes, it's handled by our jump table
422 MOV AH,3 ; Convert the port number to an index
423 MUL AH ; into the jump table by multiplying
424 ; by 3. Jump table entry are 3 bytes.
425 LEA CX,OUT_TABLE ; Get the offset of the jump table
426 ADD AX,CX ; And add it on to the index
427 JMP AX ; Jump to the jump table entry for this
428 ; port
429
430NOTCOWBOY:
431 CMP DX,80H ; Is it the manufacturing (IPL) port for
432 ; the AT?
433 JNE DOOUT ; Negative. Then let's just do a plain
434 ; vanilla "OUT" to the real port.
435MANPORT:
436 MOV AL,0FEH ; It's the IPL port! Send out a FEH to
437 OUT 064H,AL ; reset the system.
438
439HALT: HLT ; In case that trick didn't work @P1C
440 JMP HALT ; we'll just wait here until @P1C
441 ; somebody hits the big red switch.
442
443PAGE
444
445; This is the entry for an "OUT" to port 0CH. An "OUT" to this port will
446; reset the controller so that the next out will be to the least significant
447; byte of the address register. The way you set the lower 16 bits of the
448; address is by sending the two bytes to the same port, first the least
449; significant byte (LSB) and then the most significant byte (MSB). The port
450; knows that successive bytes to the port mean successively higher bytes of
451; the address. We emulate this with our DMABYTE flag. Since the user will
452; only be sending two bytes to an address port this flag will be toggled
453; between 0 and 1 indicating that the "OUT" was to the LSB if 0 and to the
454; MSB if 1. A write to port 0CH tells the controller that the next write
455; will be for the LSB. We emulate this by setting our DMABYTE flag to 0.
456
457RESET_BYTE_PTR:
458 MOV DMABYTE,0 ; Reset the byte flag
459 JMP DOOUT ; Send the "OUT" to the real port
460
461PAGE
462
463; The following entries handle the "OUT"s to the address ports for channels
464; 0 to 3. These are ports 00, 02, 04 and 06 respectively. As mentioned
465; above the address written to these ports is done in two steps. First the
466; least significant byte (LSB) is written, the the most significant byte (MSB)
467; is written. This results in a 16 bit value in the address port. Combine
468; this with the byte in the page port and you have a 24 bit DMA address. The
469; code below emulates this by putting the byte that is "OUT"ed to the LSB if
470; DMA BYTE is 0 and to the MSB if DMABYTE is 1. DMABYTE is toggled between 0
471; and 1 each time there is a write to the port. So the bytes go alternately
472; to the LSB and the MSB.
473
474; "OUT" is to port 00, the address port for channel 0.
475
476CHN_0:
477 LEA BX,DMATABLE ; Point BX to the control block for
478 ; channel 0
479 JMP MOVBYTE ; Go get the byte
480
481; "OUT" is to port 02, the address port for channel 1.
482
483CHN_1:
484 LEA BX,DMATABLE+(1*DMAENTRYLEN) ; Point BX to the control block for
485 ; channel 1
486 JMP MOVBYTE ; Go get the byte
487
488; "OUT" is to port 04, the address port for channel 2.
489
490CHN_2:
491 LEA BX,DMATABLE+(2*DMAENTRYLEN) ; Point BX to the control block for
492 ; channel 2
493 JMP MOVBYTE ; Go get the byte
494
495; "OUT" is to port 06, the address port for channel 3.
496
497CHN_3:
498 LEA BX,DMATABLE+(3*DMAENTRYLEN) ; Point BX to the control block for
499 ; channel 3
500
501MOVBYTE:
502 MOV AL,BYTE PTR SS:[BP+BP_AX] ; Get the byte from the user's AL
503 XOR DMABYTE,1 ; Toggle the DMABYTE flag
504 JZ MOVMSB ; Was the flag set to 1? If so, go
505 ; put the byte to the MSB
506 MOV BYTE PTR [BX+DMALSB],AL ; Else it was 0 so put the byte in the
507 ; LSB for this channel
508 JMP OUTEXITDMA ; And exit
509
510MOVMSB:
511 MOV BYTE PTR [BX+DMAMSB],AL ; Put the byte in the MSB for this
512 ; channel
513 JMP OUTEXITDMA ; And exit
514
515PAGE
516
517; This is the entry point for an "OUT" to port 18H. It has something to do
518; with the advanced DMA channels 4 thru 7. If the function number in the high
519; nibble of AL is 2, set the base register, then we'll save the advanced channel
520; number given in the low nibble of AL. If the function is not 2 then we will
521; inhibit the setting of the base register.
522
523CHK_FUNCTION:
524 MOV AL,BYTE PTR SS:[BP+BP_AX] ; Get the value to be "OUT"ed to 18H
525 SHR AL,4 ; Shift the function number into AL
526 CMP AL,2 ; Is this the function to set the base
527 ; register?
528 JE SAVE_CHN ; Yup. Go save the channel number.
529 MOV DMABYTE,3 ; Nope. Inhibit setting the base reg.
530 JMP DOOUT ; Send the "OUT" to the real port
531
532SAVE_CHN:
533 MOV AL,BYTE PTR SS:[BP+BP_AX] ; Get the value in AL again
534 AND AL,07H ; Mask off the function number leaving
535 ; the channel number
536 MOV DMA_ADV_CHN,AL ; And save it
537 JMP RESET_BYTE_PTR ; Go reset the byte flag
538
539PAGE
540
541; This is the entry for an "OUT" to port 1AH.
542
543CHK_BASE_REG:
544 CMP DMABYTE,3 ; Are we inhibiting setting the base
545 ; register?
546 LJAE DOOUT ; Yes. Then just sent the "OUT" to the
547 ; real port
548 LEA BX,DMATABLE ; Point BX to the channel control blocks
549 MOV AL,DMA_ADV_CHN ; Get the current advanced channel
550 MOV AH,DMAENTRYLEN ; and multiply by the size of a
551 MUL AH ; control block. Now AX is the index
552 ; for the current control block
553 ADD BX,AX ; Add this on to the base and BX points
554 ; to the control block
555 SUB AX,AX ; Purge AX
556 MOV AL,DMABYTE ; Get the byte flag
557 ADD BX,AX ; And add it on to BX
558 MOV CL,BYTE PTR SS:[BP+BP_AX] ; Get the out value into CL
559
560; Now put it in the control block. Notice that BX is the base of the control
561; block plus the byte flag. Now we add on the offset for the LSB entry. A
562; little pondering of this code will reveal that for DMABYTE = 0 the byte in
563; CL goes to the LSB entry, for DMABYTE = 1 it goes to the MSB entry and for
564; DMABYTE = 2 it goes to the page entry.
565
566 MOV BYTE PTR [BX+DMALSB],CL ; Save the byte in the control block
567 INC DMABYTE ; Increment our byte counter
568 CMP DMABYTE,3 ; Was the page entry written?
569 LJNE OUTEXITDMA ; Nope. Let's just exit.
570
571 SUB BX,AX ; The page was written. Point BX back
572 ; to the start of the control block.
573 CMP CL,10H ; Does the page point to over 1M?
574 LJAE INVALID_PAGE ; Yes. Better signal an error.
575
576 CALL XLATE ; The page is OK. Translate the virtual
577 ; address to the real address
578 MOV AL,BYTE PTR [BX+DMALR] ; Get the LSB of the real address
579 OUT 1AH,AL ; "OUT" it to the port 1AH
580 JMP $+2 ; Wait a bit
581 MOV AL,BYTE PTR [BX+DMAMR] ; Get the MSB of the real address
582 OUT 1AH,AL ; "OUT" it to the port 1AH
583 JMP $+2 ; Wait a bit
584 MOV AL,BYTE PTR [BX+DMAPR] ; Get the real page number
585 OUT 1AH,AL ; Do the "OUT" to port 1AH
586 JMP OUTEXITDMA ; That's all
587
588PAGE
589
590; XLATE is a procedure to translate the virtual DMA address to the real DMA
591; address. It takes the virtual address that was "OUT"ed to the DMA ports
592; and follows it through the page tables to get the real address. It puts
593; the real address into the current channel control block.
594
595XLATE PROC
596
597; Calcuate the page fram offset of the real address, the lower 12 bits.
598
599 MOV AX,WORD PTR [BX+DMALSB] ; Get the virtual LSB and MSB
600 AND AH,0FH ; Wipe out the top nibble. This leaves
601 ; us with only the offset into the 4K
602 ; page frame. This real address will
603 ; have the same offset into the page
604 ; frame
605 MOV WORD PTR [BX+DMALR],AX ; So save this in the real LSB
606
607; Pick up page table address.
608
609 MOV SI,SGTBLOFF ; Point ST to the first page directory
610 ; entry
611 DATAOV ; Get the address of the current page
612 LODSW ; table
613 SUB AL,AL ; Clear the access rights byte. This
614 ; makes it a real offset.
615 DATAOV ; Point SI to the page table
616 MOV SI,AX
617 MOV AX,WORD PTR [BX+DMAMSB] ; Get the MSB and page
618 SHR AX,4 ; Shift the top four bits off the end
619 ; of the register these four bits are
620 ; not used. We are dealing with a 24
621 ; bit address where only 20 bits are
622 ; used.
623 SHL AX,4-2 ; Shift back 2 bits. This puts zeroes
624 ; in the high bits while converting
625 ; the address to a page table index.
626 ; Page table entries are 4 bytes long,
627 ; hence, shift left 2 bits.
628 ADD SI,AX ; Add the page table index on to the
629 ; offset of the page table. SI now
630 ; points to the correct page table
631 ; entry.
632 ADD SI,1 ; Step over the access rights byte
633
634 MOV AX,HUGE_PTR ; Load DS with a selector that accesses
635 MOV DS,AX ; all of memory as data
636 ADDROV ; Load the address of the page frame
637 LODSW ; into EAX
638 MOV CX,SYS_PATCH_DS ; Point DS back to our data segment
639 MOV DS,CX
640
641; Now AX contains the address of the page frame shifted right 8 bits. Remember
642; that we incremented SI to skip the access rights? This gave us the page
643; frame offset with out the lower byte. The lSB real address was already set
644; above, as well as the low nibble of the real MSB. AX now contains the page
645; and MSB of the real address. The low nibble of the MSB was already set so
646; we just OR on the high nibble of the MSB. Then we set the real page. Then
647; we're all done.
648
649 OR BYTE PTR [BX+DMAMR],AL ; Turn on high 4 bits of the real MSB
650 MOV BYTE PTR [BX+DMAPR],AH ; Set the real page
651
652 RET
653
654XLATE ENDP
655
656DMAIN ENDP
657
658PROG ENDS
659
660 END