我已经写了一个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();
}
答案 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