Tăng từ 0 lên 100 trong ngôn ngữ lắp ráp


7

Đây là một điều kỳ quặc, nhưng tôi đã thu thập thông tin với trình tạo GNU ngày hôm nay (tôi muốn ít nhất có thể đọc cú pháp), và cố gắng lấy ví dụ nhỏ này của tôi để làm việc. Cụ thể là tôi chỉ muốn đi từ 0 đến 100, in ra tất cả các số trong thời gian. Vì vậy, một vài phút sau, tôi nghĩ ra điều này:

# 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: 

Tất cả những gì tôi nhận được từ lần này là 3 lần lặp đi lặp lại. Như tôi đã nói, chỉ là một ví dụ ít giả tạo, vì vậy đừng lo lắng quá nhiều về nó, nó không phải là một vấn đề sống hay chết.

(Định dạng có chút sai lầm, nhưng không có gì lớn).

+2

'xorl% eax,% eax' là hoàn toàn tương đương với 'movl $ 0,% eax', và mất 3 byte ít hơn. Chỉ cần nói. :) 10 mar. 122012-03-10 00:38:24

12

Bạn không thể tin tưởng bất kỳ quy trình được gọi nào thực hiện với bất kỳ thanh ghi nào. Hoặc đẩy các thanh ghi lên ngăn xếp và bật chúng trở lại sau khi gọi printf hoặc có các giá trị gia tăng và điểm kết thúc được giữ trong bộ nhớ và đọc/ghi vào sổ đăng ký khi bạn cần chúng.

Tôi hy vọng các công trình sau đây. Tôi giả định rằng pushl có một popl tương đương và bạn có thể đẩy thêm một vài số vào ngăn xếp.

# 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: 
+1

btw - pusha và popa sẽ đẩy tất cả các thanh ghi, và bật tất cả .. Tôi đã tìm thấy khá tiện dụng trong quá khứ 18 nov. 082008-11-18 17:08:06

  0

Từ các ghi chú bên dưới. Điều quan trọng cần lưu ý là ... "@ seanyboy, giải pháp của bạn là quá mức cần thiết. Tất cả những gì cần thiết là thay thế eax bằng một số thanh ghi khác như ecx." 13 aug. 092009-08-13 07:40:39

  0

@warren - 'pusha' và' popa' không được hỗ trợ ở chế độ 64 bit. 10 mar. 122012-03-10 00:37:57

  0

@Daniel Kozar - không bao giờ làm việc ở chế độ 64 bit, nhưng điều này là tốt để biết :) 12 mar. 122012-03-12 13:52:36


6

Tôi không quá quen thuộc với _printf, nhưng nó có thể là nó sửa đổi eax? Printf sẽ trả về số lượng ký tự được in, trong trường hợp này là hai: '0' và '\ n'. Tôi nghĩ rằng nó trả về điều này trong eax, và khi bạn tăng nó, bạn nhận được 3, đó là những gì bạn tiến hành in. Bạn có thể sử dụng một thanh ghi khác cho bộ đếm.


1

Nathan đang đi đúng hướng. Bạn không thể giả định rằng các giá trị đăng ký sẽ không được sửa đổi sau khi gọi một chương trình con. Trong thực tế, tốt nhất là giả sử chúng sẽ được sửa đổi, nếu không chương trình con sẽ không thể thực hiện công việc của nó (ít nhất là đối với các kiến ​​trúc số lượng đăng ký thấp như x86). Nếu bạn muốn giữ lại giá trị, bạn nên lưu trữ nó trong bộ nhớ (ví dụ: đẩy nó vào ngăn xếp và theo dõi vị trí của nó).

Bạn sẽ cần thực hiện tương tự cho bất kỳ biến nào khác mà bạn có. Sử dụng thanh ghi để lưu trữ các biến cục bộ được dành riêng cho kiến ​​trúc với đủ đăng ký để hỗ trợ nó (ví dụ: EPIC, amd64, v.v.)


3

Chức năng viết thường sẽ đẩy tất cả các thanh ghi vào ngăn xếp rồi bật chúng khi chúng ' được thực hiện lại để chúng không bị thay đổi trong suốt hàm. Ngoại lệ sẽ là eax chứa giá trị trả lại. Các chức năng thư viện như printf có nhiều khả năng được viết theo cách này, vì vậy tôi sẽ không làm như Wedge gợi ý:

Bạn sẽ cần thực hiện tương tự cho bất kỳ biến nào khác mà bạn có. Sử dụng thanh ghi để lưu trữ các biến cục bộ được dành khá nhiều cho các kiến ​​trúc có đủ đăng ký để hỗ trợ nó (ví dụ: EPIC, amd64, v.v.)

Thực tế, từ những gì tôi biết, trình biên dịch thường biên dịch các hàm theo cách đó để xử lý chính xác với vấn đề này.

@seanyboy, giải pháp của bạn là quá mức cần thiết. Tất cả những gì cần thiết là thay thế eax bằng một số thanh ghi khác như ecx.


-1

Bạn có thể viết lại để bạn sử dụng thanh ghi không giả sử thay đổi, ví dụ %ebp. Chỉ cần chắc chắn rằng bạn đẩy chúng vào ngăn xếp lúc đầu, và bật chúng ra vào cuối thói quen của bạn.

# 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 

5

Bạn có thể sử dụng một cách an toàn các thanh ghi được lưu "callee-save" mà không phải tự lưu chúng. Trên x86, chúng là edi, esi và ebx; các kiến ​​trúc khác có nhiều hơn.

Đây là những tài liệu trong tài liệu tham khảo ABI: http://math-atlas.sourceforge.net/devel/assembly/