summaryrefslogblamecommitdiffstats
path: root/private/mvdm/wow16/win87em/em386.asm
blob: 7e4dc57e171f458c0e5c361169f85b4d66f74d60 (plain) (tree)
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






















































































































































































































































































































































































































































































































































































                                                                                    

;
;
;	Copyright (C) Microsoft Corporation, 1987
;
;	This Module contains Proprietary Information of Microsoft
;	Corporation and should be treated as Confidential.
;
subttl	em386.asm - Main Entry Point and Address Calculation Procedure
page
;*********************************************************************;
;								      ;
;	  Main Entry Point and Address Calculation Procedure	      ;
;								      ;
;		80386 version					      ;
;								      ;
;*********************************************************************;
;
; This routine fetches the 8087 instruction, calculates memory address
; if necessary into ES:ESI and calls a routine to emulate the instruction.
; Most of the dispatching is done through tables. (see comments in CONST)
;
; The instruction dispatching is designed to favor the 386 addressing modes

ifdef	XENIX

LDT_DATA=	02Fh		; emulator data LDT
STACK_ALIAS=	027h		; 32 bit alias for stack selector

endif	;XENIX

;------------------------------------------------------------------------------
;
;	emulation entry point
;
;------------------------------------------------------------------------------

pub	protemulation

	cld				; clear direction flag forever

ifdef	XENIX
	push	ss			; save ss
endif	;XENIX

	push	eax			; for common exit code (historical)

	push	ds			; save segment registers

ifdef	XENIX

	push	eax			; UNDONE - slow

	mov	ax,LDT_DATA		; load up emulator's data segment
	mov	ds,ax
	cmp	[Einstall],0		; check if emulator is initialized
	je	installemulator 	;   no - go install it

pub	protemcont

	pop	eax			; UNDONE - slow

endif	;XENIX

	push	es
	push	ss			;   save SegOvr (bp forces SS override)

	push	edi			; save registers
	push	esi			;   must be in this order for indexing
	push	ebp
	push	esp
	push	ebx
	push	edx
	push	ecx
	push	eax

ifdef	XENIX

	mov	ax,ss			; check for 286 using user SS
	lar	eax,ax			; load extended access bits
	test	eax,00400000h		; test BIG bit
	jnz	short prot386		;   386 - ok

	mov	ax,STACK_ALIAS		; setup stack with 32 bit alias segment
	mov	ss,ax
	movzx	esp,sp			; clean up ESP
;	mov	word ptr [esp].regEIP+2,0 ; clean up EIP

pub	prot386

endif	;XENIX

	mov	ebp,esp 		; set up frame pointer
	add	[ebp].regESP,regFlg-regESP	; adjust to original esp

	mov	eax,edi 		; may use original DI to calc address
	lds	edi,fword ptr [ebp].regEIP ; ds:edi = 287 instruction address
	mov	cx,[edi]		; cx = esc 0-7 and opcode

	cmp	cl,09Bh 		; UNDONE - check FWAIT's getting through
	je	sawFWAIT		; UNDONE -   and ignore it

	add	edi,2			; point to displacement
	add	cl,28h			; set carry if esc 0-7 (and cl = 0-7)
	jnc	short protSegOvr	;   no carry - must be segment override

	mov	es,[ebp].regDS		; es = user data segment
	mov	edx,ebx 		; may use original BX to calc address

pub	CommonDispatch
	rol	ch,2			; rotate MOD field next to r/m field


; UNDONE
; UNDONE  should check for instruction prefixes such as address size prefix
; UNDONE

	lar	ebx,[ebp].regCS 	; check if 286 or 386 segment
	test	ebx,00400000h		;
	mov	bl,ch			; get copy of operation
	jz	short Have286segment	;   286 segment - assume 286 addressing

	and	ebx,1FH 		; Mask to MOD and r/m fields
	jmp	EA386Tab[4*ebx]

pub	Have286segment
	and	ebx,1FH 		; Mask to MOD and r/m fields
	jmp	EA286Tab[4*ebx]


;	protect mode Segment override case

glb	<protSegOvrTab>

protSegOvrTab	label	word

	dd	DSSegOvr	; 11
	dd	ESSegOvr	; 00
	dd	CSSegOvr	; 01
	dd	SSSegOvr	; 10


pub	protSegOvr
	mov	edx,ebx 		; may use original BX to calc 286 address
	mov	bl,cl
	shr	bl,1
	and	ebx,0Ch 		; bl = (seg+1) and 0Ch
	inc	edi			; point to displacement
	mov	cx,[edi-2]		; cx = esc 0-7 and opcode
	jmp	protSegOvrTab[ebx]	; process appropriate segment override


pub	DSSegOvr			; 00
	mov	es,[ebp].regDS		; set ES to EA segment
	jmp	short ESSegOvr

pub	CSSegOvr			; 10
	push	ds			; DS = caller's CS
	pop	es			; set ES to EA segment
	jmp	short ESSegOvr

pub	SSSegOvr			; 01
	push	ss			; SS = caller's SS
	pop	es			; set ES to EA segment

pub	ESSegOvr			; 11
	mov	[ebp].regSegOvr,es	; save for bp rel EAs
	jmp	CommonDispatch


;	386 address modes

;	SIB does not handle SS overrides for ebp

SIB	macro	modval
	local	SIBindex,SIBbase

	xor	ebx,ebx
	mov	bl,[edi]		; ebx = SIB field
	inc	edi			; bump past SIB field
	mov	eax,ebx
	and	al,7			; mask down to base register

if	modval eq 0
	cmp	al,5			; base = ebp
	jne	short SIBbase		;   yes - get base register value
	mov	eax,[edi]		; eax = disp32
	add	edi,4			; bump past displacement
	jmp	short SIBindex
endif

SIBbase:
	mov	eax,[ebp+4*eax] 	; eax = base register value

SIBindex:
	mov	[ebp].regESP,0		; no esp indexing allowed
	push	ecx			; UNDONE - slow
	mov	cl,bl
	shr	cl,6			; cl = scale factor
	shr	bl,1
	and	bl,1Ch			; ebx = 4 * index register
	mov	esi,[ebp+ebx]		; esi = index register value
	shl	esi,cl			; esi = scaled index register value
	pop	ecx			; UNDONE - slow
	add	esi,eax 		; esi = SIB address value
	endm


pub	SIB00
	SIB	00			; decode SIB field
	jmp	CommonMemory

pub	SIB01
	SIB	01			; decode SIB field
	mov	al,[edi]
	inc	edi
	cbw				; ax = al
	cwde				; eax = ax
	add	esi,eax
	jmp	short CommonMemory

pub	SIB10
	SIB	10			; decode SIB field
	mov	eax,[edi]
	add	edi,4
	add	esi,eax
	jmp	short CommonMemory


;	386 single register addressing

pub	Exx00
	and	bl,1Ch			; mask off mod bits
	mov	esi,[ebp+ebx]
	jmp	short CommonMemory

pub	Exx01
	and	bl,1Ch			; mask off mod bits
	mov	esi,[ebp+ebx]
	mov	al,[edi]
	inc	edi
	cbw				; ax = al
	cwde				; eax = ax
	add	esi,eax
	jmp	short CommonMemory

pub	Exx10
	and	bl,1Ch			; mask off mod bits
	mov	esi,[ebp+ebx]
	mov	eax,[edi]
	add	edi,4
	add	esi,eax
	jmp	short CommonMemory


;	386 direct addressing

pub	Direct386
	mov	esi,[edi]
	add	edi,4

pub	CommonMemory
	MOV	[ebp].regEIP,edi	; final return offset
	mov	ax,LDT_DATA
	mov	ds,ax
	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,4			; Move Op field to BX for Table jump
	mov	bl,ch
	and	ebx,0EH

	test	cl,1			; Arith field set?
	JZ	short ArithmeticOpMem

pub	NonArithOpMem
	CALL	NonArithOpMemTab[2*ebx] ; is CH shl 4 needed?
	JMP	EMLFINISH

pub	ArithmeticOpMem
	PUSH	ebx			; Save Op while we load the argument
	CALL	eFLDsdri		; emulate proper load
	POP	ebx

	mov	ax,ds			; ES = DS = task data area
	mov	es,ax
	MOV	esi,[CURstk]		; address top of stack
	MOV	edi,esi
	ChangeDIfromTOStoNOS
	MOV	[RESULT],edi		; Set up destination Pointer

	JMP	short DoArithmeticOpPop


pub	NoEffectiveAddress		; Either Register op or Miscellaneous

	MOV	[ebp].regEIP,edi	; final return offset

	xor	eax,eax
	mov	di,LDT_DATA
	mov	ds,di
	mov	es,di
	mov	[CURerr],ax		; clear current error, memory op bit

; CX = |Op|r/m|MOD|escape|MF|Arith|

	mov	bl,ch
	shr	bl,4			; Mov Op field to BX for jump
	and	ebx,0Eh

	TEST	CL,1			; Arith field set?
	JZ	short ArithmeticOpReg

pub	NonArithOpReg
	CALL	NonArithOpRegTab[2*ebx]
	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)

pub	ArithmeticOpReg

	call	RegAddr 		;di <= address of 2nd operand
					;carry set if invalid register
	jc	short InvalidOperand	;no, invalid operand, don't do operation

	MOV	[RESULT],esi		; Set destination to TOS
	TEST	CL,04H			; Unless Dest bit is set
	JZ	short DestIsSet 	; in which case
	MOV	[RESULT],edi		; 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	short SetUpPop
	XOR	BL,02H			; Toggle Reverse bit

pub	SetUpPop
	TEST	CL,02H
	JZ	short DoArithmeticOpNoPop

pub	DoArithmeticOpPop
	CALL	ArithmeticOpTab[2*ebx]

	POPST
	JMP	short EMLFINISH

pub	DoArithmeticOpNoPop
	CALL	ArithmeticOpTab[2*ebx]
	JMP	short EMLFINISH


;***	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	esi,[CURstk]		; address top of stack
	mov	edi,esi

;set up address of 2nd operand based on r/m field of instruction

	xor	edx,edx
	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.

	lea	edx,[2*edx+edx] 	; edx = 3 * (4 * reg #)
	sub	edi,edx 		; di is address of second operand
	cmp	edi,[BASstk]		; is register in range?
	clc				; assume valid register
	jg	short RAclc		; valid - skip next instruction
	cmc				; set carry to indicate invalid register
pub RAclc
	ret


pub	CallUnused
	CALL	UNUSED			; Treat as unimpleminted
	jmp	short EMLFINISH


;	sawFWAIT - UNDONE - workaround for a 386 bug

pub	sawFWAIT
	inc	edi			; bump past FWAIT
	MOV	[ebp].regEIP,edi	; final return offset
	xor	eax,eax
	mov	di,LDT_DATA
	mov	ds,di
	mov	[CURerr],ax		; clear current error, memory op bit

; return from routine;	restore registers and return

pub	EMLFINISH
	pop	eax
	pop	ecx
	pop	edx
	pop	ebx
	add	esp,4			; toss esp value
	pop	ebp
	pop	esi
	pop	edi
	add	esp,4			; toss regSegOvr

;	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	AH,byte ptr [UserControlWord]  ; get user's IEEE control word
	OR	AH,0C2H 		; mask reserved, IEM and denormal bits
	AND	AH,03FH 		; unmask invalid instruction,
					;    stack overflow.
	OR	AL,AH			; mask for IEEE exceptions
	NOT	AL			; make a one mean an error
	MOV	AH,byte ptr (CURerr+1)	; get stack over/underflow flags
	TEST	AX,0FFFFh-MemoryOperand ; test for errors to report

	pop	es
	pop	ds


	jnz	short ExceptionsEmulator ;   goto error handler

pub	errret
        error_return    noerror          ; common exit sequence

pub	ExceptionsEmulator
	JMP	CommonExceptions


;------------------------------------------------------------------------------
;
;	286 address modes	(for XENIX only)
;
;------------------------------------------------------------------------------

ifdef	XENIX

; In the address calculations below:
;   DX has BX original value
;   AX has DI original value
;   SI has SI original value
;   BP has BP original value
;  [EDI] is address of displacement bytes

pub BXXI0D
	MOV	eax,edx 		; use original BX index value
pub DSDI0D
	MOV	esi,eax 		; use alternate index value
pub DSSI0D
	JMP	short ADRFIN		; have offset in SI

pub BPXI1D
	XOR	eax,eax 		; no index register
pub BPDI1D
	MOV	esi,eax 		; use alternate index value
pub BPSI1D
	ADD	esi,[ebp].regEBP	; add original BP value
	mov	es,[ebp].regSegOvr	; ES = override segment (or SS if none)
	JMP	short DSSI1D		; go get one byte displacement

pub BXSI1D
	MOV	eax,esi 		; really will want SI, not DI
pub BXDI1D
	ADD	edx,eax 		; now DX is original BX plus index
pub BXXI1D
	MOV	eax,edx 		; use original BX index value
pub DSDI1D
	MOV	esi,eax 		; use alternate index value
pub DSSI1D
	MOV	AL,[edi]		; get one byte displacement
	CBW				; sign extend displacement
	INC	edi			; get past displacement byte
	JMP	short DISPAD		; go add AX to SI (time w/ ADD)

pub BPXI2D
	XOR	eax,eax 		; no index register
pub BPDI2D
	MOV	esi,eax 		; use alternate index value
pub BPSI2D
	ADD	esi,[ebp].regEBP	; add original BP value
	mov	es,[ebp].regSegOvr	; ES = override segment (or SS if none)
	JMP	short DSSI2D		; go get two byte displacement

pub BXSI2D
	MOV	eax,esi 		; really will want SI, not DI
pub BXDI2D
	ADD	edx,eax 		; now DX is original BX plus index
pub BXXI2D
	MOV	eax,edx 		; use original BX index value
pub DSDI2D
	MOV	esi,eax 		; use alternate index value
pub DSSI2D
	MOV	AX,[edi]		; get two byte displacement
	INC	edi			; get past displacement byte
	INC	edi			; get past displacement byte
	JMP	short DISPAD		; go add AX to SI (time w/ ADD)

pub DSXI2D
	MOV	SI,[edi]		; get two byte displacement
	INC	edi			; get past displacement byte
	INC	edi			; get past displacement byte
	JMP	short ADRFIN		; have offset in AX

pub BPSI0D
	MOV	eax,esi 		; really will want SI, not DI
pub BPDI0D
	MOV	edx,[ebp].regEBP	; really will want BP, not BX
	mov	es,[ebp].regSegOvr	; ES = override segment (or SS if none)
pub BXDI0D
	MOV	esi,eax 		; use alternate index value
pub BXSI0D
	MOV	eax,edx 		; use original BX (or BP) as base

pub DISPAD
	ADD	esi,eax 		; add original index value

pub	ADRFIN
	movzx	esi,si			; ES:ESI = user memory address
	jmp	CommonMemory

endif	;XENIX