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
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
|
PAGE 60,132
TITLE INDEXMA - 386 XMA Emulator - XMA Emulation
COMMENT #
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* MODULE NAME : INDEXMA *
* *
* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. *
* *
* DESCRIPTIVE NAME: Do the XMA emulation for the 80386 XMA Emulator *
* *
* STATUS (LEVEL) : Version (0) Level (1.0) *
* *
* FUNCTION : This module does the actual manipulation of the page *
* tables to emulate the XMA card. Using the 80386 *
* paging mechanism we let the 4K page frames represent *
* the 4K XMA blocks on the XMA card. We let the page *
* tables represent the translate table. *
* *
* The XMA "blocks" start at address 12000:0. The D1C*
* Emulator emulates the XMA 2 card with the INDXMAA D1C*
* device driver. On initial power up, the XMA 2 card is D1C*
* disabled. The INDXMAA device driver then disables the D1C*
* memory from 0K to 640K and backs it with memory from D1C*
* 0K to 640K on the XMA 2 card. The Emulator looks like D1C*
* it does the same thing. The XMA blocks for 0K to 640K D1C*
* are taken from the system board memory from 0K to D1C*
* 640K. This memory on the motherboard is treated as D1C*
* XMA memory. This emulates the INDXMAA device driver's D1C*
* mapping of 0K to 640K on the XMA card to real memory. D1C*
* The XMA "blocks" for 640K and up are located in high D1C*
* memory starting at 12000:0. These "blocks" run up to D1C*
* the start of the MOVEBLOCK buffer. The MOVEBLOCK D1C*
* buffer is a chunk of storage (in 16K multiples) at the D1C*
* end of available memory that is reserved for the D1C*
* MOVEBLOCK functions. D1C*
* *
* The page tables are used to emulate the translate *
* table. By setting the address of the XMA "block" into *
* the page table entry for a specific page frame we can *
* make that address access that particular XMA page *
* frame. To the user this looks just like the translate *
* table is active. *
* *
* The tricky part comes in disabling pages (blocks). On D1C*
* the XMA 2 card, when a translate table entry is D1C*
* disabled the addresses for that address range go to D1C*
* real memory. If the address is between 0K and 640K D1C*
* then any access of that storage gets nothing because D1C*
* there is no memory backed from 0K to 640K on the real D1C*
* system. All other addresses go to real memory. So D1C*
* when the user disables translation of a translate D1C*
* table entry we need to check what range that entry D1C*
* covers. If the entry points to somewhere between 0K D1C*
* and 640K then we will set the page table entry that D1C*
* corresponds to the translate table entry to point to D1C*
* non-existent memory. For all other addresses we will D1C*
* just point the page table entry back to the real D1C*
* memory at that address. D1C*
* *
* This module receives control on all "IN"s and "OUT"s *
* done by the user. If the "IN" or "OUT" is not to an *
* XMA port then it passes the I/O on to INDEDMA. If it *
* is for an XMA port then the request is handled here. *
* *
* This module keeps its own copies of the XMA registers *
* and the translate table. When any I/O comes for the *
* XMA card it updates its copies of the registers and *
* the translate table. Then it does any needed *
* modifications on the page tables to emulate the XMA *
* request. *
* *
* MODULE TYPE : ASM *
* *
* REGISTER USAGE : 80386 Standard *
* *
* RESTRICTIONS : None *
* *
* DEPENDENCIES : None *
* *
* ENTRY POINTS : INW - Emulate "IN" for a word with port number *
* in DX *
* INWIMMED - Emulate "IN" for a word with an immediate *
* port number *
* INIMMED - Emulate "IN" for a byte with an immediate *
* port number *
* XMAIN - Emulate "OUT" for a byte with port number *
* in DX *
* OUTW - Emulate "OUT" for a word with port number *
* in DX *
* OUTWIMMED - Emulate "OUT" for a word with an immediate *
* port number *
* XMAOUTIMMED - Emulate "OUT" for a byte with an immediate *
* port number *
* XMAOUT - Emulate "OUT" for a byte with port number *
* in DX *
* *
* LINKAGE : Jumped to by INDEEXC *
* *
* INPUT PARMS : None *
* *
* RETURN PARMS : None *
* *
* OTHER EFFECTS : None *
* *
* EXIT NORMAL : Go to POPIO in INDEEMU to IRET to the V86 task *
* *
* EXIT ERROR : None *
* *
* EXTERNAL *
* REFERENCES : POPIO:NEAR - Entry in INDEEMU to return to V86 task *
* HEXW:NEAR - Entry in INDEEXC to display word in AX *
* DMAIN:NEAR - Entry in INDEDMA to "IN" from DMA port *
* DMAOUT:NEAR - Entry in INDEDMA to "OUT" to DMA port *
* PGTBLOFF:WORD - Offset of the normal page tables *
* SGTBLOFF:WORD - Offset of the page directory *
* NORMPAGE:WORD - Entry for the 1st page directory entry *
* so that it points to the normal *
* page tables *
* XMAPAGE:WORD - Entry for the 1st page directory entry *
* that points to the XMA page tables *
* TTTABLE:WORD - The translate table *
* BUFF_SIZE:WORD - Size of the MOVEBLOCK buffer *
* MAXMEM:WORD - Number of kilobytes on this machine *
* *
* SUB-ROUTINES : TTARCHANGED - Put the block number at the translate table *
* entry in 31A0H into "ports" 31A2H and 31A4H *
* UPDATETT - Update the translate table and page tables *
* to reflect the new block number written to *
* either 31A2H or 31A4H *
* *
* 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 *
* CMOV - Move to and from control registers *
* *
* CONTROL BLOCKS : INDEDAT.INC - system data structures *
* *
* CHANGE ACTIVITY : *
* *
* $MOD(INDEXMA) COMP(LOAD) PROD(3270PC) : *
* *
* $D0=D0004700 410 870530 D : NEW FOR RELEASE 1.1 *
* $P1=P0000293 410 870731 D : LIMIT LINES TO 80 CHARACTERS *
* $P2=P0000312 410 870804 D : CHANGE COMPONENT FROM MISC TO LOAD *
* $D1=D0007100 410 870810 D : CHANGE TO EMULATE XMA 2 *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
#
.286P ; Enable recognition of 286 privileged instructs.
.XLIST ; Turn off the listing
INCLUDE INDEDAT.INC ; Include system data structures
IF1 ; Only include macros on the first pass
INCLUDE INDEOVP.MAC
INCLUDE INDEINS.MAC
ENDIF
.LIST ; Turn on the listing
CRT_SELECTOR EQU 00030H ; Selector for mono display buffer
SEX_ATTR EQU 04B00H ; Attribute for turquoise on red
STACK_ATTR EQU 00700H ; Attribute for white o black
BLANK EQU 00020H ; ASCII for a blank
XMA_PAGES_SEL EQU RSDA_PTR ; Selector for the XMA pages
HIMEM EQU 120H ; Adjustment for XMA pages >= 640K. @D1C
; They start at address 12000:0.
; It's the block number corresponding
; to address 12000:0.
; @D1D
PROG SEGMENT PARA PUBLIC 'PROG'
ASSUME CS:PROG
ASSUME DS:PROG
ASSUME ES:NOTHING
ASSUME SS:NOTHING
INDEXMA LABEL NEAR
; The following entry points are in other modules
EXTRN POPIO:NEAR ; Return to V86 task - in INDEEMU
EXTRN HEXW:NEAR ; Display word in AX - in INDEEXC
EXTRN DMAIN:NEAR ; "IN" from DMA port - in INDEDMA
EXTRN DMAOUT:NEAR ; "OUT" to DMA port - in INDEDMA
; The following data are in INDEI15.ASM
EXTRN PGTBLOFF:WORD ; Offset of the normal page tables
EXTRN SGTBLOFF:WORD ; Offset of the page directory
EXTRN NORMPAGE:WORD ; Entry for the 1st page directory entry
; so that it points to the normal
; page tables
EXTRN XMAPAGE:WORD ; Entry for the 1st page directory entry
; that points to the XMA page tables
EXTRN TTTABLE:WORD ; The translate table
EXTRN BUFF_SIZE:WORD ; Size of the MOVEBLOCK buffer
EXTRN MAXMEM:WORD ; Number of kilobytes on this machine
; Let the following entries be known to other modules
PUBLIC INDEXMA
PUBLIC INW
PUBLIC INWIMMED
PUBLIC INIMMED
PUBLIC XMAIN
PUBLIC OUTW
PUBLIC OUTWIMMED
PUBLIC XMAOUTIMMED
PUBLIC XMAOUT
PUBLIC NOTXMAOUT
; Let the following data be known to other modules
PUBLIC WORD_FLAG
PUBLIC XMATTAR
PUBLIC XMATTIO
PUBLIC XMATTII
PUBLIC XMATID
PUBLIC XMACTL
; The following XMA labels represent the XMA ports starting at 31A0H.
; THEY MUST BE KEPT IN THE FOLLOWING ORDER.
XMATTAR DW 0 ; Port 31A0H - Translate table index
XMATTIO DW 0 ; Port 31A2H - XMA block number
XMATTII DW 0 ; Port 31A4H - Block number with auto-increment
XMATID DB 0 ; Port 31A6H - Bank ID
XMACTL DB 02H ; Port 31A7H - Control flags. Virtual @D1C
; enable bit is initially on.
; How about some flags?
WORD_FLAG DB 0 ; If set to 1 then I/O is for a word.
; Else, it's for a byte
PAGE
; Control comes here for an "IN" for a word with the port value in DX
INW:
MOV AX,SYS_PATCH_DS ; Load DS with the selector for our
MOV DS,AX ; data segment so we can set WORD_FLAG
MOV WORD_FLAG,1 ; Flag this as a word operation
JMP XMAIN ; Go do the "IN"
; Control comes here for an "IN" for a word with an immediate port value
INWIMMED:
MOV AX,SYS_PATCH_DS ; Load DS with the selector for our
MOV DS,AX ; data segment so we can set WORD_FLAG
MOV WORD_FLAG,1 ; Flag this as a word operation
; Control comes here for an "IN" for a byte with an immediate port value
INIMMED:
ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "IN" instruction
; Get the port address from the instruction. The port address is in the byte
; immediately following the "IN" op-code. We will load the port address into
; DX. This way when we join the code below it will look like the port address
; was in DX all along.
MOV AX,HUGE_PTR ; Load DS with a selector that accesses
MOV DS,AX ; all of memory as data
MOV SS:WORD PTR [BP+BP_IP2],0 ; Clear the high words of the V86
MOV SS:WORD PTR [BP+BP_CS2],0 ; task's CS and IP
DATAOV ; Load ESI (32 bit SI) with the V86
MOV SI,SS:[BP+BP_IP] ; task's IP
DATAOV
MOV AX,SS:[BP+BP_CS] ; Load EAX with the V86 task's CS
DATAOV ; and then shift left four bits to
SHL AX,4 ; convert it to an offset
DATAOV ; Add the CS offset to "IP" in SI
ADD SI,AX ; SI now contains CS:IP as a 32 bit
; offset from 0
ADDROV ; Get the byte after the "IN" instruc-
LODSB ; tion. This is the port address.
ADDROV ; Intel bug # A0-119
NOP ; Intel bug # A0-119
SUB DX,DX ; Clear DX to prepare for one byte move
MOV DL,AL ; DX now has the port address
; Control comes here for an "IN" for a byte with the port value in DX
XMAIN PROC NEAR
MOV AX,SYS_PATCH_DS ; Load DS with the selector for our
MOV DS,AX ; data segment
CMP DX,31A0H ; Is the port address below 31A0H?
JB NOTXMAIN ; Yup. Then it's not XMA.
CMP DX,31A7H ; Is the port address above 31A7H?
JA NOTXMAIN ; Yup. Then it's not XMA.
; It's an XMA port so lets do the "IN" for the guy.
AND XMATTAR,0FFFH ; First lets clear the high nibbles of
AND XMATID,0FH ; our ports. This insures that we
AND XMACTL,0FH ; have valid values in our ports.
LEA SI,XMATTAR ; Point SI to the port requested by
ADD SI,DX ; first pointing it to port 31A0H
SUB SI,31A0H ; and then adding on the difference
; between 31A0H and the requested port
CMP WORD_FLAG,0 ; Is this a word operation?
JNE GETWORD ; Yes. Then go get a word.
LODSB ; Else get a byte from the "port"
MOV BYTE PTR SS:[BP+BP_AX],AL ; Put it in the V86 task's AL register
JMP INEXIT ; Th-th-that's all folks!
; For non-XMA ports we just pass the "IN" on to INDEDMA
NOTXMAIN:
JMP DMAIN
; The "IN" is for a word
GETWORD:
LODSW ; Get a word from the "port"
MOV WORD PTR SS:[BP+BP_AX],AX ; Put it in the V86 task's AX register
MOV WORD_FLAG,0 ; Reset the word flag
CMP DX,31A4H ; Is this an "IN" from the auto-
; increment port?
JNE INEXIT ; Nope. Then just leave.
INC XMATTAR ; The "IN" is from the auto-increment
; port so increment the translate
CALL TTARCHANGED ; table index and call TTARCHANGED
; to update the status of the "card"
INEXIT:
ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the instruction (past
; the port value for immediate insts.)
JMP POPIO ; Go return to the V86 task
PAGE
; Control comes here for an "OUT" for a word with the port value in DX
OUTW:
MOV AX,SYS_PATCH_DS ; Load DS with the selector for our
MOV DS,AX ; data segment so we can set WORD_FLAG
MOV WORD_FLAG,1 ; Flag this as a word operation
JMP XMAOUT ; Go do the "OUT"
; Control comes here for an "OUT" for a word with an immediate port value
OUTWIMMED:
MOV AX,SYS_PATCH_DS ; Load DS with the selector for our
MOV DS,AX ; data segment so we can set WORD_FLAG
MOV WORD_FLAG,1 ; Flag this as a word operation
; Control comes here for an "OUT" for a byte with an immediate port value
XMAOUTIMMED:
ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "OUT" instruction
; Get the port address from the instruction. The port address is in the byte
; immediately following the "OUT" op-code. We will load the port address into
; DX. This way when we join the code below it will look like the port address
; was in DX all along.
MOV AX,HUGE_PTR ; Load DS with a selector that accesses
MOV DS,AX ; all of memory as data
MOV SS:WORD PTR [BP+BP_IP2],0 ; Clear the high words of the V86
MOV SS:WORD PTR [BP+BP_CS2],0 ; task's CS and IP
DATAOV ; Load ESI (32 bit SI) with the V86
MOV SI,SS:[BP+BP_IP] ; task's IP
DATAOV
MOV AX,SS:[BP+BP_CS] ; Load EAX with the V86 task's CS
DATAOV ; and then shift left four bits to
SHL AX,4 ; convert it to an offset
DATAOV ; Add the CS offset to "IP" in SI
ADD SI,AX ; SI now contains CS:IP as a 32 bit
; offset from 0
ADDROV ; Get the byte after the "OUT" instruc-
LODSB ; tion. This is the port address.
ADDROV ; Intel bug # A0-119
NOP ; Intel bug # A0-119
SUB DX,DX ; Clear DX to prepare for one byte move
MOV DL,AL ; DX now has the port address
; Control comes here for an "OUT" for a byte with the port value in DX
XMAOUT:
MOV AX,SYS_PATCH_DS ; Load DS and ES with the selector for
MOV DS,AX ; our data area
MOV ES,AX
CMP DX,31A0H ; Is the port address below 31A0H?
JB NOTXMAOUT ; Yes. Then it's not XMA.
CMP DX,31A7H ; Is the port address above 31A7H?
JA NOTXMAOUT ; Yes. Then it's not XMA.
LEA DI,XMATTAR ; Point SI to the port requested by
ADD DI,DX ; first pointing it to port 31A0H
SUB DI,31A0H ; and then adding on the difference
; between 31A0H and the requested port
CMP WORD_FLAG,0 ; Is this a word operation?
JNE PUTWORD ; Yes. Then go put a word.
MOV AL,BYTE PTR SS:[BP+BP_AX] ; Put the value in the V86 task's AL
STOSB ; register into the "port"
CMP DX,31A6H ; Is this "OUT" to the bank ID port?
JE CHKCNTRL ; If so, go set the new bank
CMP DX,31A7H ; Is the "OUT" to the control port?
JE CHKCNTRL ; Affirmative. Go handle control bits.
CMP DX,31A1H ; Is this "OUT" to the TT index?
; (high byte)
JBE TTAROUT ; Yup. Go update dependent fields.
JMP OUTEXIT ; Any other ports just exit.
; The "OUT" is for a word
PUTWORD:
MOV AX,WORD PTR SS:[BP+BP_AX] ; Put the value in the V86 task's AX
STOSW ; register into the "port"
MOV WORD_FLAG,0 ; Reset the word flag
CMP DX,31A0H ; Is the "OUT" to the TT index port?
JE TTAROUT ; Si. Go update the dependent fields.
CMP DX,31A2H ; Is the "OUT" to set a block number?
JNE CHKA4 ; No. Go do some more checks.
MOV XMATTII,AX ; The "OUT" is to 31A2H. Set the auto-
; increment port to the same value.
; The two ports should always be in
; sync.
CALL UPDATETT ; Update the "translate table" with the
; new block number
JMP OUTEXIT ; That's it. Let's leave.
CHKA4:
CMP DX,31A4H ; Is "OUT" to the auto-increment port
JNE CHKCNTRL ; No. Then it must be to the bank ID/
; control byte port (31A6H).
MOV XMATTIO,AX ; The "OUT is to the auto-increment port
CALL UPDATETT ; Update the "translate table"
INC XMATTAR ; Increment the translate table index
CALL TTARCHANGED ; Update fields that depend on the
; translate table index
JMP OUTEXIT ; And return to the V86 task
; The translate table index was changed
TTAROUT:
CALL TTARCHANGED ; Update fields that depend on the
; setting of the translate table index
JMP OUTEXITDMA ; Skip flushing the page-translation
; cache since the page tables have
; not changed.
; It's not an XMA "OUT" so pass it on to INDEDMA
NOTXMAOUT:
JMP DMAOUT
; The "OUT" is to the bank ID port (31A6H), the control port (31A7H) or both
CHKCNTRL:
TEST XMACTL,02H ; Is the virtual enable bit on?
JNZ SETXMA ; Aye. Go make the specified XMA bank
; active.
DATAOV ; Nay. We simulate disabling the XMA
MOV AX,NORMPAGE ; card by making the normal page
; tables active.
MOV DI,SGTBLOFF ; This is done by setting the first
DATAOV ; entry in the page directory to
STOSW ; point to the page table for normal
; memory.
JMP OUTEXIT ; Return to the V86 task
SETXMA:
AND XMATID,0FH ; Wipe out the high nibble of the bank
MOV AL,XMATID ; ID. XMA only has 16 banks.
DATAOV ; Now multiply by 4K (shift left 12 ;P1C
SHL AX,28 ; bits) to get the offset from the
DATAOV ; base of the XMA page tables of the
SHR AX,28-12 ; page table for the requested bank.
; Page tables are 4K in length. In
; the process of shifting we shift the
; high order 16 bits off the left end
; of EAX so that they are 0 when we
; shift back.
DATAOV ; Add on the offset of the base of the
ADD AX,XMAPAGE ; page tables. EAX now has the offset
; of the page table for the XMA bank.
MOV DI,SGTBLOFF ; Point to the first entry in the page
; directory.
DATAOV ; Set the first entry in the page
STOSW ; directory to point to the XMA page
; table
; Since the page tables have changed we need to purge the page-translation
; cache. "For greatest efficiency in address translation, the processor
; stores the most recently used page-table data in an on-chip cache... The
; existence of the page-translation cache is invisible to applications
; programmers but not to systems programmers; operating-system programmers
; must flush the cache whenever the page tables are changed."
; -- 80386 Programmer's Reference Manual (C) Intel 1986
OUTEXIT:
CMOV EAX,CR3 ; Get the page directory base register
NOP ; 386 errata B0-110
CMOV CR3,EAX ; Write it back to reset the cache
NOP ; 386 errata B0-110
OUTEXITDMA:
ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "OUT" instruction
JMP POPIO ; Return to the V86 task
PAGE
; TTARCHANGED updates all the fields that depend on the translate table index
; in port 31A0H. This is mainly getting the translate table entries for the
; specified index and putting them in the block number ports 31A2H and 31A4H.
TTARCHANGED PROC
MOV AX,XMATTAR ; Get the new translate table index
AND AX,0FFFH ; The high nibble is not used
SHL AX,1 ; Change it to a word index. The
; translate table entries are words.
LEA SI,TTTABLE ; Point SI to the translate table base
ADD SI,AX ; Add on the offset into the table
LODSW ; Get the XMA block number for the
; specified translate table entry
MOV XMATTIO,AX ; Put it into "port" 31A2H
MOV XMATTII,AX ; Put it into "port" 31A4H
RET
TTARCHANGED ENDP
PAGE
; UPDATETT will update the "translate table" and the corresponding page
; tables when an XMA block number is written to either port 31A2H or the
; auto-increment port 31A4H. A write to either of these ports means to set
; the XMA block specified at the translate table entry indicated in port
; 31A0H.
;
; The Emulator is set up to look like an XMA 2 card with the INDXMAA device D1C
; driver. When the system comes up the XMA card is initially disabled. D1C
; INDXMAA then backs memory from 0K to 640K on the system board with memory D1C
; from 0K to 640K on the XMA card. To emulate this, the Emulator treats D1C
; real memory from 0K to 640K as XMA blocks from 0K to 640K on the XMA card. D1C
; This both saves memory and requires no code to back the real memory from D1C
; 0K to 640K with XMA memory on initialization. The Emulator therefore only D1C
; needs to allocate XMA memory for the XMA blocks over 640K. The XMA memory D1C
; for over 640K starts at 12000:0. The XMA blocks 00H to 9FH will be mapped D1C
; to the motherboard memory at 0K to 640K. The XMA blocks A0H and up will D1C
; be mapped to the memory at 12000:0 and up. D1C
;
; Bits 15 (IBM bit 0) and 11 (IBM bit 4) of the XMA block number have
; special meanings. When bit 15 is on it means that the block number is a
; 15 bit number. This is in anticipation of larger block numbers in the
; future. Current block numbers are 11 bits. When bit 11 is on it means
; that the XMA translation for this translation table entry should be
; disabled. The memory for this 4K block should be mapped back to real
; memory.
;
; We also check to make sure that the XMA block is not above the XMA memory
; limit. XMA memory ends where the MOVEBLOCK buffer starts. If the XMA
; block is above the end of XMA memory then the page table entry for that
; address is set to point to non-existent memory.
;
; When address translation is disabled for addresses above 640K then the D1C
; page table entry for that address is set to point back to real memory. D1C
; For disabled pages in the range 0K to 640K the page table entry is set to D1C
; point to non-existent memory. D1C
UPDATETT PROC
MOV AX,XMATTAR ; Get the index of the TT entry that
; is to be changed
AND AX,0FFFH ; Clear the high four bits. They are
; not used.
SHL AX,1 ; Change to a word offset since the TT
; entries are words.
LEA DI,TTTABLE ; Point DI to the translate table base
ADD DI,AX ; Add on the offset of the entry that
; is to be changed
MOV AX,XMATTIO ; Get the block number to be written
STOSW ; Store the block number in the TT
; Convert bank number to a page address.
; The following code works only with paging enabled at 256k boundary.
; It is intended to support up to 128M at 4k granularity.
; It interprets the high order bits as a superset of XMA.
; Following is a truth table for bits 11 (XMA inhibit bit) and 15 ("enable-hi").
; 15 11
; 0 0 = enabled 11 bit address
; 0 1 = disabled address
; 1 x = enabled 15 bit address
TEST AH,80H ; Is this a 15 bit block number?
JZ SMALL ; Far from it. Go do stuff for 11 bit
; block numbers.
; We have a 15 bit address
CMP AX,0FFFFH ; If it's FFFFH then we treat it the
; the same as 0FFFH which means
JE DISABLEPAGE ; disable the page
AND AX,7FFFH ; Turn off the 15 bit address bit
JMP BOTH ; leaving a valid block number for
; our calculations later
SMALL:
TEST AH,08H ; Is the disable bit on?
JNZ DISABLEPAGE ; Yes. Go disable the page.
AND AX,07FFH ; No. Turn off the high nibble and the
; disable bit leaving a valid block
; number for our upcoming calculations
BOTH:
CMP AX,640/4 ; Is this block number for 640K or over?
JB NOADJUST ; Yup. There's no adjustment @D1C
; needed for blocks between 0K and
; 640K since we use real memory for
; these blocks.
; XMA 1 emulation code deleted 3@D1D
ADD AX,HIMEM-(640/4) ; Add on the adjustment needed for @D1C
; blocks above 640K to point to
; the XMA blocks starting at 12000:0.
; But don't forget to subtract the
; block number for 640K. This makes
; the block number 0 based before we
; add on the block number for 12000:0.
NOADJUST:
DATAOV ; Shift the high order 16 bits of EAX
SHL AX,16 ; off the left end of the register.
DATAOV ; Now shift the block number back four
SHR AX,16-12 ; bits. This results in a net shift
; left of 12 bits which converts the
; block number to an offset, and it
; clears the high four bits.
OR AL,7 ; Set the access and present bits. This
; converts our offset to a valid page
; table entry.
DATAOV ; Save the page table entry in EBX for
MOV BX,AX ; now
; Now we must make sure the offset of our XMA page frame is within the address
; space of the XMA pages, that is, it is below the start of the MOVEBLOCK
; buffer.
DATAOV ; Clear all 32 bits of EAX
SUB AX,AX
MOV AX,MAXMEM ; Load up the number of K on the box
SUB AX,BUFF_SIZE ; Subtract the number of K reserved
; for the MOVEBLOCK buffer
DATAOV ; Multiply by 1K (shift left 10) to
SHL AX,10 ; convert it to an offset
DATAOV ; Is the XMA page address below the
CMP BX,AX ; MOVEBLOCK buffer address?
JB ENABLED ; Yup. Whew! Let's go set up the page
; table entry for this XMA block.
JMP EMPTY ; Nope. Rats! Well, we'll just have
; to point this TT entry to unbacked
; memory.
; We come here when we want to disable translation for this translate table
; entry. For TT entries for 640K and over we just point the translate table
; entry back to real memory. For TT entries between 0K and 640K we point
; the translate table to unbacked memory. This memory on the motherboard
; was disabled under real XMA so we emulate it by pointing to unbacked
; memory.
DISABLEPAGE:
; XMA 1 emulation code deleted 2@D1D
CMP BYTE PTR XMATTAR,640/4 ; Is the address at 640K or above?
JNB SPECIAL ; Aye. Go point back to real memory.
; The address is between 256K and 640K. Let's set the page table entry to
; point to non-exiatent memory.
EMPTY:
DATAOV ; Clear EAX
SUB AX,AX
MOV AX,MAXMEM ; Get the total number of K on the box
DATAOV ; Multiply by 1024 to convert to an
SHL AX,10 ; offset. AX now points to the 4K
; page frame after the end of memory.
OR AL,7 ; Turn on the accessed and present bits
; to avoid page faults
DATAOV ; Save the page table entry in EBX
MOV BX,AX
JMP ENABLED ; Go set up the page table
; If the address is above 640K then the translate table (page table) entry D1C
; is set to point back to real memory.
SPECIAL:
MOV AX,XMATTAR ; Get the index of the TT entry that is
; to be disabled
DATAOV ; Dump the high 24 bits off the left end
SHL AX,24 ; of the register
DATAOV ; Now shift it back 12 bits. This
SHR AX,24-12 ; results in a net shift left of 12
; bits which multiplies the TT index
; by 4K while at the same time clear-
; ing the high order bits. EAX is now
; the offset of the memory pointed to
; by the TT entry.
OR AL,7 ; Turn on the accessed and present bits
; to make this a page table entry
DATAOV ; Save the page table entry in EBX
MOV BX,AX
; Now let's put the new page table entry in EBX, which represents the XMA block,
; into the page table.
ENABLED:
MOV AX,XMATTAR ; Get the index of the TT entry
; Now we want ot convert the index of the TT entry to an offset into our XMA
; page tables. The bank number is now in AH and the 4K block number of the
; bank is in AL. To point to the right page table we need to multiply the bank
; number by 4K (shift left 12 bits) since page tables are 4K in length. The
; bank number is already shifted left 8 bits by virtue of it being in AH. It
; needs to be shifted left four more bits. In order to access the right entry
; in the page table, the block number in AL must be multiplied by 4 (shifted
; left two bits) because the page table entries are 4 bytes in length. So,
; first we shift AH left two bits and then shift AX left two bits. In the end
; this shifts AH, the bank ID, left four bits and shifts AL, the block number,
; two bits, which is what we wanted. A long explanation for two instructions,
; but they're pretty efficient, don't you think?
SHL AH,2 ; Shift the bank ID left two bits
SHL AX,2 ; Shift the bank ID and the block number
; left two bits
MOV DI,AX ; Load DI with the offset of the page
; table entry that is to be changed
PUSH ES ; Save ES
MOV AX,XMA_PAGES_SEL ; Load ES with the selector for our
MOV ES,AX ; XMA page tables. Now ES:DI points
; to the page table entry to be
; changed
DATAOV ; Get the new value for the page table
MOV AX,BX ; entry which was saved in EBX.
DATAOV ; Stuff it into the page table - all
STOSW ; 32 bits of it.
POP ES ; Restore ES
RET ; And return
UPDATETT ENDP
XMAIN ENDP
PROG ENDS
END
|