eratosthenes筛子 - 装配80x86

时间:2018-03-31 18:16:44

标签: assembly x86 masm sieve-of-eratosthenes

我已经写了一个Eratosthenes算法的筛子,但我有一个奇怪的错误,我无法弄清楚。

该程序要求用户输入一个数字,然后程序将该数量的素数输出到屏幕。 该程序正确打印所有素数,除了11之后,它将打印3个垃圾数字并跳过数字13,然后它将从17开始继续正确的素数。下面是20个素数的示例输出。

> Enter number of primes:
20....
prime number:            1
prime number:            2
prime number:            3
prime number:            5
prime number:            7
prime number:           11
prime number:    538976288
prime number:    909588792
prime number:      3291447
prime number:           17
prime number:           19
prime number:           23
prime number:           29
prime number:           31
prime number:           37
prime number:           41
prime number:           43
prime number:           47
prime number:           53
prime number:           59

这些数字存储在一个数组中,并且在sieve.asm中我放了一个名为“PrintLoop2”的标签,我用它来查看数组中的每个值,我可以看到13列在那里,没有垃圾,所以我不确定为什么会这样。

Sieve.asm是主程序,genprimes.asm创建素数并将它们放在堆栈上,其他文件用于I / O.

sieve.asm:

.586
.MODEL FLAT
INCLUDE io.h
EXTERN GenPrimes2:PROC
PUBLIC genPrimes
.STACK 4096                 ; reserve 4096-byte stack
.DATA                       ; reserve storage for data

count    DWORD   ?
sieve    BYTE    10000 DUP(1)
string   BYTE    40 DUP (?)
prompt1  BYTE    "Enter number of primes: ", 0
prompt2  BYTE    "prime number: ", 0
prompt3  BYTE    ", ", 0
primenum DWORD    11 DUP (?), 0
prime    BYTE     11 DUP (?), 0

.CODE

genPrimes   PROC
           ; push   ebp                  ; save base pointer
           ; mov    ebp, esp             ; establish stack frame
           ; push   ebx
            ; CODE
            call GenPrimes2 ;call function in genprimes.asm to push primes to stack

            sub esp, 4    ;move esp down
            sub esp, 4    ;esp points to first value

            mov ebx, 4 ; counter
            mov ecx, 0 ; index register to hold value of esp that will be put into primenum array


            loopArray:         ;this loop fills primenum with all primes put on the stack in genprimes.asm
                    mov ecx, [esp]
                    sub esp, 4
                    mov primenum[ebx], ecx
                    add ebx, 4
                    cmp ebx, 2200
            jb  loopArray

                mov ebx, 4
                mov eax, 0
                ;This loop is for debug purposes only, i want to see if the array primenum has the value 13, which is does
                ;because i can see it get copied into ecx. However, i get garbage in my output where 13 should be.
                PrintLoop2:             
                    mov ecx, primenum[ebx] ; Prime numbers are the non-zeros in this Array
                    add ebx, 4
                    cmp ebx, 400
                jb  PrintLoop2

                mov ebx, 4
                mov eax, 0

                add esp,2204  ;move esp back to return address
                ret                         ;exit genPrimes
genPrimes    ENDP
_sieve  PROC                            ; start of sieve program code
      input   prompt1, string, 40       ; read ASCII characters
      call  genPrimes
                atod string ; convert to integer the number of primes the user entered
                mov edx, 0

                ;this loop will print all the non-zero values stored in the array primenum, i have set all non-primes to 0's so that only
                ;they will be printed
                PrintLoop:
                mov ecx, primenum[ebx] ; Prime numbers are the non-zeros in this Array
                cmp primenum[ebx], 0
                jne printPrime

                add ebx, 4
                jmp  PrintLoop

                printPrime:
                dtoa prime, ecx ;convert the prime number to a string for printing
                output  prompt2, prime       ; output label and sum
                add ebx, 4
                inc edx
                cmp edx, eax
                jb  PrintLoop


      mov   eax, 0          ; exit with return code 0
      ret

_sieve  ENDP

END

genprimes.asm:

.586
.MODEL FLAT


.STACK 4096                 
n=550
.data
    prime DWORD n DUP(?)

.code
GenPrimes2  PROC
mov ebx, 4
mov ecx, 0
loopArray:
    inc ecx
    mov prime[ebx], ecx
    add ebx, 4
    cmp ecx, n
jb  loopArray

mov eax, 3
mov ebx, 2
mov edx, 0

mov ecx,3

sieve_loop:

    cmp eax,ebx
    je skip

    mov edx, 0 ;zero out remainder
    div  ebx
    cmp edx,0 ; if remainder 0, not a prime
    je    NotPrime ;Jump if is a factor, since it cant be prime

; compare eax with n, if equal increment ebx
    cmp ecx,n
    jge    incrementEbx

;  compare ebx with n, if equal end sieve
    cmp ebx, n
    je sieve_end

    inc ecx
    mov eax, ecx

jmp sieve_loop

skip:
inc eax
jmp sieve_loop


NotPrime:
    mov eax, ecx ; store count in eax
    imul ecx, 4
    mov prime[ecx],0
    mov ecx, eax
    inc ecx ; increment ecx count
    inc eax ; increment eax divisor
    jmp sieve_loop

incrementEbx:
inc ebx
mov eax, 3 ; dividend
mov ecx, 3 ; counter

jmp sieve_loop


sieve_end:
    mov ebx, 4
    mov eax, 0
; *************  Add break point on print loop, ecx will be loading with primes and 0's  ********************
; *************  All non-prime numbers have been changed to a 0                          ********************


    PrintLoop:
    mov ecx, prime[ebx] ; Prime numbers are the non-zeros in this Array
    push   ecx
    add ebx, 4
    cmp ebx, 2200
    jb  PrintLoop

        add esp,2196
    mov   eax, 0          ; exit with return code 0
    ret
GenPrimes2 ENDP
END

io.asm:

.586
.MODEL FLAT
PUBLIC wtoaproc, atowproc, dtoaproc, atodproc

.CODE

; wtoaproc(source, dest)
; convert integer (source) to string of 6 characters at given destination address
; source integer passed as a doubleword, but only low-order word is processed
wtoaproc    PROC
            push   ebp                  ; save base pointer
            mov    ebp, esp             ; establish stack frame
            push   eax                  ; Save registers
            push   ebx
            push   ecx
            push   edx
            push   edi
            pushfd                     ; save flags

            mov    eax, [ebp+8]        ; first parameter (source integer)
            and    eax, 0ffffh         ; mask high-order word
            mov    edi, [ebp+12]       ; second parameter (dest offset)
ifSpecW:    cmp    ax,8000h            ; special case -32,768?
            jne    EndIfSpecW          ; if not, then normal case
            mov    BYTE PTR [edi],'-'  ; manually put in ASCII codes
            mov    BYTE PTR [edi+1],'3'  ;   for -32,768
            mov    BYTE PTR [edi+2],'2'
            mov    BYTE PTR [edi+3],'7'
            mov    BYTE PTR [edi+4],'6'
            mov    BYTE PTR [edi+5],'8'
            jmp    ExitIToA            ; done with special case
EndIfSpecW:

            push eax                   ; save source number

            mov    al,' '              ; put blanks in
            mov    ecx,5               ;   first five
            cld                        ;   bytes of
            rep stosb                  ;   destination field

            pop    eax                 ; restore source number
            mov    cl,' '              ; default sign (blank for +)
IfNegW:     cmp    ax,0                ; check sign of number
            jge    EndIfNegW           ; skip if not negative
            mov    cl,'-'              ; sign for negative number
            neg    ax                  ; number in AX now >= 0
EndIfNegW:

            mov    bx,10               ; divisor

WhileMoreW: mov    dx,0                ; extend number to doubleword
            div    bx                  ; divide by 10
            add    dl,'0'              ; convert remainder to character
            mov    [edi],dl            ; put character in string
            dec    edi                 ; move forward to next position
            cmp    ax,0                ; check quotient
            jnz    WhileMoreW          ; continue if quotient not zero

            mov    [edi],cl            ; insert blank or "-" for sign

ExitIToA:   popfd                      ; restore flags and registers
            pop    edi
            pop    edx
            pop    ecx
            pop    ebx
            pop    eax
            pop    ebp
            ret                        ;exit
wtoaproc    ENDP

; dtoaproc(source, dest)
; convert double (source) to string of 11 characters at given destination address
dtoaproc    PROC   NEAR32
            push   ebp                 ; save base pointer
            mov    ebp, esp            ; establish stack frame
            push   eax                 ; Save registers
            push   ebx                 ;   used by
            push   ecx                 ;   procedure
            push   edx
            push   edi
            pushfd                      ; save flags

            mov    eax, [ebp+8]         ; first parameter (source double)
            mov    edi, [ebp+12]        ; second parameter (dest addr)
ifSpecialD: cmp    eax,80000000h        ; special case -2,147,483,648?
            jne    EndIfSpecialD        ; if not, then normal case
            mov    BYTE PTR [edi],'-'   ; manually put in ASCII codes
            mov    BYTE PTR [edi+1],'2' ;   for -2,147,483,648
            mov    BYTE PTR [edi+2],'1'
            mov    BYTE PTR [edi+3],'4'
            mov    BYTE PTR [edi+4],'7'
            mov    BYTE PTR [edi+5],'4'
            mov    BYTE PTR [edi+6],'8'
            mov    BYTE PTR [edi+7],'3'
            mov    BYTE PTR [edi+8],'6'
            mov    BYTE PTR [edi+9],'4'
            mov    BYTE PTR [edi+10],'8'
            jmp    ExitDToA            ; done with special case
EndIfSpecialD:

            push   eax                 ; save source number

            mov    al,' '              ; put blanks in
            mov    ecx,10              ;   first ten
            cld                        ;   bytes of
            rep stosb                  ;   destination field

            pop    eax                 ; copy source number
            mov    cl,' '              ; default sign (blank for +)
IfNegD:     cmp    eax,0               ; check sign of number
            jge    EndIfNegD           ; skip if not negative
            mov    cl,'-'              ; sign for negative number
            neg    eax                 ; number in EAX now >= 0
EndIfNegD:

            mov    ebx,10              ; divisor

WhileMoreD: mov    edx,0               ; extend number to doubleword
            div    ebx                 ; divide by 10
            add    dl,30h              ; convert remainder to character
            mov    [edi],dl            ; put character in string
            dec    edi                 ; move forward to next position
            cmp    eax,0               ; check quotient
            jnz    WhileMoreD          ; continue if quotient not zero

            mov    [edi],cl            ; insert blank or "-" for sign

ExitDToA:   popfd                      ; restore flags and registers
            pop    edi
            pop    edx
            pop    ecx
            pop    ebx
            pop    eax
            pop    ebp
            ret                        ;exit
dtoaproc    ENDP

; atowproc(source)
; Procedure to scan data segment starting at source address, interpreting
; ASCII characters as an word-size integer value which is returned in AX.

; Leading blanks are skipped.  A leading - or + sign is acceptable.
; Digit(s) must immediately follow the sign (if any).
; Memory scan is terminated by any non-digit.

; No error checking is done. If the number is outside the range for a
; signed word, then the return value is undefined.

atowproc    PROC
            push   ebp                 ; save base pointer
            mov    ebp, esp            ; establish stack frame
            sub    esp, 2              ; local space for sign
            push   ebx                 ; Save registers
            push   edx
            push   esi
            pushfd                     ; save flags

            mov    esi,[ebp+8]         ; get parameter (source addr)

WhileBlankW:cmp    BYTE PTR [esi],' '  ; space?
            jne    EndWhileBlankW      ; exit if not
            inc    esi                 ; increment character pointer
            jmp    WhileBlankW         ; and try again
EndWhileBlankW:

            mov    ax,1                ; default sign multiplier
IfPlusW:    cmp    BYTE PTR [esi],'+'  ; leading + ?
            je     SkipSignW           ; if so, skip over
IfMinusW:   cmp    BYTE PTR [esi],'-'  ; leading - ?
            jne    EndIfSignW          ; if not, save default +
            mov    ax,-1               ; -1 for minus sign
SkipSignW:  inc    esi                 ; move past sign
EndIfSignW:

            mov    [ebp-2],ax          ; save sign multiplier
            mov    ax,0                ; number being accumulated

WhileDigitW:cmp    BYTE PTR [esi],'0'  ; next character >= '0'
            jnge   EndWhileDigitW      ; exit if not
            cmp    BYTE PTR [esi],'9'  ; next character <= '9'
            jnle   EndWhileDigitW      ; not a digit if bigger than '9'
            imul   ax,10               ; multiply old number by 10
            mov    bl,[esi]            ; ASCII character to BL
            and    bx,000Fh            ; convert to single-digit integer
            add    ax,bx               ; add to sum
            inc    esi                 ; increment character pointer
            jmp    WhileDigitW         ; go try next character
EndWhileDigitW:

; if value is < 8000h, multiply by sign
            cmp    ax,8000h            ; 8000h?
            jnb    endIfMaxW           ; skip if not
            imul   WORD PTR [ebp-2]    ; make signed number
endIfMaxW:

            popfd                      ; restore flags
            pop    esi                 ; restore registers
            pop    edx
            pop    ebx
            mov    esp, ebp            ; delete local variable space
            pop    ebp
            ret                        ; exit
atowproc    ENDP

; atodproc(source)
; Procedure to scan data segment starting at source address, interpreting
; ASCII characters as an doubleword-size integer value which is returned in EAX.

; Leading blanks are skipped.  A leading - or + sign is acceptable.
; Digit(s) must immediately follow the sign (if any).
; Memory scan is terminated by any non-digit.

; No error checking is done. If the number is outside the range for a
; signed word, then the return value is undefined.

atodproc    PROC
            push   ebp                 ; save base pointer
            mov    ebp, esp            ; establish stack frame
            sub    esp, 4              ; local space for sign
            push   ebx                 ; Save registers
            push   edx
            push   esi
            pushfd                     ; save flags

            mov    esi,[ebp+8]         ; get parameter (source addr)

WhileBlankD:cmp    BYTE PTR [esi],' '  ; space?
            jne    EndWhileBlankD      ; exit if not
            inc    esi                 ; increment character pointer
            jmp    WhileBlankD         ; and try again
EndWhileBlankD:

            mov    eax,1               ; default sign multiplier
IfPlusD:    cmp    BYTE PTR [esi],'+'  ; leading + ?
            je     SkipSignD           ; if so, skip over
IfMinusD:   cmp    BYTE PTR [esi],'-'  ; leading - ?
            jne    EndIfSignD          ; if not, save default +
            mov    eax,-1              ; -1 for minus sign
SkipSignD:  inc    esi                 ; move past sign
EndIfSignD:

            mov    [ebp-4],eax         ; save sign multiplier
            mov    eax,0               ; number being accumulated

WhileDigitD:cmp    BYTE PTR [esi],'0'  ; compare next character to '0'
            jl     EndWhileDigitD      ; not a digit if smaller than '0'
            cmp    BYTE PTR [esi],'9'  ; compare to '9'
            jg     EndWhileDigitD      ; not a digit if bigger than '9'
            imul   eax,10              ; multiply old number by 10
            mov    bl,[esi]            ; ASCII character to BL
            and    ebx,0000000Fh       ; convert to single-digit integer
            add    eax,ebx             ; add to sum
            inc    esi                 ; increment character pointer
            jmp    WhileDigitD         ; go try next character
EndWhileDigitD:

; if value is < 80000000h, multiply by sign
            cmp    eax,80000000h       ; 80000000h?
            jnb    endIfMaxD           ; skip if not
            imul   DWORD PTR [ebp-4]   ; make signed number
endIfMaxD:

            popfd                      ; restore flags
            pop    esi                 ; restore registers
            pop    edx
            pop    ebx
            mov    esp, ebp            ; delete local variable space
            pop    ebp
            ret                        ; exit
atodproc    ENDP

            END

framework.c

#include <windows.h>
#include <stdio.h>

static char buf[255];
static char inputLabel[255];

// disables warning for strcpy use
#pragma warning(disable : 4996)

void getInput(char* inputPrompt, char* result, int maxChars)
{
    puts(inputPrompt);
    gets(buf);
    buf[maxChars - 1] = '\0';
    strcpy(result,buf);
    return;
}

void showOutput(char* outputLabel, char* outputString)
{
    printf("%s %s\n",outputLabel,outputString);
}

int sieve(void);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    AllocConsole();
    freopen("CONIN$" , "rb", stdin);
    freopen("CONOUT$", "wb", stdout);

    return sieve();
}

1 个答案:

答案 0 :(得分:3)

几个缓冲区溢出。

  

primenum DWORD 11 DUP(?),0

虽然 primenum 只有12个双字可用,但你的程序在这个数组中写了549个双字!

  

素数DWORD和DUP(?)

初始化此数组的循环会将1 dword写入其中!验证它。

返回堆栈上的值。

 mov  ebx, 4
PrintLoop:
 mov  ecx, prime[ebx] ; Prime numbers are the non-zeros in this Array
 push ecx
 add  ebx, 4
 cmp  ebx, 2200
 jb   PrintLoop

 add  esp,2196

 mov  eax, 0          ; exit with return code 0
 ret

首先你在堆栈上推549个dwords然后执行add esp, 2196你有效地说你不再关心他们了。
通过堆栈返回的值必须保持在stackpointer 之上 您可以暂时删除返回地址,然后在ret之前将其放回原位。

 pop  eax             ; Temporarily remove return address

 mov  ebx, 4
PrintLoop:
 mov  ecx, prime[ebx] ; Prime numbers are the non-zeros in this Array
 push ecx
 add  ebx, 4
 cmp  ebx, 2200
 jb   PrintLoop

 push eax             ; Put back return address

 xor  eax, eax        ; exit with return code 0
 ret

当然这会影响调用者处理这些值的方式。

 call GenPrimes2 ;call function in genprimes.asm to push primes to stack

 lea  ebp, [esp + 2196] ; EBP points beyond first value

 xor  ebx, ebx
loopArray:  ; fills primenum with all primes pushed in genprimes.asm
 add  ebx, 4
 sub  ebp, 4
 mov  ecx, [ebp]
 mov  primenum[ebx], ecx
 cmp  ebp, esp
 ja   loopArray

 mov ebx, 4
 mov eax, 0

 add  esp, 2196  ; Permanently remove results from stack
 ret
; compare eax with n, if equal increment ebx
cmp  ecx,n
jge  incrementEbx

请注意,评论与代码(eax vs ecx)不符。

mov ecx, primenum[ebx] ; Prime numbers are the non-zeros in this Array
cmp primenum[ebx], 0
jne printPrime

这是一个明显的优化。由于您刚刚在ECX中加载了数字,因此最好在寄存器上执行测试。

mov  ecx, primenum[ebx] ; Prime numbers are the non-zeros in this Array
test ecx, ecx
jnz  printPrime