2 TITLE INDEI15
- 386 XMA Emulator
- Interrupt
15 handler
5 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
7 * MODULE NAME
: INDEI15
*
10 * 5669-196 (C
) COPYRIGHT
1988 Microsoft Corp
. *
12 * DESCRIPTIVE NAME
: Interrupt 15H handler for the
80386 XMA emulator
*
14 * STATUS
(LEVEL
) : Version
(0) Level
(2.0) *
16 * FUNCTION
: This module emulates the MOVEBLOCK functions of interrupt
*
17 * 15H
. The MOVEBLOCK functions are specified by an
AH value
*
18 * of 87H to 89H
. The user can use the MOVEBLOCK functions
*
19 * to copy a block of memory to another block of memory
. *
20 * This includes copying to
and from memory above 1M
. This
*
21 * is really where this function comes
in handy
. The user
*
22 * can reserve memory above 1M for use by the MOVEBLOCK
*
23 * functions by specifying the number of K to be reserved
as *
24 * a parameter
on the line to
load the emulator
in the
*
27 * DEVICE
=INDXMAEM
.SYS bbb
*
29 * "bbb" is the number of K to reserve for MOVEBLOCK
*
32 * We allocate a buffer for the MOVEBLOCK functions
at the
*
33 * top of available memory
. Any functions dealing with this
*
34 * buffer area must be handles by us
. *
36 * Function 87H is the actual MOVEBLOCK function
. The user
*
37 * passes a
32 bit source address
and a
32 bit destination
*
38 * address
in a parameter list pointed to by
ES:SI. CX *
39 * contains the number of words to copy
. We need to
*
40 * intercept this
call and check the source
and destination
*
41 * addresses
. If either
or both of these addresses is above
*
42 * 1M then we need to adjust the addresses so that they
*
43 * access the MOVEBLOCK buffer up
at the top of memory
. You
*
44 * see
, the user thinks the extended memory starts right
*
45 * after the 1M boundary
. We want to make it look like the
*
46 * MOVEBLOCK buffer sits right after the 1M boundary
. So we
*
47 * monkey with the user
's addresses so that they access the
*
48 * MOVEBLOCK buffer instead of real memory after 1M
, because
*
49 * that memory is us
. *
51 * Function 88H queries how many K are above the 1M
*
52 * boundary
. We can
't tell him how much is really there
*
53 * because some of it is us
and our XMA pages
. So for this
*
54 * function we will just return the size of the MOVEBLOCK
*
55 * buffer
. This function was moved to a real mode P3C
*
56 * interrupt handler
in module INDE15H
. P3C
*
58 * Function 89H is reserved for the MOVEBLOCK functions but
*
59 * is
not in use right now
. So for this function we just
*
60 * return a bad return
code in AH and set the carry flag
. *
64 * REGISTER USAGE
: 80386 Standard
*
66 * RESTRICTIONS
: None
*
68 * DEPENDENCIES
: None
*
70 * ENTRY POINT
: INT15
*
72 * LINKAGE
: Jumped to
from INDEEXC
*
74 * INPUT PARMS
: None
*
76 * RETURN PARMS
: None
*
78 * OTHER EFFECTS
: None
*
80 * EXIT NORMAL
: Go to POPIO
in INDEEMU to
IRET to the V86 task
*
85 * REFERENCES
: EMULATE
- Entry point for INDEEMU
*
86 * POPIO
- Entry in INDEEMU to check for single step
*
87 * interrupts
, pop the registers
and IRET to the
*
89 * POPREGS
- Entry point
in INDEEXC to
pop the registers
*
90 * off the
stack and IRET to the V86 task P2C
*
92 * SUB-ROUTINES
: None
*
94 * MACROS
: DATAOV
- Add prefix for the next instruction so that it
*
95 * accesses
data as 32 bits wide
*
96 * ADDROV
- Add prefix for the next instruction so that it
*
97 * uses addresses that are
32 bits wide
*
99 * CONTROL BLOCKS
: INDEDAT
.INC *
101 * CHANGE ACTIVITY
: *
103 * $
MOD(INDEI15
) COMP
(LOAD) PROD
(3270PC
) : *
105 * $D0=D0004700
410 870603 D
: NEW FOR RELEASE
1.1 *
106 * $P1
=P0000293
410 870731 D
: LIMIT LINES TO
80 CHARACTERS
*
107 * $P2
=P0000312
410 870804 D
: CLEAN UP WARNING MESSAGES
*
108 * $D1=D0007100
410 870817 D
: CHANGE TO EMULATE XMA
2 *
109 * $P3
=P0000xxx
120 880331 D
: MOVE FUNCTION 88H HANDLING TO INDE15H
*
111 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
114 .286P
; Enable recognition of 286 privileged instructs.
116 .XLIST
; Turn off the listing
117 INCLUDE INDEDAT
.INC ; Include the system data structures
119 IF1
; Only include the macros on the first pass
122 .LIST
; Turn on the listing
124 PROG
SEGMENT PARA
PUBLIC 'PROG'
133 EXTRN EMULATE
:NEAR ; Entry point for INDEEMU
134 EXTRN POPIO
:NEAR ; Entry in INDEEMU to check for single
135 ; step interrupts and return to the
137 EXTRN POPREGS
:NEAR ; Entry in INDEEXC to return to the P2C
148 CLD ; All string operations go forward
150 ; We handle the INT 15H functions for MOVEBLOCK interface. These functions
151 ; are specified by an AH value of 87H to 89H. Function 87H is the MOVEBLOCK
152 ; function. Function 88H queries the size of memory above 1M. Function 89H
153 ; is reserved but not supported so we return a return code of 86H.
155 CMP SS:BYTE PTR [BP+BP_AX
+1],87H
; Is AH asking for function 87H?
156 JB NOTMINE
; No. It's too low. It's out of our
157 ; range so we'll pass it on to the
159 JE MOVEBLK
; It is 87H! Let's go do the MOVEBLOCK.
161 CMP SS:BYTE PTR [BP+BP_AX
+1],89H
; Is AH asking for function 89H?
162 JNE NOTMINE
; No. It's not our function so @P3C
163 ; so we'll pass it on to the real
166 MOV SS:BYTE PTR [BP+BP_AX
+1],86H
; It's 89H. Sorry we don't support
167 ; that function. Put an 86H return
169 OR WORD PTR SS:[BP+BP_FL
],1 ; Set the carry flag
170 JMP POPIO
; And return to the V86 task
172 ; Hey, it's not MY interrupt.
175 JMP DOINT
; Go pass the interrupt back to the
176 ; real INT 15H vector
179 ; Function 88H code to query the size of memory above 1M was moved to 3@P3D
183 ; The user wants to move a block of memory. Now the source and target of the
184 ; MOVEBLOCK can each be below 1M or above 1M. For addresses above 1M we must
185 ; make adjustments so that the MOVEBLOCK is done to and/or from the MOVEBLOCK
186 ; buffer in high memory. The user passes a parameter list which is pointed
187 ; at by ES:SI. At offset 0EH is a 32 bit pointer to the source block. At
188 ; offset 1AH is a 32 bit pointer to the destination block. CX contains the
189 ; number of words to move. Here we go!
192 MOV AX,HUGE_PTR
; Load DS and ES with a selector that
193 MOV DS,AX ; accesses all of memory as data
196 ; Get the user's ES:SI and convert it to a 32 bit offset in ESI.
200 MOV SI,SS:[BP+BP_SI
] ; Load SI with the user's SI
204 MOV BX,10H
; Set EBX to 1M by loading it with 10H
205 DATAOV
; and shifting it left 16 bits to
206 SHL BX,16 ; multiply by 64K
208 DATAOV
; Sterilize EAX
210 MOV AX,SS:[BP+BP_VMES
] ; Load AX with the user's ES
211 DATAOV
; Shift it left four bits to convert it
212 SHL AX,4 ; to an offset
214 DATAOV
; Add the ES offset on to SI. Now ESI
215 ADD SI,AX ; is the offset from 0 of the user's
217 DATAOV
; Add 1AH to SI. Now it points to the
218 ADD SI,1
AH ; 32 bit destination address.
221 ADDROV
; Get the 32 bit destination address
224 ADDROV
; Intel bug # A0-119
225 NOP ; Intel bug # A0-119
227 DATAOV
; Clear the top eight bits of any
228 SHL AX,8 ; residual gunk. Only 24 bit ;P1C
229 DATAOV
; addresses (16M) are allowed anyway.
230 SHR AX,8 ; Shift the bits off the left end and
231 ; shift zeroes back in.
232 DATAOV
; Move this clean value into EDI
233 MOV DI,AX ; EDI now has the destination address
235 DATAOV
; Check if this address is over 1M. If
236 CMP DI,BX ; so then it's going to our MOVEBLOCK
238 JB OK1
; It's below 1M? OK. Leave it alone.
240 ; The destination is above 1M so we have to modify the destination address so
241 ; that it points to our MOVEBLOCK buffer.
244 MOV AX,SYS_PATCH_DS
; Load DS with the selector for our data
247 DATAOV
; Clean up EAX
249 MOV AX,MAXMEM
; Load the total number of K on the box
250 SUB AX,BUFF_SIZE
; Subtract the MOVEBLOCK buffer size
251 SUB AX,1024 ; Subtract 1M (/1K) for 0 to 1M. This
252 ; leaves AX with the number of K from
253 ; 1M to the MOVEBLOCK buffer.
255 DATAOV
; Multiply EAX by 1K (shift left 10) to
256 SHL AX,10 ; convert it to an offset from 1M of
257 ; the MOVEBLOCK buffer
258 DATAOV
; Add this to EDI. EDI now points to
259 ADD DI,AX ; a location within (hopefully) the
260 ; MOVEBLOCK buffer as if the buffer
261 ; were located at the 1M boundary.
263 ; Now let's get the source address
266 DATAOV
; Subtract 0C from ESI to point ESI to
267 SUB SI,0CH ; offset 0E in the parameter list
270 ADDROV
; Get the 32 bit source address into
273 ADDROV
; Intel bug # A0-119
274 NOP ; Intel bug # A0-119
276 DATAOV
; Clear the top eight bits of any
277 SHL AX,8 ; residual gunk. Only 24 bit address
278 DATAOV
; (16M) are allowed. Shift the gunky
279 SHR AX,8 ; bits off the left end and shift
281 DATAOV
; Move this clean value into ESI
282 MOV SI,AX ; ESI now has the source address
284 DATAOV
; Check if this address is over 1M. If
285 CMP SI,BX ; so then it's goin' to our MOVEBLOCK
287 JB OK2
; It's below 1M? OK. Let it be.
289 ; The source is above 1M so we have to modify the source address so that it
290 ; points to our MOVEBLOCK buffer.
293 MOV AX,SYS_PATCH_DS
; Load DS with the selector for our data
296 DATAOV
; Sanitize up EAX
298 MOV AX,MAXMEM
; Load the total number of K on the box
299 SUB AX,BUFF_SIZE
; Subtract the MOVEBLOCK buffer size
300 SUB AX,1024 ; Subtract 1M (/1K) for 0 to 1M. This
301 ; leaves AX with the number of K from
302 ; 1M to the MOVEBLOCK buffer.
304 DATAOV
; Multiply EAX by 1K (shift left 10) to
305 SHL AX,10 ; convert it to an offset from 1M of
306 ; the MOVEBLOCK buffer
307 DATAOV
; Add this to ESI. ESI now points to
308 ADD SI,AX ; a location within (hopefully) the
309 ; MOVEBLOCK buffer as if the buffer
310 ; were located at the 1M boundary.
312 ; Our pointers are all set. Let's get CX and do the copy for the guy.
315 MOV CX,SS:[BP+BP_CX
] ; Get the user's CX
316 TEST CL,01H ; Is this an even number?
317 JZ MOV4
; If so, we can make the copy faster
318 ; by moving double words
319 ADDROV
; Nope. It's odd. We'll just do the
320 REP MOVSW ; copy with words.
322 ADDROV
; Intel bug # A0-119
323 NOP ; Intel bug # A0-119
325 JMP MOVCOM
; Skip over the double word copy
328 SHR CX,1 ; Divide the count by two since we'll
329 ; be copying double words
330 DATAOV
; Do it 32 bits wide
331 ADDROV
; Use the 32 bit ESI and EDI
332 REP MOVSW ; Ready? ZOOOOM!
334 ADDROV
; Intel bug # A0-119
335 NOP ; Intel bug # A0-119
337 ; Now let's set the flags and return code in AH to show that every thing went
341 MOV SS:BYTE PTR [BP+BP_AX
+1],0 ; Set a zero return code in AH
342 AND WORD PTR SS:[BP+BP_FL
],0FFFEH ; Reset the carry flag
343 OR WORD PTR SS:[BP+BP_FL
],40H
; Set the zero flag
345 JMP POPIO
; Return to the V86 task
349 ; It's not a MOVEBLOCK function so we'll just pass the interrupt on to the real
353 MOV AX,HUGE_PTR
; Load ES with a selector that accesses
354 MOV ES,AX ; all of memory as data
355 DATAOV
; Load EDI with the user's ESP
358 SUB DI,6 ; Decrement "SP" by six to make room
359 ; for our simulated interrupt that
360 ; will put the flags, CS and IP on
361 ; the stack. This assumes that there
362 ; are indeed six bytes left on the
364 MOV SS:WORD PTR [BP+BP_SP
],DI ; Set the user's new SP
366 DATAOV
; Get the user's SS into our AX
368 DATAOV
; Shift "SS" left four bits to convert
369 SHL AX,4 ; it to an offset
370 DATAOV
; Add this to "SP" in DI to make EDI
371 ADD DI,AX ; a 32 bit offset from 0 of the user's
374 ; Now put the flags, CS and IP on the V86 task's stack. They are put on in the
375 ; order IP, CS, flags. This is backwards from the INT push order of flags, CS
376 ; and then IP. This is because we are moving forward through memory (CLD)
377 ; whereas the stack grows backwards through memory as things pushed on to it.
379 MOV AX,SS:[BP+BP_IP
] ; Get the user's IP
380 ADDROV
; And put it on his stack
383 ADDROV
; Intel bug # A0-119
384 NOP ; Intel bug # A0-119
386 MOV AX,SS:[BP+BP_CS
] ; Get the user's CS
387 ADDROV
; And put it on his stack
390 ADDROV
; Intel bug # A0-119
391 NOP ; Intel bug # A0-119
393 MOV AX,SS:[BP+BP_FL
] ; Get the user's flags
394 OR AX,3000H
; Set the IOPL to 3 so we get fewer
396 ADDROV
; And put them on his stack
399 ADDROV
; Intel bug # A0-119
400 NOP ; Intel bug # A0-119
402 AND AX,3CFFH
; Clean up the flags for our IRET
403 MOV WORD PTR SS:[BP+BP_FL
],AX ; Put them on our stack
405 MOV SI,SS:[BP+BP_EX
] ; Get the interrupt number
406 SHL SI,2 ; Multiply by four 'cause interrupt
407 ; vectors are four bytes long
408 MOV AX,HUGE_PTR
; Load DS with a selector that accesses
409 MOV DS,AX ; all of memory as data
410 LODSW ; Get the IP of the interrupt vector
411 ; from the interrupt vector table
412 MOV WORD PTR SS:[BP+BP_IP
],AX ; Put it in the IP saved on our stack
413 LODSW ; Get the CS of the interrupt vector
414 ; from the interrupt vector table
415 MOV WORD PTR SS:[BP+BP_CS
],AX ; Put it in the CS saved on our stack
417 JMP POPREGS
; Now when we do an IRET we will @P2C
418 ; actually be giving control to the
419 ; real INT 15H vector.
423 ; Macros used to define data areas
425 ; DDL - Define a label and make it public
433 ; DDW - Define a word and make it public
436 IFNB
<&NAME
> ;; If a name was given then make it public
439 IFNB
<&VALUE
> ;; If a value was given then initialize the
440 &NAME
DW &VALUE
;; variable to that value
441 ELSE ;; Else initialize it to 0
447 ; Now lets define some data. Remember, these are all PUBLIC even though they
448 ; are not listed at the top of the program as being such. It's easy to lose
451 DDW REAL_SP
,0 ; Our initial SP when we come up in real mode
453 DDW REAL_SS
,0 ; Our initial SS when we come up in real mode
455 DDW REAL_CS
,0 ; Our initial CS when we come up in real mode
457 DDW PGTBLOFF
,0 ; The offset of the normal page tables
459 DDW SGTBLOFF
,0 ; The offset of the page directory
461 DDW NORMPAGE
,0 ; The entry for the first page directory entry
462 DDW
,0 ; which points to the first normal page table.
464 DDW XMAPAGE
,7 ; Page directory entry that points to the first
465 DDW
,0011H ; XMA page table at 11000:0. Access and present
466 ; bits set. It, too, is a 32 bit value.
467 DDW BUFF_SIZE
,0 ; Size of the MOVEBLOCK buffer. Initialized to 0.
469 DDW MAXMEM
,1000H
; Total amount of K in the box. Initialized to 4M.
471 DDW CRT_SELECTOR
,C_BWCRT_PTR
; Selector for the display buffer
473 ; And now, the world famous Translate Table!! YEAAAA!!
475 ; The first 160 entries (0 to 640K) are initialized to blocks 0 to '9F'X. D1A
476 ; This is to emulate the XMA 2 device driver which uses these blocks to back D1A
477 ; the memory on the mother board from 0 to 640K which it disabled. It sets D1A
478 ; up the translate table for bank 0 such that it maps the XMA memory from 0 D1A
479 ; to 640K to conventional memory at 0 to 640K. So we emulate that here by D1A
480 ; initializing the firs 160 entries in the translate table. D1A
483 BLOCK
= 0 ; Start with block number 0 @D1A
484 REPT 20 ; Do 20 times (20 x 8 = 160) @D1A
485 DW BLOCK
,BLOCK
+1,BLOCK
+2,BLOCK
+3,BLOCK
+4,BLOCK
+5,BLOCK
+6,BLOCK
+7
486 ; Define eight translate table entries @D1A
487 ; initialized to the block number D1A
488 BLOCK
= BLOCK
+ 8 ; Increment the block number to the next set @D1A
489 ENDM
; of eight translate table entries @D1A
491 DW 96 + 256*15 DUP(0FFFH) ; The rest of the translate table @D1C
493 TTTABLE_END: ; Label to mark the end of the translate table
496 ; Define our stack for when we're in protect mode
498 DDW MON_STACK_BASE
,<500 DUP(0)>
499 DDL SP_INIT
,WORD ; Top of the stack. The initial SP points here.