diff options
Diffstat (limited to 'v4.0/src/DEV/XMAEM/INDEINI.ASM')
| -rw-r--r-- | v4.0/src/DEV/XMAEM/INDEINI.ASM | 1474 |
1 files changed, 1474 insertions, 0 deletions
diff --git a/v4.0/src/DEV/XMAEM/INDEINI.ASM b/v4.0/src/DEV/XMAEM/INDEINI.ASM new file mode 100644 index 0000000..19c8b79 --- /dev/null +++ b/v4.0/src/DEV/XMAEM/INDEINI.ASM | |||
| @@ -0,0 +1,1474 @@ | |||
| 1 | PAGE 60,132 | ||
| 2 | TITLE INDEINI - 386 XMA EMULATOR - Initialization | ||
| 3 | |||
| 4 | COMMENT # | ||
| 5 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
| 6 | * * | ||
| 7 | * MODULE NAME : INDEINI * | ||
| 8 | * * | ||
| 9 | * 5669-196 (C) COPYRIGHT 1988 Microsoft Corp. * | ||
| 10 | * * | ||
| 11 | * DESCRIPTIVE NAME: 80386 XMA EMULATOR INITIALIZATION * | ||
| 12 | * * | ||
| 13 | * STATUS (LEVEL) : VERSION (0) LEVEL (2.0) * | ||
| 14 | * * | ||
| 15 | * FUNCTION : Do all the initialization needed for the 386 XMA emulator.* | ||
| 16 | * * | ||
| 17 | * The 386 XMA emulator is installed by putting the follow- * | ||
| 18 | * ing command in the CONFIG.SYS file: * | ||
| 19 | * * | ||
| 20 | * DEVICE=\386XMAEM.SYS bbb * | ||
| 21 | * * | ||
| 22 | * where "bbb" is the number of K reserved for the MOVEBLOCK * | ||
| 23 | * function. If EMS is used, this command must appear * | ||
| 24 | * before the command to load EMS. * | ||
| 25 | * * | ||
| 26 | * This module first of all does all the stuff to set up * | ||
| 27 | * the device driver linkage to DOS. The driver is a * | ||
| 28 | * character device. The only command it recognizes is P3C* | ||
| 29 | * "initialize". When it receives the initialize command * | ||
| 30 | * it does all the set up for the emulator. For information * | ||
| 31 | * on device drivers see the DOS Technical Reference. * | ||
| 32 | * * | ||
| 33 | * Then it checks to see if we're on a model_80 and the * | ||
| 34 | * emulator has not been previously installed. If this is * | ||
| 35 | * the case, then it procedes to do the following: * | ||
| 36 | * Get the MOVEBLOCK buffer size from the parameter list * | ||
| 37 | * Save the maximum XMA block number in the header * | ||
| 38 | * Relocate to high memory * | ||
| 39 | * Initialize the page directory and page tables * | ||
| 40 | * Call INDEIDT to initialize the IDT * | ||
| 41 | * Call INDEGDT to initialize the GDT * | ||
| 42 | * Switch to virtual mode * | ||
| 43 | * Initialize the TSS for the virtual 8086 task * | ||
| 44 | * Initialize the XMA page tables * | ||
| 45 | * Enable paging * | ||
| 46 | * * | ||
| 47 | * This module also contains code to handle the Generic D2A* | ||
| 48 | * IOCTL call which is used to query the highest valid D2A* | ||
| 49 | * XMA block number. This code is left resident. D2A* | ||
| 50 | * * | ||
| 51 | * MODULE TYPE : ASM * | ||
| 52 | * * | ||
| 53 | * REGISTER USAGE : 80386 STANDARD * | ||
| 54 | * * | ||
| 55 | * RESTRICTIONS : None * | ||
| 56 | * * | ||
| 57 | * DEPENDENCIES : None * | ||
| 58 | * * | ||
| 59 | * LINKAGE : Invoked as a DOS device driver * | ||
| 60 | * * | ||
| 61 | * INPUT PARMS : The number of 1K blocks reserved for the MOVE BLOCK * | ||
| 62 | * service can be specified after the DEVICE command in the * | ||
| 63 | * CONFIG.SYS file. 0K is the default. * | ||
| 64 | * * | ||
| 65 | * RETURN PARMS : A return code is returned to DOS in the device header * | ||
| 66 | * at offset 3. * | ||
| 67 | * * | ||
| 68 | * OTHER EFFECTS : None * | ||
| 69 | * * | ||
| 70 | * EXIT NORMAL : Return to DOS after device driver is loaded * | ||
| 71 | * * | ||
| 72 | * EXIT ERROR : Return to DOS after putting up error messages * | ||
| 73 | * * | ||
| 74 | * EXTERNAL * | ||
| 75 | * REFERENCES : SIDT_BLD - Entry point for INDEIDT to build the IDT * | ||
| 76 | * GDT_BLD - Entry point for INDEGDT to build the GDT * | ||
| 77 | * WELCOME - The welcome message * | ||
| 78 | * GOODLOAD - Message saying we loaded OK * | ||
| 79 | * NO_80386 - Error message for not running on a model_80 * | ||
| 80 | * WAS_INST - Error message for protect mode in use * | ||
| 81 | * SP_INIT - Initial protect mode SP * | ||
| 82 | * REAL_CS - Place to save our real mode CS * | ||
| 83 | * REAL_SS - Place to save our real mode SS * | ||
| 84 | * REAL_SP - Place to save our real mode SP * | ||
| 85 | * PGTBLOFF - Offset of the page tables * | ||
| 86 | * SGTBLOFF - Offest of the page directory * | ||
| 87 | * NORMPAGE - Normal page directory entry * | ||
| 88 | * XMAPAGE - Page directory entry for the first XMA page D1A* | ||
| 89 | * BUFF_SIZE- Size of the MOVEBLOCK buffer * | ||
| 90 | * MAXMEM - Maximum amount of memory on the box * | ||
| 91 | * CRT_SELECTOR - Selector for the display buffer * | ||
| 92 | * * | ||
| 93 | * SUB-ROUTINES : GATE_A20 - Gate on or off address bit 20 * | ||
| 94 | * GET_PARMS - Get the MOVEBLOCK buffer size specified on * | ||
| 95 | * the command in CONFIG.SYS and convert to * | ||
| 96 | * binary. * | ||
| 97 | * * | ||
| 98 | * MACROS : DATAOV - Add prefix for the next instruction so that it * | ||
| 99 | * accesses data as 32 bits wide * | ||
| 100 | * ADDROV - Add prefix for the next instruction so that it * | ||
| 101 | * uses addresses that are 32 bits wide * | ||
| 102 | * CMOV - Move to and from control registers * | ||
| 103 | * JUMPFAR - Build an instruction that will jump to the * | ||
| 104 | * offset and segment specified * | ||
| 105 | * * | ||
| 106 | * CONTROL BLOCKS : INDEDAT.INC * | ||
| 107 | * * | ||
| 108 | * CHANGE ACTIVITY : * | ||
| 109 | * * | ||
| 110 | * $MOD(INDEINI) COMP(LOAD) PROD(3270PC) : * | ||
| 111 | * * | ||
| 112 | * $D0=D0004700 410 870521 D : NEW FOR RELEASE 1.1. CHANGES TO THE ORIGINAL * | ||
| 113 | * CODE ARE MARKED WITH D0A. * | ||
| 114 | * $P1=P0000281 410 870730 D : SAVE 32 BIT REGISTERS ON model_80 * | ||
| 115 | * $P2=P0000312 410 870804 D : CHANGE COMPONENT FROM MISC TO LOAD * | ||
| 116 | * $P3=P0000335 410 870811 D : HEADER INFORMATION ALL SCREWED UP * | ||
| 117 | * $D1=D0007100 410 870810 D : CHANGE TO EMULATE XMA 2 * | ||
| 118 | * CHANGE ID STRING TO "386XMAEMULATOR10" * | ||
| 119 | * $P4=P0000649 411 880125 D : A20 NOT ENABLED WHEN PASSWORD SET * | ||
| 120 | * $P5=P0000650 411 880128 D : COPROCESSOR APPLICATIONS FAIL * | ||
| 121 | * $P6=P0000740 411 880129 D : IDSS CAPTURED DCR 87 CODE. REMOVE IT. * | ||
| 122 | * $D2=D0008700 120 880206 D : SUPPORT DOS 3.4 IOCTL CALL * | ||
| 123 | * $P7=P0000xxx 120 880331 D : FIX INT 15. LOAD AS V86 MODE HANDLER. * | ||
| 124 | * * | ||
| 125 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * | ||
| 126 | # | ||
| 127 | |||
| 128 | .286P ; Enable recognition of 286 privileged instructs. | ||
| 129 | |||
| 130 | .XLIST ; Turn off the listing | ||
| 131 | INCLUDE INDEDAT.INC | ||
| 132 | |||
| 133 | IF1 ; Only include the macros in the first pass | ||
| 134 | INCLUDE INDEOVP.MAC ; of the assembler | ||
| 135 | INCLUDE INDEINS.MAC | ||
| 136 | ENDIF | ||
| 137 | .LIST ; Turn on the listing | ||
| 138 | |||
| 139 | ; Let these variables be known to external procedures | ||
| 140 | |||
| 141 | PUBLIC POST | ||
| 142 | PUBLIC INDEINI | ||
| 143 | |||
| 144 | PROG SEGMENT PARA PUBLIC 'PROG' | ||
| 145 | |||
| 146 | ASSUME CS:PROG | ||
| 147 | ASSUME SS:NOTHING | ||
| 148 | ASSUME DS:PROG | ||
| 149 | ASSUME ES:NOTHING | ||
| 150 | |||
| 151 | INDEINI LABEL NEAR | ||
| 152 | |||
| 153 | ; These variables are located in INDEI15 | ||
| 154 | |||
| 155 | EXTRN SP_INIT:WORD ; Initial protect mode SP | ||
| 156 | EXTRN REAL_CS:WORD ; Place to save our real mode CS | ||
| 157 | EXTRN REAL_SS:WORD ; Place to save our real mode SS | ||
| 158 | EXTRN REAL_SP:WORD ; Place to save our real mode SP | ||
| 159 | EXTRN PGTBLOFF:WORD ; Offset of the page tables | ||
| 160 | EXTRN SGTBLOFF:WORD ; Offest of the page directory | ||
| 161 | EXTRN NORMPAGE:WORD ; Normal page directory entry. Points to the | ||
| 162 | ; page table that maps the real 0 to 4M. | ||
| 163 | EXTRN XMAPAGE:WORD ; Page directory entry for the first XMA | ||
| 164 | ; page table (page table for bank 0) @D1A | ||
| 165 | EXTRN BUFF_SIZE:WORD ; Size of the MOVEBLOCK buffer | ||
| 166 | EXTRN MAXMEM:WORD ; Maximum amount of memory on the box | ||
| 167 | EXTRN CRT_SELECTOR:WORD ; Selector for the display buffer | ||
| 168 | |||
| 169 | ; These are the messages | ||
| 170 | |||
| 171 | EXTRN WELCOME:BYTE ; The welcome message | ||
| 172 | EXTRN GOODLOAD:BYTE ; Message saying we loaded OK | ||
| 173 | EXTRN NO_80386:BYTE ; Error message for not running on a model_80 | ||
| 174 | EXTRN WAS_INST:BYTE ; Error message for protect mode in use | ||
| 175 | Extrn Small_Parm:Byte ; Parm value < 64 and > 0 ;an000; dms; | ||
| 176 | Extrn No_Mem:Byte ; Parm value > memory available ;an000; dms; | ||
| 177 | |||
| 178 | ; These entries are located in external procedures | ||
| 179 | |||
| 180 | EXTRN SIDT_BLD:NEAR ; Build the interrupt descriptor table (IDT) | ||
| 181 | EXTRN GDT_BLD:NEAR ; Build the global descriptor table (GDT) | ||
| 182 | |||
| 183 | ; General equates | ||
| 184 | |||
| 185 | DDSIZE EQU GDT_LOC ; Size of the device driver | ||
| 186 | HIGH_SEG EQU 0FFF0H ; The segment we relocate to | ||
| 187 | MEG_SUPPORTED EQU 24 ; Must be a multiple of 4 | ||
| 188 | XMA_PAGES_SEL EQU RSDA_PTR ; Selector for XMA pages | ||
| 189 | DISPSTRG EQU 09H ; DOS display string function number D0A | ||
| 190 | GET_VECT EQU 35H ; DOS get vector function number P7A | ||
| 191 | SET_VECT EQU 25H ; DOS set vector function number P7A | ||
| 192 | model_80 EQU 0F8H ; Model byte for the Wangler D0A | ||
| 193 | XMA640KSTRT EQU 580H ; Start address of XMA block for D0A | ||
| 194 | ; 640K (16000:0 / 1K) D0A P3C | ||
| 195 | |||
| 196 | ; ASCII character equates | ||
| 197 | |||
| 198 | TAB EQU 09H ; ASCII tab | ||
| 199 | LF EQU 0AH ; ASCII line feed | ||
| 200 | CR EQU 0DH ; ASCII carriage return | ||
| 201 | |||
| 202 | SUBTTL Structure Definitions | ||
| 203 | PAGE | ||
| 204 | ;------------------------------------------------------------------------------; | ||
| 205 | ; Request Header (Common portion) ; | ||
| 206 | ;------------------------------------------------------------------------------; | ||
| 207 | |||
| 208 | RH EQU DS:[BX] ; The Request Header structure is based off | ||
| 209 | ; of DS:[BX] | ||
| 210 | |||
| 211 | RHC STRUC ; Fields common to all request types | ||
| 212 | DB ? ; Length of Request Header (including data) | ||
| 213 | DB ? ; Unit code (subunit) | ||
| 214 | RHC_CMD DB ? ; Command code | ||
| 215 | RHC_STA DW ? ; Status | ||
| 216 | DQ ? ; Reserved for DOS | ||
| 217 | RHC ENDS ; End of common portion | ||
| 218 | |||
| 219 | ; Status values for RHC_STA | ||
| 220 | |||
| 221 | STAT_DONE EQU 0100H ; Function complete status (high order byte)@P3C | ||
| 222 | STAT_CMDERR EQU 8003H ; Invalid command code error | ||
| 223 | STAT_GEN EQU 800CH ; General error code D0A | ||
| 224 | |||
| 225 | ;------------------------------------------------------------------------------; | ||
| 226 | ; Request Header for INIT command ; | ||
| 227 | ;------------------------------------------------------------------------------; | ||
| 228 | |||
| 229 | RH0 STRUC | ||
| 230 | DB (TYPE RHC) DUP (?) ; Reserve space for the header | ||
| 231 | |||
| 232 | RH0_NUN DB ? ; Number of units | ||
| 233 | ; Set to 1 if installation succeeds, | ||
| 234 | ; Set to 0 to cause installation failure | ||
| 235 | RH0_ENDO DW ? ; Offset of ending address | ||
| 236 | RH0_ENDS DW ? ; Segment of ending address | ||
| 237 | RH0_BPBO DW ? ; Offset of BPB array address | ||
| 238 | RH0_BPBS DW ? ; Segment of BPB array address | ||
| 239 | RH0_DRIV DB ? ; Drive code (DOS 3 only) | ||
| 240 | RH0 ENDS | ||
| 241 | |||
| 242 | RH0_BPBA EQU DWORD PTR RH0_BPBO ; Offset & segment of BPB array address. | ||
| 243 | ; On the INIT command the BPB points to | ||
| 244 | ; the characters following the "DEVICE=" | ||
| 245 | ; in the CONFIG.SYS file. | ||
| 246 | |||
| 247 | ;---------------------------------------------------------------------------D2A; | ||
| 248 | ; Request Header for Generic IOCTL Request D2A; | ||
| 249 | ;---------------------------------------------------------------------------D2A; | ||
| 250 | |||
| 251 | RH19 STRUC | ||
| 252 | DB (TYPE RHC) DUP (?) ; Reserve space for the header @D2A | ||
| 253 | |||
| 254 | RH19_MAJF DB ? ; Major function @D2A | ||
| 255 | RH19_MINF DB ? ; Minor function @D2A | ||
| 256 | RH19_SI DW ? ; Contents of SI @D2A | ||
| 257 | RH19_DI DW ? ; Contents of DI @D2A | ||
| 258 | RH19_RQPK DD ? ; Pointer to Generic IOCTL request packet @D2A | ||
| 259 | RH19 ENDS | ||
| 260 | |||
| 261 | SUBTTL Device Driver Header | ||
| 262 | PAGE | ||
| 263 | POST PROC NEAR | ||
| 264 | |||
| 265 | ; Declare the device driver header | ||
| 266 | |||
| 267 | ORG 0 ; Device header must the very first thing in the | ||
| 268 | ; device driver | ||
| 269 | DD -1 ; Becomes pointer to next device header | ||
| 270 | DW 0C040H ; Character device, does IOCTL @P3C @D2C | ||
| 271 | DW OFFSET STRATEGY ; Pointer to device "strategy" routine | ||
| 272 | DW OFFSET IRPT ; Pointer to device "interrupt handler" | ||
| 273 | DB "386XMAEM" ; Device name @D0C | ||
| 274 | |||
| 275 | ; End of device driver header | ||
| 276 | |||
| 277 | ;------------------------------------------------------------------------------; | ||
| 278 | ; Request Header (RH) address, saved here by "strategy" routine ; | ||
| 279 | ;------------------------------------------------------------------------------; | ||
| 280 | |||
| 281 | RH_PTRA LABEL DWORD | ||
| 282 | RH_PTRO DW ? ; Offset of the request header | ||
| 283 | RH_PTRS DW ? ; Segment of the request header | ||
| 284 | ; Character ID "386XMAEMULATOR10" deleted 2@D2D | ||
| 285 | HI_XMA_BLK DW ? ; The highest XMA block number @D0A | ||
| 286 | EXT_MEM DW ? ; Number of K of extended memory @P7A | ||
| 287 | ; D0A | ||
| 288 | RBX DW ? ; Temporary save area for register BX @P1A | ||
| 289 | ISmodel_80 DB -1 ; model_80 flag. Set to 1 if on a model_80 @P1A | ||
| 290 | ; Set to 0 if not on a model_80 @D1C | ||
| 291 | |||
| 292 | SUBTTL Device Strategy | ||
| 293 | PAGE | ||
| 294 | ;------------------------------------------------------------------------------; | ||
| 295 | ; Device "strategy" entry point ; | ||
| 296 | ; ; | ||
| 297 | ; Retain the Request Header address for use by Interrupt routine ; | ||
| 298 | ;------------------------------------------------------------------------------; | ||
| 299 | |||
| 300 | STRATEGY PROC FAR | ||
| 301 | |||
| 302 | MOV CS:RH_PTRO,BX ; Save the offset of the request header | ||
| 303 | MOV CS:RH_PTRS,ES ; Save the segment of the request header | ||
| 304 | RET | ||
| 305 | |||
| 306 | STRATEGY ENDP | ||
| 307 | |||
| 308 | SUBTTL Device Interrupt Intry Point | ||
| 309 | PAGE | ||
| 310 | |||
| 311 | ;------------------------------------------------------------------------------; | ||
| 312 | ; Table of command processing routine entry points ; | ||
| 313 | ;------------------------------------------------------------------------------; | ||
| 314 | CMD_TABLE LABEL WORD | ||
| 315 | DW OFFSET INIT_P1 ; 0 - Initialization | ||
| 316 | DW OFFSET MEDIA_CHECK ; 1 - Media check | ||
| 317 | DW OFFSET BLD_BPB ; 2 - Build BPB | ||
| 318 | DW OFFSET INPUT_IOCTL ; 3 - IOCTL input | ||
| 319 | DW OFFSET INPUT ; 4 - Input | ||
| 320 | DW OFFSET INPUT_NOWAIT ; 5 - Non destructive input no wait | ||
| 321 | DW OFFSET INPUT_STATUS ; 6 - Input status | ||
| 322 | DW OFFSET INPUT_FLUSH ; 7 - Input flush | ||
| 323 | DW OFFSET OUTPUT ; 8 - Output | ||
| 324 | DW OFFSET OUTPUT_VERIFY ; 9 - Output with verify | ||
| 325 | DW OFFSET OUTPUT_STATUS ;10 - Output status | ||
| 326 | DW OFFSET OUTPUT_FLUSH ;11 - Output flush | ||
| 327 | DW OFFSET OUTPUT_IOCTL ;12 - IOCTL output | ||
| 328 | DW OFFSET DEVICE_OPEN ;13 - Device OPEN | ||
| 329 | DW OFFSET DEVICE_CLOSE ;14 - Device CLOSE | ||
| 330 | DW OFFSET REMOVABLE_MEDIA ;15 - Removable media | ||
| 331 | DW OFFSET INVALID_FCN ;16 - Invalid IOCTL function @D2A | ||
| 332 | DW OFFSET INVALID_FCN ;17 - Invalid IOCTL function @D2A | ||
| 333 | DW OFFSET INVALID_FCN ;18 - Invalid IOCTL function @D2A | ||
| 334 | DW OFFSET GENERIC_IOCTL ;19 - Generic IOCTL function @D2A | ||
| 335 | DW OFFSET INVALID_FCN ;20 - Invalid IOCTL function @D2A | ||
| 336 | DW OFFSET INVALID_FCN ;21 - Invalid IOCTL function @D2A | ||
| 337 | DW OFFSET INVALID_FCN ;22 - Invalid IOCTL function @D2A | ||
| 338 | DW OFFSET GET_LOG_DEVICE ;23 - Get Logical Device @D2A | ||
| 339 | MAX_CMD EQU ($-CMD_TABLE)/2 ; Highest valid command follows | ||
| 340 | DW OFFSET SET_LOG_DEVICE ;24 - Set Logical Device @D2A | ||
| 341 | |||
| 342 | ;------------------------------------------------------------------------------; | ||
| 343 | ; Device "interrupt" entry point ; | ||
| 344 | ;------------------------------------------------------------------------------; | ||
| 345 | IRPT PROC FAR ; Device interrupt entry point | ||
| 346 | |||
| 347 | ; First we must save all the registers that we use so that when we return to | ||
| 348 | ; DOS the registers are not changed. | ||
| 349 | |||
| 350 | PUSH DS ; Save the segment registers modified | ||
| 351 | PUSH ES | ||
| 352 | |||
| 353 | CMP CS:ISmodel_80,-1; Did we already check what machine we are @D2A | ||
| 354 | JNE DID_CHECK ; running on? @D2A | ||
| 355 | |||
| 356 | MOV CS:RBX,BX ; Save BX @P1A | ||
| 357 | MOV BX,0FFFFH ; Check the model byte at FFFF:000E @D0A @P1M | ||
| 358 | MOV ES,BX ; to see if we're running on a @D0A @P1M | ||
| 359 | MOV BX,0EH ; model_80 (PS/2 model 80). @D0A @P1M | ||
| 360 | CMP BYTE PTR ES:[BX],model_80 ; @P1A | ||
| 361 | MOV BX,CS:RBX ; Restore BX @P1A @D2M | ||
| 362 | JNE NO_model_80 | ||
| 363 | |||
| 364 | MOV CS:ISmodel_80,1 ; Set the flag saying we're on a @P1A @D2M | ||
| 365 | JMP DID_CHECK ; model_80 @D2A | ||
| 366 | |||
| 367 | NO_model_80: | ||
| 368 | MOV CS:ISmodel_80,0 ; Set the flag saying we're not on a @D2M | ||
| 369 | ; model_80 | ||
| 370 | |||
| 371 | DID_CHECK: ; D2A | ||
| 372 | CMP ISmodel_80,1 ; Are we on a model_80? @D2A | ||
| 373 | JE PUSH32 ; If so, go save the 32 bit registers @P1A | ||
| 374 | |||
| 375 | ; Push 16 bit registers onto the stack. | ||
| 376 | |||
| 377 | PUSH AX | ||
| 378 | PUSH BX | ||
| 379 | PUSH CX | ||
| 380 | PUSH DX | ||
| 381 | PUSH DI | ||
| 382 | PUSH SI | ||
| 383 | PUSH BP | ||
| 384 | |||
| 385 | JMP PUSHED ; @P1A | ||
| 386 | |||
| 387 | ; Push 32 bit registers onto the stack P1A | ||
| 388 | ; @D2D | ||
| 389 | PUSH32: DATAOV ; Save all the 32 bit registers. The @P1A | ||
| 390 | PUSHA ; model_80's BIOS uses 32 bit registers, @P1A | ||
| 391 | ; so we must not trash the high order P1A | ||
| 392 | ; words as well as the low order words. P1A | ||
| 393 | |||
| 394 | PUSHED: CLD ; All moves go forward | ||
| 395 | |||
| 396 | LDS BX,CS:RH_PTRA ; Get the request header address passed to the | ||
| 397 | ; "strategy" routine into DS:BX | ||
| 398 | |||
| 399 | MOV AL,RH.RHC_CMD ; Get the command code from the Request Header | ||
| 400 | CBW ; Zero AH (if AL > 7FH, next compare will | ||
| 401 | ; catch that error) | ||
| 402 | |||
| 403 | CMP AL,MAX_CMD ; If command code is too high | ||
| 404 | JA IRPT_CMD_HIGH ; Then jump to error routine | ||
| 405 | |||
| 406 | ADD AX,AX ; Double command code for table offset since | ||
| 407 | ; table entries are words | ||
| 408 | MOV DI,AX ; Put into index register for CALL | ||
| 409 | ; @D2D | ||
| 410 | ; | ||
| 411 | ; At entry to command processing routine: | ||
| 412 | ; | ||
| 413 | ; DS:BX = Request Header address | ||
| 414 | ; CS = 386XMAEM code segment address | ||
| 415 | ; AX = 0 | ||
| 416 | ; | ||
| 417 | CALL CS:CMD_TABLE[DI] ; Call routine to handle the command | ||
| 418 | JMP IRPT_CMD_EXIT | ||
| 419 | |||
| 420 | |||
| 421 | IRPT_CMD_HIGH: ; JMPed to if RHC_CMD > MAX_CMD | ||
| 422 | MOV AX,STAT_CMDERR ; Return "Invalid Command" error code | ||
| 423 | OR AX,STAT_DONE ; Add "done" bit to status word @P3C | ||
| 424 | MOV RH.RHC_STA,AX ; Store status into request header | ||
| 425 | |||
| 426 | IRPT_CMD_EXIT: ; Return from command routine | ||
| 427 | |||
| 428 | ; Restore the registers before returning to DOS. | ||
| 429 | |||
| 430 | CMP CS:ISmodel_80,1 ; Are we on a model_80? @P1A | ||
| 431 | JE POP32 ; Yes. Then pop the 32 bit registers. @P1A | ||
| 432 | |||
| 433 | ; Pop 16 bit registers off of the stack. | ||
| 434 | |||
| 435 | POP BP | ||
| 436 | POP SI | ||
| 437 | POP DI | ||
| 438 | POP DX | ||
| 439 | POP CX | ||
| 440 | POP BX | ||
| 441 | POP AX | ||
| 442 | |||
| 443 | JMP POPPED ; @P1A | ||
| 444 | |||
| 445 | ; Pop 32 bit registers off of the stack. P1A | ||
| 446 | ; P1A | ||
| 447 | POP32: DATAOV ; @P1A | ||
| 448 | POPA ; @P1A | ||
| 449 | |||
| 450 | ; Pop the segment registers off of the stack. | ||
| 451 | |||
| 452 | POPPED: POP ES | ||
| 453 | POP DS | ||
| 454 | |||
| 455 | RET | ||
| 456 | IRPT ENDP | ||
| 457 | |||
| 458 | SUBTTL Command Routines | ||
| 459 | PAGE | ||
| 460 | |||
| 461 | MEDIA_CHECK: ; | ||
| 462 | BLD_BPB: ; | ||
| 463 | INPUT_IOCTL: ; IOCTL input | ||
| 464 | INPUT: ; | ||
| 465 | INPUT_NOWAIT: ; Non-destructive input no wait | ||
| 466 | INPUT_STATUS: ; Input status | ||
| 467 | INPUT_FLUSH: ; Input flush | ||
| 468 | OUTPUT: ; | ||
| 469 | OUTPUT_VERIFY: ; | ||
| 470 | OUTPUT_IOCTL: ; IOCTL output | ||
| 471 | OUTPUT_STATUS: ; Output status | ||
| 472 | OUTPUT_FLUSH: ; Output flush | ||
| 473 | DEVICE_OPEN: ; | ||
| 474 | DEVICE_CLOSE: ; | ||
| 475 | REMOVABLE_MEDIA: ; | ||
| 476 | INVALID_FCN: ; @D2A | ||
| 477 | GET_LOG_DEVICE: ; @D2A | ||
| 478 | SET_LOG_DEVICE: ; @D2A | ||
| 479 | |||
| 480 | MOV AX,STAT_GEN ; Return general error code @D2A | ||
| 481 | OR AX,STAT_DONE ; Add "done" bit to status word @D2A | ||
| 482 | MOV RH.RHC_STA,AX ; Store status into request header @D2A | ||
| 483 | |||
| 484 | RET | ||
| 485 | |||
| 486 | SUBTTL Generic IOCTL Service Routine | ||
| 487 | PAGE | ||
| 488 | |||
| 489 | ;------------------------------------------------------------------------------; | ||
| 490 | ; This routine handles the Generic IOCTL call. The Emulator provides an D2A; | ||
| 491 | ; interface through the Generic IOCTL call to query the number of XMA D2A; | ||
| 492 | ; blocks available. When the function code in the parameter list is 0 the D2A; | ||
| 493 | ; Emulator will return the number of XMA blocks available. There are no D2A; | ||
| 494 | ; other functions uspported at this time. D2A; | ||
| 495 | ;------------------------------------------------------------------------------; | ||
| 496 | |||
| 497 | GIP EQU ES:[DI] ; @D2A | ||
| 498 | |||
| 499 | GEN_IOCTL_PARM STRUC ; @D2A | ||
| 500 | |||
| 501 | GIOPLEN DW ? ; Length of the parameter list @D2A | ||
| 502 | GIOPFCN DW ? ; Function code @D2A | ||
| 503 | GIOPBLK DW ? ; Number of XMA blocks available @D2A | ||
| 504 | |||
| 505 | GEN_IOCTL_PARM ENDS ; @D2A | ||
| 506 | |||
| 507 | MAXFCN EQU 0 ; Highest function number allowed @D2A | ||
| 508 | |||
| 509 | ; Return codes D2A | ||
| 510 | |||
| 511 | GOODRET EQU 0 ; Good return code @D2A | ||
| 512 | BADLEN EQU 1 ; Bad parameter list length @D2A | ||
| 513 | BADFCN EQU 2 ; Bad function number @D2A | ||
| 514 | |||
| 515 | GENERIC_IOCTL: ; D2A | ||
| 516 | |||
| 517 | LES DI,RH.RH19_RQPK ; Point ES:DI to the Generic IOCTL @D2A | ||
| 518 | ; request packet D2A | ||
| 519 | |||
| 520 | ; First check to make sure the parameter list is long enough to return the D2A | ||
| 521 | ; number of XMA blocks. D2A | ||
| 522 | |||
| 523 | CMP GIP.GIOPLEN,4 ; Do we have at least four bytes? @D2A | ||
| 524 | JAE GIP_CHKFCN ; Yup. Go to check function number. @D2A | ||
| 525 | |||
| 526 | MOV GIP.GIOPFCN,BADLEN ; Nope. Sorry. Return the error @D2A | ||
| 527 | JMP GIP_DONE ; code and go to the end. @D2A | ||
| 528 | |||
| 529 | ; Check if the function number in the parameter list is a valid function. D2A | ||
| 530 | |||
| 531 | GIP_CHKFCN: ; D2A | ||
| 532 | CMP GIP.GIOPFCN,MAXFCN ; Is the function code less than or @D2A | ||
| 533 | ; equal to the maximum supported? D2A | ||
| 534 | JLE GIP_CONT ; Yes. Good boy. You get to continue. @D2A | ||
| 535 | |||
| 536 | MOV GIP.GIOPFCN,BADFCN ; No. Shamey, shamey. Set the bad @D2A | ||
| 537 | JMP GIP_DONE ; return code and go to the end. @D2A | ||
| 538 | |||
| 539 | ; Parameter list is OK. Let's return the number of XMA blocks. D2A | ||
| 540 | |||
| 541 | GIP_CONT: ; D2A | ||
| 542 | MOV GIP.GIOPFCN,GOODRET ; Set a good return code @D2A | ||
| 543 | MOV AX,CS:HI_XMA_BLK ; Get the number of XMA blox @D2A | ||
| 544 | MOV GIP.GIOPBLK,AX ; Put it in the paramter list @D2A | ||
| 545 | |||
| 546 | |||
| 547 | GIP_DONE: ; D2A | ||
| 548 | MOV RH.RHC_STA,STAT_DONE ; Store done status and good return @D2A | ||
| 549 | ; code into request header D2A | ||
| 550 | RET ; @D2A | ||
| 551 | |||
| 552 | INT15F88 PROC FAR ; P7A | ||
| 553 | |||
| 554 | ; The following is the interrupt chaining structure specified in the PC AT P7A | ||
| 555 | ; Technical Reference. P7A | ||
| 556 | |||
| 557 | JMP SHORT BEGIN ; P7A | ||
| 558 | |||
| 559 | CHAINOFF DW 0 ; Offest of the previous INT 15 vect. @P7A | ||
| 560 | CHAINSEG DW 0 ; Segment of the previous INT 15 vect.@P7A | ||
| 561 | SIGNATURE DW 424BH ; Says we're doing chaining @P7A | ||
| 562 | FLAGS DB 0 ; @P7A | ||
| 563 | FIRST EQU 80H ; @P7A | ||
| 564 | JMP SHORT RESET ; @P7A | ||
| 565 | RESERVED DB 7 DUP (0) ; @P7A | ||
| 566 | |||
| 567 | ; OK. Let's see if the user asked for function 88H, query memory size. P7A | ||
| 568 | ; The function number is specified in the AL register. If it's P7A | ||
| 569 | ; function88h, then put the memory size in AX and IRET to the caller. P7A | ||
| 570 | ; Else, just pass the interrupt on to the guy who was installed in the INT P7A | ||
| 571 | ; 15 vector before us. P7A | ||
| 572 | |||
| 573 | BEGIN: CMP AH,88H ; Is it function 88H? @P7A | ||
| 574 | JNE NOT_MINE ; It's not ours to handle @P7A | ||
| 575 | |||
| 576 | MOV AX,CS:EXT_MEM ; Put the number of K into AX @P7A | ||
| 577 | IRET ; Return to the caller @P7A | ||
| 578 | |||
| 579 | NOT_MINE: JMP CS:DWORD PTR CHAINOFF | ||
| 580 | ; Pass the interrupt on to the @P7A | ||
| 581 | ; previously installed vector @P7A | ||
| 582 | |||
| 583 | RESET: RET ; This, too, is part of the interrupt@P7A | ||
| 584 | ; chaining structure. We will just@P7A | ||
| 585 | ; return on a call to reset. Note @P7A | ||
| 586 | ; that this is a far return. @P7A | ||
| 587 | INT15F88 ENDP ; @P7A | ||
| 588 | |||
| 589 | LEAVE_RES LABEL NEAR ; Leave code up to here resident.@D0A @D2M | ||
| 590 | |||
| 591 | SUBTTL Initialize Routine | ||
| 592 | PAGE | ||
| 593 | INIT_P1: | ||
| 594 | |||
| 595 | PUSH ES ; Save our code segment at the @D0A | ||
| 596 | MOV DI,0 ; fixed location 0:4F4. This @P3C | ||
| 597 | MOV ES,DI ; gives us a quick way to find CS @P3C | ||
| 598 | MOV DI,4F4H ; and also enables us to break on @P3C | ||
| 599 | MOV ES:[DI],CS ; a write to 0:4F4 which helps us @P3C | ||
| 600 | POP ES ; find the code on the ICE386. @D0A | ||
| 601 | MOV AH,DISPSTRG ; Display the welcome message. @D0A | ||
| 602 | MOV DX,OFFSET WELCOME ; @D0A | ||
| 603 | PUSH DS ; Save DS since DS:BX points to the @D0A | ||
| 604 | PUSH CS ; request header @D0A | ||
| 605 | POP DS ; DS:DX points to the message @D0A | ||
| 606 | INT 21H ; Display the message @D0A | ||
| 607 | POP DS ; Restore DS @D0A | ||
| 608 | ; @P3D | ||
| 609 | MOV RH.RH0_ENDS,CS ; Set the segment and offset of the end | ||
| 610 | MOV RH.RH0_ENDO,OFFSET LEAVE_RES ; of code to leave resident | ||
| 611 | MOV RH.RHC_STA,STAT_DONE ; Store "done" status into request | ||
| 612 | ; header | ||
| 613 | CMP CS:ISmodel_80,1 ; Check if we're on a model_80 @D0A @P1C | ||
| 614 | JE CONT ; If so, then continue @D0A | ||
| 615 | ; D0A | ||
| 616 | MOV RH.RH0_ENDO,0 ; Leave nothing resident @D0A | ||
| 617 | MOV AX,STAT_GEN ; Return general error code @D0A | ||
| 618 | OR AX,STAT_DONE ; Add "done" bit to status word@D0A @P3C | ||
| 619 | MOV RH.RHC_STA,AX ; Store status into request header @D0A | ||
| 620 | ; D0A | ||
| 621 | MOV AH,DISPSTRG ; Display the message that we are @D0A | ||
| 622 | MOV DX,OFFSET NO_80386 ; not on a model_80 @D0A | ||
| 623 | PUSH CS ; @D0A | ||
| 624 | POP DS ; @D0A | ||
| 625 | INT 21H ; @D0A | ||
| 626 | ; D0A | ||
| 627 | RET ; D0A | ||
| 628 | ; D0A | ||
| 629 | CONT: ; @D0M | ||
| 630 | SMSW AX ; Get machine status register | ||
| 631 | TEST AL,1 ; Check if the processor is already in | ||
| 632 | ; protect mode. If so, then someone | ||
| 633 | ; else (maybe us) has already taken | ||
| 634 | ; over protect mode. | ||
| 635 | JZ STILLOK ; If not, keep going @D0C | ||
| 636 | |||
| 637 | MOV RH.RH0_ENDO,0 ; Leave nothing resident @D0A | ||
| 638 | MOV AX,STAT_GEN ; Return general error code @D0A | ||
| 639 | OR AX,STAT_DONE ; Add "done" bit to status word@D0A @P3C | ||
| 640 | MOV RH.RHC_STA,AX ; Store status into request header @D0A | ||
| 641 | ; D0A | ||
| 642 | MOV AH,DISPSTRG ; Display the message that protect @D0A | ||
| 643 | MOV DX,OFFSET WAS_INST ; mode is taken. @D0A | ||
| 644 | PUSH CS ; DS:DX points to the message @D0A | ||
| 645 | POP DS ; @D0A | ||
| 646 | INT 21H ; @D0A | ||
| 647 | |||
| 648 | RET ; | ||
| 649 | STILLOK: ; D0A | ||
| 650 | PUSH 0DEADH ; Push stack delimiter | ||
| 651 | ; Don't have to set character ID @D2D | ||
| 652 | CALL GET_PARMS ; Get the MOVEBLOCK buffer size if | ||
| 653 | jnc StillOK1 | ||
| 654 | MOV RH.RH0_ENDO,0 ; Leave nothing resident @D0A | ||
| 655 | MOV AX,STAT_GEN ; Return general error code @D0A | ||
| 656 | OR AX,STAT_DONE ; Add "done" bit to status word@D0A @P3C | ||
| 657 | MOV RH.RHC_STA,AX ; Store status into request header @D0A | ||
| 658 | ; D0A | ||
| 659 | pop ax | ||
| 660 | ret ; exit program | ||
| 661 | |||
| 662 | StillOK1: | ||
| 663 | ; one was specified | ||
| 664 | CLI ; Disable interrupts | ||
| 665 | |||
| 666 | PUSH CS ; Now we can point DS to our own @P3A | ||
| 667 | POP DS ; code segment @P3A | ||
| 668 | ; 2@D2D | ||
| 669 | MOV AX,CS | ||
| 670 | MOV REAL_CS,AX ; Save real CS for when we @P3C | ||
| 671 | MOV AX,SS ; switch to protect mode | ||
| 672 | MOV REAL_SS,AX ; Save real SS @P3C | ||
| 673 | MOV AX,SP | ||
| 674 | MOV REAL_SP,AX ; Save real SP @P3C | ||
| 675 | |||
| 676 | ;------------------------------------------------------------------------------; | ||
| 677 | ; Enable address line A20 ; | ||
| 678 | ;------------------------------------------------------------------------------; | ||
| 679 | |||
| 680 | ; 3@P4D | ||
| 681 | CALL GATE_A20 | ||
| 682 | |||
| 683 | INT 11H ; Get the BIOS equipment flags | ||
| 684 | AND AL,30H ; Bits 5 and 6 on means it's a mono | ||
| 685 | CMP AL,30H | ||
| 686 | JE LEAVEBW | ||
| 687 | MOV CRT_SELECTOR,C_CCRT_PTR ; Set the CRT selector to color display | ||
| 688 | LEAVEBW: | ||
| 689 | MOV AH,88H ; Get number of 1k blocks above 1M | ||
| 690 | INT 15H | ||
| 691 | ADD AX,1024 ; Add 640K for the memory below 640K @P7C | ||
| 692 | MOV MAXMEM,AX ; Save for later | ||
| 693 | |||
| 694 | ; Get the maximum XMA block number and save it in the header up front D0A | ||
| 695 | ; All memory is treated as XMA memory. D1C | ||
| 696 | ; D0A | ||
| 697 | SUB AX,BUFF_SIZE ; Can't use the MOVEBLOCK buffer for @D0A | ||
| 698 | ; XMA memory. AX = number of K D0A | ||
| 699 | ; available. D0A | ||
| 700 | SUB AX,(1024-640) +128 ; Subtract 128k for the Emulator code ;an000; dms; | ||
| 701 | SHR AX,2 ; Divide by four to get the number of @D0A | ||
| 702 | ; 4K blocks D0A | ||
| 703 | DEC AX ; Subtract 1. This converts the @P3A | ||
| 704 | ; number of blocks available to the P3A | ||
| 705 | ; highest block number available. P3A | ||
| 706 | ; Block numbers are zero based. P3A | ||
| 707 | MOV HI_XMA_BLK,AX ; Save it in the header D0A | ||
| 708 | |||
| 709 | ;------------------------------------------------------------------------------; | ||
| 710 | ; Now lets relocate ourselves to high memory. ; | ||
| 711 | ;------------------------------------------------------------------------------; | ||
| 712 | |||
| 713 | MOV AX,HIGH_SEG ; Set ES to the highest segment value | ||
| 714 | MOV ES,AX | ||
| 715 | MOV DI,0 ; ES:DI points to the place to relocate to | ||
| 716 | MOV AX,CS | ||
| 717 | MOV DS,AX | ||
| 718 | MOV SI,0 ; DS:SI points to our code to be moved | ||
| 719 | MOV CX,DDSIZE/2 ; Length of code / 2 since moving words | ||
| 720 | CLD | ||
| 721 | REP MOVSW ; Copy myself to high memory | ||
| 722 | |||
| 723 | JUMPFAR NEXT,HIGH_SEG ; Jump to my relocated code | ||
| 724 | NEXT: | ||
| 725 | |||
| 726 | MOV AX,HIGH_SEG ; Set DS to be the same as CS | ||
| 727 | MOV DS,AX | ||
| 728 | ;------------------------------------------------------------------------------; | ||
| 729 | ; The machine is still in real mode. Zero out GDT and IDT ram. ; | ||
| 730 | ;------------------------------------------------------------------------------; | ||
| 731 | |||
| 732 | MOV DI,GDT_LOC ; DI points to GDT location | ||
| 733 | |||
| 734 | MOV CX,(GDT_LEN+SIDT_LEN)/2 ; Set GDT and IDT to zero | ||
| 735 | MOV AX,0 ; Store zeroes for now | ||
| 736 | REP STOSW | ||
| 737 | |||
| 738 | ;------------------------------------------------------------------------------; | ||
| 739 | ; Use good-old real-mode selectors to set up the page tables. The ; | ||
| 740 | ; page directory is a 4K block that is placed just before the ; | ||
| 741 | ; beginning of the GDT and on a 4K boundary. Note that the DATAOV ; | ||
| 742 | ; macro creates a prefix for the following instruction so that its ; | ||
| 743 | ; data references are 32 bits wide. ; | ||
| 744 | ;------------------------------------------------------------------------------; | ||
| 745 | |||
| 746 | DATAOV | ||
| 747 | SUB AX,AX ; Clear EAX (32 bit AX reg.) | ||
| 748 | MOV AX,HIGH_SEG ; Get the current code segment | ||
| 749 | DATAOV | ||
| 750 | SUB BX,BX ; Clear EBX (32 bit BX reg.) | ||
| 751 | MOV BX,GDT_LOC/16 ; Load the offset of the GDT, converted | ||
| 752 | ; to paragraphs | ||
| 753 | DATAOV ; Add it on to the current code segment | ||
| 754 | ADD AX,BX ; to get the segment address of the GDT. | ||
| 755 | ; This will be over 1M, so use 32 bits. | ||
| 756 | AND AX,0FF00H ; Round down to nice 4k boundary | ||
| 757 | DATAOV | ||
| 758 | SUB BX,BX ; Clear EBX | ||
| 759 | MOV BX,4096/16 ; Load with the size of the page directory | ||
| 760 | ; converted to paragraphs | ||
| 761 | DATAOV ; Subtract the number of paragraphs needed | ||
| 762 | SUB AX,BX ; for the page directory | ||
| 763 | DATAOV | ||
| 764 | SHL AX,4 ; Convert from paragraphs to bytes | ||
| 765 | CMOV CR3,EAX ; Load the address of the page directory | ||
| 766 | ; into CR3 | ||
| 767 | DATAOV | ||
| 768 | SUB BX,BX ; Clear EBX | ||
| 769 | MOV BX,HIGH_SEG ; Load our current code segment | ||
| 770 | DATAOV | ||
| 771 | SHL BX,4 ; Convert from paragraphs to bytes | ||
| 772 | DATAOV | ||
| 773 | SUB AX,BX ; Subtract from the address of the page | ||
| 774 | ; directory to get the offset of the | ||
| 775 | ; directory in our code segment | ||
| 776 | MOV SGTBLOFF,AX ; Save for later | ||
| 777 | |||
| 778 | ; Now let's clear the page directory | ||
| 779 | |||
| 780 | MOV CX,2048 ; Length is 4K/2 since storing words | ||
| 781 | DATAOV | ||
| 782 | MOV DI,AX ; ES:EDI points to beginning of directory | ||
| 783 | MOV AX,0 | ||
| 784 | REP STOSW ; Clear the page directory! | ||
| 785 | |||
| 786 | ;------------------------------------------------------------------------------; | ||
| 787 | ; Initialize the first directory entries to our page tables ; | ||
| 788 | ;------------------------------------------------------------------------------; | ||
| 789 | |||
| 790 | CMOV EAX,CR3 ; Get back CR3 - the address of the page dir. | ||
| 791 | DATAOV | ||
| 792 | MOV DI,SGTBLOFF ; Point ES:EDI to first entry in directory | ||
| 793 | DATAOV | ||
| 794 | SUB BX,BX ; Clear EBX | ||
| 795 | MOV BX,MEG_SUPPORTED/4*4096 ; Load the size of the page tables. | ||
| 796 | ; Each page table maps 4M of memory, so divide | ||
| 797 | ; the number of Meg supported by 4 to get the | ||
| 798 | ; number of page tables. Each page table is | ||
| 799 | ; 4K in size, so multiply by 4K. | ||
| 800 | DATAOV | ||
| 801 | SUB AX,BX ; Subtract the size needed for the page tables | ||
| 802 | ; from the address of the page directory to | ||
| 803 | ; get the address of the first page table. | ||
| 804 | ADD AX,7 ; Set the present bit and access rights. | ||
| 805 | ; This converts the address to a valid entry | ||
| 806 | ; for the page directory. | ||
| 807 | DATAOV | ||
| 808 | MOV NORMPAGE,AX ; Save for later | ||
| 809 | MOV CX,MEG_SUPPORTED/4 ; Load the number of page tables into CX | ||
| 810 | DATAOV | ||
| 811 | SUB BX,BX ; Clear EBX | ||
| 812 | MOV BX,1000H ; Set up 4k increment | ||
| 813 | ; | ||
| 814 | ; Now we load the page directory. EAX contains the address of the first | ||
| 815 | ; page table, EBX contains 4K, CX contains the number of page tables, and | ||
| 816 | ; ES:EDI (32 bit DI reg.) points to the first page directory entry. Now what | ||
| 817 | ; we do is stuff EAX into the 32bits pointed to by EDI. EDI is then auto- | ||
| 818 | ; incremented by four bytes, because of the 32 bit stuff, and points to the | ||
| 819 | ; next page directory entry. (Page directory and page table entries are four | ||
| 820 | ; bytes long.) Then we add the 4K in EBX to the address in EAX making EAX | ||
| 821 | ; the address of the next page table. This is done for the number of page | ||
| 822 | ; table entries in CX. Pretty slick, huh? | ||
| 823 | ; | ||
| 824 | LPT: | ||
| 825 | DATAOV ; Stuff the page table address into the | ||
| 826 | STOSW ; page directory | ||
| 827 | DATAOV ; Add 4K to the page table address in EAX | ||
| 828 | ADD AX,BX ; so that it contains the address of the | ||
| 829 | ; next page table | ||
| 830 | LOOP LPT ; Do it again | ||
| 831 | |||
| 832 | ; Now calcuate the offset from our code segment of the page tables | ||
| 833 | |||
| 834 | DATAOV | ||
| 835 | SUB BX,BX ; Clear EBX | ||
| 836 | MOV BX,HIGH_SEG ; Load our current code segment | ||
| 837 | DATAOV | ||
| 838 | SHL BX,4 ; Convert paragraphs to bytes | ||
| 839 | DATAOV ; Load EAX with the address of the first | ||
| 840 | MOV AX,NORMPAGE ; page table | ||
| 841 | DATAOV | ||
| 842 | SUB AX,BX ; Convert EAX to an offset | ||
| 843 | AND AL,0F8H ; AND off the access rights | ||
| 844 | MOV PGTBLOFF,AX ; Save for later | ||
| 845 | |||
| 846 | ;------------------------------------------------------------------------------; | ||
| 847 | ; Initialize the page tables ; | ||
| 848 | ;------------------------------------------------------------------------------; | ||
| 849 | |||
| 850 | MOV DI,PGTBLOFF ; ES:DI points to the first page table | ||
| 851 | DATAOV | ||
| 852 | SUB AX,AX ; Zero EAX | ||
| 853 | ADD AX,7 ; Set the present and access rights | ||
| 854 | MOV CX,MEG_SUPPORTED/4*1024 ; Load CX with the number of page table | ||
| 855 | ; entries to initialize. As mentioned | ||
| 856 | ; above, the number of page tables = | ||
| 857 | ; number of Meg / 4. There are 1K | ||
| 858 | ; entries per table so multiply by 1K | ||
| 859 | DATAOV | ||
| 860 | SUB BX,BX ; Clear EBX | ||
| 861 | MOV BX,1000H ; Set up 4k increment | ||
| 862 | ; | ||
| 863 | ; As with the page directory, we use a tight loop to initialize the page tables. | ||
| 864 | ; EAX contains the address of the first page frame, which is 0000, plus the | ||
| 865 | ; access rights. EBX contains a 4K increment. ES:DI points to the first entry | ||
| 866 | ; in the first page table. CX contains the number of page table entries to | ||
| 867 | ; initialize. The stuff and increment works the same as for the page directory | ||
| 868 | ; with an added touch. Note that this does all the page tables in one fell | ||
| 869 | ; swoop. When we finish stuffing the last address into the first page table | ||
| 870 | ; the next place we stuff is into the first entry in the second page table. | ||
| 871 | ; Since our page tables are back to back we can just zoom up the page tables | ||
| 872 | ; incrementing by 4K as we go and thus initialize all the page tables in one | ||
| 873 | ; fell swoop. | ||
| 874 | ; | ||
| 875 | BPT: | ||
| 876 | DATAOV ; Stuff the page frame address into the | ||
| 877 | STOSW ; page table | ||
| 878 | DATAOV | ||
| 879 | ADD AX,BX ; Next 4k page frame | ||
| 880 | LOOP BPT | ||
| 881 | |||
| 882 | ;------------------------------------------------------------------------------; | ||
| 883 | ; Now set up the first 64K over 1M to point to point to the first 64K ; | ||
| 884 | ; in low memory to simulate the segment wrap over 1M. ; | ||
| 885 | ; For now will set it up to point to itself and try to get DOS to load ; | ||
| 886 | ; the device driver up there. Will find out if anyone tries to alter ; | ||
| 887 | ; it because it will be marked for system use only. ; | ||
| 888 | ;------------------------------------------------------------------------------; | ||
| 889 | |||
| 890 | MOV DI,1024 ; 1M offset into page table | ||
| 891 | ADD DI,PGTBLOFF ; Page table offset | ||
| 892 | MOV AX,10H ; Set EAX to contain 1M address by loading | ||
| 893 | DATAOV ; it with 10H and shifting it 16 bits to | ||
| 894 | SHL AX,16 ; get 00100000. (Same as 10000:0) | ||
| 895 | ADD AX,5 ; Present, system use, read only | ||
| 896 | MOV CX,16 ; 16 entries = 64k | ||
| 897 | BPT2: | ||
| 898 | DATAOV | ||
| 899 | STOSW ; Stuff the address in the page table | ||
| 900 | DATAOV | ||
| 901 | ADD AX,BX ; Next 4k page frame | ||
| 902 | LOOP BPT2 | ||
| 903 | |||
| 904 | PAGE | ||
| 905 | ;------------------------------------------------------------------------------; | ||
| 906 | ; Build the Global Descriptor Table and load the GDT register. ; | ||
| 907 | ;------------------------------------------------------------------------------; | ||
| 908 | CALL GDT_BLD | ||
| 909 | |||
| 910 | MOV DI,GDT_PTR ; Get the offset of the GDT descriptor | ||
| 911 | ADD DI,GDT_LOC ; located in the GDT | ||
| 912 | MOV BP,DI ; Transfer the offset to BP | ||
| 913 | LGDT ES:FWORD PTR[BP] ; Put the descriptor for the GDT into | ||
| 914 | ; the GDT register | ||
| 915 | |||
| 916 | PAGE | ||
| 917 | ;------------------------------------------------------------------------------; | ||
| 918 | ; Build and initialize the system Interrupt Descriptor Table, ; | ||
| 919 | ; then load the IDT register. ; | ||
| 920 | ;------------------------------------------------------------------------------; | ||
| 921 | CALL SIDT_BLD | ||
| 922 | |||
| 923 | MOV DI,MON_IDT_PTR ; Get the offset of the IDT descriptor | ||
| 924 | ADD DI,GDT_LOC ; located in the GDT | ||
| 925 | MOV BP,DI ; Transfer the offset to BP | ||
| 926 | |||
| 927 | LIDT ES:FWORD PTR[BP] ; Put the descriptor for the IDT into | ||
| 928 | ; the IDT register | ||
| 929 | |||
| 930 | PAGE | ||
| 931 | ;------------------------------------------------------------------------------; | ||
| 932 | ; At this point we prepare to switch to virtual mode. The first ; | ||
| 933 | ; instruction after the LMSW that causes the switch must be a ; | ||
| 934 | ; jump far to set a protected mode segment selector into CS. ; | ||
| 935 | ;------------------------------------------------------------------------------; | ||
| 936 | |||
| 937 | MOV AX,VIRTUAL_ENABLE ; Machine status word needed to | ||
| 938 | LMSW AX ; switch to virtual mode | ||
| 939 | |||
| 940 | JUMPFAR DONE,SYS_PATCH_CS ; Must purge pre-fetch queue | ||
| 941 | ; and set selector into CS | ||
| 942 | DONE: | ||
| 943 | PAGE | ||
| 944 | ;------------------------------------------------------------------------------; | ||
| 945 | ; Initialize all the segment registers ; | ||
| 946 | ;------------------------------------------------------------------------------; | ||
| 947 | |||
| 948 | MOV AX,SYS_PATCH_DS ; Load DS, ES, and SS with the selector | ||
| 949 | MOV DS,AX ; for our data area. This is the same | ||
| 950 | MOV ES,AX ; as our code area but has read/write | ||
| 951 | ; access. | ||
| 952 | MOV SS,AX | ||
| 953 | MOV SP,OFFSET SP_INIT | ||
| 954 | |||
| 955 | PUSH 0002H ; Clean up our flags. Turn off all bits | ||
| 956 | POPF ; except the one that is always on. | ||
| 957 | |||
| 958 | ;------------------------------------------------------------------------------; | ||
| 959 | ; Load the LDTR to avoid faults ; | ||
| 960 | ;------------------------------------------------------------------------------; | ||
| 961 | |||
| 962 | MOV AX,SCRUBBER.TSS_PTR ; Load DS with the data descriptor for | ||
| 963 | MOV DS,AX ; the virtual machine's TSS | ||
| 964 | MOV AX,SCRUBBER.VM_LDTR ; Get the LDTR for virtual machine | ||
| 965 | MOV DS:VM_LDT,AX ; Set LDTR in TSS | ||
| 966 | LLDT AX ; Set the LDTR. Temporary for now. | ||
| 967 | |||
| 968 | ; Have to always have space allocated for the dispatch task TSS | ||
| 969 | |||
| 970 | MOV AX,SCRUBBER.VM_TR ; Low mem gets clobbered without this @P5C | ||
| 971 | LTR AX ; Set current Task Register | ||
| 972 | ; This TSS is located right after the IDT | ||
| 973 | |||
| 974 | PAGE | ||
| 975 | ;------------------------------------------------------------------------------; | ||
| 976 | ; Now we initialize the TSS (Task State Segment) for the one and only ; | ||
| 977 | ; virtual 8086 task. This task encompasses everything that runs in real ; | ||
| 978 | ; mode. First we clear the TSS and its I/O bit map. Then we initialize ; | ||
| 979 | ; the bit map for all the I/O ports we want to trap. Then we set up the ; | ||
| 980 | ; registers for the V86 task. These registers are given the same values ; | ||
| 981 | ; as we got on entry. IP is set to point to TEST_EXIT. ; | ||
| 982 | ;------------------------------------------------------------------------------; | ||
| 983 | |||
| 984 | MOV AX,SCRUBBER.TSS_PTR ; Load ES and DS with the descriptor | ||
| 985 | MOV DS,AX ; for the VM's TSS with read/write | ||
| 986 | MOV ES,AX ; access rights | ||
| 987 | CLD | ||
| 988 | MOV DI,0 ; Point ES:DI to the beginning of the TSS | ||
| 989 | MOV AX,0 ; Clear AX | ||
| 990 | MOV BX,0 ; Clear BX | ||
| 991 | MOV CX,TSS_386_LEN ; Load CX with the length of the TSS | ||
| 992 | REP STOSB ; Clear the TSS | ||
| 993 | MOV CX,TSS_BM_LEN ; Load CX with the length of the I/O bit | ||
| 994 | ; map. The bit map immediately follows | ||
| 995 | ; the TSS and is in the TSS segment. | ||
| 996 | REP STOSB ; Clear the bit map | ||
| 997 | MOV AL,0FFH ; Intel requires this byte | ||
| 998 | STOSB | ||
| 999 | |||
| 1000 | ; | ||
| 1001 | ; Now set up the bit map. Turn on bits for I/O ports that we want to trap. | ||
| 1002 | ; | ||
| 1003 | |||
| 1004 | MOV DI,0+TSS_386_LEN ; Set bits 0,2,4,6 to 1 - DMA ports | ||
| 1005 | MOV AL,055H | ||
| 1006 | STOSB | ||
| 1007 | MOV DI,1+TSS_386_LEN ; Set C to 1 - DMA port | ||
| 1008 | MOV AL,010H | ||
| 1009 | STOSB | ||
| 1010 | MOV DI,3+TSS_386_LEN ; Set 18,1A to 1 - DMA ports | ||
| 1011 | MOV AL,005H | ||
| 1012 | STOSB | ||
| 1013 | MOV DI,16+TSS_386_LEN ; Set 80-8f to 1s - DMA page ports | ||
| 1014 | MOV AL,0FFH ; + manufacturing port for ctl-alt-del | ||
| 1015 | STOSB | ||
| 1016 | STOSB | ||
| 1017 | MOV DI,0680H/8+TSS_386_LEN ; Set Roundup manuf. port to 1 | ||
| 1018 | MOV AL,001H | ||
| 1019 | STOSB | ||
| 1020 | MOV DI,31A0H/8+TSS_386_LEN ; Set 31a0-31a7 to 1s (XMA) | ||
| 1021 | MOV AL,0FFH | ||
| 1022 | STOSB | ||
| 1023 | |||
| 1024 | MOV WORD PTR [BX].ETSS_BM_OFFSET,TSS_386_LEN | ||
| 1025 | ; Put the bit map offset in the TSS | ||
| 1026 | MOV WORD PTR [BX].ETSS_SP0,OFFSET SP_INIT | ||
| 1027 | ; Put our SP as the SP for privilege | ||
| 1028 | ; level 0 | ||
| 1029 | MOV WORD PTR [BX].ETSS_SS0,SYS_PATCH_DS | ||
| 1030 | ; Put our SS as the SS for privilege | ||
| 1031 | ; level 0 | ||
| 1032 | |||
| 1033 | ; Next we set up the segment registers | ||
| 1034 | |||
| 1035 | MOV WORD PTR [BX].ETSS_GS,SEG PROG ; GS - our code segment | ||
| 1036 | MOV WORD PTR [BX].ETSS_FS,SEG PROG ; FS - our code segment | ||
| 1037 | MOV WORD PTR [BX].ETSS_DS,SEG PROG ; DS - our code segment | ||
| 1038 | MOV WORD PTR [BX].ETSS_ES,SEG PROG ; ES - our code segment | ||
| 1039 | |||
| 1040 | ; Next the SS,SP | ||
| 1041 | |||
| 1042 | MOV AX,CS:REAL_SS ; Set the real mode SS as the SS for the task | ||
| 1043 | MOV WORD PTR [BX].ETSS_SS,AX | ||
| 1044 | MOV AX,CS:REAL_SP ; Set the real mode SP as the SP for the task | ||
| 1045 | MOV WORD PTR [BX].ETSS_SP,AX | ||
| 1046 | |||
| 1047 | ; The flags register | ||
| 1048 | |||
| 1049 | MOV WORD PTR [BX].ETSS_FL2,2 ; Set the VM flag. Task is a V86 task. | ||
| 1050 | MOV WORD PTR [BX].ETSS_FL,0202H ; Set interrupts enabled | ||
| 1051 | |||
| 1052 | ; Set up CS and IP | ||
| 1053 | |||
| 1054 | MOV AX,CS:REAL_CS ; Set the real mode CS as the CS for the task | ||
| 1055 | MOV WORD PTR [BX].ETSS_CS,AX ; This is the CS we got when we loaded | ||
| 1056 | ; in low memory, before relocating | ||
| 1057 | MOV AX,OFFSET PROG:TEST_EXIT ; Set IP to the label TEST_EXIT below. | ||
| 1058 | MOV WORD PTR [BX].ETSS_IP,AX | ||
| 1059 | |||
| 1060 | ; The LDTR | ||
| 1061 | |||
| 1062 | MOV WORD PTR [BX].ETSS_LDT,SCRUBBER.VM_LDTR | ||
| 1063 | |||
| 1064 | ; And finally, CR3, the page directory base register | ||
| 1065 | |||
| 1066 | CMOV EAX,CR3 ; Get CR3 | ||
| 1067 | DATAOV | ||
| 1068 | MOV WORD PTR [BX].ETSS_CR3,AX ; Save it in the TSS | ||
| 1069 | |||
| 1070 | PAGE | ||
| 1071 | ;------------------------------------------------------------------------------; | ||
| 1072 | ; Now initialize our wonderful XMA page tables. Each table maps 4M. ; | ||
| 1073 | ; There is one table for each XMA bank since 4M is enough to map the ; | ||
| 1074 | ; 1M address space. All the XMA tables are initialized to point to ; | ||
| 1075 | ; the real memory at 0 to 4M. This is done by just copying the page ; | ||
| 1076 | ; table entry for 0 to 4M that was initialized above. ; | ||
| 1077 | ;------------------------------------------------------------------------------; | ||
| 1078 | |||
| 1079 | MOV AX,SYS_PATCH_DS ; Load DS with the selector for our data | ||
| 1080 | MOV DS,AX | ||
| 1081 | MOV SI,PGTBLOFF ; DS:SI point to the real page table for 0-4M | ||
| 1082 | MOV AX,XMA_PAGES_SEL ; Load ES with the selector for the XMA pages | ||
| 1083 | MOV ES,AX | ||
| 1084 | SUB DI,DI ; ES:DI point to the first XMA page table | ||
| 1085 | MOV CX,2048 ; Copy 4K / 2 since we're copying words | ||
| 1086 | REP MOVSW ; Copy the first XMA page table | ||
| 1087 | ; | ||
| 1088 | ; Now ES:DI points to the second XMA page table. Set DS:SI to point to the | ||
| 1089 | ; first XMA page table as the source for the copy. Now we can put a count | ||
| 1090 | ; of 15 page tables in CX. After each page is copied it is used as the source | ||
| 1091 | ; for the next page. This method lets us zip up the page tables initializing | ||
| 1092 | ; them all to be the same as the original page table for 0 - 4M. | ||
| 1093 | ; | ||
| 1094 | MOV AX,XMA_PAGES_SEL ; Load DS with the selector for the XMA page | ||
| 1095 | MOV DS,AX ; tables | ||
| 1096 | SUB SI,SI ; DS:SI points to the first XMA page table | ||
| 1097 | MOV CX,2048*15 ; Copy 15 more page tables | ||
| 1098 | REP MOVSW ; Copy to the other 15 XMA ID'S page tables | ||
| 1099 | |||
| 1100 | ; D1A | ||
| 1101 | ; Set the first page directory entry to point to the page table for bank 0. D1A | ||
| 1102 | ; This is another way of saying, "Let's make bank 0 the active bank." We D1A | ||
| 1103 | ; are now emulating the XMA 2 card along with its initialization device D1A | ||
| 1104 | ; driver, INDXMAA.SYS. When the device driver exits, it leaves the XMA 2 D1A | ||
| 1105 | ; card enabled and set to bank 0. Therefore, we must do the same. D1A | ||
| 1106 | ; D1A | ||
| 1107 | ; D1A | ||
| 1108 | MOV AX,SYS_PATCH_DS ; Load DS and ES with our data segment @D1A | ||
| 1109 | MOV DS,AX ; selector @D1A | ||
| 1110 | MOV ES,AX ; @D1A | ||
| 1111 | MOV DI,SGTBLOFF ; Point ES:DI to the first page @D1A | ||
| 1112 | ; directory entry D1A | ||
| 1113 | DATAOV ; Load AX with the page directory entry @D1A | ||
| 1114 | MOV AX,XMAPAGE ; for the first XMA page table @D1A | ||
| 1115 | DATAOV ; Stuff the address of the page table @D1A | ||
| 1116 | STOSW ; for bank 0 into the page directory @D1A | ||
| 1117 | |||
| 1118 | PAGE | ||
| 1119 | ;------------------------------------------------------------------------------; | ||
| 1120 | ; And now, the moment you've all been waiting for -- TURN ON THE PAGING ; | ||
| 1121 | ; MECHANISM!!! ; | ||
| 1122 | ;------------------------------------------------------------------------------; | ||
| 1123 | |||
| 1124 | CMOV EAX,CR0 ; Get CR0 @P5A | ||
| 1125 | MOV BX,8000H ; Set up BX to OR on the Paging Enable bit @P5C | ||
| 1126 | DATAOV | ||
| 1127 | SHL BX,16 ; It's the one all the way on the left @P5C | ||
| 1128 | DATAOV ; @P5A | ||
| 1129 | OR AX,BX ; Set the paging enabled bit @P5A | ||
| 1130 | OR AL,02H ; Set co-processor bit on @P5A | ||
| 1131 | AND AL,0F7H ; Turn off Task Switch bit @P5C | ||
| 1132 | CMOV CR0,EAX ; Here we go... | ||
| 1133 | |||
| 1134 | ; Make sure high order bits of ESP are zero - a1 errata | ||
| 1135 | |||
| 1136 | MOV AX,SP ; Save SP in AX 'cause it changes when we do... | ||
| 1137 | PUSH 0 ; this PUSH. Push 0 for high 16 bits of ESP | ||
| 1138 | PUSH AX ; Push low 16 bits of SP | ||
| 1139 | DATAOV | ||
| 1140 | POP SP ; Pop 32 bit ESP! | ||
| 1141 | |||
| 1142 | PAGE | ||
| 1143 | ;------------------------------------------------------------------------------; | ||
| 1144 | ; Now we give control back to the V86 task by setting up the stack P5C; | ||
| 1145 | ; for an IRET back to the V86 task. This requires putting the V86 P5C; | ||
| 1146 | ; task's segment registers, SS and ESP, and the EFLAGS, CS and IP on P5C; | ||
| 1147 | ; the stack. The 80386 puts all these values on the stack when it P5C; | ||
| 1148 | ; interrupts out of V86 mode, so it expects them there on an IRET P5C; | ||
| 1149 | ; back to V86 mode. But really we are giving control back to ; | ||
| 1150 | ; ourself. The CS:IP on the stack point to the label TEST_EXIT ; | ||
| 1151 | ; below, but it is in the copy of the emulator that was originally ; | ||
| 1152 | ; loaded, not the copy that was relocated to high memory and is now ; | ||
| 1153 | ; running in protect mode. This clever trick will result in the ; | ||
| 1154 | ; original copy of the emulator returning to DOS which will continue ; | ||
| 1155 | ; to load the rest of the system. The system will come up completely ; | ||
| 1156 | ; unaware that it is running in a small universe of a V86 task which ; | ||
| 1157 | ; is being monitored by the XMA emulator. ; | ||
| 1158 | ;------------------------------------------------------------------------------; | ||
| 1159 | |||
| 1160 | |||
| 1161 | MOV AX,SCRUBBER.TSS_PTR ; Load DS with the descriptor for the @P5A | ||
| 1162 | MOV DS,AX ; VM's TSS with read/write access @P5A | ||
| 1163 | MOV BX,0 ; VM's TSS with read/write access @P5A | ||
| 1164 | ; P5A | ||
| 1165 | ; Set up our stack for an IRET to the V86 task. This is an inter-level P5A | ||
| 1166 | ; IRET to a V86 task so we need the V86 task's SS, ESP, ES, DS, FS and GS P5A | ||
| 1167 | ; as well as his EFLAGS, EIP and CS. P5A | ||
| 1168 | ; P5A | ||
| 1169 | DATAOV ; @P5A | ||
| 1170 | PUSH WORD PTR [BX].ETSS_GS ; Put V86 task's GS on the stack @P5A | ||
| 1171 | DATAOV ; @P5A | ||
| 1172 | PUSH WORD PTR [BX].ETSS_FS ; Put V86 task's FS on the stack @P5A | ||
| 1173 | DATAOV ; @P5A | ||
| 1174 | PUSH WORD PTR [BX].ETSS_DS ; Put V86 task's DS on the stack @P5A | ||
| 1175 | DATAOV ; @P5A | ||
| 1176 | PUSH WORD PTR [BX].ETSS_ES ; Put V86 task's ES on the stack @P5A | ||
| 1177 | DATAOV ; @P5A | ||
| 1178 | PUSH WORD PTR [BX].ETSS_SS ; Put V86 task's SS on the stack @P5A | ||
| 1179 | DATAOV ; @P5A | ||
| 1180 | PUSH WORD PTR [BX].ETSS_SP ; Put V86 task's ESP on the stack @P5A | ||
| 1181 | DATAOV ; @P5A | ||
| 1182 | PUSH WORD PTR [BX].ETSS_FL ; Put V86 task's EFLAGS on the stack @P5A | ||
| 1183 | DATAOV ; @P5A | ||
| 1184 | PUSH WORD PTR [BX].ETSS_CS ; Put V86 task's CS on the stack @P5A | ||
| 1185 | DATAOV ; @P5A | ||
| 1186 | PUSH WORD PTR [BX].ETSS_IP ; Put V86 task's EIP on the stack @P5A | ||
| 1187 | DATAOV ; @P5A | ||
| 1188 | IRET ; @P5A | ||
| 1189 | ; @P5D | ||
| 1190 | |||
| 1191 | TEST_EXIT: ; We are now running in V86 mode | ||
| 1192 | POP AX ; Pop the stack until our DEAD delimiter is | ||
| 1193 | CMP AX,0DEADH ; found | ||
| 1194 | JNE TEST_EXIT | ||
| 1195 | |||
| 1196 | ; Replace the interrupt 15 vector with our handler (INT15F88). P7A | ||
| 1197 | |||
| 1198 | MOV AH,GET_VECT ; Get the current vector at interrupt 15H @P7A | ||
| 1199 | MOV AL,15H ; @P7A | ||
| 1200 | INT 21H ; @P7A | ||
| 1201 | |||
| 1202 | MOV CS:CHAINSEG,ES ; Save it in the chaining header in @P7A | ||
| 1203 | MOV CS:CHAINOFF,BX ; INT15F88 @P7A | ||
| 1204 | |||
| 1205 | MOV AH,SET_VECT ; Set the entry point of INT15F88 as the @P7A | ||
| 1206 | MOV AL,15H ; new interrupt 15 vector @P7A | ||
| 1207 | PUSH CS ; @P7A | ||
| 1208 | POP DS ; @P7A | ||
| 1209 | MOV DX,OFFSET INT15F88 ; @P7A | ||
| 1210 | INT 21H ; @P7A | ||
| 1211 | |||
| 1212 | ; Copy the number of K for extended memory from BUFF_SIZE to EXT_MEM. This P7A | ||
| 1213 | ; is needed because BUFF_SIZE does not stay resident, EXT_MEM does. P7A | ||
| 1214 | |||
| 1215 | MOV AX,BUFF_SIZE ; @P7A | ||
| 1216 | MOV EXT_MEM,AX ; @P7A | ||
| 1217 | |||
| 1218 | ; Issue the message that says we installed successfully | ||
| 1219 | |||
| 1220 | MOV AH,DISPSTRG ; Set AH to DOS display string function @D0A | ||
| 1221 | MOV DX,OFFSET GOODLOAD ; @D0A | ||
| 1222 | PUSH CS ; @D0A | ||
| 1223 | POP DS ; DS:DX points to the message @D0A | ||
| 1224 | INT 21H ; Display the message @D0A | ||
| 1225 | |||
| 1226 | RET ; Return to IRPT which called INIT_P1 | ||
| 1227 | |||
| 1228 | SUBTTL Gate A20 | ||
| 1229 | PAGE | ||
| 1230 | ;------------------------------------------------------------------------------; | ||
| 1231 | ; GATE_A20 ; | ||
| 1232 | ; This routine controls a signal which gates address bit 20. ; | ||
| 1233 | ; Bit 2 of port 92H controls the enabling of A20. If bit 2 is on, P4C; | ||
| 1234 | ; then A20 is enabled. Conversely, if bit 2 is off, A20 is disabled. P4C; | ||
| 1235 | ; ; | ||
| 1236 | ;------------------------------------------------------------------------------; | ||
| 1237 | |||
| 1238 | ; Equates for the Gate A20 enable | ||
| 1239 | |||
| 1240 | ENABLE_A20 EQU 02H ; Bit 2 of port 92H turns on A20 @P4C | ||
| 1241 | |||
| 1242 | GATE_A20 PROC | ||
| 1243 | |||
| 1244 | IN AL,92H ; Get the current value of port 92 @P4A | ||
| 1245 | OR AL,ENABLE_A20 ; Turn on the bit to enable A20 @P4A | ||
| 1246 | OUT 92H,AL ; Send it back out to port 92 @P4A | ||
| 1247 | RET | ||
| 1248 | ; 15@P4D | ||
| 1249 | GATE_A20 ENDP | ||
| 1250 | |||
| 1251 | SUBTTL GET_PARMS parameter line scan | ||
| 1252 | PAGE | ||
| 1253 | ;------------------------------------------------------------------------------; | ||
| 1254 | ; GET_PARMS ; | ||
| 1255 | ; This procedure converts the numeric parameter following the DEVICE statement ; | ||
| 1256 | ; in the CONFIG.SYS file to a binary number and saves it in BUFF_SIZE. The ; | ||
| 1257 | ; number is rounded up to the nearest 16K boundary. ; | ||
| 1258 | ; ; | ||
| 1259 | ; Register usage: ; | ||
| 1260 | ; DS:SI indexes parameter string ; | ||
| 1261 | ; AL contains character from parameter string ; | ||
| 1262 | ; CX value from GET_NUMBER ; | ||
| 1263 | ; ; | ||
| 1264 | ;------------------------------------------------------------------------------; | ||
| 1265 | |||
| 1266 | ASSUME DS:NOTHING ; DS:BX point to Request Header | ||
| 1267 | |||
| 1268 | GET_PARMS PROC | ||
| 1269 | |||
| 1270 | PUSH DS ; Save DS | ||
| 1271 | push bx ; save bx ;an000; dms; | ||
| 1272 | |||
| 1273 | LDS SI,RH.RH0_BPBA ; DS:SI point to all text after "DEVICE=" | ||
| 1274 | ; in CONFIG.SYS | ||
| 1275 | XOR AL,AL ; Start with a null character in AL. | ||
| 1276 | |||
| 1277 | ;------------------------------------------------------------------------------; | ||
| 1278 | ; Skip until first delimiter is found. There may be digits in the path string.; | ||
| 1279 | ; ; | ||
| 1280 | ; DS:SI points to \pathstring\386XMAEM.SYS nn nn nn ; | ||
| 1281 | ; The character following 386XMAEM.SYS may have been changed to a null (00H). ; | ||
| 1282 | ; All letters have been changed to uppercase. ; | ||
| 1283 | ;------------------------------------------------------------------------------; | ||
| 1284 | |||
| 1285 | GET_PARMS_A: | ||
| 1286 | CALL GET_PCHAR ; Get a character from the parameter string | ||
| 1287 | JZ Get_Parms_Null ; The zero flag is set if the end of the line | ||
| 1288 | ; is found. If so, then exit. | ||
| 1289 | |||
| 1290 | ; Check for various delimeters | ||
| 1291 | |||
| 1292 | OR AL,AL ; Null | ||
| 1293 | JZ GET_PARMS_B | ||
| 1294 | CMP AL,' ' ; Blank | ||
| 1295 | JE GET_PARMS_B | ||
| 1296 | CMP AL,',' ; Comma | ||
| 1297 | JE GET_PARMS_B | ||
| 1298 | CMP AL,';' ; Semi-colon | ||
| 1299 | JE GET_PARMS_B | ||
| 1300 | CMP AL,'+' ; Plus sign | ||
| 1301 | JE GET_PARMS_B | ||
| 1302 | CMP AL,'=' ; Equals | ||
| 1303 | JE GET_PARMS_B | ||
| 1304 | CMP AL,TAB ; Tab | ||
| 1305 | JNE GET_PARMS_A ; Skip until delimiter or CR is found | ||
| 1306 | |||
| 1307 | GET_PARMS_B: ; Now pointing to first delimiter | ||
| 1308 | CALL SKIP_TO_DIGIT ; Skip to first digit | ||
| 1309 | JZ Get_Parms_C ; Found EOL, no digits remain | ||
| 1310 | |||
| 1311 | CALL GET_NUMBER ; Extract the digits and convert to binary | ||
| 1312 | jmp Get_Parms_Found ; Parm found | ||
| 1313 | |||
| 1314 | Get_Parms_Null: | ||
| 1315 | |||
| 1316 | xor cx,cx ; set cx to 0 ;an000; dms; | ||
| 1317 | |||
| 1318 | Get_Parms_Found: | ||
| 1319 | |||
| 1320 | mov bx,cx ; put cx value in bx ;an000; dms; | ||
| 1321 | |||
| 1322 | cmp cx,0 ; 0 pages requested? ;an000; dms; | ||
| 1323 | jne Get_Parm_Max ; allocate maximum number ;an000; dms; | ||
| 1324 | MOV CS:BUFF_SIZE,0; Store buffer size | ||
| 1325 | jmp Get_Parms_C | ||
| 1326 | |||
| 1327 | Get_Parm_Max: | ||
| 1328 | |||
| 1329 | cmp bx,64 ; >= 64 pages requested? ;an000; dms; | ||
| 1330 | jnb Get_Parms_64_Pg ; yes - continue ;an000; dms; | ||
| 1331 | mov dx,offset Small_Parm ; Parm < 64 and > 0 ;an000; dms; | ||
| 1332 | mov ah,Dispstrg ; Display the welcome message. ;an000; dms; | ||
| 1333 | push ds ; Save DS ;an000; dms; | ||
| 1334 | push cs ; ;an000; dms; | ||
| 1335 | pop ds ; DS:DX points to the message ;an000; dms; | ||
| 1336 | int 21h ; Display the message ;an000; dms; | ||
| 1337 | pop ds ; Restore DS ;an000; dms; | ||
| 1338 | stc ; flag an error occurred ;an000; dms; | ||
| 1339 | jmp Get_Parms_C ; exit routine ;an000; dms; | ||
| 1340 | |||
| 1341 | Get_Parms_64_Pg: | ||
| 1342 | |||
| 1343 | mov ax,bx ; prepare to adjust to Kb value ;an000; dms; | ||
| 1344 | mov cx,10h ; 16Kb per page ;an000; dms; | ||
| 1345 | xor dx,dx ; clear high word ;an000; dms; | ||
| 1346 | mul cx ; get Kb value ;an000; dms; | ||
| 1347 | |||
| 1348 | mov bx,ax ; store page Kb value in bx ;an000; dms; | ||
| 1349 | add bx,128 ; adjust for emulator code | ||
| 1350 | |||
| 1351 | mov ah,88h ; get number of 1k blocks above 1Mb ;an000; dms; | ||
| 1352 | int 15h ; | ||
| 1353 | |||
| 1354 | sub ax,bx ; get number of blocks to allocate for extended ;an000; dms; | ||
| 1355 | jnc Get_Parms_Ext ; set extended memory value in buff size ;an000; dms; | ||
| 1356 | mov dx,offset No_Mem ; not enough memory for parm | ||
| 1357 | mov ah,Dispstrg ; Display the welcome message. ;an000; dms; | ||
| 1358 | push ds ; Save DS ;an000; dms; | ||
| 1359 | push cs ; ;an000; dms; | ||
| 1360 | pop ds ; DS:DX points to the message ;an000; dms; | ||
| 1361 | int 21h ; Display the message ;an000; dms; | ||
| 1362 | pop ds ; Restore DS ;an000; dms; | ||
| 1363 | stc ; flag an error ;an000; dms; | ||
| 1364 | jmp Get_Parms_C ; exit routine ;an000; dms; | ||
| 1365 | |||
| 1366 | Get_Parms_Ext: | ||
| 1367 | |||
| 1368 | MOV CS:BUFF_SIZE,ax ; Store buffer size | ||
| 1369 | clc | ||
| 1370 | |||
| 1371 | GET_PARMS_C: | ||
| 1372 | pop bx ; restore bx ;an000; dms; | ||
| 1373 | POP DS ; Restore DS | ||
| 1374 | |||
| 1375 | RET | ||
| 1376 | |||
| 1377 | ;------------------------------------------------------------------------------; | ||
| 1378 | ; GET_PCHAR -- Get a character from the parameter string into AL ; | ||
| 1379 | ;------------------------------------------------------------------------------; | ||
| 1380 | |||
| 1381 | GET_PCHAR PROC | ||
| 1382 | CMP AL,CR ; Carriage return already encountered? | ||
| 1383 | JE GET_PCHAR_X ; Don't read past end of line | ||
| 1384 | LODSB ; Get character from DS:SI, increment SI | ||
| 1385 | CMP AL,CR ; Is the character a carriage return? | ||
| 1386 | JE GET_PCHAR_X ; Yes, leave the zero flag set to signal end | ||
| 1387 | ; of line | ||
| 1388 | CMP AL,LF ; No, is it a line feed? This will leave the | ||
| 1389 | ; zero flag set if a line feed was found. | ||
| 1390 | GET_PCHAR_X: | ||
| 1391 | RET | ||
| 1392 | |||
| 1393 | GET_PCHAR ENDP | ||
| 1394 | |||
| 1395 | ;------------------------------------------------------------------------------; | ||
| 1396 | ; CHECK_NUM -- Check if the character in AL is a numeric digit, ASCII for ; | ||
| 1397 | ; 0 - 9. The zero flag is set if the character is a digit, ; | ||
| 1398 | ; otherwise it is reset. ; | ||
| 1399 | ;------------------------------------------------------------------------------; | ||
| 1400 | |||
| 1401 | CHECK_NUM PROC | ||
| 1402 | CMP AL,'0' ; If character is less than a "0" then it is not | ||
| 1403 | JB CHECK_NUM_X ; a number, so exit | ||
| 1404 | |||
| 1405 | CMP AL,'9' ; If character is greater than a "9" then it is | ||
| 1406 | JA CHECK_NUM_X ; not a number, so exit | ||
| 1407 | |||
| 1408 | CMP AL,AL ; Set the zero flag to show it is a number | ||
| 1409 | CHECK_NUM_X: | ||
| 1410 | RET ; Zero flag is left reset if character is not | ||
| 1411 | CHECK_NUM ENDP ; a number | ||
| 1412 | |||
| 1413 | ;------------------------------------------------------------------------------; | ||
| 1414 | ; SKIP_TO_DIGIT -- Scan the parameter string until a numeric character is ; | ||
| 1415 | ; found or the end of the line is encountered. If a numeric ; | ||
| 1416 | ; character is not found then the zero flag is set. Else if ; | ||
| 1417 | ; a character was found then the zero flag is reset. ; | ||
| 1418 | ;------------------------------------------------------------------------------; | ||
| 1419 | |||
| 1420 | SKIP_TO_DIGIT PROC | ||
| 1421 | CALL CHECK_NUM ; Is the current character a digit? | ||
| 1422 | JZ SKIP_TO_DIGIT_X ; If zero flag is set then it is a number | ||
| 1423 | |||
| 1424 | CALL GET_PCHAR ; Get the next character from the line | ||
| 1425 | JNZ SKIP_TO_DIGIT ; Loop until first digit or CR or LF is found | ||
| 1426 | RET ; Fall through to here if digit not found | ||
| 1427 | |||
| 1428 | SKIP_TO_DIGIT_X: | ||
| 1429 | CMP AL,0 ; Digit found, reset the zero flag to show digit | ||
| 1430 | RET ; was found | ||
| 1431 | SKIP_TO_DIGIT ENDP | ||
| 1432 | |||
| 1433 | ;------------------------------------------------------------------------------; | ||
| 1434 | ; GET_NUMBER -- Convert the character digits in the parameter string to a ; | ||
| 1435 | ; binary value. The value is returned in CX, unless the ; | ||
| 1436 | ; calculation overflows, in which case return a 0. The next ; | ||
| 1437 | ; character after the digits is left in AL. ; | ||
| 1438 | ;------------------------------------------------------------------------------; | ||
| 1439 | |||
| 1440 | C10 DW 10 | ||
| 1441 | GN_ERR DB ? ; Zero if no overflow in accumulation | ||
| 1442 | |||
| 1443 | GET_NUMBER PROC ; Convert string of digits to binary value | ||
| 1444 | XOR CX,CX ; Clear CX, the resulting number | ||
| 1445 | MOV CS:GN_ERR,CL ; No overflow yet | ||
| 1446 | |||
| 1447 | GET_NUMBER_A: | ||
| 1448 | SUB AL,'0' ; Convert the ASCII character in AL to binary | ||
| 1449 | CBW ; Clear AH | ||
| 1450 | XCHG AX,CX ; Previous accumulation in AX, new digit in CL | ||
| 1451 | MUL CS:C10 ; DX:AX = AX*10 | ||
| 1452 | OR CS:GN_ERR,DL ; Any overflow from AX goes into DX. Any non- | ||
| 1453 | ; zero value in DL will signal an error | ||
| 1454 | ADD AX,CX ; Add the new digit to ten times the previous | ||
| 1455 | ; digits | ||
| 1456 | XCHG AX,CX ; New number now in CX | ||
| 1457 | CALL GET_PCHAR ; Get the next character | ||
| 1458 | CALL CHECK_NUM ; Check if it is numeric | ||
| 1459 | JZ GET_NUMBER_A ; If so, then go back and add this digit to the | ||
| 1460 | ; result | ||
| 1461 | CMP CS:GN_ERR,0 ; Did we overflow? | ||
| 1462 | JE GET_NUMBER_B ; If not, we're done | ||
| 1463 | XOR CX,CX ; Return a zero result if overflow | ||
| 1464 | GET_NUMBER_B: | ||
| 1465 | RET | ||
| 1466 | GET_NUMBER ENDP | ||
| 1467 | |||
| 1468 | GET_PARMS ENDP | ||
| 1469 | |||
| 1470 | POST ENDP | ||
| 1471 | |||
| 1472 | PROG ENDS | ||
| 1473 | END | ||
| 1474 | |||