News:

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

Printing Hex Values

Started by redskull, October 12, 2005, 08:44:34 PM

Previous topic - Next topic

redskull

I'm in the market to print out the actual hex values of a string, not the ASCII representations(sort of like a memory dump).  For example, instead of 2F printing '/', it should print "2F".  This is what i've got so far; is there a better/quicker/more logical/more established way to do it?


WriteByte WORD ?

mov ax, 0
mov ax, 002Ah
rol ax, 4
ror al, 4
.IF al >= 0Ah
ADD al, 037h
.ENDIF
.IF al < 0Ah
ADD al, 030h
.ENDIF
.IF ah >= 0Ah
ADD ah, 037h
.ENDIF
.IF ah < 0Ah
ADD ah, 030h
.ENDIF
ROL AX, 8
mov WriteByte, ax
; WriteByte now has the right values in the right order
Strange women, lying in ponds, distributing swords, is no basis for a system of government

Biterider

#1
Hi
Here the fastest way I found to converta dword to its string representation. it can be reduced to only convert a single byte

MakeHexChar macro reg:req, pos:req
   cmp reg, "9"
   jbe @F
   add reg, 7
@@:
   mov byte ptr [eax + pos], reg
endm

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

align @WordSize
dword2hex proc pBuffer:Pointer, dValue:dword
   mov edx, [esp + 8]
   mov ecx, edx
   shr edx, 4

   and edx, 0F0F0F0Fh
   and ecx, 0F0F0F0Fh
   add edx, "0000"
   add ecx, "0000"

   mov eax, [esp + 4]

   MakeHexChar cl, 7
   MakeHexChar dl, 6
   MakeHexChar ch, 5
   MakeHexChar dh, 4
   shr ecx, 16
   shr edx, 16
   MakeHexChar cl, 3
   MakeHexChar dl, 2
   MakeHexChar ch, 1
   MakeHexChar dh, 0
   mov byte ptr [eax + 8], 0

   ret 8
dword2hex endp

OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef


Regards

Biterider

Jeff

here's my version... not necessarily the fastest but easy to follow.

.DATA
xTable BYTE "0123456789ABCDEF"  ;translation table
number DWORD 1948AB8Eh          ;translate this number

.DATA?
buffer BYTE 8 DUP(?)            ;buffer

.CODE
...
    MOV edi,OFFSET buffer       ;destination buffer for ascii representation
    MOV ebx,OFFSET xTable       ;translation table
    MOV eax,number              ;number to represent
    MOV ecx,8                   ;convert a 32bit number
    TOP:
        ROL eax,4               ;rotate the leftmost nibble to the low part (since it's read left-right)
        PUSH eax                ;save the current rotation
        AND eax,0Fh             ;zero out all but the first nibble (to be used as an index)
        XLATB                   ;use the index to get the ascii representation
        STOSB                   ;move the translated byte to the buffer
        POP eax                 ;restore the current rotation
    LOOP TOP                    ;repeat (8 times)...
...

;   Working out the logic:
;       0x1948AB8E
;         ^         convert the first nibble to ascii...
;       0x948AB8E1
;                ^  rotate to the beginning
;       0x00000001
;                ^  save and zero out
;              '1'
;               ^   convert the nibble to ascii
;                                                               "1       "
;                          move the translation into the buffer  ^     
;       0x948AB8E1
;         ^         restore and convert the next nibble...
;       0x48AB8E19
;                ^  rotate to the beginning
;       0x00000009
;                ^  save and zero out
;              '9'
;               ^   convert the nibble to ascii
;                                                               "19      "
;                          move the translation into the buffer   ^     
;       ... and repeat

dsouza123

Will translate a dword to a hex string.

If the __REPEAT count__ matches the __add esi, count__
it will translate count bytes from high to low into a hex string,
also the __hexstr  dw count dup (0)__ should have a matching count.


.data
     b2hlut  db "000102030405060708090A0B0C0D0E0F"
             db "101112131415161718191A1B1C1D1E1F"
             db "202122232425262728292A2B2C2D2E2F"
             db "303132333435363738393A3B3C3D3E3F"
             db "404142434445464748494A4B4C4D4E4F"
             db "505152535455565758595A5B5C5D5E5F"
             db "606162636465666768696A6B6C6D6E6F"
             db "707172737475767778797A7B7C7D7E7F"
             db "808182838485868788898A8B8C8D8E8F"
             db "909192939495969798999A9B9C9D9E9F"
             db "A0A1A2A3A4A5A6A7A8A9AAABACADAEAF"
             db "B0B1B2B3B4B5B6B7B8B9BABBBCBDBEBF"
             db "C0C1C2C3C4C5C6C7C8C9CACBCCCDCECF"
             db "D0D1D2D3D4D5D6D7D8D9DADBDCDDDEDF"
             db "E0E1E2E3E4E5E6E7E8E9EAEBECEDEEEF"
             db "F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF"
     testdd  dd 12345678h
     szbuff  db "Dword is "
     hexstr  dw 4 dup (0)
     szterm  db 0

.code
start:
        lea   esi, testdd
        lea   edi, hexstr

        add   esi, 4
        sub   edi, 2

      REPEAT 4
        dec   esi
        inc   edi
        inc   edi
        movzx ecx, byte ptr [esi]
        mov   ax, word ptr [b2hlut+ecx*2]
        mov   [edi], ax
      ENDM

gabor

Hello!


Those several ideas are a bit to vast. :)))

But, the task was NOT to write a Dword2Hex converter but to create a byte dump. For this purpose I believe that redskull's solution is quite optimal...


mov ax, 0    ; this is by accident here, isn't it?
mov ax, 002Ah
rol ax, 4    ; is maybe shl and shr faster?
ror al, 4


I would rewrite the following part like this:


ADD ax,3030h
.IF al >= 0Ah
ADD al,07h
.ENDIF
.IF ah >= 0Ah
ADD ah, 07h
.ENDIF


Finally:

ROL AX, 8

You want to exchange al and ah because of the strange Intel byte order (LSB first)? I think xchg al,ah is faster?


Greets, Gábor

MichaelW

If the goal is to display a hex dump of some arbitrary string, I think a table-lookup approach would be easier to understand and easier to code. Each table entry could include two hex digits, a space to separate the digit pairs, and a terminating null so each table entry could be printed as a string. The code would need to scan through the string adding each byte value to the offset address of the table before passing the result to a string print routine, continuing until the terminating null (in the original string) is found.

eschew obfuscation

redskull

Gabor: can you explain how your code works?  Won't the IF statements always test true?  if the first thing is adding 30 to al and ah, then it will always be greater or equal to 0Ah, and all the numbers would be offset (0=7, 1=8, 2=9, etc)?
thanks for the replies
Strange women, lying in ponds, distributing swords, is no basis for a system of government

gabor

Hello!


MichaelW's idea is the fastest and easiest I must admit. With a table entry of 2 hex digits (in ASCII), A space character end a terminating 0 it perfectly aligns to DWORD, so the ofset is really easy to be calculated and also the DWORD alignment of data is satisfied. Very smart solution!
When I tried to create a code for this I found a need for a slight modification, here is my code:



lineLen     EQU 16 ; I assumed a dump with 16 byte values in a line
lineBuffer  db lineLen dup (20h) ,0   ; 16*(2 hex letters+separeting space char)

; the table must be stored in reverse order because of Intel's LSB first byte order...
table       dw '00','10','20',...,'F0'
            dw '01','11','21',....,'F1'
            ...
            dw '0F','1F','2F',....,'FF'


dumpLine    PROC USES ebx esi edi lpData:DWORD
            xor ebx,ebx
            mov esi,lpData
@1:
            mov bl,[esi]
            mov ax,[table+ebx*2]
            add esi,1
            mov [edi],ax
            add edi,2
            cmp edi,offset table+lineLen*3
            jnz @1
; lineBuffer is ready, contains lineLen long dump
; It can be either printed or copied into another buffer.
; Or originally the its content could have been written
; into a big buffer, that will hold several lines before it is printed out.
            ret
lineDump    ENDP

Redskull!

The code is not hard to understand, especiall if I didn't make that mistake about the IF condition.
Of course al and ah must be compared to 3Ah! Sorry for the confusion. I guess everything else is clear, since it is almost the same as your code.

Greets, Gábor

Ratch

redskull,
     Here is my entry.  As you can see, it is look up table driven.  It's not a good idea to put the spaces within the LUT because it makes the table too long.  I can also write a non-table version if you like.  Ratch



.686
;.MMX
;.K3D
;.XMM
.MODEL FLAT,STDCALL
;      option casemap :none   ; case sensitive
;.LISTMACROALL

.NOLIST
      include \masm32\include\windows.inc
      include \masm32\include\user32.inc
      include \masm32\include\kernel32.inc
      include \masm32\include\gdi32.inc
      include \masm32\include\advapi32.inc
.LIST   
      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\kernel32.lib
      includelib \masm32\lib\gdi32.lib
      includelib \masm32\lib\advapi32.lib

HEXTABLE MACRO
   LOCAL I,J
   I = '0'
   REPEAT 10
     J = '0'
     REPEAT 10
       BYTE I,J
       J = J+1
     ENDM
     J = 'A'
     REPEAT 6
      BYTE I,J
      J = J+1
     ENDM
     I = I+1
   ENDM
   I = 'A'
   REPEAT 6
     J = '0'
     REPEAT 10
       BYTE I,J
       J = J+1
     ENDM
     J = 'A'
     REPEAT 6
      BYTE I,J
      J = J+1
     ENDM
     I = I+1
   ENDM
ENDM

W        EQU     WORD PTR
B        EQU     BYTE PTR

.DATA?
szBuffer BYTE 120 DUP (?)

.DATA
STRING1 BYTE 'ABCDEFGHIJKLMNOPQRSTUVWSXZ1234567890'
TEXT1   BYTE 'HEX DUMP',0


.CODE
BTH:
BTH$ STRUC
EDISAV DWORD ?
ESISAV DWORD ?
RETURN DWORD ?
BTH$1 = $
STRSIZ DWORD ?
BUFFER DWORD ?
STRING DWORD ?
BTH$2 = $
BTH$ ENDS

.DATA
HEXTAB LABEL BYTE
HEXTABLE
.CODE
PUSH ESI
PUSH EDI
MOV ESI,[ESP.BTH$.STRING]
MOV EDI,[ESP.BTH$.BUFFER]
MOV ECX,[ESP.BTH$.STRSIZ]

.REPEAT
   MOVZX EAX,B[ESI]
   MOVZX EDX,W[HEXTAB+2*EAX]
   INC ESI
   MOV W[EDI],DX
   INC EDI
   INC EDI
   MOV B[EDI],' '
   INC EDI
  DEC ECX
.UNTIL ZERO?

  MOV B[EDI],0

POP ESI
POP EDI
RET  BTH$2-BTH$1

MAIN:
PUSH OFFSET STRING1
PUSH OFFSET szBuffer
PUSH SIZEOF STRING1
CALL BTH
INVOKE MessageBox,0,OFFSET szBuffer,OFFSET TEXT1,0

XOR EAX,EAX
INVOKE ExitProcess,EAX
END MAIN

Tedd

A bit cryptic, but short, no tables, no loops..

    ;al = byte to print as hex
    mov edx,eax
    and eax,0fh     ;al = _X
    cmp al,0Ah
    sbb al,69h
    das             ;al => hexchar
    xchg eax,edx
    shr eax,4
    and eax,0fh     ;al = X_
    cmp al,0Ah
    sbb al,69h
    das             ;al => hexchar

    mov ah,dl       ;ax = hexchars
    mov WriteByte,ax
No snowflake in an avalanche feels responsible.

Ratch

#10
To All,

     Here is my entry for a non-LUT hex dump routine.  Notice how MOV [EDI],EAX automatically tacks a zero terminator on the end of the string.  Many thanks to Norbert Juffa for his clever coding sequence.  Ratch

http://www.df.lth.se/~john_e/gems/gem003a.html


.686
;.MMX
;.K3D
;.XMM
.MODEL FLAT,STDCALL
;      option casemap :none   ; case sensitive
;.LISTMACROALL

.NOLIST
      include \masm32\include\windows.inc
      include \masm32\include\user32.inc
      include \masm32\include\kernel32.inc
      include \masm32\include\gdi32.inc
      include \masm32\include\advapi32.inc
.LIST   
      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\kernel32.lib
      includelib \masm32\lib\gdi32.lib
      includelib \masm32\lib\advapi32.lib

W        EQU     WORD PTR
B        EQU     BYTE PTR

.DATA?
szBuffer BYTE 120 DUP (?)

.DATA
STRING1 BYTE 'ABCDEFGHIJKLMNOPQRSTUVWSXZ1234567890'
TEXT1   BYTE 'HEX DUMP',0


.CODE
BTH:
BTH$ STRUC
EDISAV DWORD ?
ESISAV DWORD ?
RETURN DWORD ?
BTH$1 = $
STRSIZ DWORD ?
BUFFER DWORD ?
STRING DWORD ?
BTH$2 = $
BTH$ ENDS

.DATA
.CODE
PUSH ESI
PUSH EDI
MOV ESI,[ESP.BTH$.STRING]
MOV EDI,[ESP.BTH$.BUFFER]
MOV ECX,[ESP.BTH$.STRSIZ]

  .REPEAT
   MOVZX EAX,B[ESI]
   SHL EAX,4              ;shift 'n shuffle
   INC ESI                ;bump up the input pointer
   SHR AL,4               ;shift 'n shuffle

   CMP AL,10              ;thanks to Norbert Juffa for this 3 line code sequence
   SBB AL,069H
   DAS

   ROR AX,8               ;XCHG bytes

   CMP AL,10              ;thanks to Norbert Juffa for this 3 line code sequence
   SBB AL,069H
   DAS

   OR EAX,' ' SHL 16      ;insert a space

   MOV [EDI],EAX          ;EAX automatically writes a zero terminator on the end
   
   ADD EDI,WORD+BYTE      ;bump up the output pointer
  DEC ECX
.UNTIL ZERO?
POP EDI
POP ESI
RET BTH$2-BTH$1          ;return to sender

MAIN:

PUSH OFFSET STRING1
PUSH OFFSET szBuffer
PUSH SIZEOF STRING1
CALL BTH

INVOKE MessageBox,0,OFFSET szBuffer,OFFSET TEXT1,0

XOR EAX,EAX
INVOKE ExitProcess,EAX
END MAIN

Howard

How about this snippet from FPTOA.ASM in the M32Library - written by Tim Roberts

rol ax, 12             ; BBBB xxxx xxxx AAAA
rol ah, 4              ; xxxx BBBB xxxx AAAA
and ax, 0f0fh          ; 0000 BBBB 0000 AAAA
add ax, 3030h          ; 3B3A




Howard

Tedd

Quote from: Howard on October 16, 2005, 11:38:49 PM
How about this snippet from FPTOA.ASM in the M32Library - written by Tim Roberts
rol ax, 12             ; BBBB xxxx xxxx AAAA
rol ah, 4              ; xxxx BBBB xxxx AAAA
and ax, 0f0fh          ; 0000 BBBB 0000 AAAA
add ax, 3030h          ; 3B3A


...nice, but that doesn't work for hex (characters 3A to 3F are symbols.)
No snowflake in an avalanche feels responsible.