Announcement

Collapse
No announcement yet.

Hex Conversion

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Hex Conversion

    Here's an interesting little subroutine, similar to one published in a recent issue of the PowerBASIC Gazette. It converts a binary value in register AL (0-255) to the equivalent ASCII hex digits (00-FF). The hi-order hex digit is returned in register DL, the lo-order hex digit in register DH, so you can easily store it in memory with: MOV [MemoryWord], DX.

    Code:
    Bin8ToHex:
             push    eax
             ror     al, four
             call    Bin8a
             mov     dl, dh
             pop     eax
    Bin8a:
             and     al,00001111b
             add     al,90h
             daa
             adc     al,40h
             daa
             mov     dh, al
             ret

  • #2
    Very interesting to me.
    Is it possible to extend this to place these tow bytes into a dynamic string?
    For conversion i would like to populate a dynamic string of twice the size of a binary string, so the target address must shift on each conversion.

    Or is a native functionality equally fast?
    I use conversion to hex very often.

    Thanks,
    hellobasic

    Comment


    • #3
      Is it possible to extend this to place these tow bytes into a dynamic string?
      For conversion i would like to populate a dynamic string of twice the size of a binary string, so the target address must shift on each conversion.
      Yes. http://www.powerbasic.com/support/pb...ad.php?t=25176

      (Assumes: No Assembly Required).
      Michael Mattias
      Tal Systems (retired)
      Port Washington WI USA
      [email protected]
      http://www.talsystems.com

      Comment


      • #4
        I already do the same.
        hellobasic

        Comment


        • #5
          Well, OK.

          What Mr. Zale posted does not do quite the same thing.....

          ... which is why, I guess, he posted this in the first place. After all, he has already created his own very fine HEX$() function to put numbers into hex character strings, and that works pretty well.
          Michael Mattias
          Tal Systems (retired)
          Port Washington WI USA
          [email protected]
          http://www.talsystems.com

          Comment


          • #6
            Edwin, I got about 17x faster using the below code:
            Code:
            #COMPILE EXE
            #DIM ALL
            MACRO Bin8ToHex()
                    !push    eax
                    !ror     al, 4
                    !call    Bin8a
                    !mov     dl, dh
                    !pop     eax
                    !call    Bin8a
                    EXIT MACRO
                Bin8a:
                    !and     al,&b00001111
                    !add     al,&h90
                    !daa
                    !adc     al,&h40
                    !daa
                    !mov     dh, al
                    !ret
            END MACRO
            
            FUNCTION PBMAIN () AS LONG
               LOCAL wrdVar AS WORD, ii AS LONG, hexStr AS STRING
               LOCAL convStrPtr AS LONG, convStr, convStr2, cErr AS STRING, t, t2, t3 AS SINGLE
               
               convStr = SPACE$(40000000)
               convStrPtr = STRPTR(convStr)
               t = TIMER
               FOR ii = 0 TO 20000000 - 1
                  !mov eax, ii
                  Bin8ToHex
                  !mov wrdVar, dx
                  POKE WORD, convStrPtr + 2 * ii, wrdVar
               NEXT
               t2 = TIMER
               convStr2 = convStr
               
               FOR ii = 0 TO 20000000 - 1
                  
                  POKE$ convStrPtr + 2 * ii, HEX$(ii, 2)
                  
               NEXT
               t3 = TIMER
               IF convStr2 <> convStr THEN cErr = "oops, there is an error"
               ? "asm took " & STR$(t2 - t, 4) & $CRLF & _
                 "hex took " & STR$(t3 - t2, 4) & " so asm was" & _
                  STR$((t3 - t2) / (t2 - t), 4) & " times faster" & $CRLF & cErr
            END FUNCTION

            Comment


            • #7
              Store repeated hex bytes in a buffer (string) addressed by EDI:


              Code:
              Start:
                        cld
                        lea       edi, TargetOffset
                        mov       al, BinNumber
                        call      Bin8ToHex
                        <RepeatAsNeeded>
                        <exit>
              
              Bin8ToHex:
                         push        eax
                         ror         al, four
                         call        Bin8a
                         pop         eax
              Bin8a:
                         and         al,00001111b
                         add         al,90h
                         daa
                         adc         al,40h
                         daa
                         stosb
                         ret

              Comment


              • #8
                Wow, and i thought i had a fast conversion.. using a similar table as MCM describes (byte ptr for/next)
                It took: 6.188 secs



                Will investigate the asm version, the last asm code, i am not able to implement that since i don't know to interact between two languages.
                I can only read asm somewhat.
                Can't tell if that part would be faster.

                PS, i don't see seperate output string.
                I would like to copy an original dynamic string (using ptr and length) to a 2nd predimensioned dynamic string (also ptr).
                hellobasic

                Comment


                • #9
                  Edwin, here it is in a function. I tested it at about 11x-12x faster.
                  Code:
                  #COMPILE EXE
                  #DIM ALL
                  %STRSIZE = 20000000
                  
                  FUNCTION PBMAIN () AS LONG
                     LOCAL wrdVar AS WORD, ii AS LONG, hexStr AS STRING
                     LOCAL convStrPtr AS WORD PTR, byteStrIn, convStrOut, convStr2, cErr AS STRING, t, t2, t3 AS SINGLE
                     LOCAL byteStrPtr AS BYTE PTR, inByte AS BYTE, convStrPtr2 AS STRING PTR * 2
                  
                     'byteStrIn can be any string length that will fit into memory concurrent
                     'with its twice as long converted string.
                     byteStrIn = SPACE$(%STRSIZE)
                  
                     'put random bytes into the byteStrIn, which can hold any byte value (0-255).
                     FOR ii = 1 TO %STRSIZE
                        ASC(byteStrIn, ii) = RND(0, 255)
                     NEXT
                  
                  
                     t = TIMER
                  
                     convStrOut = convertStrToHex(byteStrIn)
                  
                     t2 = TIMER
                  
                     convStr2 = convStrOut
                     byteStrPtr = STRPTR(byteStrIn)
                     convStrOut = SPACE$(2 * LEN(byteStrIn)) 'make converted string 2x longer than the input string
                     convStrPtr2 = STRPTR(convStrOut)
                  
                     FOR ii = 0 TO LEN(byteStrIn) - 1
                  
                        @convStrPtr2 = HEX$(@byteStrPtr, 2)
                        INCR byteStrPtr
                        INCR convStrPtr2
                  
                     NEXT
                     t3 = TIMER
                     IF convStr2 <> convStrOut THEN cErr = "oops, there is an error"
                     ? "asm took " & STR$(t2 - t, 4) & $CRLF & _
                       "hex took " & STR$(t3 - t2, 4) & " so asm was" & _
                        STR$((t3 - t2) / (t2 - t), 4) & " times faster" & $CRLF & cErr
                  END FUNCTION
                  
                  FUNCTION convertStrToHex(byteStrIn AS STRING) AS STRING
                     LOCAL convStrOut AS STRING
                     LOCAL byteStrPtr, convStrPtr AS LONG
                     REGISTER ii AS LONG
                  
                     convStrOut = SPACE$(2 * LEN(byteStrIn)) 'make converted string 2x longer than the input string
                  
                     byteStrPtr = STRPTR(byteStrIn)
                     convStrPtr = STRPTR(convStrOut)
                  
                     FOR ii = 0 TO LEN(byteStrIn) - 1
                  
                        !mov edx, byteStrPtr
                        !mov ecx, ii
                        [COLOR="Red"]!movzx eax, byte ptr[edx+ecx][/COLOR]
                        !call Bin8toHex
                        !mov eax, convStrPtr
                        !mov ecx, ii
                        !mov [eax+ecx*2], dx
                  
                     NEXT
                  
                     FUNCTION = convStrOut
                     EXIT FUNCTION
                  
                     Bin8toHex:
                          !push    eax
                          !ror     al, 4
                          !call    Bin8a
                          !mov     dl, dh
                          !pop     eax
                          !call    Bin8a
                          !ret
                      Bin8a:
                          !and     al,&b00001111
                          !add     al,&h90
                          !daa
                          !adc     al,&h40
                          !daa
                          !mov     dh, al
                          !ret
                  
                  END FUNCTION
                  Last edited by John Gleason; 8 Oct 2009, 03:17 PM. Reason: asm line in red, only move 1 byte

                  Comment


                  • #10
                    I tested the earlier posted code today on Windows98, while i didn't test the actual result, the original code did not finish for some reason.

                    I used win98SE in a virtual pc with maybe using not enough ram?
                    A space of 40 000 000 is only 40MB and should work imo.
                    i tested with smaller sizes (afaik 4MB) and that worked.

                    My question in short is, is the assembly code ok to use on any Windows XP computer and up?
                    Win98 is not important but if you have an answer for that version as well?

                    Thanks for the new code, i'll test it soon.
                    hellobasic

                    Comment


                    • #11
                      I tested it only on Win 98, but yes, it should work on any platform. It's not doing anything at all unusual that I know of. It's self testing--that is, assuming my all-PB code is correct. Feel free to hammer on the code, because the more testing the better.

                      btw, I remove the test code for my timings.

                      Comment


                      • #12
                        Just a thought, why not move the asm code in the loop?
                        It saves two calls and a ret imo.
                        and maybe even the push and a pop?

                        hellobasic

                        Comment


                        • #13
                          why not move the asm code in the loop?
                          Indeed, it looks like 5%+ faster. Furthermore, you could make the two calls inline too for maybe a few less ticks.
                          Code:
                          FUNCTION convertStrToHex(byteStrIn AS STRING) AS STRING
                             LOCAL convStrOut AS STRING
                             LOCAL byteStrPtr, convStrPtr AS LONG
                             REGISTER ii AS LONG
                          
                             convStrOut = SPACE$(2 * LEN(byteStrIn)) 'make converted string 2x longer than the input string
                          
                             byteStrPtr = STRPTR(byteStrIn)
                             convStrPtr = STRPTR(convStrOut)
                          
                             FOR ii = 0 TO LEN(byteStrIn) - 1
                          
                                  !mov     edx, byteStrPtr
                                  !mov     ecx, ii
                                  !movzx   eax, byte ptr[edx+ecx]
                          '  here was the original Bin8toHex start point
                                  !push    eax
                                  !ror     al, 4
                                  !call    Bin8a
                                  !mov     dl, dh
                                  !pop     eax
                                  !call    Bin8a
                                  !mov     eax, convStrPtr
                                  !mov     ecx, ii
                                  !mov     [eax+ecx*2], dx
                             NEXT
                          
                             FUNCTION = convStrOut
                             EXIT FUNCTION
                             
                              Bin8a:
                                  !and     al,&b00001111
                                  !add     al,&h90
                                  !daa
                                  !adc     al,&h40
                                  !daa
                                  !mov     dh, al
                                  !ret
                          
                          END FUNCTION

                          Comment


                          • #14
                            Well, since it could be done inline, I did it. It showed another maybe 8-10% boost.
                            Code:
                            FUNCTION convertStrToHex(byteStrIn AS STRING) AS STRING
                               LOCAL hexStrOut AS STRING
                               LOCAL byteStrPtr, hexStrPtr AS LONG
                               REGISTER ii AS LONG
                            
                               hexStrOut = SPACE$(2 * LEN(byteStrIn)) 'make converted string 2x longer than the input string
                            
                                    !mov     eax, byteStrIn
                                    !mov     edx, hexStrOut
                                    !mov     ecx, [eax]        ;dereference byteStrIn
                                    !mov     hexStrPtr, edx    ;STRPTR(hexStrOut)
                                    !mov     byteStrPtr, ecx   ;STRPTR(byteStrIn)
                            
                               FOR ii = 0 TO LEN(byteStrIn) - 1
                            
                                    !mov     edx, byteStrPtr
                                    !mov     ecx, ii
                                    !movzx   eax, byte ptr[edx+ecx]
                            '  here was the original Bin8toHex start point
                                    !push    eax
                                    !ror     al, 4
                            
                            '  here is inline call to original Bin8a
                                    !and     al,&b00001111
                                    !add     al,&h90
                                    !daa
                                    !adc     al,&h40
                                    !daa
                                    !mov     dl, al
                                    !pop     eax
                            
                            '  here is inline 2nd call to original Bin8a
                                    !and     al,&b00001111
                                    !add     al,&h90
                                    !daa
                                    !adc     al,&h40
                                    !daa
                                    !mov     dh, al
                            
                                    !mov     eax, hexStrPtr
                                    !mov     ecx, ii
                                    !mov     [eax+ecx*2], dx
                               NEXT
                            
                               FUNCTION = hexStrOut
                            
                            END FUNCTION

                            Comment


                            • #15
                              Still havent tested the final output but i have increased the ram for the win98 VPC and does 40MB in 0.992 seconds

                              On my normal XP in 0.325.
                              (Wasn't it faster yesterday??)
                              hellobasic

                              Comment


                              • #16
                                One more method: a lookup table. Its disadvantage is that on first call, it may be slower because it needs to make the lookup table, but overall it will likely be quite a bit faster.
                                Code:
                                FUNCTION convertStrToHexL(byteStrIn AS STRING) AS STRING
                                   LOCAL hexStrOut AS STRING
                                   LOCAL byteStrPtr, hexStrPtr AS LONG
                                   STATIC oneTime, hexLookupPtr AS LONG
                                   REGISTER ii AS LONG
                                
                                   IF oneTime = 0 THEN
                                      oneTime = 1
                                      DIM hexLookupTable(255) AS STATIC WORD
                                      FOR ii = 0 TO 255
                                         hexLookupTable(ii) = CVWRD(HEX$(ii, 2))
                                      NEXT
                                      hexLookupPtr = VARPTR(hexLookupTable(0))
                                   END IF
                                         
                                   hexStrOut = SPACE$(2 * LEN(byteStrIn)) 'make converted string 2x longer than the input string
                                
                                        !mov     eax, byteStrIn
                                        !mov     edx, hexStrOut
                                        !mov     ecx, [eax]        ;dereference byteStrIn
                                        !mov     hexStrPtr, edx    ;STRPTR(hexStrOut)
                                        !mov     byteStrPtr, ecx   ;STRPTR(byteStrIn)
                                
                                   FOR ii = 0 TO LEN(byteStrIn) - 1
                                
                                        !mov     edx, byteStrPtr
                                        !mov     ecx, ii
                                        !movzx   eax, byte ptr[edx+ecx]
                                        
                                        !mov     edx, hexLookupPtr
                                        !movzx   edx, word ptr[edx+eax*2]
                                
                                        !mov     eax, hexStrPtr
                                        !mov     [eax+ecx*2], dx
                                   NEXT
                                
                                   FUNCTION = hexStrOut
                                
                                END FUNCTION

                                Comment


                                • #17
                                  I pulled out a few more stops with the code, and I think found a bug in the docs too during testing.

                                  This LONG function speeds up significantly over the above STRING function, ~60% in my testing, by passing 2 string parameters to it: the in and out strings. Probably there is less string manipulation in the LONG function, which might explain the speed increase.

                                  You don't have to calculate the size of the output string, so the function is just as easy to use as the string version of the function. The translation table is now built in, so there is no speed penalty on its first call.
                                  Code:
                                  'it can be called eg.
                                        LOCAL byteStrIn, hexStrOut as string
                                        convertStrToHexL2 byteStrIn, hexStrOut
                                  Code:
                                  FUNCTION convertStrToHexL2(byteStrIn AS STRING, hexStrOut AS STRING) AS LONG
                                     LOCAL byteStrPtr, hexStrPtr AS LONG
                                     REGISTER ii AS LONG
                                  
                                  !jmp endTable
                                  hexLookupTable:
                                     !db 48,48,48,49,48,50,48,51,48,52,48,53,48,54,48,55,48,56,48,57,48,65,48,66,48,67,48,68,48,69,48,70,49,48
                                     !db 49,49,49,50,49,51,49,52,49,53,49,54,49,55,49,56,49,57,49,65,49,66,49,67,49,68,49,69,49,70,50,48
                                     !db 50,49,50,50,50,51,50,52,50,53,50,54,50,55,50,56,50,57,50,65,50,66,50,67,50,68,50,69,50,70,51,48
                                     !db 51,49,51,50,51,51,51,52,51,53,51,54,51,55,51,56,51,57,51,65,51,66,51,67,51,68,51,69,51,70,52,48
                                     !db 52,49,52,50,52,51,52,52,52,53,52,54,52,55,52,56,52,57,52,65,52,66,52,67,52,68,52,69,52,70,53,48
                                     !db 53,49,53,50,53,51,53,52,53,53,53,54,53,55,53,56,53,57,53,65,53,66,53,67,53,68,53,69,53,70,54,48
                                     !db 54,49,54,50,54,51,54,52,54,53,54,54,54,55,54,56,54,57,54,65,54,66,54,67,54,68,54,69,54,70,55,48
                                     !db 55,49,55,50,55,51,55,52,55,53,55,54,55,55,55,56,55,57,55,65,55,66,55,67,55,68,55,69,55,70,56,48
                                     !db 56,49,56,50,56,51,56,52,56,53,56,54,56,55,56,56,56,57,56,65,56,66,56,67,56,68,56,69,56,70,57,48
                                     !db 57,49,57,50,57,51,57,52,57,53,57,54,57,55,57,56,57,57,57,65,57,66,57,67,57,68,57,69,57,70,65,48
                                     !db 65,49,65,50,65,51,65,52,65,53,65,54,65,55,65,56,65,57,65,65,65,66,65,67,65,68,65,69,65,70,66,48
                                     !db 66,49,66,50,66,51,66,52,66,53,66,54,66,55,66,56,66,57,66,65,66,66,66,67,66,68,66,69,66,70,67,48
                                     !db 67,49,67,50,67,51,67,52,67,53,67,54,67,55,67,56,67,57,67,65,67,66,67,67,67,68,67,69,67,70,68,48
                                     !db 68,49,68,50,68,51,68,52,68,53,68,54,68,55,68,56,68,57,68,65,68,66,68,67,68,68,68,69,68,70,69,48
                                     !db 69,49,69,50,69,51,69,52,69,53,69,54,69,55,69,56,69,57,69,65,69,66,69,67,69,68,69,69,69,70,70,48
                                     !db 70,49,70,50,70,51,70,52,70,53,70,54,70,55,70,56,70,57,70,65,70,66,70,67,70,68,70,69,70,70
                                  endTable:
                                  
                                     hexStrOut = SPACE$(2 * LEN(byteStrIn)) 'make converted string 2x longer than the input string
                                  
                                          !mov     eax, byteStrIn
                                          !mov     edx, hexStrOut
                                          !mov     ecx, [eax]        ;dereference byteStrIn
                                          !mov     edx, [edx]        ;dereference hexStrOut
                                          !mov     byteStrPtr, ecx   ;STRPTR(byteStrIn)
                                          !mov     hexStrPtr, edx    ;STRPTR(hexStrOut)
                                  
                                     FOR ii = 0 TO LEN(byteStrIn) - 1
                                  
                                          !mov     edx, byteStrPtr
                                          !mov     ecx, ii
                                          !movzx   eax, byte ptr[edx+ecx]   ;get byte in
                                  
                                          !lea     edx, hexLookupTable      ;Ptr to hex conversion table
                                          !movzx   edx, word ptr[edx+eax*2]
                                  
                                          !mov     eax, hexStrPtr
                                          !mov     [eax+ecx*2], dx          ;2 hex chars out
                                     NEXT
                                  
                                  END FUNCTION
                                  The bug I mentioned from the docs is this:
                                  To display a Quad-integer in hex format, use the following technique:

                                  DIM q AS QUAD, r AS STRING

                                  q = &H0ABCEF0EE1234567F&&

                                  r = HEX$(HI(LONG, q)) + HEX$(LO(DWORD, q))
                                  The one line needs to be changed to:
                                  r = HEX$(HI(LONG, q)) + HEX$(LO(DWORD, q), 8)
                                  because it drops needed leading zeros from the LO portion of the quad.

                                  If this has been corrected in the new .02 update from this week, whoopsie! never mind.

                                  Comment


                                  • #18
                                    I am not so happy modifying var's addresses, if you say it's ok..?

                                    --

                                    You may consider an asm do:loop instead if the for/next.
                                    Can you bring the table-in-mem back? or does it bring a speed-penalty?
                                    hellobasic

                                    Comment


                                    • #19
                                      Originally posted by Edwin Knoppert View Post
                                      I am not so happy modifying var's addresses, if you say it's ok..?
                                      If you have already filled hexStrOut with data and don't want it erased, then it's not ok, this new version will not do for you. But, if you have just declared hexStrOut normally, eg. LOCAL hexStrOut AS STRING, and have not yet used it for anything, or have used it and no longer need any of it, then the new function will work fine, plus be faster.

                                      --
                                      You may consider an asm do:loop instead if the for/next.
                                      Can you bring the table-in-mem back? or does it bring a speed-penalty?
                                      Probably no speed change will occur with do:loop instead of the for/next. It's closing in now I think to pretty good optimization.

                                      I can bring the table-in-mem back with little or no speed penality I believe. I'll post it soon.

                                      Comment


                                      • #20
                                        I thought that using a long for the Strptr() would do was my point actually.
                                        You have used some asm which i do'nt understand and thought it swapped the addresses.

                                        Side note: i'll convert this function to a pmem+len, pmemout function, it shouldn't care about the memory type.
                                        I said using dynamic strings to avoid discussions like using asciiz or whatever.

                                        Function ToHex(byval pMen as long, byval nSize as long, Byval pmemout as long ) As long


                                        --

                                        I said do:loop but i meant a counter actually, asm code of course..

                                        Last edited by Edwin Knoppert; 10 Oct 2009, 01:05 PM.
                                        hellobasic

                                        Comment

                                        Working...
                                        X