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