user2499298
user2499298

Reputation: 197

Print 64 bit number stored in EDX:EAX to standard out

I have large 64 bit number stored in EDX:EAX as 21C3677C:82B40000 respectively. I'm trying to print the number out to the console as a decimal 2432902008176640000 Is there a system call that will allow me to accomplish this?

Upvotes: 1

Views: 3458

Answers (3)

hkamphor
hkamphor

Reputation: 11

Here is an example how to print the 64 bit number with printf. This code works with YASM.

segment .data
    format db "Number %ld", 0x0a, 0 ; Format string for printf
    result dq 0                     ; Quad word to store the EDX:EAX result in 

segment .text
    global main
    extern printf

main:
    ; Store the 64 bit number in the quad word "result"
    mov edx, 0x21C3677C  ; Store the EDX value
    mov eax, 0x82B40000  ; Store the EAX value
    mov [result], eax    ; Lower half of "result" will be EAX
    mov [result+4], edx  ; Upper half of "result" will be EDX

    ; Print "result" with the printf function
    xor eax, eax         ; No float parameters for printf
    lea rdi, [format]    ; First parameter for printf (format string)
    mov rsi, [result]    ; Second parameter for printf ("result")
    call printf          ; Call printf

    ; End program
    xor     eax, eax     ; Return 0
    ret   

Build the binary with the following commands:

yasm -f elf64 example.asm
gcc -o example example.o

Update: Example without using memory

segment .data
    format db "Number %ld", 0x0a, 0 ; Format string for printf

segment .text
    global main
    extern printf

main:
    ; Store the 64 bit number in register rsi
    mov edx, 0x21C3677C  ; Store the EDX value (upper half)
    mov eax, 0x82B40000  ; Store the EAX value (lower half)
    mov esi, edx         ; Place upper half in esi
    shl rsi, 32          ; Shift left 32 bits
    or  rsi, rax         ; Place the lower half in rsi

    ; Print "result" with the printf function
    xor eax, eax         ; No float parameters for printf
    lea rdi, [format]    ; First parameter for printf (format string)
    call printf          ; Call printf

    ; End program
    xor     eax, eax     ; Return 0
    ret   

Upvotes: 1

Frank Kotler
Frank Kotler

Reputation: 3119

Someone has to have mercy on this guy and his classmates. If "just call printf" isn't cheating, using this shouldn't be cheating either. I stole this from one of the first asm programs I ever encountered. It used a DOS interrupt to find the disk size and printed dx:ax with commas every three digits. You guys probably don't need the commas. I've tinkered with it for years - probably do an rdx:rax version sometime soon. Great for displaying factorials. It is very naive and inefficient, but still works. Feel free to improve it. After you've got the characters in the buffer, of course, it's just "Hello World" with different lyrics.

;-----------------------------------------------
; u64toda - converts (64 bit) integer in edx:eax
; to (comma delimited) decimal representation in
; zero (was "$") terminated string in buffer pointed to by edi
;----------------------------------------
u64toda:
                pusha
                mov ebx, edx     ; stash high dword
                mov esi,0Ah      ; prepare to divide by 10
                xor ecx, ecx     ; zero the digit count
                jmp highleft     ; check is high word 0 ?
highword:
                xchg eax,ebx    ; swap high & low words
                xor edx,edx     ; zero edx for the divide!
                div esi         ; divide high word by 10
                xchg eax,ebx    ; swap 'em back
                div esi         ; divide low word including remainder
                push edx        ; remainder is our digit - save it
                inc ecx         ; count digits
highleft:
                or ebx,ebx
                jnz highword
lowleft:
                xor edx,edx          ; zero high word
                div esi              ; divide low word by 10
                push edx             ; our digit
                inc ecx              ; count it
                or eax,eax           ; 0 yet ?
                jne lowleft
                cmp ecx, byte 4      ; commas needed ?
                jl write2buf         ; nope
                xor edx,edx            ; zero high word for divide
                mov eax,ecx            ; number of digits
                mov ebx,3
                div ebx
                mov esi,edx            ; remainder = number digits before comma
                test edx,edx
                jnz write2buf        ; no remainder?
                mov esi,3             ; we can write 3 digits, then.
write2buf:
                pop eax              ; get digit back - in right order
                add al,30H           ; convert to ascii character
                stosb                ; write it to our buffer
                dec esi               ; digits before comma needed
                jnz moredigits       ; no comma needed yet
                cmp ecx,2             ; we at the end?
                jl moredigits        ; don't need comma
                mov al,','           ; write a comma
                stosb
                mov esi,03h           ; we're good for another 3 digits
moredigits:
                loop write2buf       ; write more digits - cx of 'em
                mov al,00h           ; terminate buffer with zero
                stosb
                popa
                ret
;------------------------

Upvotes: 1

amdn
amdn

Reputation: 11582

Which operating system? On linux you use the write system call. This is the kind of thing that is done through standard libraries for the language you are using. You'll have to do the conversion from binary to decimal ASCII characters yourself, then point the write system call to that ASCII text, give it the length, and tell it which file descriptor to write to, stdout would be file descriptor 1.

Here's one way of finding out what system call is used in Linux. Write a simple program and invoke it under strace, that will trace all system calls the program makes.

For example, I wrote this C++ program

#include <iostream>
int main( int argc, char* argv[] )
{
    static volatile unsigned long long x = 0x21C3677C82B40000;
    std::cout << x << std::endl;
    return 0;
}

Compiled it as follows:

g++ -O3 main.cpp -o main

Then ran it with strace redirecting stderr to a file main.trace

$ strace -y ./main 2> main.trace
2432902008176640000
$

It wrote the decimal number you expected. Look for I/O to a device by grep'ing for "dev" in the trace

$ grep dev main.trace
fstat(1</dev/pts/3>, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
write(1</dev/pts/3>, "2432902008176640000\n", 20) = 20
$

You'll notice the C++ standard library did an fstat to the file descriptor associated with stdout (1), followed by a write system call with a pointer to the data 2432902008176640000\n and the length 20.

Upvotes: 0

Related Questions