汇编中三个数字的平均值

Posted

技术标签:

【中文标题】汇编中三个数字的平均值【英文标题】:Average of three numbers in assembly 【发布时间】:2018-10-03 20:13:16 【问题描述】:

谁能帮我写一个程序来计算汇编中 3 个数字的平均值(NASM 64 位)?

我尝试了什么:

section .data
    num1 db 3
    num2 db 4
    num3 db 5
    divisor db 3
    digit db 0, 10

section .text
    global _start
    _start:
        mov rax, num1
        mov rax, num2
        div rax, num3
        mov rbx, divisor
        div rbx
        mov rax, 60
        mov rdi, 0
        syscall
    _printRAX:
        add rax, 48
        mov [digit], al
        mov rax, 1
        mov rdi, 1
        mov rsi, digit
        mov rdx, 2
        syscall
        ret

【问题讨论】:

问:操作系统是什么?问:你对那个操作系统做了什么“系统调用”?问:您的具体问题/遇到了什么问题? 我很困惑,你认为在汇编中找到平均值看起来像什么?好像你把一堆东西移到了 rax 中。 mov rax, num1rax 设置为num1 的地址,采用NASM 语法。 mov rax, [num1] 将是一个设置rax = 0x????0a0003050403 的 64 位负载(x86 是小端,db 只有 1 个字节宽)。除此之外,您还没有设置 RDX。对于div,它需要归零。在静态链接的二进制文件中,Linux 会在 _start 之前将其归零,但在其他任何地方,此代码都可能因 SIGFPE 而因#DE 除法异常而崩溃。 不管怎样,你要么想movzx eax, byte [num1] 等到不同的寄存器中为add 设置,要么你想add al, [num2] 等等为后面的数字。 @paulsm4 操作系统是 Linux 64bit (mint)。 【参考方案1】:

好的,这里有两个可能有帮助的例子:

示例 1:

;
; Standalone NASM "Hello world"
;
; BUILD:
; nasm -f elf64 hello.asm
; ld -s -o hello hello.o
;
; EXAMPLE OUTPUT:
; Hello, world!
;
section .text                   ;code section (shareable between processes)
    global  _start              ;loader entry point

_start:
    mov     edx,len             ;arg3: msg len
    mov     ecx,msg             ;arg2: *msg
    mov     ebx,1               ;arg1: 1 (stdout)
    mov     eax,4               ;syscall@4 (sys_write)
    int     0x80

    mov     ebx,0               ;arg1: exit code (0)
    mov     eax,1               ;sycall@1 (sys_exit)
    int     0x80

section .data                   ;data section (per process)
msg db      "Hello, world!",0xa ;our dear string
len equ     $ - msg             ;length of our dear string

示例 2:

;
; "Hello world" using standard C library
;
; BUILD:
; nasm -f elf64 avg3.asm
; gcc -m64 -o avg avg.o
;
; EXAMPLE OUTPUT:
; sum=12
; avg=4
;
extern  printf                  ;stdlib C function

section .text                   ;code section
    global  main                ;standard GCC entry point

main:
    push rbp                    ;set up stack frame: must be aligned

    ; Add 3+4+5
    mov     rax,3
    add     ax,4
    add     ax,5

    ; Save and print sum
    push    rax
    mov     rdi,fmt1            ;printf format string
    mov     rsi,rax             ;1st parameter
    mov     rdx,0               ;No 2nd parameter
    mov     rax,0               ;No xmm registers
    call    printf

    ; Compute and print average
    mov     dx,0                ;Clear dividend, high
    pop     rax                 ;dividend, low <= sum
    mov     cx,3                ;divisor
    div     cx                  ;ax= high, dx= low

    ; Print average
    mov     rdi,fmt2            ;printf format string
    mov     rsi,rax             ;1st parameter
    mov     rdx,0               ;No 2nd parameter
    mov     rax,0               ;No xmm registers
    call    printf

    ; Exit program
    pop rbp
    mov rax,0
    ret

section .data                   ;data section
fmt1:
    db      "sum=%d",0xa,0
fmt2:
    db      "avg=%d",0xa,0

注意事项:

就个人而言,我更喜欢“Gnu Assembler”(gas)。它使在不同架构之间以及在 C 和内联汇编程序之间切换变得容易,而不会出现处理英特尔语法的“认知失调”;)

我强烈建议您尽可能多地利用标准 C 库。实际上,这意味着使用gcc 而不是ld 链接您的可执行文件。

您的“计算平均值”程序就是一个很好的例子,原因是:让 printf 格式化程序计算出正确的输出要容易得多,而不是将二进制值转换为 ASCII 数字,然后弄清楚如何将它们格式化为字符串手动。

'希望有帮助!

【讨论】:

在您的第一个示例中,int 0x80 仅在 64 位代码中工作,因为您在静态缓冲区上使用 sys_write,并且您创建了一个位置相关(非 PIE)可执行文件因此静态代码/数据位于虚拟地址空间的低 32 位。 What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?. 点得好 - 谢谢。我只是想创建“最简单的示例”。如果您提出“更好的建议”,我很乐意更新示例。 在您的第二个示例中,您通过调用 printf 而堆栈未按 16 对齐(函数进入后的 2x push)违反了 ABI。它恰好适用于当前的 glibc(AL=0),但允许出现段错误。不要使用 RBP 作为帧指针,而是使用它来保存总和。例如mov ebp, 3 / add ebp, 4+5 / mov esi, ebp / ... printf / mov eax, ebp / xor edx,edx / div / mov esi, eax。 (或者只使用 3 个 args 进行一次 printf 调用,因此您不需要保存/恢复它的总和。)在这里使用 16 位寄存器也没有任何好处。 修复您的第一个示例:OP 已经使用 64 位 sys_writesys_exit,而我对 What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code? 的回答有一个 64 位 Hello World(包括一些额外的东西要制作该答案的一个例子,但它可以被剥离。) 问:push rax 的哪一部分没有使堆栈以 16 的倍数对齐?我不是在“争论”——我只是好奇在这个例子中什么是“有效的推动”。

以上是关于汇编中三个数字的平均值的主要内容,如果未能解决你的问题,请参考以下文章

编写程序,输入三个数字,输出三个数字平均值。提示:利用eval()函数

如何从包含数字和字母的字符串中计算平均值?

1.怎样理解平均负载?

彻底理解CPU Load-这一篇就够了

彻底理解CPU Load-这一篇就够了

获取数据流的平均值、p95 和 p99