summaryrefslogtreecommitdiffstats
path: root/private/mvdm/wow16/win87em/emmain.asm
blob: d160479f1b99399c8863e1d5963cbb59e553b966 (plain) (blame)
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