summaryrefslogtreecommitdiff
path: root/v4.0/src/DEV/XMAEM/INDEINI.ASM
diff options
context:
space:
mode:
authorGravatar Mark Zbikowski2024-04-25 21:24:10 +0100
committerGravatar Microsoft Open Source2024-04-25 22:32:27 +0000
commit2d04cacc5322951f187bb17e017c12920ac8ebe2 (patch)
tree80ee017efa878dfd5344b44249e6a241f2a7f6e2 /v4.0/src/DEV/XMAEM/INDEINI.ASM
parentMerge pull request #430 from jpbaltazar/typoptbr (diff)
downloadms-dos-main.tar.gz
ms-dos-main.tar.xz
ms-dos-main.zip
MZ is back!HEADmain
Diffstat (limited to 'v4.0/src/DEV/XMAEM/INDEINI.ASM')
-rw-r--r--v4.0/src/DEV/XMAEM/INDEINI.ASM1474
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 @@
1PAGE 60,132
2TITLE INDEINI - 386 XMA EMULATOR - Initialization
3
4COMMENT #
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
144PROG SEGMENT PARA PUBLIC 'PROG'
145
146 ASSUME CS:PROG
147 ASSUME SS:NOTHING
148 ASSUME DS:PROG
149 ASSUME ES:NOTHING
150
151INDEINI 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
185DDSIZE EQU GDT_LOC ; Size of the device driver
186HIGH_SEG EQU 0FFF0H ; The segment we relocate to
187MEG_SUPPORTED EQU 24 ; Must be a multiple of 4
188XMA_PAGES_SEL EQU RSDA_PTR ; Selector for XMA pages
189DISPSTRG EQU 09H ; DOS display string function number D0A
190GET_VECT EQU 35H ; DOS get vector function number P7A
191SET_VECT EQU 25H ; DOS set vector function number P7A
192model_80 EQU 0F8H ; Model byte for the Wangler D0A
193XMA640KSTRT EQU 580H ; Start address of XMA block for D0A
194 ; 640K (16000:0 / 1K) D0A P3C
195
196; ASCII character equates
197
198TAB EQU 09H ; ASCII tab
199LF EQU 0AH ; ASCII line feed
200CR EQU 0DH ; ASCII carriage return
201
202SUBTTL Structure Definitions
203PAGE
204;------------------------------------------------------------------------------;
205; Request Header (Common portion) ;
206;------------------------------------------------------------------------------;
207
208RH EQU DS:[BX] ; The Request Header structure is based off
209 ; of DS:[BX]
210
211RHC STRUC ; Fields common to all request types
212 DB ? ; Length of Request Header (including data)
213 DB ? ; Unit code (subunit)
214RHC_CMD DB ? ; Command code
215RHC_STA DW ? ; Status
216 DQ ? ; Reserved for DOS
217RHC ENDS ; End of common portion
218
219; Status values for RHC_STA
220
221STAT_DONE EQU 0100H ; Function complete status (high order byte)@P3C
222STAT_CMDERR EQU 8003H ; Invalid command code error
223STAT_GEN EQU 800CH ; General error code D0A
224
225;------------------------------------------------------------------------------;
226; Request Header for INIT command ;
227;------------------------------------------------------------------------------;
228
229RH0 STRUC
230 DB (TYPE RHC) DUP (?) ; Reserve space for the header
231
232RH0_NUN DB ? ; Number of units
233 ; Set to 1 if installation succeeds,
234 ; Set to 0 to cause installation failure
235RH0_ENDO DW ? ; Offset of ending address
236RH0_ENDS DW ? ; Segment of ending address
237RH0_BPBO DW ? ; Offset of BPB array address
238RH0_BPBS DW ? ; Segment of BPB array address
239RH0_DRIV DB ? ; Drive code (DOS 3 only)
240RH0 ENDS
241
242RH0_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
251RH19 STRUC
252 DB (TYPE RHC) DUP (?) ; Reserve space for the header @D2A
253
254RH19_MAJF DB ? ; Major function @D2A
255RH19_MINF DB ? ; Minor function @D2A
256RH19_SI DW ? ; Contents of SI @D2A
257RH19_DI DW ? ; Contents of DI @D2A
258RH19_RQPK DD ? ; Pointer to Generic IOCTL request packet @D2A
259RH19 ENDS
260
261SUBTTL Device Driver Header
262PAGE
263POST 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
281RH_PTRA LABEL DWORD
282RH_PTRO DW ? ; Offset of the request header
283RH_PTRS DW ? ; Segment of the request header
284 ; Character ID "386XMAEMULATOR10" deleted 2@D2D
285HI_XMA_BLK DW ? ; The highest XMA block number @D0A
286EXT_MEM DW ? ; Number of K of extended memory @P7A
287 ; D0A
288RBX DW ? ; Temporary save area for register BX @P1A
289ISmodel_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
292SUBTTL Device Strategy
293PAGE
294;------------------------------------------------------------------------------;
295; Device "strategy" entry point ;
296; ;
297; Retain the Request Header address for use by Interrupt routine ;
298;------------------------------------------------------------------------------;
299
300STRATEGY 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
306STRATEGY ENDP
307
308SUBTTL Device Interrupt Intry Point
309PAGE
310
311;------------------------------------------------------------------------------;
312; Table of command processing routine entry points ;
313;------------------------------------------------------------------------------;
314CMD_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
339MAX_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;------------------------------------------------------------------------------;
345IRPT 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
367NO_model_80:
368 MOV CS:ISmodel_80,0 ; Set the flag saying we're not on a @D2M
369 ; model_80
370
371DID_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
389PUSH32: 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
394PUSHED: 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
421IRPT_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
426IRPT_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
447POP32: DATAOV ; @P1A
448 POPA ; @P1A
449
450; Pop the segment registers off of the stack.
451
452POPPED: POP ES
453 POP DS
454
455 RET
456IRPT ENDP
457
458SUBTTL Command Routines
459PAGE
460
461MEDIA_CHECK: ;
462BLD_BPB: ;
463INPUT_IOCTL: ; IOCTL input
464INPUT: ;
465INPUT_NOWAIT: ; Non-destructive input no wait
466INPUT_STATUS: ; Input status
467INPUT_FLUSH: ; Input flush
468OUTPUT: ;
469OUTPUT_VERIFY: ;
470OUTPUT_IOCTL: ; IOCTL output
471OUTPUT_STATUS: ; Output status
472OUTPUT_FLUSH: ; Output flush
473DEVICE_OPEN: ;
474DEVICE_CLOSE: ;
475REMOVABLE_MEDIA: ;
476INVALID_FCN: ; @D2A
477GET_LOG_DEVICE: ; @D2A
478SET_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
486SUBTTL Generic IOCTL Service Routine
487PAGE
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
497GIP EQU ES:[DI] ; @D2A
498
499GEN_IOCTL_PARM STRUC ; @D2A
500
501GIOPLEN DW ? ; Length of the parameter list @D2A
502GIOPFCN DW ? ; Function code @D2A
503GIOPBLK DW ? ; Number of XMA blocks available @D2A
504
505GEN_IOCTL_PARM ENDS ; @D2A
506
507MAXFCN EQU 0 ; Highest function number allowed @D2A
508
509; Return codes D2A
510
511GOODRET EQU 0 ; Good return code @D2A
512BADLEN EQU 1 ; Bad parameter list length @D2A
513BADFCN EQU 2 ; Bad function number @D2A
514
515GENERIC_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
531GIP_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
541GIP_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
547GIP_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
552INT15F88 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
559CHAINOFF DW 0 ; Offest of the previous INT 15 vect. @P7A
560CHAINSEG DW 0 ; Segment of the previous INT 15 vect.@P7A
561SIGNATURE DW 424BH ; Says we're doing chaining @P7A
562FLAGS DB 0 ; @P7A
563FIRST EQU 80H ; @P7A
564 JMP SHORT RESET ; @P7A
565RESERVED 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
573BEGIN: 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
579NOT_MINE: JMP CS:DWORD PTR CHAINOFF
580 ; Pass the interrupt on to the @P7A
581 ; previously installed vector @P7A
582
583RESET: 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
587INT15F88 ENDP ; @P7A
588
589LEAVE_RES LABEL NEAR ; Leave code up to here resident.@D0A @D2M
590
591SUBTTL Initialize Routine
592PAGE
593INIT_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
629CONT: ; @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 ;
649STILLOK: ; 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
662StillOK1:
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
688LEAVEBW:
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
724NEXT:
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;
824LPT:
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;
875BPT:
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
897BPT2:
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
904PAGE
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
916PAGE
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
930PAGE
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
942DONE:
943PAGE
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
974PAGE
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
1070PAGE
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
1118PAGE
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
1142PAGE
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
1191TEST_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
1228SUBTTL Gate A20
1229PAGE
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
1240ENABLE_A20 EQU 02H ; Bit 2 of port 92H turns on A20 @P4C
1241
1242GATE_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
1249GATE_A20 ENDP
1250
1251SUBTTL GET_PARMS parameter line scan
1252PAGE
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
1268GET_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
1285GET_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
1307GET_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
1314Get_Parms_Null:
1315
1316 xor cx,cx ; set cx to 0 ;an000; dms;
1317
1318Get_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
1327Get_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
1341Get_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
1366Get_Parms_Ext:
1367
1368 MOV CS:BUFF_SIZE,ax ; Store buffer size
1369 clc
1370
1371GET_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
1381GET_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.
1390GET_PCHAR_X:
1391 RET
1392
1393GET_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
1401CHECK_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
1409CHECK_NUM_X:
1410 RET ; Zero flag is left reset if character is not
1411CHECK_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
1420SKIP_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
1428SKIP_TO_DIGIT_X:
1429 CMP AL,0 ; Digit found, reset the zero flag to show digit
1430 RET ; was found
1431SKIP_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
1440C10 DW 10
1441GN_ERR DB ? ; Zero if no overflow in accumulation
1442
1443GET_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
1447GET_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
1464GET_NUMBER_B:
1465 RET
1466GET_NUMBER ENDP
1467
1468GET_PARMS ENDP
1469
1470POST ENDP
1471
1472PROG ENDS
1473 END
1474