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