Author Topic: DWTOA with base (radix) parameter  (Read 2180 times)

MichaelW

  • Global Moderator
  • Member
  • *****
  • Posts: 1209
DWTOA with base (radix) parameter
« on: July 13, 2013, 08:16:23 PM »
I needed a 32-bit integer to ascii conversion procedure that could handle multiple bases (2, 10, and 16) for a 16-bit project, so I duplicated the base range for the CRT itoa function (2-36), and did this 32-bit version to get the code working and test it:
Code: [Select]
;==============================================================================
include \masm32\include\masm32rt.inc
include \masm32\macros\timers.asm
.686
;==============================================================================
.data
    buff1 db 100 dup(0)
    buff2 db 100 dup(0)
.code
;==============================================================================
.data
    dwtoa_base_digits db "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
.code
dwtoa_base proc src:DWORD, pbuff:DWORD, base:DWORD
    push ebx
    push edi
    mov eax, src
    mov edi, pbuff
    cmp base, 10
    jne @F
    test eax, eax
    jns @F
    neg eax
    mov BYTE PTR [edi], '-'
    inc edi
  @@:
    mov ebx, base
    xor ecx, ecx
  @@:
    cdq
    div ebx
    push edx
    inc ecx
    test eax, eax
    jnz @B
  @@:
    pop eax
    mov dl, dwtoa_base_digits[eax]
    mov [edi], dl
    inc edi
    dec ecx
    jnz @B
    pop edi
    pop ebx
    ret
dwtoa_base endp
;==============================================================================
start:
;==============================================================================

    printf("base 10 negative values\n")
    xor ebx, ebx
    .WHILE SDWORD PTR ebx > -1000000
        invoke dwtoa_base, ebx, ADDR buff1, 10
        invoke crt__itoa, ebx, ADDR buff2, 10
        invoke crt__stricmp, ADDR buff1, ADDR buff2
        invoke RtlZeroMemory, ADDR buff1, SIZEOF buff1
        invoke RtlZeroMemory, ADDR buff2, SIZEOF buff2
        test eax, eax
        jz  @F
        printf("%s\t%s\n", ADDR buff1, ADDR buff2)
      @@:
        dec ebx
    .ENDW

    FOR base,<2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,36>
        printf("base %d\n", base)
        xor ebx, ebx
        .WHILE ebx < 1000000
            invoke dwtoa_base, ebx, ADDR buff1, base
            invoke crt__itoa, ebx, ADDR buff2, base
            invoke crt__stricmp, ADDR buff1, ADDR buff2
            invoke RtlZeroMemory, ADDR buff1, SIZEOF buff1
            invoke RtlZeroMemory, ADDR buff2, SIZEOF buff2
            test eax, eax
            jz  @F
            printf("%s\t%s\n", ADDR buff1, ADDR buff2)
          @@:
            inc ebx
        .ENDW
    ENDM

    printf("\n")

    invoke GetCurrentProcess
    invoke SetProcessAffinityMask, eax, 1

    invoke Sleep, 5000

    REPEAT 4
        counter_begin 1000000, HIGH_PRIORITY_CLASS
        counter_end
        printf("%d cycles\n", eax)

        counter_begin 1000000, HIGH_PRIORITY_CLASS
            invoke dwtoa_base, 123456789, ADDR buff1, 10
        counter_end
        printf("%d cycles\n", eax)

        counter_begin 1000000, HIGH_PRIORITY_CLASS
            invoke crt__itoa, 123456789, ADDR buff2, 10
        counter_end
        printf("%d cycles\n\n", eax)
    ENDM

    inkey
    exit
;==============================================================================
end start

For my application the conversion speed, within reason, does not matter so I used simple, straightforward code, and was somewhat surprised that it is faster than the CRT function, at least when running on my P3.
Code: [Select]
base 10 negative values
base 2
base 3
base 4
base 5
base 6
base 7
base 8
base 9
base 10
base 11
base 12
base 13
base 14
base 15
base 16
base 36

0 cycles
429 cycles
443 cycles

0 cycles
429 cycles
443 cycles

0 cycles
429 cycles
443 cycles

0 cycles
429 cycles
443 cycles

Well Microsoft, here’s another nice mess you’ve gotten us into.