1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
|
page ,132
subttl emmain.asm - Main Entry Point and Address Calculation Procedure
;***
;emmain.asm - Main Entry Point and Address Calculation Procedure
;
; Copyright (c) 1987-89, Microsoft Corporation
;
;Purpose:
; Main Entry Point and Address Calculation Procedure
;
; This Module contains Proprietary Information of Microsoft
; Corporation and should be treated as Confidential.
;
;Revision History:
; See emulator.hst
;
;*******************************************************************************
;*********************************************************************;
; ;
; Main Entry Point and Address Calculation Procedure ;
; ;
;*********************************************************************;
;
; This routine fetches the 8087 instruction, calculates memory address
; if necessary into ES:SI and calls a routine to emulate the instruction.
; Most of the dispatching is done through tables. (see comments in CONST)
ProfBegin MAIN
ifdef XENIX
ifdef i386
LDT_DATA= 02Fh ; UNDONE - 386 emulator data LDT
else
LDT_DATA= 037h ; UNDONE - 286 emulator data LDT
endif ;i386
endif ;XENIX
ifdef PROTECT
; protect mode Segment override case
glb <protSegOvrTab>
protSegOvrTab label word
dw DSSegOvr ; 11
dw ESSegOvr ; 00
dw CSSegOvr ; 01
dw SSSegOvr ; 10
endif ;PROTECT
ifdef DOS3
; isolated FWAIT
ifdef WINDOWS
pub FWtrap
cld ; this CLD is a nop.
iret
else ;not WINDOWS
pub FWtrap
PUSH BP ; fix up isolated FWAIT
PUSH DS
PUSH SI
MOV BP,SP ; Point to stack
LDS SI,DWORD PTR 6[BP] ; Fetch ret address,(points to after instruction)
DEC SI ; Make DI point to instruction
DEC SI
MOV 6[BP],SI ; Change ret address to return to instruction
mov word ptr [si],0C0h*256+89h ; change interrupt to mov ax,ax
POP SI
POP DS
POP BP
IRET ; interrupt return
endif ;not WINDOWS
; Segment override case
glb <SegOvrTab>
SegOvrTab label word
dw DSSegOvr
dw SSSegOvr
dw CSSegOvr
dw ESSegOvr
pub SOtrap
STI ; re-enable interrupts
push ax
push es ; set up frame
push ds
push di
push si
push dx
push cx
push bx
sub sp,2 ; reserve for regSegOvr
push bp
mov bp,sp ; set up frame pointer
CLD ; clear direction flag forever
; get address mode information, dispatch to address calculation
MOV DX,BX ; may use original BX to calc address
MOV AX,DI ; may use original DI to calc address
LDS DI,dword ptr [bp].regIP ; DS:DI is caller's CS:IP
INC DI ; increment past operation byte
INC DI ; increment past operation byte
MOV CX,[DI-2] ; get trap number, opcode (DS=caller CS)
mov bx,cx ; upper 2 bits indicate segment override
rol bl,1
rol bl,1
and bx,3
rol bx,1
jmp SegOvrTab[bx]
endif ;DOS3
pub DSSegOvr ; 00
mov es,[bp].regDS ; set ES to EA segment
jmp short ESSegOvr
pub CSSegOvr ; 10
mov bx,ds ; DS = caller's CS
mov es,bx ; set ES to EA segment
jmp short ESSegOvr
pub SSSegOvr ; 01
mov bx,ss ; SS = caller's SS
mov es,bx ; set ES to EA segment
pub ESSegOvr ; 11
mov [bp].regSegOvr,es ; save for bp rel EAs
jmp short CommonDispatch
ifdef PROTECT
pub protSegOvr
mov dx,bx ; may use original BX to calc address
mov bl,cl
.286
shr bl,2
ifndef DOS5only
.8086
endif
and bx,6 ; bl = (seg+1) and 6
inc di ; point to displacement
mov cx,[di-2] ; cx = esc 0-7 and opcode
jmp protSegOvrTab[bx] ; process appropriate segment override
ifdef XENIX
pub jinstall
jmp installemulator
endif ;XENIX
pub protemulation
cld ; clear direction flag forever
ifdef XENIX
push ax ; UNDONE - slow
push ds ; UNDONE - slow
mov ax,LDT_DATA ; load up emulator's data segment
mov ds,ax
cmp [Einstall],0 ; check if emulator is initialized
je jinstall ; no - go install it
pub protemcont
pop ds ; UNDONE - slow
pop ax ; UNDONE - slow
endif ;XENIX
push ax
push es ; set up frame
push ds
push di
push si
push dx
push cx
push bx
push ss ; save SegOvr (bp forces SS override)
push bp
mov bp,sp ; set up frame pointer
mov dx,ds ; save original DS for default case
mov ax,di ; may use original DI to calc address
lds di,dword ptr [bp].regIP ; ds:di = 287 instruction address
mov cx,[di] ; cx = esc 0-7 and opcode
add di,2 ; point to displacement
add cl,28h ; set carry if esc 0-7 (and cl = 0-7)
jnc protSegOvr ; no carry - must be segment override
mov es,dx ; es = user data segment
mov dx,bx ; may use original BX to calc address
endif ;PROTECT
ifdef DOS3and5
jmp short CommonDispatch
endif ;DOS3and5
ifdef DOS3
; normal entry point for emulator interrupts
even
pub DStrap
STI ; re-enable interrupts
push ax
push es ; set up frame
push ds
push di
push si
push dx
push cx
push bx
push ss ; save SegOvr (bp forces SS override)
push bp
mov bp,sp ; set up frame pointer
mov ax,ds
mov es,ax ; ES = caller's DS
CLD ; clear direction flag forever
; get address mode information, dispatch to address calculation
MOV DX,BX ; may use original BX to calc address
MOV AX,DI ; may use original DI to calc address
LDS DI,dword ptr [bp].regIP ; DS:DI is caller's CS:IP
INC DI ; increment past operation byte
MOV CX,[DI-2] ; get trap number, opcode (DS=caller CS)
; Otherwise, CL contains BEGINT + |MF|Arith| so we must unbias it
SUB CL,BEGINT
endif ;DOS3
; DS:DI = original CS:IP of displacement field
; ES = Effective Address segment (original DS if no segment override)
; DX = original BX
; AX = original DI
; SI = original SI
; CX = (opcode,0-7 from ESC byte)
; stack = saved register set
pub CommonDispatch
ROL CH,1 ; rotate MOD field next to r/m field
ROL CH,1
MOV BL,CH ; get copy of operation
AND BX,1FH ; Mask to MOD and r/m fields
SHL BX,1 ; make into word offset
JMP EA286Tab[BX]
OneByteDisp macro
mov al, [di] ;; get one byte displacement
cbw ;; sign extend displacement
inc di ;; get past displacement byte
add si, ax ;; add one byte displacement
endm
TwoByteDisp macro
add si, [di] ;; add word displacement
add di, 2 ;; get past displacement word
endm
even
pub BXXI0D
MOV SI,DX ; use original BX index value
JMP short ADRFIN ; have offset in SI
pub DSDI0D
MOV SI,AX ; use alternate index value
JMP short ADRFIN ; have offset in SI
even
pub BPXI1D
mov SI,[bp].regBP ; add original BP value
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
OneByteDisp
JMP short ADRFIN
even
pub BPDI1D
MOV SI,AX ; use alternate index value
pub BPSI1D
ADD SI,[bp].regBP ; add original BP value
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
OneByteDisp
JMP short ADRFIN
even
pub BXSI1D
MOV AX,SI ; really will want SI, not DI
pub BXDI1D
ADD DX,AX ; now DX is original BX plus index
pub BXXI1D
MOV AX,DX ; use original BX index value
pub DSDI1D
MOV SI,AX ; use alternate index value
pub DSSI1D
OneByteDisp
JMP short ADRFIN
even
pub BPXI2D
mov SI,[bp].regBP ; add original BP value
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
TwoByteDisp
JMP short ADRFIN
even
pub BPDI2D
MOV SI,AX ; use alternate index value
pub BPSI2D
ADD SI,[bp].regBP ; add original BP value
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
TwoByteDisp
JMP short ADRFIN
even
pub BXSI2D
MOV AX,SI ; really will want SI, not DI
pub BXDI2D
ADD DX,AX ; now DX is original BX plus index
pub BXXI2D
MOV AX,DX ; use original BX index value
pub DSDI2D
MOV SI,AX ; use alternate index value
pub DSSI2D
TwoByteDisp
JMP short ADRFIN
even
pub BPDI0D
MOV SI,AX ; use alternate index value
pub BPSI0D
add si,[bp].regBP ; really will want BP, not BX
mov es,[bp].regSegOvr ; ES = override segment (or SS if none)
jmp short ADRFIN
even
pub BXDI0D
MOV SI,AX ; si = regDI
pub BXSI0D
add si,dx ; si = regSI+regBX
jmp short ADRFIN
even
pub DSXI2D
MOV SI,[DI] ; get two byte displacement
INC DI ; get past displacement byte
INC DI ; get past displacement byte
pub DSSI0D ; SI = EA (original SI for DSSI0D)
pub ADRFIN
MOV [bp].regIP,DI ; final return offset
ifdef LOOK_AHEAD
mov bl,[di] ; get byte of next instruction
endif
ifdef MTHREAD
LOADthreadDS ; macro in emthread.asm
; loads thread's DS, trashes AX
else ;not MTHREAD
ifdef standalone
xor ax,ax
mov ds,ax
mov ds,ds:[4*TSKINT+2] ; DS = emulator task data segment
elseifdef XENIX
mov ax,LDT_DATA
mov ds,ax
elseifdef _COM_
mov ds, [__EmDataSeg]
else
mov ax, edataBASE
mov ds,ax
endif
endif ;not MTHREAD
ifdef LOOK_AHEAD
mov [NextOpCode], bl ; save byte of next instruction
endif
mov [CURerr],MemoryOperand ; clear current error, set mem. op bit
; At this point ES:SI = memory address, CX = |Op|r/m|MOD|escape|MF|Arith|
SHR CH,1
SHR CH,1 ; Move Op field to BX for Table jump
SHR CH,1
SHR CH,1
MOV BL,CH
AND BX,0EH
TEST CL,1 ; Arith field set?
JZ ArithmeticOpMem
pub NonArithOpMem
mov eax,offset EMLFINISH
push eax
jmp NonArithOpMemTab[BX]
even
pub ArithmeticOpMem
PUSH BX ; Save Op while we load the argument
MOV BX,CX ; Dispatch on MF
AND ebx,6H
ifdef i386
call FLDsdriTab[2*ebx] ; emulate proper load
else
call FLDsdriTab[ebx] ; emulate proper load
endif
POP BX
mov ax,ds ; ES = DS = task data area
mov es,ax
MOV SI,[CURstk] ; address top of stack
MOV DI,SI
ChangeDIfromTOStoNOS
MOV [RESULT],DI ; Set up destination Pointer
JMP short DoArithmeticOpPop
even
pub NoEffectiveAddress ; Either Register op or Miscellaneous
MOV [bp].regIP,DI ; final return offset
ifdef LOOK_AHEAD
mov bl, [di] ; get first byte of next instruction.
endif
ifdef MTHREAD
LOADthreadDS ; macro in emthread.asm
; loads thread's DS; trashes AX
mov ax,ds
mov es,ax ; DS = ES = per-thread em. data area
xor ax,ax
else ;not MTHREAD
xor ax,ax
ifdef standalone
mov ds,ax
mov di,ds:[4*TSKINT+2] ; DI = emulator task data segment
elseifdef XENIX
mov di,LDT_DATA
elseifdef _COM_
mov di, [__EmDataSeg]
else
mov di, edataBASE
endif
mov ds,di ; di = emulator data segment
mov es,di ; ax = 0
endif ;not MTHREAD
; ES = emulator data segment
; DS = emulator data segment
; AX = 0
ifdef LOOK_AHEAD
mov [NextOpCode], bl ; save first byte of next instruction
endif
mov [CURerr],ax ; clear current error, memory op bit
; CX = |Op|r/m|MOD|escape|MF|Arith|
MOV BL,CH
SHR BL,1 ; Mov Op field to BX for jump
SHR BL,1
SHR BL,1
SHR BL,1
AND BX,0EH
TEST CL,1 ; Arith field set?
JZ ArithmeticOpReg
pub NonArithOpReg
CALL NonArithOpRegTab[BX]
JMP EMLFINISH
; For register arithmetic operations, one operand is always the stack top.
; The r/m field of the instruction is used to determine the address of
; the other operand (ST(0) - ST(7))
; CX = xxxRRRxxxxxxxxxx (x is don't care, RRR is relative register # 0-7)
even
pub ArithmeticOpReg
call RegAddr ;di <= address of 2nd operand
;carry set if invalid register
jc InvalidOperand ;no, invalid operand, don't do operation
MOV [RESULT],SI ; Set destination to TOS
TEST CL,04H ; Unless Dest bit is set
JZ DestIsSet ; in which case
MOV [RESULT],DI ; Set destination to DI
pub DestIsSet
; Need to Toggle Reverse bit for DIV or SUB
TEST BL,08H ; OP = 1xx for DIV and SUB; BX = |0000|OP|O|
JZ SetUpPop
XOR BL,02H ; Toggle Reverse bit
pub SetUpPop
TEST CL,02H
JZ DoArithmeticOpNoPop
pub DoArithmeticOpPop
CALL ArithmeticOpTab[BX]
mov esi,[CURstk]
cmp esi,[BASstk] ; 15 was it last register in the chunk ?
jz short AOPstovr ; 16 yes, overflow
AOPstok:
sub esi,Reg87Len ; 4 decrement SI to previous register
mov [CURstk],esi ; 15 set current top of stack
JMP short EMLFINISH
AOPstovr:
call UnderStk ; stack underflow error
jmp AOPstok
even
pub DoArithmeticOpNoPop
mov eax,offset EMLFINISH
push eax
jmp ArithmeticOpTab[BX]
;*** InvalidOperand - register operand does not exist
;
; RETURNS
; sets Stack Underflow and Invalid bits in [CURerr]
;
; DESCRIPTION
; a reference was made to a register that does not
; exist on the stack. Set error bits and exit.
pub InvalidOperand
call UnderStk ;indicate stack underflow error
or [CURerr],Invalid ;indicate invalid operand
jmp short EMLFINISH ;don't execute instruction
;*** RegAddr - compute register address
;
; ARGUMENTS
; CX = |Op|r/m|MOD|esc|MF|Arith|
; r/m = register whose address is to be computed
;
; RETURNS
; SI = address of top of stack
; DI = address of requested register
; PSW.C set if register is not valid
; PSW.C reset if register is valid
;
; DESCRIPTION
; multiply register number by 12 and subtract this from
; [CURstk] (the address of TOS) to compute address of
; register referenced by r/m.
;
; REGISTERS
; modifies dx
pub RegAddr
MOV SI,[CURstk] ; address top of stack
MOV DI,SI
;set up address of 2nd operand based on r/m field of instruction
mov dl,ch ; dl <== byte containing reg#
and dl,01ch ; mask all but r/m field
; Since r/m field contains the relative reg # in bits 2-4 of dl,
; and bits 0-1 of dl are zero, dl now contains 4*(reg #). To compute
; the memory location of this register, calculate 12*(reg #) and
; subtract this from di, which contains the address of the TOS. reg #
; is multiplied by 12 because that is the number of bytes in each stack
; entry.
mov dh,dl ; dh = dl = 4*(reg #)
shl dh,1 ; dh = 2*dh = 8*(reg #)
add dl,dh ; dl = 8*(reg #) + 4*(reg #) = 12*(reg #)
xor dh,dh ; zero out high byte of DX
sub di,dx ; di is address of second operand
cmp di,[BASstk] ; is register in range?
clc ; assume valid register
jg $+3 ; valid - skip next instruction
cmc ; set carry to indicate invalid register
ret
pub CallUnused
CALL UNUSED ; Treat as unimpleminted
jmp EMLFINISH
; out of line returns from emulator
pub SawException
pop bp ; restore registers
add sp,2 ; toss regSegOvr
pop bx
pop cx
pop dx
pop si
pop di
pop ds
pop es
pub ExceptionsEmulator
JMP CommonExceptions
ifdef LOOK_AHEAD
pub NoPipeLine
pop bp ; restore registers
add sp,2 ; toss regSegOvr
pop bx
pop cx
pop dx
pop si
pop di
pop ds
pop es
pub errret
error_return noerror ; common exit sequence
endif ;LOOK_AHEAD
; return from routine; restore registers and return
even
pub EMLFINISH
; check for errors
MOV AX,[CURerr] ; fetch errors
or [UserStatusWord],ax ; save all exception errors
OR [SWerr],AL ; set errors in sticky error flag
NOT AL ; make a zero mean an error
MOV bh,byte ptr [UserControlWord] ; get user's IEEE control word
OR bh,0C2H ; mask reserved, IEM and denormal bits
AND bh,03FH ; unmask invalid instruction,
; stack overflow.
OR AL,bh ; mask for IEEE exceptions
NOT AL ; make a one mean an error
TEST AX,0FFFFh-MemoryOperand ; test for errors to report
jnz SawException ; goto error handler
ifndef LOOK_AHEAD
pop bp ; restore registers
add sp,2 ; toss regSegOvr
pop bx
pop cx
pop dx
pop si
pop di
pop ds
pop es
pub errret
error_return noerror ; common exit sequence
else ;LOOK_AHEAD
ifdef DOS3and5
jmp [LookAheadRoutine]
endif
ifdef DOS3
pub DOSLookAhead
cmp [NextOpcode], fINT ; Quick check. If first byte isn't
jne NoPipeLine ; an int instruction then exit.
; can stay in the emulator - set up registers for CommonDispatch
mov bp, sp ; set up frame pointer
lds di, dword ptr [bp].regIP ; DS:DI = address of next instruction
add di, 3 ; skip 3 bytes to displacement field
mov cx, [di-2] ; CX = (opcode byte,0-7 from ESC)
sub cl, BEGINT
cmp cl, 7 ; Can't handle segment overrides with
ja NoPipeLine ; pipe lining.
mov ax, [bp].regDI ; ax = original di
mov dx, [bp].regBX ; dx = original bx
mov si, [bp].regSI ; si = original si
mov es, [bp].regDS ; es = original ds (no segment override)
mov [bp].regSegOvr, ss ; reset override segment
rol ch, 1 ; rotate MOD field next to r/m field
rol ch, 1
mov bl, ch ; get copy of operation
and bx, 1fh ; Mask to MOD and r/m fields
shl bx, 1 ; make into word offset
jmp EA286Tab[bx]
endif ;DOS3
ifdef PROTECT
pub ProtLookAhead
mov cl, [NextOpcode]
cmp cl, fFWAIT
je CheckNextByte
cmp cl, iNOP
je CheckNextByte
xor cl, 20h ; See if this is a floating point instruction.
cmp cl, 0f8h
jbNoPipeLine:
jb NoPipeLine
mov bp, sp ; set up frame pointer
lds di, dword ptr [bp].regIP ; ds:di = address of next instruction
jmp short CanDoPipeLine
pub CheckNextByte
mov bp, sp ; set up frame pointer
lds di, dword ptr [bp].regIP ; ds:di = address of next instruction
inc di ; next instruction was NOP or FWAIT
mov cl, [di]
xor cl, 20h
cmp cl, 0f8h
jb jbNoPipeLine
pub CanDoPipeLine
mov ch, [di+1] ; we already have first byte of next
add di, 2 ; instruction in cl
add cl, 8h ; clear out what's left of escape
mov ax, [bp].regDI ; ax = original di
mov dx, [bp].regBX ; dx = original bx
mov si, [bp].regSI ; si = original si
mov es, [bp].regDS ; es = original ds (no segment override)
mov [bp].regSegOvr, ss ; reset override segment
rol ch, 1 ; rotate MOD field next to r/m field
rol ch, 1
mov bl, ch ; get copy of operation
and bx, 1fh ; Mask to MOD and r/m fields
shl bx, 1 ; make into word offset
jmp EA286Tab[bx]
endif ;PROTECT
endif ;LOOK_AHEAD
ProfEnd MAIN
|