page 60,132 ;----------------------------------------------------------------------------- ; ; FISTBUG ; ; Copyright (c) 1997-Present Robert Collins ; ; You have my permission to copy and distribute this software for ; non-commercial purposes. Any commercial use of this software or ; source code is allowed, so long as the appropriate copyright ; attributions (to me) are intact, *AND* my email address is properly ; displayed. ; ; Basically, give me credit, where credit is due, and show my email ; address. ; ;----------------------------------------------------------------------------- ; ; Robert R. Collins email: rcollins@x86.org ; ;----------------------------------------------------------------------------- ; ; FISTBUG -- ; ; If all you're interested in seeing is the source code which should be ; offered for academic and peer review, please refer to the subrutines ; 'FistTest16' and 'FistTest32.' ; ; Synopsis: ; This program tests for a bug in the Pentium Pro and Pentium II floating ; point unit. This program may be run on a variety of microprocessors ; from the 80286 on up. For a complete description of the bug, please refer ; to http://www.x86.org/secrets/Dan0411.html. ; ; To assemble this source code: ; * You will need Microsoft Macro Assembler, version 6.11d (though just about ; any other version of MASM will probably work). Borland TASM will probably ; work also. ; * Run the makefile using the 'nmake' utility supplied with MASM. ; * You may compile this without the nmake utility by invoking the command ; line: ; C: > ml /Fl fistbug.asm - to generate non-verbose version ; C: > ml /Fl /DVERBOSE fistbug.asm - to genrate verbose version ; ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ; Assembler directives ;----------------------------------------------------------------------------- .xlist ; disable list file .286 ;----------------------------------------------------------------------------- ; Include file section ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ; Equates ;----------------------------------------------------------------------------- FSW_IE equ 1 ; Status Word IE bit ;----------------------------------------------------------------------------- ; Macros ;----------------------------------------------------------------------------- PRINT_PASS_FAIL MACRO ifdef VERBOSE pushf ; save results of comparison mov ah,9 ; print string function ID mov dx,offset PassMsg ; prepare for test passed jnz @F ; test did pass mov dx,offset FailMsg ; get address of failed message @@: int 21h ; print message popf ; restore flags endif ; VERBOSE ENDM PRINT_MSG MACRO MSG ifdef VERBOSE mov ah,9 mov dx,offset MSG int 21h endif ; VERBOSE ENDM ;----------------------------------------------------------------------------- ; 16-bit Floating point environment ; ; I intentionally chose a 16-bit floating point environment to allow this ; program to be run on the 80286 microprocessor. ;----------------------------------------------------------------------------- FPU_Struct STRUCT FCW dw ? ; Control word FSW dw ? ; Status word FTW dw ? ; Tag word dw ? ; Floating point IP dw ? ; Floating point CS dw ? ; Operand offset dw ? ; Operand selector ST0 dt ? ; ST0 ST1 dt ? ; ST1 ST2 dt ? ; ST2 ST3 dt ? ; ST3 ST4 dt ? ; ST4 ST5 dt ? ; ST5 ST6 dt ? ; ST6 ST7 dt ? ; ST7 FPU_Struct ENDS .list ;----------------------------------------------------------------------------- ; Dummy segments ;----------------------------------------------------------------------------- INTSEG segment at 0 org 6*4 INT06 dd ? INTSEG ends ;----------------------------------------------------------------------------- ; Data segment ;----------------------------------------------------------------------------- _DATA segment use16 para public 'DATA' ;----------------------------------------------------------------------------- ; Instantiate the floating point environment structure. I might as well make ; this appear at the paragraph boundary of the data segment. ;----------------------------------------------------------------------------- FENV FPU_Struct <> ;----------------------------------------------------------------------------- ; This is where the list of floating point numbers is stored. If you want ; to add to this list, simply insert a floating point number in the 16-bit ; section, or the 32-bit section, depending on what your needs are. For ; example, you may want to verify that positive numbers aren't affected by ; this bug. To do so, you could simply insert a line such as: ; dt 65536.0 ; The operand doesn't need to be given in hex as my examples were. My ; examples were choosen in hex to easily demonstrate the boundary conditions ; of the Dan-0411 bug. ; ; If you insert new operands, you don't need to make any other program ; changes, but you will need to reassemble the source code. The program ; automatically adjust the nuber of test cases according to how many ; operands appear in the Op16 or Op32 list. ;----------------------------------------------------------------------------- Op16 dt 0c06e8000000000000001h dt 0c06e8000000000000010h dt 0c06e8000000000000100h dt 0c06e8000000000001000h dt 0c06e8000000000010000h dt 0c06e8000000000100000h dt 0c06e8000000001000000h dt 0c06e8000000010000000h dt 0c06e8000000100000000h dt 0c06e8000001000000000h dt 0c06e8000010000000000h dt 0c06e8000100000000000h dt 0c06e80007fffffffffffh N16 equ ($-Op16) / sizeof Op16 Op32 dt 0c05e8000000000000001h dt 0c05e8000000000000010h dt 0c05e8000000000000100h dt 0c05e8000000000001000h dt 0c05e8000000000010000h dt 0c05e8000000000100000h dt 0c05e8000000001000000h dt 0c05e8000000010000000h dt 0c05e800000007fffffffh N32 equ ($-Op32) / sizeof Op32 ;----------------------------------------------------------------------------- ; D16 and D32 listed below are the destinations for the FIST and FISTP ; (Floating-to-Integer Store) instructions. The StatusWord variable is used ; to collect the resultant status words for the four different test cases on ; each floating point operand. 'Results' is used to accumulate the results. ; At the end of this test, if any bit is set in 'Results' then the test is ; considered a failure, and you've got the Dan-0411 bug. ;----------------------------------------------------------------------------- D16 dw 55aah D32 dd 55aa55aah StatusWord dw 4 dup (0) Results dw 0 ; Cumulative results ;----------------------------------------------------------------------------- ; Misc data storage. ;----------------------------------------------------------------------------- CPUIDVal dd 0 ; Results of CPUID instruction OrigINT06 dd 0 ; Temp holding spot for INT06 vector ;----------------------------------------------------------------------------- ; String messages used for formatting the screen output ;----------------------------------------------------------------------------- ; 16-bit Value FLD/FIST FRSTOR/FIST FLD/FISTP FRSTOR/FISTP ; xxxxxxxxxxxxxxxxxx PASS PASS PASS PASS HeaderMsg16 db " 16-bit Value FLD/FIST FRSTOR/FIST FLD/FISTP FRSTOR/FISTP",0dh,0ah,24h HeaderMsg32 db " 32-bit Value FLD/FIST FRSTOR/FIST FLD/FISTP FRSTOR/FISTP",0dh,0ah,24h ValueBuf db " ",24h Spaces db " ",24h PassMsg db " PASS ",24h FailMsg db "**FAIL**",24h CRLFMsg db 0dh,0ah,24h PMMsg db "Running in protected mode (probably Windows). Results are most",0dh,0ah db "reliable while running in real mode (booting clean to DOS).",0dh,0ah db "I'll try running anyways.",0dh,0ah,24h CPUIDMsg db "Microprocessor ID: " FamilyString db "Unknown " db " Vendor String: " IDString db "Not Detected",0dh,0ah,24h Dan0411Failed db "*** Dan-0411 bug found. ***",0dh,0ah,24h Dan0411Passed db "Dan-0411 not found.",0dh,0ah,24h _DATA ENDS ;----------------------------------------------------------------------------- ; Beginning of main code segment ;----------------------------------------------------------------------------- _TEXT segment para public use16 'CODE' ASSUME CS:_TEXT, DS:_DATA, ES:_DATA, SS:STACK ;----------------------------------------------------------------------------- ; Code starts here ; * Set up stack ;----------------------------------------------------------------------------- FISTBUG proc far mov ax,seg STACK ; setup stack segment mov ss,ax mov sp,sizeof StackPtr xor ax,ax ; clear it pushf push ds ; save far return on stack push ax ;----------------------------------------------------------------------------- ; * Disable interrupts during this test ; * Set up data segments ;----------------------------------------------------------------------------- cli ; disable interrupts mov ax,seg _DATA ; get data segment mov ds,ax mov es,ax ifdef VERBOSE ;----------------------------------------------------------------------------- ; The purpoase of this section of code is to determine a few things about ; the target computer. I'll detect whether or not we're in protected mode ; (like running in a DOS-box of Windows) and print a message accordingly. ; Whether or not we're in protected mode shouldn't make any difference, as ; I'm not executing any priviledged instryctions. ; ; Also in this section, I check for the processor stepping information and ; vendor string. If present, I'll print this information to the screen. ;----------------------------------------------------------------------------- ; Test for protected mode ;----------------------------------------------------------------------------- smsw ax ; get lower bits of CR0 test al,1 ; in protected mode? jz @F ; nope PRINT_MSG PMMsg ; print protected mode message PRINT_MSG CRLFMsg ; print ;----------------------------------------------------------------------------- ; Install INT06 (Invalid opcode) exception handler ;----------------------------------------------------------------------------- @@: push es ; save mov ax,seg INTSEG mov es,ax mov ax,offset OurINT6 ; get pointer to our INT6 handler mov dx,cs ; get our code segment xchg ax,word ptr es:INT06 ; swap 'em xchg dx,word ptr es:INT06[2] ; swap vector mov word ptr OrigINT06,ax ; save original vector mov word ptr OrigINT06[2],dx; vector now saved pop es ; restore original segment ;----------------------------------------------------------------------------- ; Execute CPUID instruction and print results on string ; ; This is a real down-and-dirty way to detect CPUID. I've installed an ; invalid exception handler, and pointed DX to a return address in the case ; that an invalid opcode exception occurs. If we're an 80286, then the ; 'xor eax,eax' instruction will cause the invalid opcode fault. If we're ; running on a processor that doesn't support CPUID, the 'cpuid' instruction ; will cause the invalid opcode fault. In either case, if the fault occurs, ; execution continues beyond the processor detection code. At that point, ; the processor stepping information is printed anyways with a default ; response. If CPUID does work, then the default results are filled in with ; the appropriate CPUID return values. ;----------------------------------------------------------------------------- .586 mov dx,offset @NoCPUID ; set destination location xor eax,eax ; real dirty way to detect 80386+ cpuid ; try CPUID instruction mov dword ptr IDString[0],ebx mov dword ptr IDString[4],edx mov dword ptr IDString[8],ecx mov eax,1 ; do next level of CPUID cpuid ; get processor stepping mov CPUIDVal,eax ; save it mov si,offset CPUIDVal ; get source of CPUID stepping mov di,offset FamilyString ; get destination of string mov cx,4 ; # of bytes to convert call hex_string ; convert data @NoCPUID: .286 PRINT_MSG CPUIDMsg ; print CPUID message PRINT_MSG CRLFMsg ; print ;----------------------------------------------------------------------------- ; Restore invalid opcode interrupt handler ;----------------------------------------------------------------------------- push es ; save mov ax,seg INTSEG mov es,ax mov ax,word ptr OrigINT06[0]; get pointer to our INT6 handler mov dx,word ptr OrigINT06[2]; get our code segment xchg ax,word ptr es:INT06 ; swap 'em xchg dx,word ptr es:INT06[2] ; swap vector pop es ; restore original segment ;----------------------------------------------------------------------------- ; Print header message ;----------------------------------------------------------------------------- PRINT_MSG HeaderMsg16 ; print beginning header message endif ;----------------------------------------------------------------------------- ; Time to start test ;----------------------------------------------------------------------------- mov cx,N16 ; # of 16-bit operands to test xor si,si ; Initialize index pointer to operands @FistLoop16: ifdef VERBOSE ;----------------------------------------------------------------------------- ; The purpose of this section is printing the 80-bit hex operands to the ; screen. Each 80-bit operand is converted to ASCII and printed on the ; screen. ;----------------------------------------------------------------------------- push cx push si ;----------------------------------------------------------------------------- ; * Convert data to ASCII ; * Print value on screen ;----------------------------------------------------------------------------- lea si,Op16[si] ; get pointer to data mov di,offset ValueBuf ; get output buffer mov cx,sizeof Op16 ; get # of bytes to convert call hex_string PRINT_MSG ValueBuf ; print 10-byte hex value on screen pop si pop cx endif ; VERBOSE ;----------------------------------------------------------------------------- ; Start test ;----------------------------------------------------------------------------- call FistTest16 ; Check various form of executing ; FIST[P] and saving FSW ;----------------------------------------------------------------------------- ; This section tests for the results of each of the operand test cases. ; Each test case is checked for the FSW.IE flag set. A pass/fail message is ; printed if running in VERBOSE mode. Otherwise, results are collected in ; the 'Results' variable for processing at the end of the test. ;----------------------------------------------------------------------------- ; Check for results ;----------------------------------------------------------------------------- push cx ; save current count xor di,di mov cx,4 ; # of results to check ;----------------------------------------------------------------------------- ; Check for FSW.IE. If set, indicate results. If VERBOSE, print to the ; screen (macros handle verbose mode printing). Iterate through each test ; case until finished. ;----------------------------------------------------------------------------- Check16: test StatusWord[di],FSW_IE ; check for correct exception PRINT_PASS_FAIL jnz @F ; yes, behavior correct or Results,-1 ; set results to indicate failure @@: add di,2 ; point to next datum dec cx ; are we done yet? jz @F ; yes PRINT_MSG Spaces jmp Check16 @@: PRINT_MSG CRLFMsg ; print pop cx add si,sizeof Op16 loop @FistLoop16 ;----------------------------------------------------------------------------- ; Now done testing all 16-bit operands. ; Time to test 32-bit operands. ;----------------------------------------------------------------------------- ; Print header message ;----------------------------------------------------------------------------- PRINT_MSG CRLFMsg ; print PRINT_MSG HeaderMsg32 ; print header message for 32-bit test ;----------------------------------------------------------------------------- ; Put code here... ;----------------------------------------------------------------------------- mov cx,N32 ; # of 32-bit operands to test xor si,si ; Initialize index pointer to operands @FistLoop32: ifdef VERBOSE ;----------------------------------------------------------------------------- ; The purpose of this section is printing the 80-bit hex operands to the ; screen. Each 80-bit operand is converted to ASCII and printed on the ; screen. ;----------------------------------------------------------------------------- push cx push si ;----------------------------------------------------------------------------- ; * Convert data to ASCII ; * Print value on screen ;----------------------------------------------------------------------------- lea si,Op32[si] ; get pointer to data mov di,offset ValueBuf ; get output buffer mov cx,sizeof Op32 ; get # of bytes to convert call hex_string PRINT_MSG ValueBuf ; print 10-byte data on screen pop si pop cx endif ; VERBOSE ;----------------------------------------------------------------------------- ; Start test ;----------------------------------------------------------------------------- call FistTest32 ; Check various form of executing ; FIST[P] and saving FSW ;----------------------------------------------------------------------------- ; This section tests for the results of each of the operand test cases. ; Each test case is checked for the FSW.IE flag set. A pass/fail message is ; printed if running in VERBOSE mode. Otherwise, results are collected in ; the 'Results' variable for processing at the end of the test. ;----------------------------------------------------------------------------- ; Check for results ;----------------------------------------------------------------------------- push cx ; save current count xor di,di mov cx,4 ; # of results to check ;----------------------------------------------------------------------------- ; Check for FSW.IE. If set, indicate results. If VERBOSE, print to the ; screen (macros handle verbose mode printing). Iterate through each test ; case until finished. ;----------------------------------------------------------------------------- Check32: test StatusWord[di],FSW_IE ; check for correct exception PRINT_PASS_FAIL jnz @F ; yes, behavior correct or Results,-1 ; set results to indicate failure @@: add di,2 ; point to next datum dec cx ; are we done yet? jz @F ; yes PRINT_MSG Spaces jmp Check32 @@: PRINT_MSG CRLFMsg ; print pop cx add si,sizeof Op32 loop @FistLoop32 ;----------------------------------------------------------------------------- ; Now done testing all 32-bit operands. ; Now let's print the final results of the test. Does your processor have ; the 'Dan-0411' bug? If so, the results are printed here. ;----------------------------------------------------------------------------- mov ah,9 ; get function to print string mov dx,offset CRLFMsg ; get pointer to CRLF message int 21h mov dx,offset Dan0411Passed ; prepare to pass test Results,-1 ; anything fail? jz @F ; nope mov dx,offset Dan0411Failed ; get failed message @@: int 21h ;----------------------------------------------------------------------------- ; Terminate and return to DOS. ;----------------------------------------------------------------------------- iret ; return to DOS FISTBUG endp ;----------------------------------------------------------------------------- FistTest16 proc near ;----------------------------------------------------------------------------- ; FistTest16 checks four different ways of executing the FIST[P] instruction ; and storing the status word to memory. Before each sub-test is attempted, ; the floating point unit is re-initialized with the fninit instruction. ; The FPU re-initialization ensures that any pending ("sticky") floating point ; errors from the previous test will be cleared. This also guarantees that ; the each sub-tests is performed on a pristine FPU environment. ; ; The four test cases are as follows: ; 1) * Floating point load using FLD instruction ; * Floating point store using FIST instruction ; * Store status word using FNSTSW instruction. ; ; 2) * Floating point load using FLD instruction ; * Save floating point environment with FNSAVE instruction. This ; instruction has the side-effect of re-initializing the FPU state (a ; good thing). ; * Restore the FPU state with FRSTOR instruction. This ensures that ; the Dan-0411 error wasn't a byproduct of the FLD instruction. ; * Floating point store using FIST instruction ; * Store status word using FNSTSW instruction. ; ; 3) * Floating point load using FLD instruction ; * Floating point store using FISTP instruction ; * Store status word using FNSTSW instruction. ; ; 4) * Floating point load using FLD instruction ; * Save floating point environment with FNSAVE instruction. This ; instruction has the side-effect of re-initializing the FPU state (a ; good thing). ; * Restore the FPU state with FRSTOR instruction. This ensures that ; the Dan-0411 error wasn't a byproduct of the FLD instruction. ; * Floating point store using FISTP instruction ; * Store status word using FNSTSW instruction. ; ;----------------------------------------------------------------------------- ; Input: DS:SI = Index pointer to 80-bit floating point operand ; Output: StatusWord filled in with FPU status words for each test case. ;----------------------------------------------------------------------------- ; Restore pristine environment. ;----------------------------------------------------------------------------- xor di,di ; initialize pointer to results fninit ; initialize floating point unit ;----------------------------------------------------------------------------- ; 1) * Floating point load using FLD instruction ; * Floating point store using FIST instruction ; * Store status word using FNSTSW instruction. ;----------------------------------------------------------------------------- fld Op16[si] ; load a value fist D16 fnstsw StatusWord[di] ; save results ;----------------------------------------------------------------------------- ; Restore pristine environment. ;----------------------------------------------------------------------------- add di,2 ; point to next FSW results datum fninit ; initialize floating point unit ;----------------------------------------------------------------------------- ; 2) * Floating point load using FLD instruction ; * Save floating point environment with FNSAVE instruction. This ; instruction has the side-effect of re-initializing the FPU state (a ; good thing). ; * Restore the FPU state with FRSTOR instruction. This ensures that ; the Dan-0411 error wasn't a byproduct of the FLD instruction. ; * Floating point store using FIST instruction ; * Store status word using FNSTSW instruction. ;----------------------------------------------------------------------------- fld Op16[si] ; load a value fnsave FENV ; save a copy of environment ; does implicit fninit frstor FENV ; restore FPU environment fist D16 fnstsw StatusWord[di] ; save results ;----------------------------------------------------------------------------- ; Restore pristine environment. ;----------------------------------------------------------------------------- add di,2 ; point to next data value fninit ; initialize floating point unit ;----------------------------------------------------------------------------- ; 3) * Floating point load using FLD instruction ; * Floating point store using FISTP instruction ; * Store status word using FNSTSW instruction. ; ;----------------------------------------------------------------------------- fld Op16[si] ; load a value fistp D16 fnstsw StatusWord[di] ; save results ;----------------------------------------------------------------------------- ; Restore pristine environment. ;----------------------------------------------------------------------------- add di,2 ; point to next data value fninit ; initialize floating point unit ;----------------------------------------------------------------------------- ; 4) * Floating point load using FLD instruction ; * Save floating point environment with FNSAVE instruction. This ; instruction has the side-effect of re-initializing the FPU state (a ; good thing). ; * Restore the FPU state with FRSTOR instruction. This ensures that ; the Dan-0411 error wasn't a byproduct of the FLD instruction. ; * Floating point store using FISTP instruction ; * Store status word using FNSTSW instruction. ;----------------------------------------------------------------------------- fld Op16[si] ; load a value fnsave FENV ; save a copy of environment ; does implicit fninit frstor FENV ; restore FPU environment fistp D16 fnstsw StatusWord[di] ; save results ret FistTest16 endp ;----------------------------------------------------------------------------- FistTest32 proc near ;----------------------------------------------------------------------------- ; ; FistTest32 checks four different ways of executing the FIST[P] instruction ; and storing the status word to memory. Before each sub-test is attempted, ; the floating point unit is re-initialized with the fninit instruction. ; The FPU re-initialization ensures that any pending ("sticky") floating point ; errors from the previous test will be cleared. This also guarantees that ; the each sub-tests is performed on a pristine FPU environment. ; ; The four test cases are as follows: ; 1) * Floating point load using FLD instruction ; * Floating point store using FIST instruction ; * Store status word using FNSTSW instruction. ; ; 2) * Floating point load using FLD instruction ; * Save floating point environment with FNSAVE instruction. This ; instruction has the side-effect of re-initializing the FPU state (a ; good thing). ; * Restore the FPU state with FRSTOR instruction. This ensures that ; the Dan-0411 error wasn't a byproduct of the FLD instruction. ; * Floating point store using FIST instruction ; * Store status word using FNSTSW instruction. ; ; 3) * Floating point load using FLD instruction ; * Floating point store using FISTP instruction ; * Store status word using FNSTSW instruction. ; ; 4) * Floating point load using FLD instruction ; * Save floating point environment with FNSAVE instruction. This ; instruction has the side-effect of re-initializing the FPU state (a ; good thing). ; * Restore the FPU state with FRSTOR instruction. This ensures that ; the Dan-0411 error wasn't a byproduct of the FLD instruction. ; * Floating point store using FISTP instruction ; * Store status word using FNSTSW instruction. ; ;----------------------------------------------------------------------------- ; Input: DS:SI = Index pointer to 80-bit floating point operand ; Output: StatusWord filled in with FPU status words for each test case. ;----------------------------------------------------------------------------- ; Restore pristine environment. ;----------------------------------------------------------------------------- xor di,di ; initialize pointer to results fninit ; initialize floating point unit ;----------------------------------------------------------------------------- ; 1) * Floating point load using FLD instruction ; * Floating point store using FIST instruction ; * Store status word using FNSTSW instruction. ;----------------------------------------------------------------------------- fld Op32[si] ; load a value fist D32 fnstsw StatusWord[di] ; save results ;----------------------------------------------------------------------------- ; Restore pristine environment. ;----------------------------------------------------------------------------- add di,2 ; point to next data value fninit ; initialize floating point unit ;----------------------------------------------------------------------------- ; 2) * Floating point load using FLD instruction ; * Save floating point environment with FNSAVE instruction. This ; instruction has the side-effect of re-initializing the FPU state (a ; good thing). ; * Restore the FPU state with FRSTOR instruction. This ensures that ; the Dan-0411 error wasn't a byproduct of the FLD instruction. ; * Floating point store using FIST instruction ; * Store status word using FNSTSW instruction. ;----------------------------------------------------------------------------- fld Op32[si] ; load a value fnsave FENV ; save a copy of environment ; does implicit fninit frstor FENV ; restore FPU environment fist D32 fnstsw StatusWord[di] ; save results ;----------------------------------------------------------------------------- ; Restore pristine environment. ;----------------------------------------------------------------------------- add di,2 ; point to next data value fninit ; initialize floating point unit ;----------------------------------------------------------------------------- ; 3) * Floating point load using FLD instruction ; * Floating point store using FISTP instruction ; * Store status word using FNSTSW instruction. ; ;----------------------------------------------------------------------------- fld Op32[si] ; load a value fistp D32 fnstsw StatusWord[di] ; save results ;----------------------------------------------------------------------------- ; Restore pristine environment. ;----------------------------------------------------------------------------- add di,2 ; point to next data value fninit ; initialize floating point unit ;----------------------------------------------------------------------------- ; 4) * Floating point load using FLD instruction ; * Save floating point environment with FNSAVE instruction. This ; instruction has the side-effect of re-initializing the FPU state (a ; good thing). ; * Restore the FPU state with FRSTOR instruction. This ensures that ; the Dan-0411 error wasn't a byproduct of the FLD instruction. ; * Floating point store using FISTP instruction ; * Store status word using FNSTSW instruction. ;----------------------------------------------------------------------------- fld Op32[si] ; load a value fnsave FENV ; save a copy of environment ; does implicit fninit frstor FENV ; restore FPU environment fistp D32 fnstsw StatusWord[di] ; save results ret FistTest32 endp ;----------------------------------------------------------------------------- ; HEX_STRING: Convert a string of 8-bit hex numbers to ASCII. ; Input: DS:SI = Pointer to hex data ; ES:DI = Buffer to get output ; CX = # of bytes to convert ; Output: ES:DI = Filled in w/ ASCII hex# ;----------------------------------------------------------------------------- Hex_String proc near ;----------------------------------------------------------------------------- jcxz @Hex_str_exit ; go split push ax ; [bp][0ah] push cx ; [bp][8] push dx ; [bp][6] push si ; [bp][4] push di ; [bp][2] push bp ; [bp] mov bp,sp add si,cx @@: dec si mov al,ds:[si] ; get hex digit mov dl,al mov cl,4 ; shift count rol dl,cl mov al,dl ; save it and al,0fh ; keep low nibble daa add al,0f0h adc al,40h ; here is the ASCII stosb ; save it mov cl,4 ; shift count rol dl,cl mov al,dl ; save it and al,0fh ; keep low nibble daa add al,0f0h adc al,40h ; here is the ASCII stosb ; save it dec word ptr [bp][8] ; are we done yet? jnz @B pop bp pop di pop si pop dx pop cx pop ax @Hex_str_exit: ret Hex_String endp ;----------------------------------------------------------------------------- ; This is a real down-and-dirty invalid opcode exception handler. All this ; handler does, is take the value in DX and use it as the return address. ;----------------------------------------------------------------------------- ; Input: DX = Return address ; Output: None ;----------------------------------------------------------------------------- OurINT6 proc far pop ax ; get IP from stack mov ax,dx ; point to return address push ax ; save it iret ; go split OurINT6 endp _TEXT ENDS STACK segment para public 'STACK' ;----------------------------------------------------------------------------- ; Stack segment ;----------------------------------------------------------------------------- StackPtr db 400h dup (?) STACK ends end FISTBUG