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.

805 lines
17 KiB

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