埃拉托色尼筛 x86 组装

Sieve of Eratosthenes x86 Assembly

我是汇编新手,正在尝试实现 Erathothenes 筛法,我有代码,但它只能在 1 到 600 之间工作,出于某种原因,当我输入 n=1000 时它会失控,这是完整的代码,任何帮助都可以。

Include Irvine32.inc
n=1000

.data
prime DWORD n DUP(?)

.code
main PROC
        mov ecx,n       ;initialize ecx to 1000
        mov esi, OFFSET prime
;--------------------------------------------------------
;   Initialize Array with 1's
;--------------------------------------------------------
FillArray:  mov eax,1
        mov [esi],eax
        mov eax,[esi]
        add esi,TYPE DWORD              ;4 to jump to the next index
        loop FillArray

    mov ebx,8
    mov edx,0
    mov ecx,n
    mov ebp,2
    mov esi,0
;----------------------------------------------------------------
;   Sieve
;----------------------------------------------------------------
SIEVE:      mov eax,prime[ebx]
        mov edx,ebx
        mov edi,ebx
        add edx,edi                 ;j=i+i
        mov esi,ebp
        mov ecx,n
        sub ecx,ebp
        add ecx,2
        add ebx,TYPE DWORD
        cmp eax,1
        je FLIP
        inc ebp
        loop SIEVE
        jmp K

FLIP:       mov eax,0
        mov prime[edx],eax
        add edx,edi
        cmp esi,n
        jg SIEVE
        add esi,ebp
        loop FLIP

K:

    mov esi, OFFSET prime
    mov ecx,n
    sub ecx,2   
    mov ebx,1
    mov edx,8           ;Start checking from index of second element

PRINT:  mov eax,prime[edx]          ;
        add edx,TYPE DWORD
        inc ebx
        cmp eax,1
        jne H
        mov eax,ebx
        call WriteDec
        call Crlf
        loop PRINT
        jmp D
H: loop PRINT
D:
    exit

main ENDP
END main

你对 "going haywire" 的含义不够具体,你可能没有调试你的代码,也没有对它进行足够的评论以使其易于理解,所以在看了几分钟后我跟着我的直觉告诉我从头开始并以常规方式编写它。

这是否对您有帮助 - 我不知道,可能不会,至少在不付出巨大 "analyse this" 努力的情况下不会。

但我相信这个标题为"Sieve of Eratosthenes x86 Assembly"[=的问题的有效答案55=].

所以你开始吧,尝试完全理解外国来源并尝试掌握所使用的思想;在某种程度上,它会在您编写自己的代码时开始为您提供帮助。祝你好运。 :)


首先,在写一些代码之前,您应该首先清理数学方面的任务。

如果你愿意,你会知道没有必要 "sieve" 进一步超出 sqrt(n) 值。

因为如果 sqrt(n) 是素数,那么它乘以 2*sqrt(n), 3*sqrt(n), ..., previous_prime *sqrt(n) 已经 "sieved" 来自循环中较早筛选的那些较小的素数。

第一个 "yet unsieved" 数字是 sqrt(n) * sqrt(n),即 n 本身,并且超出了数组边界。所以在达到 number >= sqrt(n) 之后你可以结束你的算法主循环(我正在使用尖锐的 number > sqrt(n) 测试,因为 sqrt(n) 被截断为整数,所以对于特殊 "n"(整数 sqrt(n))我会尝试 "sieve" n 本身一次,但是 flip_loop 内的测试会检测到并终止。


Include Irvine32.inc
n=1000
n_terminal=31
    ; terminal compare value is min(sqrt(n), 0FFFFh) (last value to process)
    ; no point to sieve beyond sqrt(n), because first unset multiply is
    ; sqrt(n)*sqrt(n) = n => beyond array
    ; and no point to sieve 10000h, because 10000h*10000h is out of 32b

.data
prime BYTE n DUP(?)

.code
main PROC
    ; fill array with "1" (from number 2 to n)
    mov   edi,OFFSET prime+2
    mov   ecx,n-2
    mov   eax,1         ; eax=1 will be used also later (also ah=0)
    rep stosb
    ; set "0" and "1" as non-prime too
    mov   WORD PTR [prime],cx   ; (cx == 0 after "rep stosb")
    ; set "0" to all other non-primes by sieving
    mov   esi,eax       ; esi=1, start the loop as if "1" was handled last
sieve_loop:
    inc   esi           ; next number to test
    ; sqrt(n) is enough to process, beyond that n <= number*number => out of array
    cmp   esi,n_terminal
    ja    sieve_end     ; out of table, end sieving
    cmp   prime[esi],al ; compare with "1"
    jne   sieve_loop    ; not a prime, don't flip the rest of sieve
    ; esi is prime, flip the other multiplies of it, starting from esi*esi (!)
    mov   edi,esi       ; esi*esi as smaller multiples are already covered by
    imul  edi,edi       ; smaller prime numbers sieving the array before
flip_loop:
    cmp   edi,n         ; check if the multiply points beyond table
    jae   sieve_loop    ; if yes, continue main loop with next "prime"
    mov   prime[edi],ah ; set this number to "0" (not prime)
    add   edi,esi       ; edi = next multiply of esi prime
    jmp   flip_loop

sieve_end:
    ; print all primes starting from 2
    mov   esi,eax       ; esi = 1, as if "1" was handled last
print_loop:
    inc   esi           ; while (++number < n)
    cmp   esi,n
    jae   end_print_loop
    cmp   BYTE PTR prime[esi],1 ; eax/al is modified from "WriteDec"
    jne   print_loop    ; not a prime number, loop
    ; esi is prime number, print it
    mov   eax,esi
    call  WriteDec      ; these two calls must preserve esi
    call  Crlf
    jmp   print_loop

end_print_loop:
    exit

main ENDP
END main

我确实在 linux 和 NASM 下测试过它,所以我不得不稍微更改语法来编译它,然后将语法改回 MASM/TASM + irvine,所以可能在转换过程中出现了一些小错误,如果它不起作用请告诉我。

对于仍然有此问题的任何人,我已经从 python 到汇编实现了以下代码。

from math import sqrt

def FindPrimes(limit):
    isPrime = {}

    isPrime[1] = False
    for i in range(2, limit + 1):
        isPrime[i] = True

    checkLimit = int(sqrt(limit)) + 1
    for i in range(2, checkLimit):
        if isPrime[i]:
            for factor in range(2, limit + 1):
                j = i * factor
                if (j > limit): break
                isPrime[j] = False

    primes = []
    for i in range(1, limit + 1):
        if isPrime[i]:
            primes.append(i)

    return primes

请注意,以下程序集实现是在 MASM 中,它需要您手动输入平方根加 1,因为我们将第一个 DWORD 处理为 "isPrime[1]",当然还有极限 "FindPrimes(limit)".

.386
.model flat,stdcall
.stack 4096
ExitProcess proto,dwExitCode:dword

n=1000
sqrt=32

.data
    sieveArray DWORD n DUP(0)
    primeNumbers DWORD n DUP(0)
    limit DWORD 0
    varInnerLoop DWORD 0                            ;variable j
    falseValue DWORD 0
    primerHelper DWORD 1

.code
 main PROC

    mov ecx,LENGTHOF sieveArray-1
    mov edi,OFFSET sieveArray+TYPE DWORD*2
    mov eax,1

fillUp:
    mov [edi],eax
    add edi,TYPE DWORD
    loop fillUp

checkForPrime:
    mov edi,OFFSET sieveArray+TYPE DWORD*2      ;reset for iteration
    mov esi,[edi]                               ;pointer helper
    mov eax,TYPE DWORD                          ;offset helper
    mov ebx,1                                   ;counter for loopOverNumbers reference
    mov ecx,sqrt-1                              ;sqrt(limit)+1 `limit`¨, -1 because index 0 = index 1, counter
    mov edx,1                                   ;outer loop variable helper for inner loop

    loopOverNumbers:
        cmp ebx,ecx
        je iterateOverPrimes                    ;take me out of the loop because I have iterated over range(1,edx)
        cmp esi,1
        pushad                                  ;save my flags for outer loop, specially ebx and ecx
        je iterateOverFactorsSetUp
        continueIteration:
        popad
        add edi,eax
        mov esi,[edi]
        inc edx
        loop loopOverNumbers

            iterateOverFactorsSetUp:
                mov eax,1                           ;factor for inner loop          
                mov ecx,n+1

                iterateOverFactors:
                    cmp ebx,ecx
                    je continueIteration
                    push edx
                    push eax
                    inc eax                         ;pointer must increment to reflect real value
                    inc edx                         ;pointer must increment to reflect real value
                    imul edx,eax
                    mov varInnerLoop,edx            ;j = i * factor
                    cmp varInnerLoop,n              ;if (j > limit): break
                    jg continueIterationHelper
                    imul edx,TYPE DWORD
                    mov edi,OFFSET sieveArray
                    add edi,edx
                    mov eax,0
                    mov [edi],eax                   ;reset to old value before pointer
                    pop eax
                    pop edx                         
                    inc eax
                    loop iterateOverFactors

            continueIterationHelper:                ;have to pop eax and edx in order to get original values when returning to 
                    pop eax
                    pop edx 
                    jmp continueIteration

iterateOverPrimes:
    mov eax,TYPE DWORD
    mov ecx,n+1                             ;limit helper
    mov ebx,0
    mov edi,OFFSET sieveArray+TYPE DWORD

    checkifPrime:
        mov esi,[edi]
        cmp esi,1
        pushad
        je appendToPrimeArray
        continueSearch:
        popad
        add edi,eax
        inc ebx
        loop checkifPrime
        jmp searchDone

        appendToPrimeArray:
            mov eax,TYPE DWORD
            mov edi,OFFSET primeNumbers
            imul eax,primerHelper                       ;pointer for primeNumbers helper
            add edi,eax
            inc ebx
            mov [edi],ebx
            inc primerHelper
            jmp continueSearch

    searchDone:


INVOKE ExitProcess,0
main ENDP
END main

为了检查您的结果,您可以检查 primeNumbers,它将连续包含每个素数作为 DWORD。

如果您有任何疑虑,请告诉我:)