Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

567 lines
13 KiB

  1. ;
  2. ;
  3. ; Copyright (C) Microsoft Corporation, 1987
  4. ;
  5. ; This Module contains Proprietary Information of Microsoft
  6. ; Corporation and should be treated as Confidential.
  7. ;
  8. subttl em386.asm - Main Entry Point and Address Calculation Procedure
  9. page
  10. ;*********************************************************************;
  11. ; ;
  12. ; Main Entry Point and Address Calculation Procedure ;
  13. ; ;
  14. ; 80386 version ;
  15. ; ;
  16. ;*********************************************************************;
  17. ;
  18. ; This routine fetches the 8087 instruction, calculates memory address
  19. ; if necessary into ES:ESI and calls a routine to emulate the instruction.
  20. ; Most of the dispatching is done through tables. (see comments in CONST)
  21. ;
  22. ; The instruction dispatching is designed to favor the 386 addressing modes
  23. ifdef XENIX
  24. LDT_DATA= 02Fh ; emulator data LDT
  25. STACK_ALIAS= 027h ; 32 bit alias for stack selector
  26. endif ;XENIX
  27. ;------------------------------------------------------------------------------
  28. ;
  29. ; emulation entry point
  30. ;
  31. ;------------------------------------------------------------------------------
  32. pub protemulation
  33. cld ; clear direction flag forever
  34. ifdef XENIX
  35. push ss ; save ss
  36. endif ;XENIX
  37. push eax ; for common exit code (historical)
  38. push ds ; save segment registers
  39. ifdef XENIX
  40. push eax ; UNDONE - slow
  41. mov ax,LDT_DATA ; load up emulator's data segment
  42. mov ds,ax
  43. cmp [Einstall],0 ; check if emulator is initialized
  44. je installemulator ; no - go install it
  45. pub protemcont
  46. pop eax ; UNDONE - slow
  47. endif ;XENIX
  48. push es
  49. push ss ; save SegOvr (bp forces SS override)
  50. push edi ; save registers
  51. push esi ; must be in this order for indexing
  52. push ebp
  53. push esp
  54. push ebx
  55. push edx
  56. push ecx
  57. push eax
  58. ifdef XENIX
  59. mov ax,ss ; check for 286 using user SS
  60. lar eax,ax ; load extended access bits
  61. test eax,00400000h ; test BIG bit
  62. jnz short prot386 ; 386 - ok
  63. mov ax,STACK_ALIAS ; setup stack with 32 bit alias segment
  64. mov ss,ax
  65. movzx esp,sp ; clean up ESP
  66. ; mov word ptr [esp].regEIP+2,0 ; clean up EIP
  67. pub prot386
  68. endif ;XENIX
  69. mov ebp,esp ; set up frame pointer
  70. add [ebp].regESP,regFlg-regESP ; adjust to original esp
  71. mov eax,edi ; may use original DI to calc address
  72. lds edi,fword ptr [ebp].regEIP ; ds:edi = 287 instruction address
  73. mov cx,[edi] ; cx = esc 0-7 and opcode
  74. cmp cl,09Bh ; UNDONE - check FWAIT's getting through
  75. je sawFWAIT ; UNDONE - and ignore it
  76. add edi,2 ; point to displacement
  77. add cl,28h ; set carry if esc 0-7 (and cl = 0-7)
  78. jnc short protSegOvr ; no carry - must be segment override
  79. mov es,[ebp].regDS ; es = user data segment
  80. mov edx,ebx ; may use original BX to calc address
  81. pub CommonDispatch
  82. rol ch,2 ; rotate MOD field next to r/m field
  83. ; UNDONE
  84. ; UNDONE should check for instruction prefixes such as address size prefix
  85. ; UNDONE
  86. lar ebx,[ebp].regCS ; check if 286 or 386 segment
  87. test ebx,00400000h ;
  88. mov bl,ch ; get copy of operation
  89. jz short Have286segment ; 286 segment - assume 286 addressing
  90. and ebx,1FH ; Mask to MOD and r/m fields
  91. jmp EA386Tab[4*ebx]
  92. pub Have286segment
  93. and ebx,1FH ; Mask to MOD and r/m fields
  94. jmp EA286Tab[4*ebx]
  95. ; protect mode Segment override case
  96. glb <protSegOvrTab>
  97. protSegOvrTab label word
  98. dd DSSegOvr ; 11
  99. dd ESSegOvr ; 00
  100. dd CSSegOvr ; 01
  101. dd SSSegOvr ; 10
  102. pub protSegOvr
  103. mov edx,ebx ; may use original BX to calc 286 address
  104. mov bl,cl
  105. shr bl,1
  106. and ebx,0Ch ; bl = (seg+1) and 0Ch
  107. inc edi ; point to displacement
  108. mov cx,[edi-2] ; cx = esc 0-7 and opcode
  109. jmp protSegOvrTab[ebx] ; process appropriate segment override
  110. pub DSSegOvr ; 00
  111. mov es,[ebp].regDS ; set ES to EA segment
  112. jmp short ESSegOvr
  113. pub CSSegOvr ; 10
  114. push ds ; DS = caller's CS
  115. pop es ; set ES to EA segment
  116. jmp short ESSegOvr
  117. pub SSSegOvr ; 01
  118. push ss ; SS = caller's SS
  119. pop es ; set ES to EA segment
  120. pub ESSegOvr ; 11
  121. mov [ebp].regSegOvr,es ; save for bp rel EAs
  122. jmp CommonDispatch
  123. ; 386 address modes
  124. ; SIB does not handle SS overrides for ebp
  125. SIB macro modval
  126. local SIBindex,SIBbase
  127. xor ebx,ebx
  128. mov bl,[edi] ; ebx = SIB field
  129. inc edi ; bump past SIB field
  130. mov eax,ebx
  131. and al,7 ; mask down to base register
  132. if modval eq 0
  133. cmp al,5 ; base = ebp
  134. jne short SIBbase ; yes - get base register value
  135. mov eax,[edi] ; eax = disp32
  136. add edi,4 ; bump past displacement
  137. jmp short SIBindex
  138. endif
  139. SIBbase:
  140. mov eax,[ebp+4*eax] ; eax = base register value
  141. SIBindex:
  142. mov [ebp].regESP,0 ; no esp indexing allowed
  143. push ecx ; UNDONE - slow
  144. mov cl,bl
  145. shr cl,6 ; cl = scale factor
  146. shr bl,1
  147. and bl,1Ch ; ebx = 4 * index register
  148. mov esi,[ebp+ebx] ; esi = index register value
  149. shl esi,cl ; esi = scaled index register value
  150. pop ecx ; UNDONE - slow
  151. add esi,eax ; esi = SIB address value
  152. endm
  153. pub SIB00
  154. SIB 00 ; decode SIB field
  155. jmp CommonMemory
  156. pub SIB01
  157. SIB 01 ; decode SIB field
  158. mov al,[edi]
  159. inc edi
  160. cbw ; ax = al
  161. cwde ; eax = ax
  162. add esi,eax
  163. jmp short CommonMemory
  164. pub SIB10
  165. SIB 10 ; decode SIB field
  166. mov eax,[edi]
  167. add edi,4
  168. add esi,eax
  169. jmp short CommonMemory
  170. ; 386 single register addressing
  171. pub Exx00
  172. and bl,1Ch ; mask off mod bits
  173. mov esi,[ebp+ebx]
  174. jmp short CommonMemory
  175. pub Exx01
  176. and bl,1Ch ; mask off mod bits
  177. mov esi,[ebp+ebx]
  178. mov al,[edi]
  179. inc edi
  180. cbw ; ax = al
  181. cwde ; eax = ax
  182. add esi,eax
  183. jmp short CommonMemory
  184. pub Exx10
  185. and bl,1Ch ; mask off mod bits
  186. mov esi,[ebp+ebx]
  187. mov eax,[edi]
  188. add edi,4
  189. add esi,eax
  190. jmp short CommonMemory
  191. ; 386 direct addressing
  192. pub Direct386
  193. mov esi,[edi]
  194. add edi,4
  195. pub CommonMemory
  196. MOV [ebp].regEIP,edi ; final return offset
  197. mov ax,LDT_DATA
  198. mov ds,ax
  199. mov [CURerr],MemoryOperand ; clear current error, set mem. op bit
  200. ; At this point ES:SI = memory address, CX = |Op|r/m|MOD|escape|MF|Arith|
  201. shr ch,4 ; Move Op field to BX for Table jump
  202. mov bl,ch
  203. and ebx,0EH
  204. test cl,1 ; Arith field set?
  205. JZ short ArithmeticOpMem
  206. pub NonArithOpMem
  207. CALL NonArithOpMemTab[2*ebx] ; is CH shl 4 needed?
  208. JMP EMLFINISH
  209. pub ArithmeticOpMem
  210. PUSH ebx ; Save Op while we load the argument
  211. CALL eFLDsdri ; emulate proper load
  212. POP ebx
  213. mov ax,ds ; ES = DS = task data area
  214. mov es,ax
  215. MOV esi,[CURstk] ; address top of stack
  216. MOV edi,esi
  217. ChangeDIfromTOStoNOS
  218. MOV [RESULT],edi ; Set up destination Pointer
  219. JMP short DoArithmeticOpPop
  220. pub NoEffectiveAddress ; Either Register op or Miscellaneous
  221. MOV [ebp].regEIP,edi ; final return offset
  222. xor eax,eax
  223. mov di,LDT_DATA
  224. mov ds,di
  225. mov es,di
  226. mov [CURerr],ax ; clear current error, memory op bit
  227. ; CX = |Op|r/m|MOD|escape|MF|Arith|
  228. mov bl,ch
  229. shr bl,4 ; Mov Op field to BX for jump
  230. and ebx,0Eh
  231. TEST CL,1 ; Arith field set?
  232. JZ short ArithmeticOpReg
  233. pub NonArithOpReg
  234. CALL NonArithOpRegTab[2*ebx]
  235. JMP EMLFINISH
  236. ; For register arithmetic operations, one operand is always the stack top.
  237. ; The r/m field of the instruction is used to determine the address of
  238. ; the other operand (ST(0) - ST(7))
  239. ; CX = xxxRRRxxxxxxxxxx (x is don't care, RRR is relative register # 0-7)
  240. pub ArithmeticOpReg
  241. call RegAddr ;di <= address of 2nd operand
  242. ;carry set if invalid register
  243. jc short InvalidOperand ;no, invalid operand, don't do operation
  244. MOV [RESULT],esi ; Set destination to TOS
  245. TEST CL,04H ; Unless Dest bit is set
  246. JZ short DestIsSet ; in which case
  247. MOV [RESULT],edi ; Set destination to DI
  248. pub DestIsSet
  249. ; Need to Toggle Reverse bit for DIV or SUB
  250. TEST BL,08H ; OP = 1xx for DIV and SUB; BX = |0000|OP|O|
  251. JZ short SetUpPop
  252. XOR BL,02H ; Toggle Reverse bit
  253. pub SetUpPop
  254. TEST CL,02H
  255. JZ short DoArithmeticOpNoPop
  256. pub DoArithmeticOpPop
  257. CALL ArithmeticOpTab[2*ebx]
  258. POPST
  259. JMP short EMLFINISH
  260. pub DoArithmeticOpNoPop
  261. CALL ArithmeticOpTab[2*ebx]
  262. JMP short EMLFINISH
  263. ;*** InvalidOperand - register operand does not exist
  264. ;
  265. ; RETURNS
  266. ; sets Stack Underflow and Invalid bits in [CURerr]
  267. ;
  268. ; DESCRIPTION
  269. ; a reference was made to a register that does not
  270. ; exist on the stack. Set error bits and exit.
  271. pub InvalidOperand
  272. call UnderStk ;indicate stack underflow error
  273. or [CURerr],Invalid ;indicate invalid operand
  274. jmp short EMLFINISH ;don't execute instruction
  275. ;*** RegAddr - compute register address
  276. ;
  277. ; ARGUMENTS
  278. ; CX = |Op|r/m|MOD|esc|MF|Arith|
  279. ; r/m = register whose address is to be computed
  280. ;
  281. ; RETURNS
  282. ; SI = address of top of stack
  283. ; DI = address of requested register
  284. ; PSW.C set if register is not valid
  285. ; PSW.C reset if register is valid
  286. ;
  287. ; DESCRIPTION
  288. ; multiply register number by 12 and subtract this from
  289. ; [CURstk] (the address of TOS) to compute address of
  290. ; register referenced by r/m.
  291. ;
  292. ; REGISTERS
  293. ; modifies dx
  294. pub RegAddr
  295. mov esi,[CURstk] ; address top of stack
  296. mov edi,esi
  297. ;set up address of 2nd operand based on r/m field of instruction
  298. xor edx,edx
  299. mov dl,ch ; dl <== byte containing reg#
  300. and dl,01ch ; mask all but r/m field
  301. ; Since r/m field contains the relative reg # in bits 2-4 of dl,
  302. ; and bits 0-1 of dl are zero, dl now contains 4*(reg #). To compute
  303. ; the memory location of this register, calculate 12*(reg #) and
  304. ; subtract this from di, which contains the address of the TOS. reg #
  305. ; is multiplied by 12 because that is the number of bytes in each stack
  306. ; entry.
  307. lea edx,[2*edx+edx] ; edx = 3 * (4 * reg #)
  308. sub edi,edx ; di is address of second operand
  309. cmp edi,[BASstk] ; is register in range?
  310. clc ; assume valid register
  311. jg short RAclc ; valid - skip next instruction
  312. cmc ; set carry to indicate invalid register
  313. pub RAclc
  314. ret
  315. pub CallUnused
  316. CALL UNUSED ; Treat as unimpleminted
  317. jmp short EMLFINISH
  318. ; sawFWAIT - UNDONE - workaround for a 386 bug
  319. pub sawFWAIT
  320. inc edi ; bump past FWAIT
  321. MOV [ebp].regEIP,edi ; final return offset
  322. xor eax,eax
  323. mov di,LDT_DATA
  324. mov ds,di
  325. mov [CURerr],ax ; clear current error, memory op bit
  326. ; return from routine; restore registers and return
  327. pub EMLFINISH
  328. pop eax
  329. pop ecx
  330. pop edx
  331. pop ebx
  332. add esp,4 ; toss esp value
  333. pop ebp
  334. pop esi
  335. pop edi
  336. add esp,4 ; toss regSegOvr
  337. ; check for errors
  338. MOV AX,[CURerr] ; fetch errors
  339. or [UserStatusWord],ax ; save all exception errors
  340. OR [SWerr],AL ; set errors in sticky error flag
  341. NOT AL ; make a zero mean an error
  342. MOV AH,byte ptr [UserControlWord] ; get user's IEEE control word
  343. OR AH,0C2H ; mask reserved, IEM and denormal bits
  344. AND AH,03FH ; unmask invalid instruction,
  345. ; stack overflow.
  346. OR AL,AH ; mask for IEEE exceptions
  347. NOT AL ; make a one mean an error
  348. MOV AH,byte ptr (CURerr+1) ; get stack over/underflow flags
  349. TEST AX,0FFFFh-MemoryOperand ; test for errors to report
  350. pop es
  351. pop ds
  352. jnz short ExceptionsEmulator ; goto error handler
  353. pub errret
  354. error_return noerror ; common exit sequence
  355. pub ExceptionsEmulator
  356. JMP CommonExceptions
  357. ;------------------------------------------------------------------------------
  358. ;
  359. ; 286 address modes (for XENIX only)
  360. ;
  361. ;------------------------------------------------------------------------------
  362. ifdef XENIX
  363. ; In the address calculations below:
  364. ; DX has BX original value
  365. ; AX has DI original value
  366. ; SI has SI original value
  367. ; BP has BP original value
  368. ; [EDI] is address of displacement bytes
  369. pub BXXI0D
  370. MOV eax,edx ; use original BX index value
  371. pub DSDI0D
  372. MOV esi,eax ; use alternate index value
  373. pub DSSI0D
  374. JMP short ADRFIN ; have offset in SI
  375. pub BPXI1D
  376. XOR eax,eax ; no index register
  377. pub BPDI1D
  378. MOV esi,eax ; use alternate index value
  379. pub BPSI1D
  380. ADD esi,[ebp].regEBP ; add original BP value
  381. mov es,[ebp].regSegOvr ; ES = override segment (or SS if none)
  382. JMP short DSSI1D ; go get one byte displacement
  383. pub BXSI1D
  384. MOV eax,esi ; really will want SI, not DI
  385. pub BXDI1D
  386. ADD edx,eax ; now DX is original BX plus index
  387. pub BXXI1D
  388. MOV eax,edx ; use original BX index value
  389. pub DSDI1D
  390. MOV esi,eax ; use alternate index value
  391. pub DSSI1D
  392. MOV AL,[edi] ; get one byte displacement
  393. CBW ; sign extend displacement
  394. INC edi ; get past displacement byte
  395. JMP short DISPAD ; go add AX to SI (time w/ ADD)
  396. pub BPXI2D
  397. XOR eax,eax ; no index register
  398. pub BPDI2D
  399. MOV esi,eax ; use alternate index value
  400. pub BPSI2D
  401. ADD esi,[ebp].regEBP ; add original BP value
  402. mov es,[ebp].regSegOvr ; ES = override segment (or SS if none)
  403. JMP short DSSI2D ; go get two byte displacement
  404. pub BXSI2D
  405. MOV eax,esi ; really will want SI, not DI
  406. pub BXDI2D
  407. ADD edx,eax ; now DX is original BX plus index
  408. pub BXXI2D
  409. MOV eax,edx ; use original BX index value
  410. pub DSDI2D
  411. MOV esi,eax ; use alternate index value
  412. pub DSSI2D
  413. MOV AX,[edi] ; get two byte displacement
  414. INC edi ; get past displacement byte
  415. INC edi ; get past displacement byte
  416. JMP short DISPAD ; go add AX to SI (time w/ ADD)
  417. pub DSXI2D
  418. MOV SI,[edi] ; get two byte displacement
  419. INC edi ; get past displacement byte
  420. INC edi ; get past displacement byte
  421. JMP short ADRFIN ; have offset in AX
  422. pub BPSI0D
  423. MOV eax,esi ; really will want SI, not DI
  424. pub BPDI0D
  425. MOV edx,[ebp].regEBP ; really will want BP, not BX
  426. mov es,[ebp].regSegOvr ; ES = override segment (or SS if none)
  427. pub BXDI0D
  428. MOV esi,eax ; use alternate index value
  429. pub BXSI0D
  430. MOV eax,edx ; use original BX (or BP) as base
  431. pub DISPAD
  432. ADD esi,eax ; add original index value
  433. pub ADRFIN
  434. movzx esi,si ; ES:ESI = user memory address
  435. jmp CommonMemory
  436. endif ;XENIX