diff options
| author | 2024-04-25 21:24:10 +0100 | |
|---|---|---|
| committer | 2024-04-25 22:32:27 +0000 | |
| commit | 2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch) | |
| tree | 80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/DEV/XMAEM/INDEXMA.ASM | |
| parent | Merge pull request #430 from jpbaltazar/typoptbr (diff) | |
| download | ms-dos-main.tar.gz ms-dos-main.tar.xz ms-dos-main.zip | |
Diffstat (limited to 'v4.0/src/DEV/XMAEM/INDEXMA.ASM')
| -rw-r--r-- | v4.0/src/DEV/XMAEM/INDEXMA.ASM | 781 |
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 @@ | |||
| 1 | PAGE 60,132 | ||
| 2 | TITLE INDEXMA - 386 XMA Emulator - XMA Emulation | ||
| 3 | |||
| 4 | COMMENT # | ||
| 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 | |||
| 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. | ||
| 173 | ; @D1D | ||
| 174 | |||
| 175 | PROG SEGMENT PARA PUBLIC 'PROG' | ||
| 176 | |||
| 177 | ASSUME CS:PROG | ||
| 178 | ASSUME DS:PROG | ||
| 179 | ASSUME ES:NOTHING | ||
| 180 | ASSUME SS:NOTHING | ||
| 181 | |||
| 182 | INDEXMA 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 | |||
| 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. | ||
| 235 | |||
| 236 | ; How about some flags? | ||
| 237 | |||
| 238 | WORD_FLAG DB 0 ; If set to 1 then I/O is for a word. | ||
| 239 | ; Else, it's for a byte | ||
| 240 | |||
| 241 | PAGE | ||
| 242 | |||
| 243 | ; Control comes here for an "IN" for a word with the port value in DX | ||
| 244 | |||
| 245 | INW: | ||
| 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 | |||
| 253 | INWIMMED: | ||
| 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 | |||
| 260 | INIMMED: | ||
| 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 | |||
| 295 | XMAIN 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 | |||
| 325 | NOTXMAIN: | ||
| 326 | JMP DMAIN | ||
| 327 | |||
| 328 | ; The "IN" is for a word | ||
| 329 | |||
| 330 | GETWORD: | ||
| 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" | ||
| 344 | INEXIT: | ||
| 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 | |||
| 349 | PAGE | ||
| 350 | |||
| 351 | ; Control comes here for an "OUT" for a word with the port value in DX | ||
| 352 | |||
| 353 | OUTW: | ||
| 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 | |||
| 361 | OUTWIMMED: | ||
| 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 | |||
| 368 | XMAOUTIMMED: | ||
| 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 | |||
| 403 | XMAOUT: | ||
| 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 | |||
| 438 | PUTWORD: | ||
| 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 | |||
| 458 | CHKA4: | ||
| 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 | |||
| 471 | TTAROUT: | ||
| 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 | |||
| 480 | NOTXMAOUT: | ||
| 481 | JMP DMAOUT | ||
| 482 | |||
| 483 | ; The "OUT" is to the bank ID port (31A6H), the control port (31A7H) or both | ||
| 484 | |||
| 485 | CHKCNTRL: | ||
| 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 | |||
| 499 | SETXMA: | ||
| 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 | |||
| 528 | OUTEXIT: | ||
| 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 | |||
| 534 | OUTEXITDMA: | ||
| 535 | ADD WORD PTR SS:[BP+BP_IP],1 ; Step IP past the "OUT" instruction | ||
| 536 | JMP POPIO ; Return to the V86 task | ||
| 537 | |||
| 538 | PAGE | ||
| 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 | |||
| 544 | TTARCHANGED 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 | |||
| 559 | TTARCHANGED ENDP | ||
| 560 | |||
| 561 | PAGE | ||
| 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 | |||
| 599 | UPDATETT 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 | |||
| 637 | SMALL: | ||
| 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 | ||
| 644 | BOTH: | ||
| 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. | ||
| 658 | NOADJUST: | ||
| 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 | |||
| 698 | DISABLEPAGE: | ||
| 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 | |||
| 706 | EMPTY: | ||
| 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. | ||
| 721 | SPECIAL: | ||
| 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. | ||
| 740 | ENABLED: | ||
| 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 | |||
| 775 | UPDATETT ENDP | ||
| 776 | |||
| 777 | XMAIN ENDP | ||
| 778 | |||
| 779 | PROG ENDS | ||
| 780 | |||
| 781 | END | ||