News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Converting hexadecimal as a string !

Started by LL, October 25, 2005, 11:31:28 AM

Previous topic - Next topic

LL

Hi to everyone,

I think I understand how to convert "Hex" to "Dec". However, I can only do this outside a program (basicaly, by hand and calculator). For example, I wanted to learn how to use "GetLocalTime". It sends the result in a pre-defined buffer. The result I got is displayed below:   

0012FBB4  |   000A07D5 = (000Ah = 10 or wMonth) & (07D5h = 2005 or wYear)
0012FBB8  |   00180001 = (0018h = 24 or wDay) & (0001h = 1 or wDayOfWeek)
0012FBBC  |   002F000F = (002Fh = 47 or wMinute) & (000Fh = 15 or wHour)
0012FBC0  |   032C0018 = (032Ch = 812 or wMilliseconds) & (0018h = 24 or wSecond

Converting, for ex. 07D5h to 2005D, (this is what I did to convert the value in order to make sure I understood what was a "Hex" value:    0 * 4096 = 0
            7 *  256 = 1792
            D *   16  =  208
            5 *     1 =     5      ( 0 + 1792 + 208 + 5 = 2005D, or the year 2005 ).  The calculator could of converted the "Hex" value for me, but I just wanted to make sure I understood what I was learnig.)  I'm one of those newbie).

So, If I understand this correctly: I ran my small program on Monday, the 24 of October 2005 at 15:47:24. I'll ingnore the milliseconds.
My problem is "How do I get a program to convert: for ex. 07D5h to a string value that I can display afterward in a window ?" I've search the internet with: "displaying hexadecimal", "converting hexadecimal", etc.. All of which explains how to by hand. Is there a place that I can get an example or a tutorial on how to display such a value.  Converting "Hex/Dec" is, in my opinion, important.  But getting your program to do this for you is not an easy task. Can anyone Help ?

LL

LL

Hi again,

I did an extra search within The MASM Forum, under GetLocalTime.  I found what I was looking for in The Campus, posted by "six L". I learned of two extra Functions:  GetDateFormat & GetTimeFormat. So this is the result I was looking for:

DATA SECTION
MsgCaption      db "L'example par moi",0
MsgBoxTex   db "Le xx / xx / xxxx",0
STM      db 100 dup 0
DATEFORMAT   db " yyyy:MM:dd ",0
BUFFER      db 100 dup 0

CODE SECTION
start:
   PUSH ADDR STM
   CALL GetLocalTime
   PUSH 100D
   PUSH ADDR BUFFER
   PUSH ADDR DATEFORMAT
   PUSH ADDR STM
   PUSH 0
   PUSH 0400h
   call GetDateFormatA

   XOR AX,AX
   MOV AX,W[BUFFER+9]
   MOV W[MsgBoxTex+3],AX      ;copy "wDay" part in my "MsgBoxTex"
   MOV AX,W[BUFFER+6]
   MOV W[MsgBoxTex+8],AX      ;copy "wMonth" part in my "MsgBoxTex"
   XOR EAX,EAX
   MOV EAX,D[BUFFER+1]
   MOV D[MsgBoxTex+13D],EAX                   ;copy "wYear" part in my "MsgBoxTex"

   PUSH   0         ;MB_OK
   PUSH   ADDR MsgCaption      ;ADDR MsgCaption
   PUSH   ADDR MsgBoxTex      ;ADDR MsgBoxTex
   PUSH   0         ;NULL
   CALL   MessageBoxA      ;invoke MessageBox

   PUSH 0
   CALL ExitProcess

LL

redskull

Heres my way:  pass it a DWORD value with a hex value, and it displays a message box with the decimal.  If anybody has a better way, or suggestions to modify this, let me know.

PS, you have to zero out the string between calls, or else the higher bytes won't get overwritten


Write_Decimal proc DecimalByte:DWORD
xor ecx, ecx
mov eax, DecimalByte
mov esi, 10
.WHILE (eax > 0)
   XOR edx, edx
   div esi
   push edx
   inc ecx
.ENDW
.WHILE (ecx != 0)
   POP edx
   add dx, 30h
   cmp dx, 03ah
   jl printbyte
   add dx, 07h
  printbyte:
   lea ebx, DecimalString
   add ebx, 10
   sub ebx, ecx
   mov [ebx], dl
   dec ecx
.ENDW
   invoke MessageBox, 0, addr DecimalString, addr MsgCap,0
ret
Write_Decimal endp


to explain it a little bit; by doing integer division on a number by 10, you basically move the right handmost digit into dx and shift the other to the right.  then it pushes them onto the stack, and pops them back off so there in the right order.  Then it sticks them in a string variable, right to left, and displays it.

PS. I forgot to mention, when you declare DecimalString, declare it as DB 10 DUP ("0"), 0 or else it won't display anything

PPS - this method came out of Peter Nortons Assembly Language book for the IBM PC; the orginal and still the best
Strange women, lying in ponds, distributing swords, is no basis for a system of government

redskull

i was just looking at this and realized you don't even need to push them onto the stack since it gets stored right-left.  instead of the PUSH, just store them in [decimalbyte - ecx].
hooray for code optimization clarity through gin consumption! :dance:
Strange women, lying in ponds, distributing swords, is no basis for a system of government

rags

If all you want to do is display the date in a message box, have a look at this code, it is a little easier to read.

    include \masm32\include\masm32rt.inc


.data?
    DateBuff  db 260 DUP (?)
   
.data
    DateFormat db "ddd',' MMM dd yyyy",0
    MBTittle   db "My Time",0
    .code
ALIGN 4
start:
    call main
    invoke ExitProcess, 0

main proc

    invoke GetDateFormat, LOCALE_USER_DEFAULT, NULL, NULL, ADDR DateFormat, ADDR DateBuff, 260
    invoke MessageBox, NULL, ADDR DateBuff, ADDR MBTittle,MB_OK
    ret
main endp

end start

it will display  the current date in a messagebox.

Rags
God made Man, but the monkey applied the glue -DEVO

LL

Hi,

I looked at both your examples and I will have to study them a little bit more since assembly for me is not a given.  Yes, I did add an extra line to what I had written.  After the line:  MOV D[MsgBoxTex+13D],EAX   ;copy "wYear" part in my "MsgBoxTex", I had added the next line:                                                 MOV D[MsgBoxTex+17D],0   ;make sure the string ends with "0".  I write in this format because it's more visual for me. I'm also just learning and I'm most familiar with Goasm, Gorc and Golink. I was also surprise that  it gets stored right-left. Most of what I do at this point in time is a lot of guesswork. For now, I used a message box because it was simply smaller and faster. Thank you both for your imput to my learning assembly for the simple joy of learning how to do some programming.

LL

redskull

Learning was just as daunting for me, so i'll try to explain what my code does.  This has been re-worked a little bit from above to make it quicker, easier to read.  Again, i'm fairly new to this game myself, so don't take anything I say as correct.

DecimalString  db 10 DUP ("0"), 0   ; Define 10 digits of "0", and one null character

lea ebx, DecimalString   ; load the address of the string we want to put the characters in
add ebx, 10                ; add 10 to the address (move to the end of the string)
xor ecx, ecx                ; Zero out cx to use as a counter (could use mov ecx, 0)
mov eax, 12345          ; move the value to write into eax
mov esi, 10                 ; move 10 (0Ah) into esi to use to divide by

.WHILE (eax > 0)        ; do the loop while eax isn't 0
   XOR edx, edx           ; zero edx
   div esi                     ; integer divide our number by 10 (eg 12345/10 gives you 1234 remainder 5
    add dx, 30h             ; add 30h to our remaind (the rightmost digit of the number)
  .IF (dx >= 03ah)       ; if the number is greater than 03Ah, it's a letter so...
    add dx, 07h            ; add an extra 7 to correct the ASCII digit skip (check the ASCII table)
  .ENDIF
   sub ebx,1              ; subtract 1 from the adress (move to the next position left in the string)
   mov [ebx], dl           ; move the byte into the address of ebx (into the string)
   inc ecx
.ENDW

;write it in a messagebox, or do whatever you want to it
invoke MessageBox, 0, addr DecimalString, addr MsgCap,0
Strange women, lying in ponds, distributing swords, is no basis for a system of government

LL

 :U
Hi again,

Yes to both of you; rags and redskull.  Rags, your way of displaying the date is far more simpler than what I had thought up. I've also tried "DATE_LONGDATE" jsut to make sure that I could understand other features of the function "GetDateFormat". Which was great.

For you redskull, the first look at your example was hard at the begining.  Your second explanation, with more detail, help me a great deal.  Yes, I was also looking to understand how we could convert a "hex" value in a string format. After running your example, with for example: MOV EAX, 0F546Bh, the invoke MessageBox displayed it's decimal equivalent: 1004651.

Thank you both for your contribution.

LL

LL

 :bg

Hi redskull,

I should of mention that I needed to make the "DecimalString    db 11 DUP  0" in order to zero out the string. Also, in my call to the messagebox, where I should of "Push ADDR DecimalString", I simply "Push EBX".  EBX is now my starting point within the "DecimalString".  Otherwise, the string still had some zero's in the begining.

Again, thank you.

LL

redskull

Yah, i noticed too that on repeated calls with lesser numbers than the previous one, the old digits don't get overwritten...you could always use a STOSB at the top of the function, or maybe a counter to write zero to the leftover.  using 'db 11 "0"' isn't a good idea, because the messagebox function looks for a Null-terminated string...so it needs 10 digits of zero (030h) and one digit of null (0h)
Personally, i'd stay away from PUSHing an index into the DecimalString, as far as a 'modular' programming style would be concerend.  To really make it a viable, reusable function, i would just put a function to clear out the string at the beginning, and then have it return an pointer to DecimalString in eax on the return.  That way, any function just has to pass the DWORD it wants to convert, and assume that [eax] is a null-terminated string.  That's just me.  And as far as the PUSH, i'd go with the 'invoke' command for ease of use; i found it to be quite the ASM programming help.

alan
Strange women, lying in ponds, distributing swords, is no basis for a system of government

LL

Hi Alan,

I payed a little more attention to what I was seeing within: "DecimalString".  But I did try your way again: (DecimalString  db 10 DUP ("0"), 0 ), and yes it works very well.  However, when I used: "DecimalString     db 11 DUP 0", I wanted to make sure that my string ended with a null character (or simply a 0). I still used: "add ebx, 10 ; add 10 to the address ". So the string still ended with a null character.

I also tried using STOSB. This way is very much better than simply Push EBX. Here is what I came to understand from your example:

DATA SECTION

   DecimalString     db 10 DUP ("0"), 0  ; Define 10 digits of "0", and one null character
       MsgCap                      db "String Out !",0

CODE SECTION
start:
      call WinMain
       invoke ExitProcess, 0

WinMain:
   lea ebx, DecimalString     ; load the address of the string we want to put the characters in
   add ebx, 10                   ; add 10 to the address (move to the end of the string)
   xor ecx, ecx                  ; Zero out cx to use as a counter (could use mov ecx, 0)
   mov eax, 0F546Bh         ; move the value to write into eax (0F546Bh = 1004651D)
   mov esi, 10D                 ; move 10 (0Ah) into esi to use to divide by

L1:   CMP EAX,0                      ;.WHILE (eax > 0); do the loop while eax isn't 0
   JE >L2   
   XOR edx, edx                ; zero edx
      div esi                           ; integer divide our number by 10 (eg 12345/10 gives you 1234 remainder 5
       add dx, 30h                   ; add 30h to our remaind (the rightmost digit of the number)
   CMP DX,03Ah        ;.IF (dx >= 03ah) ; if the number is greater than 03Ah, it's a letter so...
   JAE >L3
       JMP >L4

L3:   add dx, 07h                   ; add an extra 7 to correct the ASCII digit skip (check the ASCII table)
             ;.ENDIF
L4:      sub ebx,1                       ; subtract 1 from the adress (move to the next position left in the string)
      mov [ebx], dl                  ; move the byte into the address of ebx (into the string)
      inc ecx
   JMP <L1
L2:   INC ECX
   lea EDI, DecimalString
L02:   MOV   EAX,D[EBX]
   STOSB
   INC EBX
   DEC ECX
   CMP ECX,0
   JNE <L02
   PUSH   0         ;MB_OK
   PUSH   ADDR MsgCap      ;ADDR MsgCaption
   PUSH   ADDR DecimalString                   ;ADDR DecimalString
   PUSH   0         ;NULL
   CALL   MessageBoxA      ;invoke MessageBox
   RET                                                           ;invoke MessageBox, 0, addr DecimalString, addr MsgCap,0

LL

LL

Hi alan,

I can't stop thinking of why you would not use: "DecimalString     db 11 DUP 0". It simply fills the "DecimalString buffer area" with null characters.  If you're trying to tell me that the string displayed via a "messagebox" can only be 10 digits long, I think you could be wrong.  While I was trying to understand "icztutes no.2 tutorial" , I noticed that the "messagebox" stretches to the width of the string you want to display. This is what I had done to understand his tutorial:

Data section
MsgCaption      db "L'example no. 02 d'Iczelion fut modifié par Léonard",0
MsgBoxText      db '                    "Win32 Assembly" est intéressant !',0

Code section
start:
   PUSH   0         ;MB_OK
   PUSH   ADDR MsgCaption      ;addr MsgCaption
   PUSH   ADDR MsgBoxText      ;addr MsgBoxTex
   PUSH   0         ;NULL
   CALL   MessageBoxA      ;invoke MessageBox
   PUSH   0         ;NULL
   CALL   ExitProcess                      ;invoke ExitProcess

Leonard

redskull

i'm not saying it can only be 10 digits, but messagebox stops at the first null digit it encounters...so if you put db 11 0 ( 11 null characters), and the function fills it right to left, if you used a hex digit that was less than 10 digits long, it would stop before hand:
for example (using 3039h or 12345d)

0,0,0,0,0,0,0,0,0,0,0    ; 11 null characters
0,0,0,0,0,0,0,0,0,5,0    ; First divison
0,0,0,0,0,0,0,0,4,5,0    ; second
0,0,0,0,0,0,0,3,4,5,0    ; third
0,0,0,0,0,0,2,3,4,5,0    ; fourth
*0*,0,0,0,0,1,2,3,4,5,0    ; fifth, messagebox stops at first null, so you get an empty string





This works, but when you call MessageBox, it looks at the starting address of DecimalString, and the first digit is a null, so it stops there and you get an empty message box.  You could do something like adding a counter in ecx for every time you do a division, and then do OFFSET DecimalString + (10 - ecx) to pick up the right spot.  I don't really care about the leading zeros.  This wouldn't be a problem when print 10 digit decimal numbers, but anything less would result in an empty message box
By using db 10 "0", we put something other than a null  (actaully 030h) in there to let MessageBox display all the way to the end.
Strange women, lying in ponds, distributing swords, is no basis for a system of government

LL

 :bg

Ok, I think I understand what you mean. Your way displays a 10 digits string.  In your example: 12345d, the messagebox would display ( 0000012345 ).  I was looking to simply display, again with your example (12345d), the final number: 12345.  When I first started to think of how to display a hex value, I was thinking of displaying ( 07D5h ) or simply the year 2005. Your way would display 0000002005. So I added a loop to move the digits in the begining of the DecimalString:


L2:   INC ECX         ;ecx had the lenght of the new string (+ 1 to get the null character)
   LEA EDI, DecimalString   ;I'm setting up EDI with "DecimalString"
L02:   MOV EAX,D[EBX]      ;[EBX] has the string value to be moved
   STOSB         ;Store EAX at EDI location (inc EDI)
   INC EBX         ;set up for next string value to be moved
   LOOP L02      ;Loop until ECX is equal to 0

Your true help was getting me to understand how I could use a routine to convert a hex value to a decimal value.  Which I could only do with the help of a calculator.  How I'm going to use this info will depend on what I want to display.  For me, I was thinking of, Let's say, displaying a string like: The year is 2005.  So yes I would need to add an OFFSET DecimalString + (10 - ecx)  to pick up the right spot.  By the way, my first attempt my way did display an empty messagebox.  Was I ever confused.  Ha! Ha!

LL