summaryrefslogtreecommitdiff
path: root/v4.0/src/DEV/XMAEM/INDEXMA.ASM
diff options
context:
space:
mode:
authorGravatar Mark Zbikowski2024-04-25 21:24:10 +0100
committerGravatar Microsoft Open Source2024-04-25 22:32:27 +0000
commit2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch)
tree80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/DEV/XMAEM/INDEXMA.ASM
parentMerge pull request #430 from jpbaltazar/typoptbr (diff)
downloadms-dos-main.tar.gz
ms-dos-main.tar.xz
ms-dos-main.zip
MZ is back!HEADmain
Diffstat (limited to 'v4.0/src/DEV/XMAEM/INDEXMA.ASM')
-rw-r--r--v4.0/src/DEV/XMAEM/INDEXMA.ASM781
1 files changed, 781 insertions, 0 deletions
diff --git a/v4.0/src/DEV/XMAEM/INDEXMA.ASM b/v4.0/src/DEV/XMAEM/INDEXMA.ASM
new file mode 100644
index 0000000..c35dd24
--- /dev/null
+++ b/v4.0/src/DEV/XMAEM/INDEXMA.ASM
@@ -0,0 +1,781 @@
1PAGE 60,132
2TITLE INDEXMA - 386 XMA Emulator - XMA Emulation
3
4COMMENT #
5* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
6* *
7* MODULE NAME : INDEXMA *
8* *
9* 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. *
10* *
11* DESCRIPTIVE NAME: Do the XMA emulation for the 80386 XMA Emulator *
12* *
13* STATUS (LEVEL) : Version (0) Level (1.0) *
14* *
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. *
20* *
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*
38* *
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 *
44* table is active. *
45* *
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*
61* *
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. *
66* *
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 *
72* request. *
73* *
74* MODULE TYPE : ASM *
75* *
76* REGISTER USAGE : 80386 Standard *
77* *
78* RESTRICTIONS : None *
79* *
80* DEPENDENCIES : None *
81* *
82* ENTRY POINTS : INW - Emulate "IN" for a word with port number *
83* in DX *
84* INWIMMED - Emulate "IN" for a word with an immediate *
85* port number *
86* INIMMED - Emulate "IN" for a byte with an immediate *
87* port number *
88* XMAIN - Emulate "OUT" for a byte with port number *
89* in DX *
90* OUTW - Emulate "OUT" for a word with port number *
91* in DX *
92* OUTWIMMED - Emulate "OUT" for a word with an immediate *
93* port number *
94* XMAOUTIMMED - Emulate "OUT" for a byte with an immediate *
95* port number *
96* XMAOUT - Emulate "OUT" for a byte with port number *
97* in DX *
98* *
99* LINKAGE : Jumped to by INDEEXC *
100* *
101* INPUT PARMS : None *
102* *
103* RETURN PARMS : None *
104* *
105* OTHER EFFECTS : None *
106* *
107* EXIT NORMAL : Go to POPIO in INDEEMU to IRET to the V86 task *
108* *
109* EXIT ERROR : None *
110* *
111* EXTERNAL *
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 1st page directory entry *
119* so that it points to the normal *
120* page tables *
121* XMAPAGE:WORD - Entry for the 1st 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 *
126* *
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 *
132* *
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 *
138* *
139* CONTROL BLOCKS : INDEDAT.INC - system data structures *
140* *
141* CHANGE ACTIVITY : *
142* *
143* $MOD(INDEXMA) COMP(LOAD) PROD(3270PC) : *
144* *
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 *
149* *
150* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
151#
152
153 .286P ; Enable recognition of 286 privileged instructs.
154
155 .XLIST ; Turn off the listing
156 INCLUDE INDEDAT.INC ; Include system data structures
157
158 IF1 ; Only include macros on the first pass
159 INCLUDE INDEOVP.MAC
160 INCLUDE INDEINS.MAC
161 ENDIF
162 .LIST ; Turn on the listing
163
164CRT_SELECTOR EQU 00030H ; Selector for mono display buffer
165SEX_ATTR EQU 04B00H ; Attribute for turquoise on red
166STACK_ATTR EQU 00700H ; Attribute for white o black
167BLANK EQU 00020H ; ASCII for a blank
168XMA_PAGES_SEL EQU RSDA_PTR ; Selector for the XMA pages
169HIMEM 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.
173 ; @D1D
174
175PROG SEGMENT PARA PUBLIC 'PROG'
176
177 ASSUME CS:PROG
178 ASSUME DS:PROG
179 ASSUME ES:NOTHING
180 ASSUME SS:NOTHING
181
182INDEXMA LABEL NEAR
183
184; The following entry points are in other modules
185
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
190
191; The following data are in INDEI15.ASM
192
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
197 ; page tables
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
203
204; Let the following entries be known to other modules
205
206 PUBLIC INDEXMA
207 PUBLIC INW
208 PUBLIC INWIMMED
209 PUBLIC INIMMED
210 PUBLIC XMAIN
211 PUBLIC OUTW
212 PUBLIC OUTWIMMED
213 PUBLIC XMAOUTIMMED
214 PUBLIC XMAOUT
215 PUBLIC NOTXMAOUT
216
217; Let the following data be known to other modules
218
219 PUBLIC WORD_FLAG
220 PUBLIC XMATTAR
221 PUBLIC XMATTIO
222 PUBLIC XMATTII
223 PUBLIC XMATID
224 PUBLIC XMACTL
225
226; The following XMA labels represent the XMA ports starting at 31A0H.
227; THEY MUST BE KEPT IN THE FOLLOWING ORDER.
228
229XMATTAR DW 0 ; Port 31A0H - Translate table index
230XMATTIO DW 0 ; Port 31A2H - XMA block number
231XMATTII DW 0 ; Port 31A4H - Block number with auto-increment
232XMATID DB 0 ; Port 31A6H - Bank ID
233XMACTL DB 02H ; Port 31A7H - Control flags. Virtual @D1C
234 ; enable bit is initially on.
235
236; How about some flags?
237
238WORD_FLAG DB 0 ; If set to 1 then I/O is for a word.
239 ; Else, it's for a byte
240
241PAGE
242
243; Control comes here for an "IN" for a word with the port value in DX
244
245INW:
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"
250
251; Control comes here for an "IN" for a word with an immediate port value
252
253INWIMMED:
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
257
258; Control comes here for an "IN" for a byte with an immediate port value
259
260INIMMED:
261
262 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "IN" instruction
263
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.
268
269 MOV AX,HUGE_PTR ; Load DS with a selector that accesses
270 MOV DS,AX ; all of memory as data
271
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
274
275 DATAOV ; Load ESI (32 bit SI) with the V86
276 MOV SI,SS:[BP+BP_IP] ; task's IP
277 DATAOV
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
283 ; offset from 0
284 ADDROV ; Get the byte after the "IN" instruc-
285 LODSB ; tion. This is the port address.
286
287 ADDROV ; Intel bug # A0-119
288 NOP ; Intel bug # A0-119
289
290 SUB DX,DX ; Clear DX to prepare for one byte move
291 MOV DL,AL ; DX now has the port address
292
293; Control comes here for an "IN" for a byte with the port value in DX
294
295XMAIN PROC NEAR
296
297 MOV AX,SYS_PATCH_DS ; Load DS with the selector for our
298 MOV DS,AX ; data segment
299
300 CMP DX,31A0H ; Is the port address below 31A0H?
301 JB NOTXMAIN ; Yup. Then it's not XMA.
302
303 CMP DX,31A7H ; Is the port address above 31A7H?
304 JA NOTXMAIN ; Yup. Then it's not XMA.
305
306; It's an XMA port so lets do the "IN" for the guy.
307
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.
311
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.
318
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!
322
323; For non-XMA ports we just pass the "IN" on to INDEDMA
324
325NOTXMAIN:
326 JMP DMAIN
327
328; The "IN" is for a word
329
330GETWORD:
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
333
334 MOV WORD_FLAG,0 ; Reset the word flag
335
336 CMP DX,31A4H ; Is this an "IN" from the auto-
337 ; increment port?
338 JNE INEXIT ; Nope. Then just leave.
339
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"
344INEXIT:
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
348
349PAGE
350
351; Control comes here for an "OUT" for a word with the port value in DX
352
353OUTW:
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"
358
359; Control comes here for an "OUT" for a word with an immediate port value
360
361OUTWIMMED:
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
365
366; Control comes here for an "OUT" for a byte with an immediate port value
367
368XMAOUTIMMED:
369
370 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "OUT" instruction
371
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.
376
377 MOV AX,HUGE_PTR ; Load DS with a selector that accesses
378 MOV DS,AX ; all of memory as data
379
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
382
383 DATAOV ; Load ESI (32 bit SI) with the V86
384 MOV SI,SS:[BP+BP_IP] ; task's IP
385 DATAOV
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
391 ; offset from 0
392 ADDROV ; Get the byte after the "OUT" instruc-
393 LODSB ; tion. This is the port address.
394
395 ADDROV ; Intel bug # A0-119
396 NOP ; Intel bug # A0-119
397
398 SUB DX,DX ; Clear DX to prepare for one byte move
399 MOV DL,AL ; DX now has the port address
400
401; Control comes here for an "OUT" for a byte with the port value in DX
402
403XMAOUT:
404 MOV AX,SYS_PATCH_DS ; Load DS and ES with the selector for
405 MOV DS,AX ; our data area
406 MOV ES,AX
407
408 CMP DX,31A0H ; Is the port address below 31A0H?
409 JB NOTXMAOUT ; Yes. Then it's not XMA.
410
411 CMP DX,31A7H ; Is the port address above 31A7H?
412 JA NOTXMAOUT ; Yes. Then it's not XMA.
413
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.
420
421 MOV AL,BYTE PTR SS:[BP+BP_AX] ; Put the value in the V86 task's AL
422 STOSB ; register into the "port"
423
424 CMP DX,31A6H ; Is this "OUT" to the bank ID port?
425 JE CHKCNTRL ; If so, go set the new bank
426
427 CMP DX,31A7H ; Is the "OUT" to the control port?
428 JE CHKCNTRL ; Affirmative. Go handle control bits.
429
430 CMP DX,31A1H ; Is this "OUT" to the TT index?
431 ; (high byte)
432 JBE TTAROUT ; Yup. Go update dependent fields.
433
434 JMP OUTEXIT ; Any other ports just exit.
435
436; The "OUT" is for a word
437
438PUTWORD:
439 MOV AX,WORD PTR SS:[BP+BP_AX] ; Put the value in the V86 task's AX
440 STOSW ; register into the "port"
441
442 MOV WORD_FLAG,0 ; Reset the word flag
443
444 CMP DX,31A0H ; Is the "OUT" to the TT index port?
445 JE TTAROUT ; Si. Go update the dependent fields.
446
447 CMP DX,31A2H ; Is the "OUT" to set a block number?
448 JNE CHKA4 ; No. Go do some more checks.
449
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
453 ; sync.
454 CALL UPDATETT ; Update the "translate table" with the
455 ; new block number
456 JMP OUTEXIT ; That's it. Let's leave.
457
458CHKA4:
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
468
469; The translate table index was changed
470
471TTAROUT:
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
476 ; not changed.
477
478; It's not an XMA "OUT" so pass it on to INDEDMA
479
480NOTXMAOUT:
481 JMP DMAOUT
482
483; The "OUT" is to the bank ID port (31A6H), the control port (31A7H) or both
484
485CHKCNTRL:
486
487 TEST XMACTL,02H ; Is the virtual enable bit on?
488 JNZ SETXMA ; Aye. Go make the specified XMA bank
489 ; active.
490 DATAOV ; Nay. We simulate disabling the XMA
491 MOV AX,NORMPAGE ; card by making the normal page
492 ; tables active.
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
496 ; memory.
497 JMP OUTEXIT ; Return to the V86 task
498
499SETXMA:
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
510 ; shift back.
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
515 ; directory.
516 DATAOV ; Set the first entry in the page
517 STOSW ; directory to point to the XMA page
518 ; table
519
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
527
528OUTEXIT:
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
533
534OUTEXITDMA:
535 ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "OUT" instruction
536 JMP POPIO ; Return to the V86 task
537
538PAGE
539
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.
543
544TTARCHANGED PROC
545
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
556
557 RET
558
559TTARCHANGED ENDP
560
561PAGE
562
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
567; 31A0H.
568;
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
580;
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
587; memory.
588;
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.
593;
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
598
599UPDATETT PROC
600
601 MOV AX,XMATTAR ; Get the index of the TT entry that
602 ; is to be changed
603 AND AX,0FFFH ; Clear the high four bits. They are
604 ; not used.
605 SHL AX,1 ; Change to a word offset since the TT
606 ; entries are words.
607 LEA DI,TTTABLE ; Point DI to the translate table base
608 ADD DI,AX ; Add on the offset of the entry that
609 ; is to be changed
610 MOV AX,XMATTIO ; Get the block number to be written
611 STOSW ; Store the block number in the TT
612
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").
618; 15 11
619; 0 0 = enabled 11 bit address
620; 0 1 = disabled address
621; 1 x = enabled 15 bit address
622
623 TEST AH,80H ; Is this a 15 bit block number?
624 JZ SMALL ; Far from it. Go do stuff for 11 bit
625 ; block numbers.
626
627; We have a 15 bit address
628
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
632
633 AND AX,7FFFH ; Turn off the 15 bit address bit
634 JMP BOTH ; leaving a valid block number for
635 ; our calculations later
636
637SMALL:
638 TEST AH,08H ; Is the disable bit on?
639 JNZ DISABLEPAGE ; Yes. Go disable the page.
640
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
644BOTH:
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
649 ; these blocks.
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.
658NOADJUST:
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
668 ; table entry.
669 DATAOV ; Save the page table entry in EBX for
670 MOV BX,AX ; now
671
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
674; buffer.
675
676 DATAOV ; Clear all 32 bits of EAX
677 SUB AX,AX
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
689 ; memory.
690
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
696; memory.
697
698DISABLEPAGE:
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.
702
703; The address is between 256K and 640K. Let's set the page table entry to
704; point to non-exiatent memory.
705
706EMPTY:
707 DATAOV ; Clear EAX
708 SUB AX,AX
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
716 MOV BX,AX
717 JMP ENABLED ; Go set up the page table
718
719; If the address is above 640K then the translate table (page table) entry D1C
720; is set to point back to real memory.
721SPECIAL:
722 MOV AX,XMATTAR ; Get the index of the TT entry that is
723 ; to be disabled
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
732 ; by the TT entry.
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
736 MOV BX,AX
737
738; Now let's put the new page table entry in EBX, which represents the XMA block,
739; into the page table.
740ENABLED:
741 MOV AX,XMATTAR ; Get the index of the TT entry
742
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?
755
756 SHL AH,2 ; Shift the bank ID left two bits
757 SHL AX,2 ; Shift the bank ID and the block number
758 ; left two bits
759 MOV DI,AX ; Load DI with the offset of the page
760 ; table entry that is to be changed
761 PUSH ES ; Save ES
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
765 ; changed
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.
770
771 POP ES ; Restore ES
772
773 RET ; And return
774
775UPDATETT ENDP
776
777XMAIN ENDP
778
779PROG ENDS
780
781 END