Assembly 将存储在EDX:EAX中的64位数字打印到标准输出

Assembly 将存储在EDX:EAX中的64位数字打印到标准输出,assembly,x86,system-calls,32-bit,Assembly,X86,System Calls,32 Bit,我在EDX:EAX中存储了大的64位数字,分别为21C3677C:82B40000。我正试图将数字以十进制2432902008176640000打印到控制台。是否有系统调用允许我完成此操作?哪个操作系统?在linux上,您使用write系统调用。这是通过您正在使用的语言的标准库来完成的。您必须自己完成从二进制ASCII字符到十进制ASCII字符的转换,然后将write系统调用指向该ASCII文本,给出其长度,并告诉它要写入哪个文件描述符,stdout将是文件描述符1 这里有一种方法可以找出Lin

我在EDX:EAX中存储了大的64位数字,分别为21C3677C:82B40000。我正试图将数字以十进制2432902008176640000打印到控制台。是否有系统调用允许我完成此操作?

哪个操作系统?在linux上,您使用
write
系统调用。这是通过您正在使用的语言的标准库来完成的。您必须自己完成从二进制ASCII字符到十进制ASCII字符的转换,然后将
write
系统调用指向该ASCII文本,给出其长度,并告诉它要写入哪个文件描述符,stdout将是文件描述符
1

这里有一种方法可以找出Linux中使用了什么系统调用。编写一个简单的程序并在
strace
下调用它,该程序将跟踪程序进行的所有系统调用

例如,我写了这个C++程序< /p>
#include <iostream>
int main( int argc, char* argv[] )
{
    static volatile unsigned long long x = 0x21C3677C82B40000;
    std::cout << x << std::endl;
    return 0;
}
然后使用
strace
将stderr重定向到文件
main.trace

$ strace -y ./main 2> main.trace
2432902008176640000
$
它写的是你期望的十进制数。通过在跟踪中搜索“dev”来查找设备的I/O

$ 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
$
$grep dev main.trace
fstat(1,{st_mode=S_IFCHR | 0620,st_rdev=makedev(136,3),…})=0
写入(1,“2432902008176640000\n”,20)=20
$

<>你会注意到C++标准库对<代码> STDUD> <代码>(1)的文件描述符做了<代码> fSTAT <代码>,随后是“代码>写/代码>系统调用,指针指向数据<代码> 243290200817664000 \n/COD>长度>代码> 20 < < /P> > P有人必须怜悯这家伙和他的同学。如果“justcallprintf”不是作弊,那么使用它也不应该是作弊。我从我遇到的第一个asm程序中偷了这个。它使用DOS中断查找磁盘大小,并每隔三位数打印一次带有逗号的
dx:ax
。你们可能不需要逗号。我已经对它进行了多年的修补——可能很快就会有一个
rdx:rax
版本。非常适合显示阶乘。这是非常幼稚和低效的,但仍然有效。请随意改进它。当然,在缓冲区中有了角色之后,就只有“Hello World”和不同的歌词了

;----------------------------------------------- ; 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 ;------------------------
;----------------------------------------------- ; u64toda-转换edx:eax中的(64位)整数 ; 到中的(逗号分隔的)十进制表示形式 ; edi指向的缓冲区中以“$”结尾的字符串为零 ;---------------------------------------- u64toda: 普沙 mov ebx、edx;藏高德沃德 movesi,0Ah;准备除以10 异或ecx,ecx;将数字计数归零 jmp左上角;检查是否为高位字0? 高地: xchg-eax,ebx;交换高低字 xor-edx,edx;零edx表示除法! 分区esi;将高单词除以10 xchg-eax,ebx;换回来 分区esi;除低位字,包括余数 推动edx;余数是我们的数字-保存它 ecx公司;数数数字 左上方: 还是ebx,ebx jnz高字 低空: xor-edx,edx;零高位字 分区esi;将低位字除以10 推动edx;我们的数字 ecx公司;数一数 或eax,eax;0了吗? 低空 cmp-ecx,字节4;需要逗号吗? jl write2buf;不 xor-edx,edx;零高位字表示除法 mov-eax,ecx;位数 mov-ebx,3 div ebx mov esi,edx;余数=逗号前的数字 测试edx,edx jnz write2buf;没有余数? movesi,3;那么我们可以写3位数字。 write2buf: pop-eax;以正确的顺序返回数字 添加铝,30小时;转换为ascii字符 stosb;将其写入缓冲区 dec esi;需要逗号前的数字 jnz-moredigits;还不需要逗号 cmp-ecx,2;我们到底在干什么? jl-更多数字;不需要逗号 mov al,',';写一个逗号 斯托斯 movesi,03h;我们还有3位数 更多数字: 循环写入2buf;写更多的数字-它们的cx mov-al,00h;用零终止缓冲区 斯托斯 波帕 ret ;------------------------
下面是一个如何使用printf打印64位数字的示例。这段代码与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   
使用以下命令生成二进制文件:

yasm -f elf64 example.asm
gcc -o example example.o
更新:不使用内存的示例

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   

为什么要通过内存获取
edx:eax
rsi
?shift/or或shift/shld将完成此操作。更有趣的是使用32位代码的示例。(这应该仍然有效:我认为32位系统上的printf应该仍然支持
int64_t
)。当然可以使用
rsi
或其他寄存器,但我认为这样的代码更具可读性。我用一个额外的例子更新了答案,没有使用内存空间。你真的不需要一个
div
3。只需使用向下计数器并在其变为零时插入逗号(并将计数器重置为3)<代码>如果(!--counter){counter=3;插入逗号;}。i、 e.
dec ebp/jz.逗号和重置
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