2 TITLE INDEINI
- 386 XMA EMULATOR
- Initialization
5 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7 * MODULE NAME
: INDEINI
*
9 * 5669-196 (C
) COPYRIGHT
1988 Microsoft Corp
. *
11 * DESCRIPTIVE NAME
: 80386 XMA EMULATOR INITIALIZATION
*
13 * STATUS
(LEVEL
) : VERSION
(0) LEVEL
(2.0) *
15 * FUNCTION
: Do all the initialization needed for the
386 XMA emulator
.*
17 * The
386 XMA emulator is installed by putting the follow
- *
18 * ing command
in the CONFIG
.SYS
file: *
20 * DEVICE
=\386XMAEM
.SYS bbb
*
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
. *
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
. *
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
*
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
*
53 * REGISTER USAGE
: 80386 STANDARD
*
55 * RESTRICTIONS
: None
*
57 * DEPENDENCIES
: None
*
59 * LINKAGE
: Invoked
as a DOS device driver
*
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
. *
65 * RETURN PARMS
: A return
code is returned to DOS
in the device header
*
68 * OTHER EFFECTS
: None
*
70 * EXIT NORMAL
: Return to DOS after device driver is loaded
*
72 * EXIT ERROR
: Return to DOS after putting up error messages
*
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
*
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
*
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
*
106 * CONTROL BLOCKS
: INDEDAT
.INC *
108 * CHANGE ACTIVITY
: *
110 * $
MOD(INDEINI
) COMP
(LOAD) PROD
(3270PC
) : *
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
. *
125 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
128 .286P
; Enable recognition of 286 privileged instructs.
130 .XLIST
; Turn off the listing
133 IF1
; Only include the macros in the first pass
134 INCLUDE INDEOVP
.MAC
; of the assembler
137 .LIST
; Turn on the listing
139 ; Let these variables be known to external procedures
144 PROG
SEGMENT PARA
PUBLIC 'PROG'
153 ; These variables are located in INDEI15
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
169 ; These are the messages
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;
178 ; These entries are located in external procedures
180 EXTRN SIDT_BLD
:NEAR ; Build the interrupt descriptor table (IDT)
181 EXTRN GDT_BLD
:NEAR ; Build the global descriptor table (GDT)
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
196 ; ASCII character equates
198 TAB EQU
09H ; ASCII tab
199 LF EQU
0AH ; ASCII line feed
200 CR EQU
0DH ; ASCII carriage return
202 SUBTTL Structure Definitions
204 ;------------------------------------------------------------------------------;
205 ; Request Header (Common portion) ;
206 ;------------------------------------------------------------------------------;
208 RH EQU
DS:[BX] ; The Request Header structure is based off
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
219 ; Status values for RHC_STA
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 800
CH ; General error code D0A
225 ;------------------------------------------------------------------------------;
226 ; Request Header for INIT command ;
227 ;------------------------------------------------------------------------------;
230 DB (TYPE RHC
) DUP (?
) ; Reserve space for the header
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)
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.
247 ;---------------------------------------------------------------------------D2A;
248 ; Request Header for Generic IOCTL Request D2A;
249 ;---------------------------------------------------------------------------D2A;
252 DB (TYPE RHC
) DUP (?
) ; Reserve space for the header @D2A
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
261 SUBTTL Device Driver Header
265 ; Declare the device driver header
267 ORG 0 ; Device header must the very first thing in the
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
275 ; End of device driver header
277 ;------------------------------------------------------------------------------;
278 ; Request Header (RH) address, saved here by "strategy" routine ;
279 ;------------------------------------------------------------------------------;
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
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
292 SUBTTL Device Strategy
294 ;------------------------------------------------------------------------------;
295 ; Device "strategy" entry point ;
297 ; Retain the Request Header address for use by Interrupt routine ;
298 ;------------------------------------------------------------------------------;
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
308 SUBTTL Device Interrupt Intry Point
311 ;------------------------------------------------------------------------------;
312 ; Table of command processing routine entry points ;
313 ;------------------------------------------------------------------------------;
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
342 ;------------------------------------------------------------------------------;
343 ; Device "interrupt" entry point ;
344 ;------------------------------------------------------------------------------;
345 IRPT PROC
FAR ; Device interrupt entry point
347 ; First we must save all the registers that we use so that when we return to
348 ; DOS the registers are not changed.
350 PUSH DS ; Save the segment registers modified
353 CMP CS:ISmodel_80
,-1; Did we already check what machine we are @D2A
354 JNE DID_CHECK
; running on? @D2A
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
364 MOV CS:ISmodel_80
,1 ; Set the flag saying we're on a @P1A @D2M
365 JMP DID_CHECK
; model_80 @D2A
368 MOV CS:ISmodel_80
,0 ; Set the flag saying we're not on a @D2M
372 CMP ISmodel_80
,1 ; Are we on a model_80? @D2A
373 JE PUSH32
; If so, go save the 32 bit registers @P1A
375 ; Push 16 bit registers onto the stack.
387 ; Push 32 bit registers onto the stack P1A
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
394 PUSHED: CLD ; All moves go forward
396 LDS BX,CS:RH_PTRA
; Get the request header address passed to the
397 ; "strategy" routine into DS:BX
399 MOV AL,RH
.RHC_CMD
; Get the command code from the Request Header
400 CBW ; Zero AH (if AL > 7FH, next compare will
403 CMP AL,MAX_CMD
; If command code is too high
404 JA IRPT_CMD_HIGH
; Then jump to error routine
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
411 ; At entry to command processing routine:
413 ; DS:BX = Request Header address
414 ; CS = 386XMAEM code segment address
417 CALL CS:CMD_TABLE
[DI] ; Call routine to handle the command
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
426 IRPT_CMD_EXIT: ; Return from command routine
428 ; Restore the registers before returning to DOS.
430 CMP CS:ISmodel_80
,1 ; Are we on a model_80? @P1A
431 JE POP32
; Yes. Then pop the 32 bit registers. @P1A
433 ; Pop 16 bit registers off of the stack.
445 ; Pop 32 bit registers off of the stack. P1A
450 ; Pop the segment registers off of the stack.
458 SUBTTL Command Routines
463 INPUT_IOCTL: ; IOCTL input
465 INPUT_NOWAIT: ; Non-destructive input no wait
466 INPUT_STATUS: ; Input status
467 INPUT_FLUSH: ; Input flush
470 OUTPUT_IOCTL: ; IOCTL output
471 OUTPUT_STATUS: ; Output status
472 OUTPUT_FLUSH: ; Output flush
477 GET_LOG_DEVICE: ; @D2A
478 SET_LOG_DEVICE: ; @D2A
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
486 SUBTTL Generic IOCTL Service Routine
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 ;------------------------------------------------------------------------------;
497 GIP EQU
ES:[DI] ; @D2A
499 GEN_IOCTL_PARM
STRUC ; @D2A
501 GIOPLEN
DW ?
; Length of the parameter list @D2A
502 GIOPFCN
DW ?
; Function code @D2A
503 GIOPBLK
DW ?
; Number of XMA blocks available @D2A
505 GEN_IOCTL_PARM ENDS
; @D2A
507 MAXFCN EQU
0 ; Highest function number allowed @D2A
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
517 LES DI,RH
.RH19_RQPK
; Point ES:DI to the Generic IOCTL @D2A
520 ; First check to make sure the parameter list is long enough to return the D2A
521 ; number of XMA blocks. D2A
523 CMP GIP
.GIOPLEN
,4 ; Do we have at least four bytes? @D2A
524 JAE GIP_CHKFCN
; Yup. Go to check function number. @D2A
526 MOV GIP
.GIOPFCN
,BADLEN
; Nope. Sorry. Return the error @D2A
527 JMP GIP_DONE
; code and go to the end. @D2A
529 ; Check if the function number in the parameter list is a valid function. 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
536 MOV GIP
.GIOPFCN
,BADFCN
; No. Shamey, shamey. Set the bad @D2A
537 JMP GIP_DONE
; return code and go to the end. @D2A
539 ; Parameter list is OK. Let's return the number of XMA blocks. 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
548 MOV RH
.RHC_STA
,STAT_DONE
; Store done status and good return @D2A
549 ; code into request header D2A
552 INT15F88 PROC
FAR ; P7A
554 ; The following is the interrupt chaining structure specified in the PC AT P7A
555 ; Technical Reference. P7A
557 JMP SHORT BEGIN
; P7A
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 424
BH ; Says we're doing chaining @P7A
564 JMP SHORT RESET
; @P7A
565 RESERVED
DB 7 DUP (0) ; @P7A
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
573 BEGIN: CMP AH,88H
; Is it function 88H? @P7A
574 JNE NOT_MINE
; It's not ours to handle @P7A
576 MOV AX,CS:EXT_MEM
; Put the number of K into AX @P7A
577 IRET ; Return to the caller @P7A
579 NOT_MINE: JMP CS:DWORD PTR CHAINOFF
580 ; Pass the interrupt on to the @P7A
581 ; previously installed vector @P7A
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
589 LEAVE_RES
LABEL NEAR ; Leave code up to here resident.@D0A @D2M
591 SUBTTL Initialize Routine
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
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
613 CMP CS:ISmodel_80
,1 ; Check if we're on a model_80 @D0A @P1C
614 JE CONT
; If so, then continue @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
621 MOV AH,DISPSTRG
; Display the message that we are @D0A
622 MOV DX,OFFSET NO_80386
; not on a model_80 @D0A
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
635 JZ STILLOK
; If not, keep going @D0C
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
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
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
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
664 CLI ; Disable interrupts
666 PUSH CS ; Now we can point DS to our own @P3A
667 POP DS ; code segment @P3A
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
674 MOV REAL_SP
,AX ; Save real SP @P3C
676 ;------------------------------------------------------------------------------;
677 ; Enable address line A20 ;
678 ;------------------------------------------------------------------------------;
683 INT 11H
; Get the BIOS equipment flags
684 AND AL,30H
; Bits 5 and 6 on means it's a mono
687 MOV CRT_SELECTOR
,C_CCRT_PTR
; Set the CRT selector to color display
689 MOV AH,88H
; Get number of 1k blocks above 1M
691 ADD AX,1024 ; Add 640K for the memory below 640K @P7C
692 MOV MAXMEM
,AX ; Save for later
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
697 SUB AX,BUFF_SIZE
; Can't use the MOVEBLOCK buffer for @D0A
698 ; XMA memory. AX = number of K 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
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
709 ;------------------------------------------------------------------------------;
710 ; Now lets relocate ourselves to high memory. ;
711 ;------------------------------------------------------------------------------;
713 MOV AX,HIGH_SEG
; Set ES to the highest segment value
715 MOV DI,0 ; ES:DI points to the place to relocate to
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
721 REP MOVSW ; Copy myself to high memory
723 JUMPFAR NEXT
,HIGH_SEG
; Jump to my relocated code
726 MOV AX,HIGH_SEG
; Set DS to be the same as CS
728 ;------------------------------------------------------------------------------;
729 ; The machine is still in real mode. Zero out GDT and IDT ram. ;
730 ;------------------------------------------------------------------------------;
732 MOV DI,GDT_LOC
; DI points to GDT location
734 MOV CX,(GDT_LEN
+SIDT_LEN
)/2 ; Set GDT and IDT to zero
735 MOV AX,0 ; Store zeroes for now
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 ;------------------------------------------------------------------------------;
747 SUB AX,AX ; Clear EAX (32 bit AX reg.)
748 MOV AX,HIGH_SEG
; Get the current code segment
750 SUB BX,BX ; Clear EBX (32 bit BX reg.)
751 MOV BX,GDT_LOC
/16 ; Load the offset of the GDT, converted
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
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
764 SHL AX,4 ; Convert from paragraphs to bytes
765 CMOV
CR3,EAX ; Load the address of the page directory
768 SUB BX,BX ; Clear EBX
769 MOV BX,HIGH_SEG
; Load our current code segment
771 SHL BX,4 ; Convert from paragraphs to bytes
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
778 ; Now let's clear the page directory
780 MOV CX,2048 ; Length is 4K/2 since storing words
782 MOV DI,AX ; ES:EDI points to beginning of directory
784 REP STOSW ; Clear the page directory!
786 ;------------------------------------------------------------------------------;
787 ; Initialize the first directory entries to our page tables ;
788 ;------------------------------------------------------------------------------;
790 CMOV
EAX,CR3 ; Get back CR3 - the address of the page dir.
792 MOV DI,SGTBLOFF
; Point ES:EDI to first entry in directory
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.
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.
808 MOV NORMPAGE
,AX ; Save for later
809 MOV CX,MEG_SUPPORTED
/4 ; Load the number of page tables into CX
811 SUB BX,BX ; Clear EBX
812 MOV BX,1000H
; Set up 4k increment
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?
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
830 LOOP LPT
; Do it again
832 ; Now calcuate the offset from our code segment of the page tables
835 SUB BX,BX ; Clear EBX
836 MOV BX,HIGH_SEG
; Load our current code segment
838 SHL BX,4 ; Convert paragraphs to bytes
839 DATAOV
; Load EAX with the address of the first
840 MOV AX,NORMPAGE
; page table
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
846 ;------------------------------------------------------------------------------;
847 ; Initialize the page tables ;
848 ;------------------------------------------------------------------------------;
850 MOV DI,PGTBLOFF
; ES:DI points to the first page table
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
860 SUB BX,BX ; Clear EBX
861 MOV BX,1000H
; Set up 4k increment
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
876 DATAOV
; Stuff the page frame address into the
879 ADD AX,BX ; Next 4k page frame
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 ;------------------------------------------------------------------------------;
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
899 STOSW ; Stuff the address in the page table
901 ADD AX,BX ; Next 4k page frame
905 ;------------------------------------------------------------------------------;
906 ; Build the Global Descriptor Table and load the GDT register. ;
907 ;------------------------------------------------------------------------------;
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
917 ;------------------------------------------------------------------------------;
918 ; Build and initialize the system Interrupt Descriptor Table, ;
919 ; then load the IDT register. ;
920 ;------------------------------------------------------------------------------;
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
927 LIDT ES:FWORD PTR[BP] ; Put the descriptor for the IDT into
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 ;------------------------------------------------------------------------------;
937 MOV AX,VIRTUAL_ENABLE
; Machine status word needed to
938 LMSW AX ; switch to virtual mode
940 JUMPFAR DONE
,SYS_PATCH_CS
; Must purge pre-fetch queue
941 ; and set selector into CS
944 ;------------------------------------------------------------------------------;
945 ; Initialize all the segment registers ;
946 ;------------------------------------------------------------------------------;
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
953 MOV SP,OFFSET SP_INIT
955 PUSH 0002H ; Clean up our flags. Turn off all bits
956 POPF ; except the one that is always on.
958 ;------------------------------------------------------------------------------;
959 ; Load the LDTR to avoid faults ;
960 ;------------------------------------------------------------------------------;
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.
968 ; Have to always have space allocated for the dispatch task TSS
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
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 ;------------------------------------------------------------------------------;
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
988 MOV DI,0 ; Point ES:DI to the beginning of the TSS
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
1001 ; Now set up the bit map. Turn on bits for I/O ports that we want to trap.
1004 MOV DI,0+TSS_386_LEN
; Set bits 0,2,4,6 to 1 - DMA ports
1007 MOV DI,1+TSS_386_LEN
; Set C to 1 - DMA port
1010 MOV DI,3+TSS_386_LEN
; Set 18,1A to 1 - DMA ports
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
1017 MOV DI,0680H/8+TSS_386_LEN
; Set Roundup manuf. port to 1
1020 MOV DI,31A0H
/8+TSS_386_LEN
; Set 31a0-31a7 to 1s (XMA)
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
1029 MOV WORD PTR [BX].ETSS_SS0
,SYS_PATCH_DS
1030 ; Put our SS as the SS for privilege
1033 ; Next we set up the segment registers
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
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
1047 ; The flags register
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
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
1062 MOV WORD PTR [BX].ETSS_LDT
,SCRUBBER
.VM_LDTR
1064 ; And finally, CR3, the page directory base register
1066 CMOV
EAX,CR3 ; Get CR3
1068 MOV WORD PTR [BX].ETSS_CR3
,AX ; Save it in the TSS
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 ;------------------------------------------------------------------------------;
1079 MOV AX,SYS_PATCH_DS
; Load DS with the selector for our data
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
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
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.
1094 MOV AX,XMA_PAGES_SEL
; Load DS with the selector for the XMA page
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
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
1108 MOV AX,SYS_PATCH_DS
; Load DS and ES with our data segment @D1A
1109 MOV DS,AX ; selector @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
1119 ;------------------------------------------------------------------------------;
1120 ; And now, the moment you've all been waiting for -- TURN ON THE PAGING ;
1122 ;------------------------------------------------------------------------------;
1124 CMOV
EAX,CR0 ; Get CR0 @P5A
1125 MOV BX,8000H
; Set up BX to OR on the Paging Enable bit @P5C
1127 SHL BX,16 ; It's the one all the way on the left @P5C
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...
1134 ; Make sure high order bits of ESP are zero - a1 errata
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
1140 POP SP ; Pop 32 bit ESP!
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 ;------------------------------------------------------------------------------;
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
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
1170 PUSH WORD PTR [BX].ETSS_GS
; Put V86 task's GS on the stack @P5A
1172 PUSH WORD PTR [BX].ETSS_FS
; Put V86 task's FS on the stack @P5A
1174 PUSH WORD PTR [BX].ETSS_DS
; Put V86 task's DS on the stack @P5A
1176 PUSH WORD PTR [BX].ETSS_ES
; Put V86 task's ES on the stack @P5A
1178 PUSH WORD PTR [BX].ETSS_SS
; Put V86 task's SS on the stack @P5A
1180 PUSH WORD PTR [BX].ETSS_SP
; Put V86 task's ESP on the stack @P5A
1182 PUSH WORD PTR [BX].ETSS_FL
; Put V86 task's EFLAGS on the stack @P5A
1184 PUSH WORD PTR [BX].ETSS_CS
; Put V86 task's CS on the stack @P5A
1186 PUSH WORD PTR [BX].ETSS_IP
; Put V86 task's EIP on the stack @P5A
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
1196 ; Replace the interrupt 15 vector with our handler (INT15F88). P7A
1198 MOV AH,GET_VECT
; Get the current vector at interrupt 15H @P7A
1202 MOV CS:CHAINSEG
,ES ; Save it in the chaining header in @P7A
1203 MOV CS:CHAINOFF
,BX ; INT15F88 @P7A
1205 MOV AH,SET_VECT
; Set the entry point of INT15F88 as the @P7A
1206 MOV AL,15H
; new interrupt 15 vector @P7A
1209 MOV DX,OFFSET INT15F88
; @P7A
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
1215 MOV AX,BUFF_SIZE
; @P7A
1216 MOV EXT_MEM
,AX ; @P7A
1218 ; Issue the message that says we installed successfully
1220 MOV AH,DISPSTRG
; Set AH to DOS display string function @D0A
1221 MOV DX,OFFSET GOODLOAD
; @D0A
1223 POP DS ; DS:DX points to the message @D0A
1224 INT 21H
; Display the message @D0A
1226 RET ; Return to IRPT which called INIT_P1
1230 ;------------------------------------------------------------------------------;
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;
1236 ;------------------------------------------------------------------------------;
1238 ; Equates for the Gate A20 enable
1240 ENABLE_A20 EQU
02H ; Bit 2 of port 92H turns on A20 @P4C
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
1251 SUBTTL GET_PARMS parameter line scan
1253 ;------------------------------------------------------------------------------;
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. ;
1260 ; DS:SI indexes parameter string ;
1261 ; AL contains character from parameter string ;
1262 ; CX value from GET_NUMBER ;
1264 ;------------------------------------------------------------------------------;
1266 ASSUME
DS:NOTHING
; DS:BX point to Request Header
1271 push bx ; save bx ;an000; dms;
1273 LDS SI,RH
.RH0_BPBA
; DS:SI point to all text after "DEVICE="
1275 XOR AL,AL ; Start with a null character in AL.
1277 ;------------------------------------------------------------------------------;
1278 ; Skip until first delimiter is found. There may be digits in the path string.;
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 ;------------------------------------------------------------------------------;
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.
1290 ; Check for various delimeters
1298 CMP AL,';' ; Semi-colon
1300 CMP AL,'+' ; Plus sign
1305 JNE GET_PARMS_A
; Skip until delimiter or CR is found
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
1311 CALL GET_NUMBER
; Extract the digits and convert to binary
1312 jmp Get_Parms_Found
; Parm found
1316 xor cx,cx ; set cx to 0 ;an000; dms;
1320 mov bx,cx ; put cx value in bx ;an000; dms;
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
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;
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;
1348 mov bx,ax ; store page Kb value in bx ;an000; dms;
1349 add bx,128 ; adjust for emulator code
1351 mov ah,88h
; get number of 1k blocks above 1Mb ;an000; dms;
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;
1368 MOV CS:BUFF_SIZE
,ax ; Store buffer size
1372 pop bx ; restore bx ;an000; dms;
1377 ;------------------------------------------------------------------------------;
1378 ; GET_PCHAR -- Get a character from the parameter string into AL ;
1379 ;------------------------------------------------------------------------------;
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
1388 CMP AL,LF
; No, is it a line feed? This will leave the
1389 ; zero flag set if a line feed was found.
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 ;------------------------------------------------------------------------------;
1402 CMP AL,'0' ; If character is less than a "0" then it is not
1403 JB CHECK_NUM_X
; a number, so exit
1405 CMP AL,'9' ; If character is greater than a "9" then it is
1406 JA CHECK_NUM_X
; not a number, so exit
1408 CMP AL,AL ; Set the zero flag to show it is a number
1410 RET ; Zero flag is left reset if character is not
1411 CHECK_NUM ENDP
; a number
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 ;------------------------------------------------------------------------------;
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
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
1429 CMP AL,0 ; Digit found, reset the zero flag to show digit
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 ;------------------------------------------------------------------------------;
1441 GN_ERR
DB ?
; Zero if no overflow in accumulation
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
1448 SUB AL,'0' ; Convert the ASCII character in AL to binary
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
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
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