x86汇编"访问冲突执行位置0x00000000"

时间:2018-03-26 17:37:44

标签: x86

我必须编写一个汇编程序,它将调用外部汇编文件来生成用户指定的素数。现在我想生成小于10的素数,然后将其报告回屏幕,并稍后担心用户输入。 genprimes.asm中的代码将正确生成素数并将它们存储到数组中。我想将它们推入堆栈(参见genprimes.asm中的第79行),并在从seive.asm中调用该函数返回后恢复它们。我的代码在genprimes.asm中工作,如果我注释掉" push ecx;"在第79行.sieve.asm在第32行中断,这是代码"调用genPrimes"。我试着在" genprimes.asm"中逐步完成我的所有代码。我得到了return语句,然后visual studio说我无法进一步调试,因为它不支持跳回主文件。我不知道为什么这会破裂。我怎样才能将我的素数带回主文件" sieve.asm"我打电话给#34; genprimes.asm"从?

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
primenum BYTE    11 DUP (?), 0

.CODE

genPrimes   PROC
           ; push   ebp                  ; save base pointer
           ; mov    ebp, esp             ; establish stack frame
           ; push   ebx
            ; CODE
            call GenPrimes2  ;*** breaks here ***

           ; pop    ebx
           ; pop    ebp

            ret                         ;exit genPrimes
genPrimes    ENDP

_sieve  PROC                            ; start of sieve program code

      input   prompt1, string, 40       ; read ASCII characters

      call  genPrimes

      output  prompt2, primenum         ; output label and sum

      mov   eax, 0          ; exit with return code 0
      ret

_sieve  ENDP

END

genprimes.asm:

.586
.MODEL FLAT


.STACK 4096                 
n=10
.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  ; **This is somehow breaking it, but doesnt throw and error  
                 ;** when steeping through in Visual Studio?!?
    add ebx, 4
    cmp ebx, 40
    jb  PrintLoop


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

3 个答案:

答案 0 :(得分:4)

推送ecx指令会使您的堆栈失去平衡。没有相应的流行音乐。您将返回到从ecx推送的最后一个值而不是返回地址给出的地址。您需要弹出所推动的内容或将sp保存在条目中并在退出返回之前将其恢复。

答案 1 :(得分:1)

您需要了解调用约定。

简而言之:当你执行call时,下一条指令的IP被放入堆栈。 ret将从堆栈顶部获取地址,并将该地址移至该地址。

但您正在推动PUSH CX,因此ret会找到错误的返回地址。通常您返回AX(作为返回值)或将数据放在返回地址之前。这应该从调用者设置。

答案 2 :(得分:0)

有几种不同的方法可以从数组中的函数返回信息。

  1. 在调用者中分配数组,并将指向数组及其大小的指针传递给函数。请注意,此分配可以在堆栈上,静态或动态(例如,使用malloc)。
  2. 在函数中声明一个静态数组,并从函数返回一个指向数组的指针。
  3. 在函数中动态分配数组并返回指针。
  4. 在高级语言中,返回动态数组。
  5. 这些方法中的每一种都有利有弊。例如,使用选项3,调用者必须知道在完成数组操作时它负责释放数组。

    在您的情况下,由于调用者将指定它想要生成多少个值,因此选项1可能是最好的。如果只有被调用的函数知道它想要返回多少项,则选项3或4通常是最好的。

    有关利弊的更多详情:

    选项1:来电者必须知道要预留多少空间。如果没有足够的空间,则该函数必须返回错误或指示有更多项目。一个例子是sprintf。

    选项2:当需要的空间量固定时,这可能是一个不错的选择。但调用者必须知道,如果它调用该函数两次,则会覆盖第一个值。一个例子是C库函数ctime,它总是返回一个固定长度的字符串。

    选项3:调用者必须知道它负责在完成数组时释放数组,并且它需要使用相同的分配器来释放数组作为用于分配它的函数。这方面的一个例子是strdup。

    选项4:如果您使用支持它的语言,这通常是最佳选择。对于汇编语言编程,实现它的基础结构可能过于复杂,除非在整个项目中使用它足以使其值得。