;***************************************************************************** TITLE debugger.inc ; This include file is designed to be included with assembly programs in order ; to facilitate debugging your code. It should not be included in the final ; version of your program. You must also include "io32.inc" in your source ; file since this include file makes calls to "io32.inc" ; Make sure "debugger.inc" is in your path, or it will not be found. It is ; recommended that it be located in your working directory. ; ; The MACROs provided are: ; Pause (waits for a key to be pressed, no parameters used) ; WriteSpace (prints number of spaces requested, i.e. WriteSpace 4) ; NextLine (advances cursor to next line, no parameters used) ; ShowRegister EAX,EAX (displays contents of a single register, ; i.e. 'ShowRegister EAX,EAX') ; ShowFlag (displays contents of a single flag, i.e. ShowFlag ZF,6) ; ShowString (displays an ASCII string, i.e. ShowString MyString) ; ShowDwordInt (display a dword (base 10), i.e. ShowDwordInt MyDwordNum) ; ShowDwordHex (display a dword (base 16), i.e. ShowDwordHex MyDwordNum) ; ShowReal4 (display a REAL4 floating point, i.e. ShowReal4 MyReal4Num) ; ; The PROCedures provided are: ; DumpRegs (display 32 bit registers and flags, no parameters used) ; DumpMem (display memory starting at specified location. ; Requires: ESI = starting offset, ECX = number of units, ; EBX = unit size (1=byte, 2=word, or 4=doubleword)) ; DumpFPU (display the contents of the floating point unit ; i.e. 'call DumpFPU' ; WriteHex (write the hexadecimal number in EAX to console) ; HexByte (write the hexadecimal number in AL to console) ; ; All Macros and Procedures are Non-Destructive (registers are protected). ; Some Macros and Procedures require input parameters. See the Macro and ; Procedure headers for additional details. ; ; Written by Joe Toone 2/28/2008 5:10:28 PM ; Modified:4/2/2008 9:56:57 AM, added ShowDwordHex, ShowReal4, and DumpFPU ;***************************************************************************** .NOLIST ;==== MACRO Deffinitions ===================================================== ;----------------------------------------------------------------------------- Pause MACRO LOCAL keyIn,prompt ; ; Receives: nothing ; Process: Prompt user to enter a key and wait for keypress. Uses local data ; items "keyIn" and "prompt" which are not visable outside of this ; macro. ; Returns: nothing ;----------------------------------------------------------------------------- .data keyIn BYTE ? ;private data prompt BYTE "Press [Enter] to continue.",10,13,0 ; .code pushad ;preserve all registers pushfd ; lea esi,prompt ;point to local prompt string call PrintString ;call PrintString from io32.inc lea esi,keyIn ;point to local byte storage call GetChar ;call GetChar from io32.inc popfd ;restore all registers popad ENDM ;----------------------------------------------------------------------------- WriteSpace MACRO spaces LOCAL oneSpace,nextSpc ; ; Receives: The number of spaces to print. ; Sample invocation: "WriteSpace 4" ; Process: Print the requested number of spaces on the present line. Uses ; local "oneSpace" data and local "nextSpc" label which are not ; visable outside of this macro. ; Returns: nothing ;----------------------------------------------------------------------------- .data oneSpace BYTE " ", 0 ;private data .code pushad ;preserve all registers pushfd ; mov ecx,spaces ;set loop counter to # spaces requested nextSpc:lea esi, oneSpace ;point to local string call PrintString ; and go print it loop nextSpc ;loop for next space popfd ;restore all registers popad ENDM ;----------------------------------------------------------------------------- NextLine MACRO LOCAL newLineStr ; ; Receives: nothing ; Sample invocation: "NextLine" ; Process: Advance the cursor to the next line. Uses local "newLineStr" ; which is not visable outside of this macro. ; Returns: nothing ;----------------------------------------------------------------------------- .data newLineStr BYTE 0ah, 0dh, 0 ;private data .code pushad ;preserve all registers pushfd ; lea esi, newLineStr ;point to local string call PrintString ; and go print it popfd ;restore all registers popad ENDM ;----------------------------------------------------------------------------- ShowRegister MACRO regName, regValue LOCAL tempStr, hexStr ; ; Receives: Name of the register to display as a string and the actual ; register contents to display. ; Sample invocation: "ShowRegister EAX,EAX" ; Process: Display a register's name (string) and contents. Uses local ; "tempStr" and "hexStr" which are not visable outside this macro. ; Returns: nothing ;----------------------------------------------------------------------------- .data tempStr BYTE " ®Name=",0 ;private data hexStr BYTE 80d dup(0) ; .code pushad ;preserve all registers pushfd ; ;Display the register name by lea esi,tempStr ; pointing to the local string call PrintString ; and printing it ;Display the register contents by mov eax,regValue ; copying the register value to eax lea edi,hexStr ; and pointing to receiving string, call WriteHex ; then call WriteHex to convert, lea esi,hexStr ; point to local string again call PrintString ; and print it. popfd ;restore all registers popad ENDM ;----------------------------------------------------------------------------- ShowFlag MACRO flagName, shiftCount LOCAL flagStr, flagVal, L1 ; ; Receives: Name string of the flag and bit location of the flag ; Sample invocation: "ShowFlag ZF,6" ; See the comment at the end of the macro for more details. ; ; Process: Display a single CPU flag value preceded by the name supplied ; by the caller. The global "eFlags" (DWORD) variable must be set ; before invoking this macro. "pushfd" and then "pop eFlags" ; Returns: nothing ;----------------------------------------------------------------------------- .data flagStr BYTE " &flagName=" ;private data flagVal BYTE ?,0 ; .code pushad ;preserve all registers pushfd ; mov eax,eFlags ;retrieve the flags mov flagVal,'1' ;set output variable default value to 1 shr eax,shiftCount+1 ;shift desired bit into carry flag jc L1 ;if the carry is set then the flag is set mov flagVal,'0' ; else move 0 into output variable L1: lea esi,flagStr ;point to the output string call PrintString ; and print the flag name followed popfd ; immediately by its value. popad ;restore all registers ENDM ; Flags Register Bit Positions ; ; The flags register is a 32-bit register named EFLAGS. The low-order 16 bits ; of EFLAGS is named FLAGS for compatibility with older 8086 and 80286 code. ; There are three basic groups of flags, status flags, control flags and the ; system flags. The flags are as follows: ; ; 16 15 0 ; ---- ---- ---- ---- ---X CF Carry Flag ; ---- ---- ---- ---- -X-- PF Parity Flag ; ---- ---- ---- ---X ---- AF Auxiliary Carry ; ---- ---- ---- -X-- ---- ZF Zero Flag ; ---- ---- ---- X--- ---- SF Sign Flag ; ---- ---- ---X ---- ---- TF Trap Flag ; ---- ---- --X- ---- ---- IF Interrupt Flag ; ---- ---- -X-- ---- ---- DF Direction Flag ; ---- ---- X--- ---- ---- OF Overflow Flag ; ---- --XX ---- ---- ---- PL I/O Privilege Level ; ---- -X-- ---- ---- ---- NT Nested Task Flag ; ---X ---- ---- ---- ---- RF Resume Flag ; --X- ---- ---- ---- ---- v8 Virtual 8086 MODE ; ; The remaining bits are reserved for future Intel use. A flag is considered ; cleared when it is zero, set when it is 1. ;----------------------------------------------------------------------------- ShowString MACRO localStr LOCAL StrName ; ; Receives: The name of any data segment string to be displayed. ; Sample invocation: "ShowString MyString" ; Process: Displays a single NULL (0) terminated string. Uses local "StrName" ; to display the string name before the actual string is printed. ; Returns: nothing ;----------------------------------------------------------------------------- .data StrName BYTE 0ah,0dh,"&localStr=",0 ;private data .code pushad ;preserve all registers pushfd ; lea esi, StrName ;point to the string name string call PrintString ; and print it lea esi, LocalStr ;point to the actual string call PrintString ; and print it NextLine ;advance the cursor to the next line popfd ;restore all registers popad ENDM ;----------------------------------------------------------------------------- ShowDwordInt MACRO localInt LOCAL IntName,intStr ; ; Receives: The name of a dword integer in the data segment ; Sample invocation: "ShowDwordInt MyBigNum" ; Process: Displays the named integer. Uses local "intName" to print the ; name of the integer and local "intStr" to display the ASCII digits ; Returns: nothing ;----------------------------------------------------------------------------- .data intName BYTE 0ah,0dh,"&localInt=",0 ;private data intStr BYTE 10d dup(0),0ah,0dh,0 ; .code pushad ;preserve all registers pushfd ; lea esi, intName ;point to the string name call PrintString ; and go print it lea esi,localInt ;point to the number lea edi,intStr ;point to output string call Int2Ascii ; go convert int to ASCII lea esi, intStr ;point to the output string call PrintString ; and go print it NextLine ;advance cursor to next line popfd ;restore all registers popad ENDM ;----------------------------------------------------------------------------- ShowDwordHex MACRO localHex LOCAL hexName ; ; Receives: The name of a dword integer in the data segment ; Sample invocation: "ShowDwordInt MyBigNum" ; Process: Displays the named integer. Uses local "intName" to print the ; name of the integer and local "intStr" to display the ASCII digits ; Returns: nothing ;----------------------------------------------------------------------------- .data hexName BYTE 0ah,0dh,"&localHex=",0 ;private data .code pushad ;preserve all registers pushfd ; lea esi, hexName ;point to the string name call PrintString ; and go print it mov eax,localHex ;copy the number to EAX call WriteHex ; go print the hexadecimal value NextLine ;advance cursor to next line popfd ;restore all registers popad ENDM ;----------------------------------------------------------------------------- ShowReal4 MACRO localReal4 LOCAL real4Name,real4Str ; ; Receives: The name of a dword integer in the data segment ; Sample invocation: "ShowDwordInt MyBigNum" ; Process: Displays the named integer. Uses local "intName" to print the ; name of the integer and local "intStr" to display the ASCII digits ; Returns: nothing ;----------------------------------------------------------------------------- .data real4Name BYTE 0ah,0dh,"&localReal4=",0 ;private data real4Str BYTE 12d dup(0),0ah,0dh,0 ; .code pushad ;preserve all registers pushfd ; lea esi, real4Name ;point to the string name call PrintString ; and go print it lea esi,localReal4 ;point to the real4 number lea edi,real4Str ;point to output string call fp2adbg ; go convert real4 to ASCII lea esi, real4Str ;point to the output string call PrintString ; and go print it NextLine ;advance cursor to next line popfd ;restore all registers popad ENDM ;==== PROC Deffinitions ====================================================== ;----------------------------------------------------------------------------- DumpRegs PROC NEAR32 ; ; Receives: nothing ; Sample call: "call DumpRegs" ; Process: Displays EAX, EBX, ECX, EDX, ESI, EDI, EBP, ESP, EIP, ; and EFlags in hexadecimal. Also displays the Carry, Parity, ; Auxiliary Carry (4 bits), Zero, Sign, Direction, and Overflow flags. ; The Auxiliary Carry is set to one when there is a carry from the ; low order four bits of an eight bit register to the high order four ; bits of that same eight bit register. Adding one to 0Fh in AL sets ; the Auxiliary Carry. ; Returns: nothing ; ; Warning: do not create any local variables or stack parameters inside of this ; procedure because they will alter the EBP register. ;----------------------------------------------------------------------------- .data saveIP DWORD ? ;these data items are added to the global saveESP DWORD ? ; data segment eFlags DWORD ? ;do not use these identifiers in your data .code ; segment or you will get an error pop saveIP ;get current EIP from stack mov saveESP,esp ;save ESP's value at entry push saveIP ;restore EIP on stack pushad ;preserve all registers pushfd ; pushfd ;push flags again, and pop eFlags ; save them in a variable ShowRegister EAX,EAX ;show the general purpose registers ShowRegister EBX,EBX ; ShowRegister ECX,ECX ; ShowRegister EDX,EDX ; NextLine ;move cursor to the next line ShowRegister ESI,ESI ;show the pointer registers ShowRegister EDI,EDI ; ShowRegister EBP,EBP ; mov eax,saveESP ;copy saved ESP to EAX so we can print it ShowRegister ESP,EAX ; NextLine ;move cursor to the next line mov eax,saveIP ;copy saved EIP to EAX so we can print it ShowRegister EIP,EAX ; mov eax,eFlags ;copy save eFlags to EAX so we can print it ShowRegister EFL,EAX ; ShowFlag CF,0 ;Show the flags (using the eFlags variable) ShowFlag PF,2 ; ShowFlag AF,4 ; ShowFlag ZF,6 ; ShowFlag SF,7 ; ShowFlag DF,10 ; ShowFlag OF,11 ; NextLine ;advance the cursor two lines down NextLine ; popfd ;restore all registers popad ; ret DumpRegs ENDP ;----------------------------------------------------------------------------- DumpMem PROC NEAR32 LOCAL unitSize:dword, byteCount:word ; ; Receives: ESI = starting offset (memory address in the data segment), ; EBX = unit size (1=byte, 2=word, or 4=doubleword), and ; ECX = number of units to display. ; Sample call: lea esi,MyDataItems ;pointer to data starting point ; mov ebx,1d ;print as bytes ; mov ecx,256d ;number of bytes to display ; call DumpMem ;call the procedure ; Process: Writes a range of memory to standard output in hexadecimal and ; ASCII if the unit size is a byte. Uses local "unitSize" and ; "byteCount" as well as global data "oneSpace", "dumpPrompt", ; "dashLine", and "asciiOut". Labels beginning with "@@" are local. ; Returns: nothing ;----------------------------------------------------------------------------- .data ;do not use these identifiers in your oneSpace BYTE ' ',0 ; code since these data items are added dumpPrompt BYTE 13,10,"Dump of offset ",0 ;to the global data segment dashLine BYTE "-------------------------------",13,10,0 asciiOut BYTE 16d dup(0),0 .code pushad ;preserve all registers pushfd ; mov eax,esi ;get memory offset to dump push esi ;preserve starting memory address lea esi,dumpPrompt ;point to dumpPrompt call PrintString ; and go print it call WriteHex ;print the starting memory address NextLine ;advance cursor to next line lea esi,dashLine ;point to dashLine call PrintString ; and go print it pop esi ;restore starting memory address mov byteCount,0 ;initialize counter for bytes to be printed mov unitSize,ebx ;set user requested unit size cmp ebx,4 ;if size if 4 bytes je @@L1 ; then print as dwords cmp ebx,2 ;if size is 2 bytes je @@L2 ; then print as words lea edi,asciiOut ;else point to ascii output string jmp @@L3 ; and print as bytes @@L1: ;32-bit doubleword output mov eax,[esi] ;copy a dword to print from memory call WriteHex ; and go print it WriteSpace 2 ; then print 2 blanks add esi,ebx ;move to next dword to print Loop @@L1 ; and loop to print it jmp @@L4 ;jmp to end of procedure @@L2: ;16-bit word output mov ax,[esi] ;copy a word from memory ror ax,8 ;rotate 8 bits right placing high byte in AL call HexByte ; then go print the byte in AL ror ax,8 ;rotate 8 bits right placing low byte in AL call HexByte ; then go print the byte in AL WriteSpace 1 ;print 1 space between the words add esi,unitSize ;move to next word to print Loop @@L2 ; and loop to print it jmp @@L4 ;jmp to end of procedure @@L3: ;8-bit byte output, 16 bytes per line mov al,[esi] ;move integer value to al .if al > 31d ;replace control characters with '.' mov [edi],al ;if value is > 31, store the ASCII value .else ;else mov [edi],byte ptr '.' ; store a '.' as ASCII to be printed .endif ; inc edi ;move to next character storage location call HexByte ;go print HEX value of the byte inc byteCount ; and count the byte printed WriteSpace 1 ; print one space inc esi ;move to next byte mov dx,0 ;if( byteCount mod 16 == 0 ) call Crlf mov ax,byteCount ; mov bx,16d ; div bx ; cmp dx,0 ; jne @@L3B ;remainder not zero, so keep looping push esi ;else 16 have been printed, so save esi lea esi,asciiOut ;point to the ASCII string call PrintString ; and go print it pop esi ;get esi back NextLine ;move cursor to next line lea edi,asciiOut ;point to front of ascii output string @@L3B: Loop @@L3 ;loop for next line of output @@L4: NextLine ;move cursor to next line popfd ;restore all registers popad ; ret DumpMem ENDP ;----------------------------------------------------------------------------- WriteHex PROC NEAR32 ; ; Receives: an unsigned 32-bit hexadecimal number in EAX ; Sample call: mov eax,MyHexNum ; call WriteHex ; Process: Writes hexadecimal number in EAX to standard output. Uses global ; "DISPLAY_SIZE" and "bufferLH" and local label beginning with "@@". ; Returns: nothing ;----------------------------------------------------------------------------- ;global declarations DISPLAY_SIZE = 8 ;total number of digits to display .data bufferLH BYTE DISPLAY_SIZE dup(?),0 ;global data "bufferLH" .code pushad ;preserve all registers pushfd ; mov ecx,0 ;initalize digit counter mov edi,offset bufferLH ;point to start of output string buffer add edi,(DISPLAY_SIZE - 1) ;move pointer to end of string buffer mov ebx,16d ;set divisor to 16d (hexadecimal base) @@WH1: mov edx,0 ;set high order dword in dividend to zero div ebx ;divide EAX by the radix (16) xchg eax,edx ;swap quotient, remainder or eax,0030h ;Convert Hex to Ascii by adding 30h cmp eax,39h ;if (eax <= 39h) then jle @@storeIt ; go store the ascii digit add eax,07h ;else the value is alpha, so convert @@storeIt: ; and then store the A - F character mov [edi],al ;store the ascii digit or character dec edi ;back up in buffer for next character xchg eax,edx ;swap quotient and remainder inc ecx ;increment digit count or eax,eax ;is quotient = 0? (sets or clears zero flag) jnz @@WH1 ; no, then divide again ;Insert leading zeros mov ax,DISPLAY_SIZE ;copy number bytes to display into AX sub ax,cx ;if (characters == display size) jz @@WH3 ; display now since leading zeros not required movzx ecx,ax ;else ECX = number of leading zeros to insert @@WH2: mov byte ptr [edi],'0' ; insert a zero dec edi ; back up string pointer loop @@WH2 ; continue looping to add leading zeros ;Display the digits @@WH3: lea esi,bufferLH ;point to the output buffer call PrintString ; and print it popfd ;restore all registers popad ; ret WriteHex ENDP ;----------------------------------------------------------------------------- HexByte PROC NEAR32 ; ; Receives: a hexadecimal number in AL register ; Sample call: mov al,0abh ; call HexByte ; Process: Display the byte in AL in hexadecimal. Uses global data items ; "HBbuffer" and "xtable". ; Returns: nothing ;----------------------------------------------------------------------------- .data ;global data items HBbuffer BYTE 0,0,0 ;three byte buffer for hex string output xtable BYTE "0123456789ABCDEF",0 ;lookup table for hex alpha characters .code pushad ;preserve all registers pushfd ; mov dl,al ;copy hex number to DL rol dl,4 ;swap places with the high and low nibbles mov al,dl ;copy hex number back to AL and al,0Fh ;mask out high order nibble mov ebx,offset xtable ;point hex ASCII table xlat ;copy character from hex table into AL mov HBbuffer,al ;save first character to the string buffer rol dl,4 ;swap places with the high and low nibbles mov al,dl ;copy hex number back to AL and al,0Fh ;mask out the high order nibble xlat ;copy character from hex table into AL mov [HBbuffer+1],al ;save second character to the string buffer mov [HBbuffer+2],0 ;copy null byte to the string buffer lea esi,HBbuffer ;point to the string buffer call PrintString ; and go print it popfd ;restore all registers popad ; ret HexByte ENDP ;----------------------------------------------------------------------------- DumpFPU PROC NEAR32 ; ; Receives: nothing ; Sample call: "call DumpFPU" ; Process: Displays all eight FPU stack entries (st(0) - st(7)) ; All registers and the FPU are preserved. ; Returns: nothing ;----------------------------------------------------------------------------- .data ;These data items are added to the global RealFP REAL4 ? ; data items, so you may not use them AsciiFP BYTE MAXSTR dup(0) ;used to store input string of digits STout BYTE 10,13,' ST(?) = ',0 ;used for output string SaveFPU BYTE 108d dup(?) ;used to save state of FPU .code pushad ;preserve all registers pushfd ; mov cl,30h ;set cl to starting ascii index value lea ebx,STout ;point to output string @@LoopTop: ;private label for top of loop cmp cl,38h ;have we printed all elements? jz @@Done ; yes, then exit mov [ebx+7],cl ; and store the index lea esi,STout ;point to ST string call PrintString ; and print it fst RealFP ;store fpu entry into data variable fincstp ;increment the stack pointer for next ; value. Original value is preserved lea esi,RealFP ;point to the real value lea edi,AsciiFP ;point to string storage call fp2adbg ; and go convert real to string lea esi,AsciiFP ;point to ascii string call PrintString ; and print it inc cl ;move to next stack position jmp @@LoopTop ; and keep looping @@Done: NextLine popfd ;restore all registers popad ret DumpFPU ENDP ;------------------------------------------------------------------------------ fp2adbg PROC NEAR32 ; Receives: ESI pointing to FP value to convert and EDI pointing to the output ; string destination. ; Process : Build ASCII string with format [blank/-]d.dddddE[+/-]dd and store ; the string in the data segment. (Output is always 12 characters.) ; No registers are changed and the flags are not affected and the ; FPU is preserved. ; Return : Nothing ;------------------------------------------------------------------------------ C3Dbg equ 0100000000000000b ;Condition codes which are checked C2Dbg equ 0000010000000000b ; in the FPU Status register C0Dbg equ 0000000100000000b ;These are added to global equates .data oneDbg REAL4 1.0 ;these data items are added to the global tenDbg REAL4 10.0 ; data segment roundDbg REAL4 0.000005 ;do not use these identifiers in your data byteTenDbg BYTE 10 ; segment or you will get an error pointDbg BYTE ? minusDbg BYTE ? digitDbg WORD ? exponentDbg WORD ? controlWdDbg WORD ? .code fsave SaveFPU ;preserve the FPU state pushad ;Save the contents of all registers pushfd ; fstcw controlWdDbg ; get control word push controlWdDbg ; save control word or controlWdDbg, 0000110000000000b fldcw controlWdDbg ; set control to chop fld REAL4 PTR [esi] ; load ST with the FP value mov exponentDbg, 0 ; exponent := 0 ftst ; value >= 0? fstsw ax ; status word to AX and ax, C0Dbg ; check C0 jnz @@elseNeg ; skip if set (value negative) mov al,' ' ; blank for positive stosb ; write the blank to the string jmp @@NotNeg ; jump over negative sign @@elseNeg:mov al,'-' ; minus for negative stosb ; write the minus to the string fchs ; convert number to positive @@NotNeg: mov exponentDbg, 0 ; exponent := 0 ftst ; value = 0? fstsw ax ; status word to AX and ax, C3Dbg ; check C3 jne @@endifZero ; skip if zero fcom tenDbg ; value > 10? fstsw ax ; status word to AX and ax, C3Dbg or C2Dbg or C0Dbg ; check for all C3=C2=C0=0 jnz @@elseLess ; skip if value not > 10 @@untilLess: inc exponentDbg ; add 1 to exponent fdiv tenDbg ; value := value/10 fcom tenDbg ; value < 10 fstsw ax ; status word to AX and ax, C0Dbg ; check C0 jz @@untilLess ; continue until value < 10 jmp @@endifBigger ; exit if @@elseLess: @@whileLess: fcom oneDbg ; value < 1 fstsw ax ; status word to AX and ax, C0Dbg ; check C0 jz @@endwhileLess ; exit if not less fmul tenDbg ; value := 10*value dec exponentDbg ; subtract 1 from exponent jmp @@whileLess ; continue while value < 1 @@endwhileLess: @@endifBigger: @@endifZero: fadd roundDbg ; add rounding value fcom tenDbg ; value > 10? fstsw ax ; status word to AX and ax, C3Dbg or C2Dbg or C0Dbg ; C3=C2=C0=0? (value > 10?) jnz @@endifOver ; skip if not fdiv tenDbg ; value := value/10 inc exponentDbg ; add 1 to exponent @@endifOver: ; at this point 1.0 <= value < 10.0 fist digitDbg ; store integer part mov bx, digitDbg ; copy integer to BX or bx, 30h ; convert digit to character mov al,bl ; copy character to AL for writing stosb ; write the character to the string mov al,'.' ; load AL with decimal point stosb ; write decimal point to string mov ecx,5 ; count of remaining digits @@forDigit: fisub digitDbg ; subtract integer part fmul tenDbg ; multiply by 10 fist digitDbg ; store integer part mov bx, digitDbg ; copy integer to BX or bx, 30h ; convert digit to character mov al,bl ; copy character to write stosb ; write the character to the string loop @@forDigit ; repeat 5 times mov al,'E' ; exponent indicator stosb ; write the character to the string mov ax, exponentDbg ; get exponent push ax ; save the exponenet cmp ax, 0 ; exponent >= 0 ? jnge @@NegExp mov al,'+' ; non-negative exponent stosb ; write the character to the string pop ax ; restore the exponent if not negative jmp @@endifNegExp @@NegExp: mov al,'-' ; negative exponent stosb ; write the character to the string pop ax ; restore the exponent if negative neg ax ; change exponent to positive @@endifNegExp: div byteTenDbg ; convert exponent to 2 digits or ax, 3030h ; convert both digits to ASCII stosb ; write AL character to the string mov al,ah ; copy low order character to DL stosb ; write the character to the string xor al,al ; zero the AL register stosb ; write NULL to terminate string pop controlWdDbg ; restore control word fldcw controlWdDbg ; load the control word back to FPU popfd ;Restore the registers popad ; frstor SaveFPU ;restore the FPU state ret ;Return to calling procedure fp2adbg ENDP .LIST