문제

이것은 좀 이상한 일이지만, 나는 오늘 GNU 어셈블러를 가지고 이리저리 살펴보며(적어도 구문을 읽을 수 있기를 원합니다) 이 작은 인위적인 예제를 작동시키려고 노력했습니다.즉, 0에서 100까지 이동하면서 계속해서 숫자를 인쇄하고 싶습니다.그래서 몇 분 후에 나는 이것을 생각해 냈습니다.

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,   %ebx # The ending point.

_loop:
    # Display the current value.
    pushl   %eax
    pushl   $string
    call     _printf
    addl     $8, %esp

    # Check against the ending value.
    cmpl    %eax, %ebx
    je    _end

    # Increment the current value.
    incl    %eax
    jmp _loop   

_end:

내가 이것에서 얻는 것은 3이 계속해서 인쇄되는 것입니다.내가 말했듯이, 단지 약간 조작된 예일 뿐이므로 너무 걱정하지 마십시오. 이것은 삶과 죽음의 문제가 아닙니다.

(형식은 약간 엉망이지만 큰 것은 아닙니다).

도움이 되었습니까?

해결책

호출된 프로시저가 레지스터에 대해 수행하는 작업을 신뢰할 수 없습니다.레지스터를 스택에 푸시하고 printf를 호출한 후 다시 팝하거나, 증분 및 끝점 값을 메모리에 유지하고 필요할 때 레지스터에 읽거나 쓰도록 합니다.

다음이 작동하기를 바랍니다.나는 pushl에 동등한 popl이 있고 추가로 두 개의 숫자를 스택에 푸시할 수 있다고 가정합니다.

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    movl    $0, %eax # The starting point/current value.
    movl    $100,       %ebx # The ending point.

_loop:
    # Remember your registers.
    pushl   %eax
    pushl   %ebx

    # Display the current value.
    pushl   %eax
    pushl   $string
    call     _printf
    addl     $8, %esp

    # reinstate registers.
    popl   %ebx
    popl   %eax

    # Check against the ending value.
    cmpl    %eax, %ebx
    je    _end

    # Increment the current value.
    incl    %eax
    jmp _loop   

_end:

다른 팁

나는 _printf에 익숙하지 않지만 이것이 eax를 수정하는 것일 수 있습니까?Printf는 인쇄된 문자 수를 반환해야 하며, 이 경우에는 2개입니다.'0'과 ' '.내 생각엔 이것이 eax로 반환되고, 이를 증가시키면 3이 되고, 이것이 바로 인쇄를 진행하는 것입니다.카운터에 다른 레지스터를 사용하는 것이 더 나을 수도 있습니다.

"호출 수신자가 저장한" 레지스터를 직접 저장하지 않고도 안전하게 사용할 수 있습니다.x86에서는 edi, esi 및 ebx가 있습니다.다른 아키텍처에는 더 많은 것이 있습니다.

이는 ABI 참조에 문서화되어 있습니다. http://math-atlas.sourceforge.net/devel/assemblies/

잘 작성된 함수는 일반적으로 모든 레지스터를 스택에 푸시한 다음 완료되면 이를 팝하여 함수 중에 변경되지 않은 상태로 유지합니다.반환 값을 포함하는 eax는 예외입니다.printf와 같은 라이브러리 함수는 이런 방식으로 작성될 가능성이 높으므로 Wedge가 제안한 대로 수행하지 않을 것입니다.

다른 변수에 대해서도 동일한 작업을 수행해야 합니다.레지스터를 사용하여 지역 변수를 저장하는 것은 이를 지원하기에 충분한 레지스터가 있는 아키텍처에 거의 적용됩니다(예:EPIC, amd64 등)

사실, 내가 아는 바로는 컴파일러는 일반적으로 이 문제를 정확하게 처리하기 위해 그런 식으로 함수를 컴파일합니다.

@seanyboy, 귀하의 솔루션은 과잉입니다.필요한 것은 eax를 ecx와 같은 다른 레지스터로 바꾸는 것뿐입니다.

Nathan은 올바른 길을 가고 있습니다.서브루틴을 호출한 후에 레지스터 값이 수정되지 않을 것이라고 가정할 수 없습니다.실제로 수정될 것이라고 가정하는 것이 가장 좋습니다. 그렇지 않으면 서브루틴이 작업을 수행할 수 없습니다(적어도 x86과 같은 낮은 레지스터 개수 아키텍처의 경우).값을 보존하려면 메모리에 저장해야 합니다(예:스택에 밀어넣고 위치를 추적합니다.)

다른 변수에 대해서도 동일한 작업을 수행해야 합니다.레지스터를 사용하여 지역 변수를 저장하는 것은 이를 지원하기에 충분한 레지스터가 있는 아키텍처에 거의 적용됩니다(예:EPIC, amd64 등)

예를 들어 변경되지 않는 레지스터를 사용하도록 다시 작성할 수 있습니다. %ebp.처음에는 스택에 밀어 넣고 루틴이 끝나면 꺼내십시오.

# count.s: print the numbers from 0 to 100. 
    .text
string: .asciz "%d\n"
    .globl _main

_main:
    push    %ecx
    push    %ebp
    movl    $0, %ecx # The starting point/current value.
    movl    $100,       %ebp # The ending point.

_loop:
    # Display the current value.
    pushl   %ecx
    pushl   $string
    call     _printf
    addl     $8, %esp

    # Check against the ending value.
    cmpl    %ecx, %ebp
    je    _end

    # Increment the current value.
    incl    %ecx
    jmp _loop   

_end:
    pop     %ebp
    pop     %ecx
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top