【问题标题】:Finding the nth prime in nasm x86_64 assembly在 nasm x86_64 程序集中查找第 n 个素数
【发布时间】:2021-11-04 22:16:41
【问题描述】:

我正在尝试编写一个程序来查找第 n 个素数 - 在本例中为第 10001 个素数。

目前,程序将每个数字都检测为质数,所以我的结果最终是 10002。

在 GDB 中单步执行程序时,从要除的素数数组中检索的值不是预期的 - 即在确定 3 是素数之后,下一次迭代 4,程序从数组中检索 770。

代码是:

%include '../resources.asm'

SECTION .data

SECTION .text
global main
extern malloc, free, calloc
main:
    ; Allocate space for primes
    mov rsi, 8
    mov rdi, 10001
    call calloc
    ; Store address in r10
    mov r10, rax
    ; First prime is 2
    mov qword [r10], 2
    ; r11 is current number of primes found
    mov r11, 1
    ; r12 is current number being checked
    mov r12, 2
.outer:
    ; Move to next number
    inc r12
    ; Reset array index
    mov rcx, 0
.inner:
    ; Get number and divisor in rax and rbx respectively
    mov rax, r12
    mov qword rbx, [r10 + rcx] ;;;; Issue here, rbx is 770?
    ; Increment array index
    inc rcx
    ; Get modulus
    call umod
    ; If modulus is 0, number is not prime, move to next number
    cmp rax, 0
    jz .outer
    ; See if we've hit the end of current list of primes
    cmp rcx, r11
    jnz .inner
    ; If we are at the end of the list of primes, move the current number into the array
    mov qword [r10 + rcx], r12
    inc r11
    ; Stop if we've got the 10001st prime
    cmp r11, 10001
    jl .outer

还有来自资源的 umod sn-p

;
; int udiv(int rax, int rbx) -> rax, rbx
; Unsigned division
udiv:
    push rdx

    xor rdx, rdx
    div rbx
    mov rbx, rdx

    pop rdx
    ret

;
; int umod(int rax, int rbx) -> rax
; Unsigned modulus operation
umod:
    call udiv
    mov rax, rbx

    ret

注意,我意识到我可能没有遵循参数传递等的正确约定。如果您认为对您有帮助,请随时做笔记。

【问题讨论】:

  • 在存储 8 字节实体时,您需要将索引缩放到主缓冲区中。
  • @500-InternalServerError 谢谢。我不知道为什么我没有考虑扩展我的索引!

标签: assembly x86-64 nasm


【解决方案1】:

正如 cmets 中所指出的,我没有将索引寄存器缩放 8 以补偿存储的 8 字节值。生成的固定代码在这里:

%include '../resources.asm'

SECTION .data

SECTION .text
global main
extern malloc, free, calloc
main:
    ; Allocate space for primes
    mov rsi, 8
    mov rdi, 10001
    call calloc
    ; Store address in r10
    mov r10, rax
    ; First prime is 2
    mov qword [r10], 2
    ; r11 is current number of primes found
    mov r11, 1
    ; r12 is current number being checked
    mov r12, 2
.outer:
    ; Move to next number
    inc r12
    ; Reset array index
    mov rcx, 0
.inner:
    ; Get number and divisor in rax and rbx respectively
    mov rax, r12
    mov r13, rcx
    imul r13, 8
    mov qword rbx, [r10 + r13]
    ; Increment array index
    inc rcx
    ; Get modulus
    call umod
    ; If modulus is 0, number is not prime, move to next number
    cmp rax, 0
    jz .outer
    ; See if we've hit the end of current list of primes
    cmp rcx, r11
    jnz .inner
    ; If we are at the end of the list of primes, move the current number into the array
    mov r13, rcx
    imul r13, 8
    mov qword [r10 + r13], r12
    inc r11
    ; Stop if we've got the 10001st prime
    cmp r11, 10001
    jb .outer

    ; Print out result
    mov rax, r12
    call itoa
    mov rdi, rax
    call sprintlf
    call free



    call exit

【讨论】:

  • 实际上,有一种更简洁的方法可以做到这一点:mov qword rbx, [r10 + r13*8] - 现在,我无法确定它是否适用于任何索引寄存器,但我认为它可能适用于 64 位至少。这仅适用于值 2、4、8(当然还有隐含的 1)。
  • 这样更简洁了,谢谢!针对每个问题学习新事物。
  • 或者你可以只增加一个指针8,所以你只需要一个寄存器。此外,您不需要将函数包装在像 div 这样的指令周围,这更难阅读。如果你想分解成函数,你可以编写一个循环,直到找到某个起点上方的下一个素数,然后迭代它。或者不是,因为您正在使用现有的素数列表进行试除,而不是例如只是奇数。仍然比筛子慢,但比例如更好。只是尝试每个奇数除数,只要你停在 sqrt (当商
  • 相关:Checking if a number is prime in NASM Win64 Assembly 代码审查回复:素数检查显示商 divisor <= sqrt(n) 条件的一种方式。
  • 顺便说一句,如果您要进行乘法运算,imul r13, rcx, 8 可以进行复制和移位。或mov/shl r13, 3。当然,最好将其作为寻址模式的一部分,或者像我说的那样,更好地增加一个指针,这样寻址模式就是一个简单的单寄存器div qword [r11] 或其他东西。请注意,R12..R15 在 x86-64 SysV 中保留调用,如 RBX、RBP 和 RSP。 Main 的调用者通常不在乎,但在不保存/恢复的情况下修改它们仍然违反 ABI。
猜你喜欢
  • 1970-01-01
  • 2019-08-02
  • 2014-01-27
  • 1970-01-01
  • 2022-10-14
  • 2015-02-21
  • 2011-04-22
  • 2012-01-07
  • 1970-01-01
相关资源
最近更新 更多