2 TITLE INDEXMA
- 386 XMA Emulator
- XMA Emulation
5 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7 * MODULE NAME
: INDEXMA
*
9 * 5669-196 (C
) COPYRIGHT
1988 Microsoft Corp
. *
11 * DESCRIPTIVE NAME
: Do the XMA emulation for the
80386 XMA Emulator
*
13 * STATUS
(LEVEL
) : Version
(0) Level
(1.0) *
15 * FUNCTION
: This module does the actual manipulation of the page
*
16 * tables to emulate the XMA card
. Using the
80386 *
17 * paging mechanism we let the 4K page frames represent
*
18 * the 4K XMA blocks
on the XMA card
. We let the page
*
19 * tables represent the translate table
. *
21 * The XMA
"blocks" start
at address
12000:0. The D1C
*
22 * Emulator emulates the XMA
2 card with the INDXMAA D1C
*
23 * device driver
. On initial power up
, the XMA
2 card is D1C
*
24 * disabled
. The INDXMAA device driver then disables the D1C
*
25 * memory
from 0K to 640K
and backs it with memory
from D1C
*
26 * 0K to 640K
on the XMA
2 card
. The Emulator looks like D1C
*
27 * it does the same thing
. The XMA blocks for 0K to 640K D1C
*
28 * are taken
from the system board memory
from 0K to D1C
*
29 * 640K
. This memory
on the motherboard is treated
as D1C
*
30 * XMA memory
. This emulates the INDXMAA device driver
's D1C
*
31 * mapping of 0K to 640K
on the XMA card to real memory
. D1C
*
32 * The XMA
"blocks" for 640K
and up are located
in high D1C
*
33 * memory starting
at 12000:0. These
"blocks" run up to D1C
*
34 * the start of the MOVEBLOCK buffer
. The MOVEBLOCK D1C
*
35 * buffer is a chunk of storage
(in 16K multiples
) at the D1C
*
36 * end of available memory that is reserved for the D1C
*
37 * MOVEBLOCK functions
. D1C
*
39 * The page tables are
used to emulate the translate
*
40 * table
. By setting the address of the XMA
"block" into *
41 * the page table
entry for a specific page frame we can
*
42 * make that address access that particular XMA page
*
43 * frame
. To the user this looks just like the translate
*
46 * The tricky part comes
in disabling pages
(blocks
). On D1C
*
47 * the XMA
2 card
, when a translate table
entry is D1C
*
48 * disabled the addresses for that address range go to D1C
*
49 * real memory
. If the address is between 0K
and 640K D1C
*
50 * then any access of that storage gets nothing because D1C
*
51 * there is no memory backed
from 0K to 640K
on the real D1C
*
52 * system
. All other addresses go to real memory
. So D1C
*
53 * when the user disables translation of a translate D1C
*
54 * table
entry we need to check what range that
entry D1C
*
55 * covers
. If the
entry points to somewhere between 0K D1C
*
56 * and 640K then we will set the page table
entry that D1C
*
57 * corresponds to the translate table
entry to point to D1C
*
58 * non
-existent memory
. For all other addresses we will D1C
*
59 * just point the page table
entry back to the real D1C
*
60 * memory
at that address
. D1C
*
62 * This module receives control
on all
"IN"s
and "OUT"s
*
63 * done by the user
. If the
"IN" or "OUT" is
not to an
*
64 * XMA port then it passes the I
/O
on to INDEDMA
. If it
*
65 * is for an XMA port then the request is handled here
. *
67 * This module keeps its own copies of the XMA registers
*
68 * and the translate table
. When any I
/O comes for the
*
69 * XMA card it updates its copies of the registers
and *
70 * the translate table
. Then it does any needed
*
71 * modifications
on the page tables to emulate the XMA
*
76 * REGISTER USAGE
: 80386 Standard
*
78 * RESTRICTIONS
: None
*
80 * DEPENDENCIES
: None
*
82 * ENTRY POINTS
: INW
- Emulate
"IN" for a
word with port number
*
84 * INWIMMED
- Emulate
"IN" for a
word with an immediate
*
86 * INIMMED
- Emulate
"IN" for a
byte with an immediate
*
88 * XMAIN
- Emulate
"OUT" for a
byte with port number
*
90 * OUTW
- Emulate
"OUT" for a
word with port number
*
92 * OUTWIMMED
- Emulate
"OUT" for a
word with an immediate
*
94 * XMAOUTIMMED
- Emulate
"OUT" for a
byte with an immediate
*
96 * XMAOUT
- Emulate
"OUT" for a
byte with port number
*
99 * LINKAGE
: Jumped to by INDEEXC
*
101 * INPUT PARMS
: None
*
103 * RETURN PARMS
: None
*
105 * OTHER EFFECTS
: None
*
107 * EXIT NORMAL
: Go to POPIO
in INDEEMU to
IRET to the V86 task
*
109 * EXIT ERROR
: None
*
112 * REFERENCES
: POPIO
:NEAR - Entry in INDEEMU to return to V86 task
*
113 * HEXW
:NEAR - Entry in INDEEXC to
display word in AX *
114 * DMAIN
:NEAR - Entry in INDEDMA to
"IN" from DMA port
*
115 * DMAOUT
:NEAR - Entry in INDEDMA to
"OUT" to DMA port
*
116 * PGTBLOFF
:WORD - Offset of the normal page tables
*
117 * SGTBLOFF
:WORD - Offset of the page directory
*
118 * NORMPAGE
:WORD - Entry for the 1
st page directory
entry *
119 * so that it points to the normal
*
121 * XMAPAGE
:WORD - Entry for the 1
st page directory
entry *
122 * that points to the XMA page tables
*
123 * TTTABLE
:WORD - The translate table
*
124 * BUFF_SIZE
:WORD - Size of the MOVEBLOCK buffer
*
125 * MAXMEM
:WORD - Number of kilobytes
on this machine
*
127 * SUB-ROUTINES
: TTARCHANGED
- Put the block number
at the translate table
*
128 * entry in 31A0H
into "ports" 31A2H
and 31A4H
*
129 * UPDATETT
- Update the translate table
and page tables
*
130 * to reflect the new block number written to
*
131 * either 31A2H
or 31A4H
*
133 * MACROS
: DATAOV
- Add prefix for the next instruction so that it
*
134 * accesses
data as 32 bits wide
*
135 * ADDROV
- Add prefix for the next instruction so that it
*
136 * uses addresses that are
32 bits wide
*
137 * CMOV
- Move to
and from control registers
*
139 * CONTROL BLOCKS
: INDEDAT
.INC - system
data structures
*
141 * CHANGE ACTIVITY
: *
143 * $
MOD(INDEXMA
) COMP
(LOAD) PROD
(3270PC
) : *
145 * $D0=D0004700
410 870530 D
: NEW FOR RELEASE
1.1 *
146 * $P1
=P0000293
410 870731 D
: LIMIT LINES TO
80 CHARACTERS
*
147 * $P2
=P0000312
410 870804 D
: CHANGE COMPONENT
FROM MISC TO
LOAD *
148 * $D1=D0007100
410 870810 D
: CHANGE TO EMULATE XMA
2 *
150 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
153 .286P
; Enable recognition of 286 privileged instructs.
155 .XLIST
; Turn off the listing
156 INCLUDE INDEDAT
.INC ; Include system data structures
158 IF1
; Only include macros on the first pass
162 .LIST
; Turn on the listing
164 CRT_SELECTOR EQU
00030H ; Selector for mono display buffer
165 SEX_ATTR EQU
04B00H ; Attribute for turquoise on red
166 STACK_ATTR EQU
00700H ; Attribute for white o black
167 BLANK EQU
00020H ; ASCII for a blank
168 XMA_PAGES_SEL EQU RSDA_PTR
; Selector for the XMA pages
169 HIMEM EQU 120H
; Adjustment for XMA pages >= 640K. @D1C
170 ; They start at address 12000:0.
171 ; It's the block number corresponding
172 ; to address 12000:0.
175 PROG
SEGMENT PARA
PUBLIC 'PROG'
184 ; The following entry points are in other modules
186 EXTRN POPIO
:NEAR ; Return to V86 task - in INDEEMU
187 EXTRN HEXW
:NEAR ; Display word in AX - in INDEEXC
188 EXTRN DMAIN
:NEAR ; "IN" from DMA port - in INDEDMA
189 EXTRN DMAOUT
:NEAR ; "OUT" to DMA port - in INDEDMA
191 ; The following data are in INDEI15.ASM
193 EXTRN PGTBLOFF
:WORD ; Offset of the normal page tables
194 EXTRN SGTBLOFF
:WORD ; Offset of the page directory
195 EXTRN NORMPAGE
:WORD ; Entry for the 1st page directory entry
196 ; so that it points to the normal
198 EXTRN XMAPAGE
:WORD ; Entry for the 1st page directory entry
199 ; that points to the XMA page tables
200 EXTRN TTTABLE
:WORD ; The translate table
201 EXTRN BUFF_SIZE
:WORD ; Size of the MOVEBLOCK buffer
202 EXTRN MAXMEM
:WORD ; Number of kilobytes on this machine
204 ; Let the following entries be known to other modules
217 ; Let the following data be known to other modules
226 ; The following XMA labels represent the XMA ports starting at 31A0H.
227 ; THEY MUST BE KEPT IN THE FOLLOWING ORDER.
229 XMATTAR
DW 0 ; Port 31A0H - Translate table index
230 XMATTIO
DW 0 ; Port 31A2H - XMA block number
231 XMATTII
DW 0 ; Port 31A4H - Block number with auto-increment
232 XMATID
DB 0 ; Port 31A6H - Bank ID
233 XMACTL
DB 02H ; Port 31A7H - Control flags. Virtual @D1C
234 ; enable bit is initially on.
236 ; How about some flags?
238 WORD_FLAG
DB 0 ; If set to 1 then I/O is for a word.
239 ; Else, it's for a byte
243 ; Control comes here for an "IN" for a word with the port value in DX
246 MOV AX,SYS_PATCH_DS
; Load DS with the selector for our
247 MOV DS,AX ; data segment so we can set WORD_FLAG
248 MOV WORD_FLAG
,1 ; Flag this as a word operation
249 JMP XMAIN
; Go do the "IN"
251 ; Control comes here for an "IN" for a word with an immediate port value
254 MOV AX,SYS_PATCH_DS
; Load DS with the selector for our
255 MOV DS,AX ; data segment so we can set WORD_FLAG
256 MOV WORD_FLAG
,1 ; Flag this as a word operation
258 ; Control comes here for an "IN" for a byte with an immediate port value
262 ADD WORD PTR SS:[BP+BP_IP
],1 ; Step IP past the "IN" instruction
264 ; Get the port address from the instruction. The port address is in the byte
265 ; immediately following the "IN" op-code. We will load the port address into
266 ; DX. This way when we join the code below it will look like the port address
267 ; was in DX all along.
269 MOV AX,HUGE_PTR
; Load DS with a selector that accesses
270 MOV DS,AX ; all of memory as data
272 MOV SS:WORD PTR [BP+BP_IP2
],0 ; Clear the high words of the V86
273 MOV SS:WORD PTR [BP+BP_CS2
],0 ; task's CS and IP
275 DATAOV
; Load ESI (32 bit SI) with the V86
276 MOV SI,SS:[BP+BP_IP
] ; task's IP
278 MOV AX,SS:[BP+BP_CS
] ; Load EAX with the V86 task's CS
279 DATAOV
; and then shift left four bits to
280 SHL AX,4 ; convert it to an offset
281 DATAOV
; Add the CS offset to "IP" in SI
282 ADD SI,AX ; SI now contains CS:IP as a 32 bit
284 ADDROV
; Get the byte after the "IN" instruc-
285 LODSB ; tion. This is the port address.
287 ADDROV
; Intel bug # A0-119
288 NOP ; Intel bug # A0-119
290 SUB DX,DX ; Clear DX to prepare for one byte move
291 MOV DL,AL ; DX now has the port address
293 ; Control comes here for an "IN" for a byte with the port value in DX
297 MOV AX,SYS_PATCH_DS
; Load DS with the selector for our
298 MOV DS,AX ; data segment
300 CMP DX,31A0H
; Is the port address below 31A0H?
301 JB NOTXMAIN
; Yup. Then it's not XMA.
303 CMP DX,31A7H
; Is the port address above 31A7H?
304 JA NOTXMAIN
; Yup. Then it's not XMA.
306 ; It's an XMA port so lets do the "IN" for the guy.
308 AND XMATTAR
,0FFFH ; First lets clear the high nibbles of
309 AND XMATID
,0FH ; our ports. This insures that we
310 AND XMACTL
,0FH ; have valid values in our ports.
312 LEA SI,XMATTAR
; Point SI to the port requested by
313 ADD SI,DX ; first pointing it to port 31A0H
314 SUB SI,31A0H
; and then adding on the difference
315 ; between 31A0H and the requested port
316 CMP WORD_FLAG
,0 ; Is this a word operation?
317 JNE GETWORD
; Yes. Then go get a word.
319 LODSB ; Else get a byte from the "port"
320 MOV BYTE PTR SS:[BP+BP_AX
],AL ; Put it in the V86 task's AL register
321 JMP INEXIT
; Th-th-that's all folks!
323 ; For non-XMA ports we just pass the "IN" on to INDEDMA
328 ; The "IN" is for a word
331 LODSW ; Get a word from the "port"
332 MOV WORD PTR SS:[BP+BP_AX
],AX ; Put it in the V86 task's AX register
334 MOV WORD_FLAG
,0 ; Reset the word flag
336 CMP DX,31A4H
; Is this an "IN" from the auto-
338 JNE INEXIT
; Nope. Then just leave.
340 INC XMATTAR
; The "IN" is from the auto-increment
341 ; port so increment the translate
342 CALL TTARCHANGED
; table index and call TTARCHANGED
343 ; to update the status of the "card"
345 ADD WORD PTR SS:[BP+BP_IP
],1 ; Step IP past the instruction (past
346 ; the port value for immediate insts.)
347 JMP POPIO
; Go return to the V86 task
351 ; Control comes here for an "OUT" for a word with the port value in DX
354 MOV AX,SYS_PATCH_DS
; Load DS with the selector for our
355 MOV DS,AX ; data segment so we can set WORD_FLAG
356 MOV WORD_FLAG
,1 ; Flag this as a word operation
357 JMP XMAOUT
; Go do the "OUT"
359 ; Control comes here for an "OUT" for a word with an immediate port value
362 MOV AX,SYS_PATCH_DS
; Load DS with the selector for our
363 MOV DS,AX ; data segment so we can set WORD_FLAG
364 MOV WORD_FLAG
,1 ; Flag this as a word operation
366 ; Control comes here for an "OUT" for a byte with an immediate port value
370 ADD WORD PTR SS:[BP+BP_IP
],1 ; Step IP past the "OUT" instruction
372 ; Get the port address from the instruction. The port address is in the byte
373 ; immediately following the "OUT" op-code. We will load the port address into
374 ; DX. This way when we join the code below it will look like the port address
375 ; was in DX all along.
377 MOV AX,HUGE_PTR
; Load DS with a selector that accesses
378 MOV DS,AX ; all of memory as data
380 MOV SS:WORD PTR [BP+BP_IP2
],0 ; Clear the high words of the V86
381 MOV SS:WORD PTR [BP+BP_CS2
],0 ; task's CS and IP
383 DATAOV
; Load ESI (32 bit SI) with the V86
384 MOV SI,SS:[BP+BP_IP
] ; task's IP
386 MOV AX,SS:[BP+BP_CS
] ; Load EAX with the V86 task's CS
387 DATAOV
; and then shift left four bits to
388 SHL AX,4 ; convert it to an offset
389 DATAOV
; Add the CS offset to "IP" in SI
390 ADD SI,AX ; SI now contains CS:IP as a 32 bit
392 ADDROV
; Get the byte after the "OUT" instruc-
393 LODSB ; tion. This is the port address.
395 ADDROV
; Intel bug # A0-119
396 NOP ; Intel bug # A0-119
398 SUB DX,DX ; Clear DX to prepare for one byte move
399 MOV DL,AL ; DX now has the port address
401 ; Control comes here for an "OUT" for a byte with the port value in DX
404 MOV AX,SYS_PATCH_DS
; Load DS and ES with the selector for
405 MOV DS,AX ; our data area
408 CMP DX,31A0H
; Is the port address below 31A0H?
409 JB NOTXMAOUT
; Yes. Then it's not XMA.
411 CMP DX,31A7H
; Is the port address above 31A7H?
412 JA NOTXMAOUT
; Yes. Then it's not XMA.
414 LEA DI,XMATTAR
; Point SI to the port requested by
415 ADD DI,DX ; first pointing it to port 31A0H
416 SUB DI,31A0H
; and then adding on the difference
417 ; between 31A0H and the requested port
418 CMP WORD_FLAG
,0 ; Is this a word operation?
419 JNE PUTWORD
; Yes. Then go put a word.
421 MOV AL,BYTE PTR SS:[BP+BP_AX
] ; Put the value in the V86 task's AL
422 STOSB ; register into the "port"
424 CMP DX,31A6H
; Is this "OUT" to the bank ID port?
425 JE CHKCNTRL
; If so, go set the new bank
427 CMP DX,31A7H
; Is the "OUT" to the control port?
428 JE CHKCNTRL
; Affirmative. Go handle control bits.
430 CMP DX,31A1H
; Is this "OUT" to the TT index?
432 JBE TTAROUT
; Yup. Go update dependent fields.
434 JMP OUTEXIT
; Any other ports just exit.
436 ; The "OUT" is for a word
439 MOV AX,WORD PTR SS:[BP+BP_AX
] ; Put the value in the V86 task's AX
440 STOSW ; register into the "port"
442 MOV WORD_FLAG
,0 ; Reset the word flag
444 CMP DX,31A0H
; Is the "OUT" to the TT index port?
445 JE TTAROUT
; Si. Go update the dependent fields.
447 CMP DX,31A2H
; Is the "OUT" to set a block number?
448 JNE CHKA4
; No. Go do some more checks.
450 MOV XMATTII
,AX ; The "OUT" is to 31A2H. Set the auto-
451 ; increment port to the same value.
452 ; The two ports should always be in
454 CALL UPDATETT
; Update the "translate table" with the
456 JMP OUTEXIT
; That's it. Let's leave.
459 CMP DX,31A4H
; Is "OUT" to the auto-increment port
460 JNE CHKCNTRL
; No. Then it must be to the bank ID/
461 ; control byte port (31A6H).
462 MOV XMATTIO
,AX ; The "OUT is to the auto-increment port
463 CALL UPDATETT
; Update the "translate table"
464 INC XMATTAR
; Increment the translate table index
465 CALL TTARCHANGED
; Update fields that depend on the
466 ; translate table index
467 JMP OUTEXIT
; And return to the V86 task
469 ; The translate table index was changed
472 CALL TTARCHANGED
; Update fields that depend on the
473 ; setting of the translate table index
474 JMP OUTEXITDMA
; Skip flushing the page-translation
475 ; cache since the page tables have
478 ; It's not an XMA "OUT" so pass it on to INDEDMA
483 ; The "OUT" is to the bank ID port (31A6H), the control port (31A7H) or both
487 TEST XMACTL
,02H ; Is the virtual enable bit on?
488 JNZ SETXMA
; Aye. Go make the specified XMA bank
490 DATAOV
; Nay. We simulate disabling the XMA
491 MOV AX,NORMPAGE
; card by making the normal page
493 MOV DI,SGTBLOFF
; This is done by setting the first
494 DATAOV
; entry in the page directory to
495 STOSW ; point to the page table for normal
497 JMP OUTEXIT
; Return to the V86 task
500 AND XMATID
,0FH ; Wipe out the high nibble of the bank
501 MOV AL,XMATID
; ID. XMA only has 16 banks.
502 DATAOV
; Now multiply by 4K (shift left 12 ;P1C
503 SHL AX,28 ; bits) to get the offset from the
504 DATAOV
; base of the XMA page tables of the
505 SHR AX,28-12 ; page table for the requested bank.
506 ; Page tables are 4K in length. In
507 ; the process of shifting we shift the
508 ; high order 16 bits off the left end
509 ; of EAX so that they are 0 when we
511 DATAOV
; Add on the offset of the base of the
512 ADD AX,XMAPAGE
; page tables. EAX now has the offset
513 ; of the page table for the XMA bank.
514 MOV DI,SGTBLOFF
; Point to the first entry in the page
516 DATAOV
; Set the first entry in the page
517 STOSW ; directory to point to the XMA page
520 ; Since the page tables have changed we need to purge the page-translation
521 ; cache. "For greatest efficiency in address translation, the processor
522 ; stores the most recently used page-table data in an on-chip cache... The
523 ; existence of the page-translation cache is invisible to applications
524 ; programmers but not to systems programmers; operating-system programmers
525 ; must flush the cache whenever the page tables are changed."
526 ; -- 80386 Programmer's Reference Manual (C) Intel 1986
529 CMOV
EAX,CR3 ; Get the page directory base register
530 NOP ; 386 errata B0-110
531 CMOV
CR3,EAX ; Write it back to reset the cache
532 NOP ; 386 errata B0-110
535 ADD WORD PTR SS:[BP+BP_IP
],1 ; Step IP past the "OUT" instruction
536 JMP POPIO
; Return to the V86 task
540 ; TTARCHANGED updates all the fields that depend on the translate table index
541 ; in port 31A0H. This is mainly getting the translate table entries for the
542 ; specified index and putting them in the block number ports 31A2H and 31A4H.
546 MOV AX,XMATTAR
; Get the new translate table index
547 AND AX,0FFFH ; The high nibble is not used
548 SHL AX,1 ; Change it to a word index. The
549 ; translate table entries are words.
550 LEA SI,TTTABLE
; Point SI to the translate table base
551 ADD SI,AX ; Add on the offset into the table
552 LODSW ; Get the XMA block number for the
553 ; specified translate table entry
554 MOV XMATTIO
,AX ; Put it into "port" 31A2H
555 MOV XMATTII
,AX ; Put it into "port" 31A4H
563 ; UPDATETT will update the "translate table" and the corresponding page
564 ; tables when an XMA block number is written to either port 31A2H or the
565 ; auto-increment port 31A4H. A write to either of these ports means to set
566 ; the XMA block specified at the translate table entry indicated in port
569 ; The Emulator is set up to look like an XMA 2 card with the INDXMAA device D1C
570 ; driver. When the system comes up the XMA card is initially disabled. D1C
571 ; INDXMAA then backs memory from 0K to 640K on the system board with memory D1C
572 ; from 0K to 640K on the XMA card. To emulate this, the Emulator treats D1C
573 ; real memory from 0K to 640K as XMA blocks from 0K to 640K on the XMA card. D1C
574 ; This both saves memory and requires no code to back the real memory from D1C
575 ; 0K to 640K with XMA memory on initialization. The Emulator therefore only D1C
576 ; needs to allocate XMA memory for the XMA blocks over 640K. The XMA memory D1C
577 ; for over 640K starts at 12000:0. The XMA blocks 00H to 9FH will be mapped D1C
578 ; to the motherboard memory at 0K to 640K. The XMA blocks A0H and up will D1C
579 ; be mapped to the memory at 12000:0 and up. D1C
581 ; Bits 15 (IBM bit 0) and 11 (IBM bit 4) of the XMA block number have
582 ; special meanings. When bit 15 is on it means that the block number is a
583 ; 15 bit number. This is in anticipation of larger block numbers in the
584 ; future. Current block numbers are 11 bits. When bit 11 is on it means
585 ; that the XMA translation for this translation table entry should be
586 ; disabled. The memory for this 4K block should be mapped back to real
589 ; We also check to make sure that the XMA block is not above the XMA memory
590 ; limit. XMA memory ends where the MOVEBLOCK buffer starts. If the XMA
591 ; block is above the end of XMA memory then the page table entry for that
592 ; address is set to point to non-existent memory.
594 ; When address translation is disabled for addresses above 640K then the D1C
595 ; page table entry for that address is set to point back to real memory. D1C
596 ; For disabled pages in the range 0K to 640K the page table entry is set to D1C
597 ; point to non-existent memory. D1C
601 MOV AX,XMATTAR
; Get the index of the TT entry that
603 AND AX,0FFFH ; Clear the high four bits. They are
605 SHL AX,1 ; Change to a word offset since the TT
607 LEA DI,TTTABLE
; Point DI to the translate table base
608 ADD DI,AX ; Add on the offset of the entry that
610 MOV AX,XMATTIO
; Get the block number to be written
611 STOSW ; Store the block number in the TT
613 ; Convert bank number to a page address.
614 ; The following code works only with paging enabled at 256k boundary.
615 ; It is intended to support up to 128M at 4k granularity.
616 ; It interprets the high order bits as a superset of XMA.
617 ; Following is a truth table for bits 11 (XMA inhibit bit) and 15 ("enable-hi").
619 ; 0 0 = enabled 11 bit address
620 ; 0 1 = disabled address
621 ; 1 x = enabled 15 bit address
623 TEST AH,80H
; Is this a 15 bit block number?
624 JZ SMALL
; Far from it. Go do stuff for 11 bit
627 ; We have a 15 bit address
629 CMP AX,0FFFFH ; If it's FFFFH then we treat it the
630 ; the same as 0FFFH which means
631 JE DISABLEPAGE
; disable the page
633 AND AX,7FFFH
; Turn off the 15 bit address bit
634 JMP BOTH
; leaving a valid block number for
635 ; our calculations later
638 TEST AH,08H ; Is the disable bit on?
639 JNZ DISABLEPAGE
; Yes. Go disable the page.
641 AND AX,07FFH ; No. Turn off the high nibble and the
642 ; disable bit leaving a valid block
643 ; number for our upcoming calculations
645 CMP AX,640/4 ; Is this block number for 640K or over?
646 JB NOADJUST
; Yup. There's no adjustment @D1C
647 ; needed for blocks between 0K and
648 ; 640K since we use real memory for
650 ; XMA 1 emulation code deleted 3@D1D
651 ADD AX,HIMEM
-(640/4) ; Add on the adjustment needed for @D1C
652 ; blocks above 640K to point to
653 ; the XMA blocks starting at 12000:0.
654 ; But don't forget to subtract the
655 ; block number for 640K. This makes
656 ; the block number 0 based before we
657 ; add on the block number for 12000:0.
659 DATAOV
; Shift the high order 16 bits of EAX
660 SHL AX,16 ; off the left end of the register.
661 DATAOV
; Now shift the block number back four
662 SHR AX,16-12 ; bits. This results in a net shift
663 ; left of 12 bits which converts the
664 ; block number to an offset, and it
665 ; clears the high four bits.
666 OR AL,7 ; Set the access and present bits. This
667 ; converts our offset to a valid page
669 DATAOV
; Save the page table entry in EBX for
672 ; Now we must make sure the offset of our XMA page frame is within the address
673 ; space of the XMA pages, that is, it is below the start of the MOVEBLOCK
676 DATAOV
; Clear all 32 bits of EAX
678 MOV AX,MAXMEM
; Load up the number of K on the box
679 SUB AX,BUFF_SIZE
; Subtract the number of K reserved
680 ; for the MOVEBLOCK buffer
681 DATAOV
; Multiply by 1K (shift left 10) to
682 SHL AX,10 ; convert it to an offset
683 DATAOV
; Is the XMA page address below the
684 CMP BX,AX ; MOVEBLOCK buffer address?
685 JB ENABLED
; Yup. Whew! Let's go set up the page
686 ; table entry for this XMA block.
687 JMP EMPTY
; Nope. Rats! Well, we'll just have
688 ; to point this TT entry to unbacked
691 ; We come here when we want to disable translation for this translate table
692 ; entry. For TT entries for 640K and over we just point the translate table
693 ; entry back to real memory. For TT entries between 0K and 640K we point
694 ; the translate table to unbacked memory. This memory on the motherboard
695 ; was disabled under real XMA so we emulate it by pointing to unbacked
699 ; XMA 1 emulation code deleted 2@D1D
700 CMP BYTE PTR XMATTAR
,640/4 ; Is the address at 640K or above?
701 JNB SPECIAL
; Aye. Go point back to real memory.
703 ; The address is between 256K and 640K. Let's set the page table entry to
704 ; point to non-exiatent memory.
709 MOV AX,MAXMEM
; Get the total number of K on the box
710 DATAOV
; Multiply by 1024 to convert to an
711 SHL AX,10 ; offset. AX now points to the 4K
712 ; page frame after the end of memory.
713 OR AL,7 ; Turn on the accessed and present bits
714 ; to avoid page faults
715 DATAOV
; Save the page table entry in EBX
717 JMP ENABLED
; Go set up the page table
719 ; If the address is above 640K then the translate table (page table) entry D1C
720 ; is set to point back to real memory.
722 MOV AX,XMATTAR
; Get the index of the TT entry that is
724 DATAOV
; Dump the high 24 bits off the left end
725 SHL AX,24 ; of the register
726 DATAOV
; Now shift it back 12 bits. This
727 SHR AX,24-12 ; results in a net shift left of 12
728 ; bits which multiplies the TT index
729 ; by 4K while at the same time clear-
730 ; ing the high order bits. EAX is now
731 ; the offset of the memory pointed to
733 OR AL,7 ; Turn on the accessed and present bits
734 ; to make this a page table entry
735 DATAOV
; Save the page table entry in EBX
738 ; Now let's put the new page table entry in EBX, which represents the XMA block,
739 ; into the page table.
741 MOV AX,XMATTAR
; Get the index of the TT entry
743 ; Now we want ot convert the index of the TT entry to an offset into our XMA
744 ; page tables. The bank number is now in AH and the 4K block number of the
745 ; bank is in AL. To point to the right page table we need to multiply the bank
746 ; number by 4K (shift left 12 bits) since page tables are 4K in length. The
747 ; bank number is already shifted left 8 bits by virtue of it being in AH. It
748 ; needs to be shifted left four more bits. In order to access the right entry
749 ; in the page table, the block number in AL must be multiplied by 4 (shifted
750 ; left two bits) because the page table entries are 4 bytes in length. So,
751 ; first we shift AH left two bits and then shift AX left two bits. In the end
752 ; this shifts AH, the bank ID, left four bits and shifts AL, the block number,
753 ; two bits, which is what we wanted. A long explanation for two instructions,
754 ; but they're pretty efficient, don't you think?
756 SHL AH,2 ; Shift the bank ID left two bits
757 SHL AX,2 ; Shift the bank ID and the block number
759 MOV DI,AX ; Load DI with the offset of the page
760 ; table entry that is to be changed
762 MOV AX,XMA_PAGES_SEL
; Load ES with the selector for our
763 MOV ES,AX ; XMA page tables. Now ES:DI points
764 ; to the page table entry to be
766 DATAOV
; Get the new value for the page table
767 MOV AX,BX ; entry which was saved in EBX.
768 DATAOV
; Stuff it into the page table - all
769 STOSW ; 32 bits of it.