Từ C đến hội


16

Giả sử rằng chúng ta có đoạn mã C sau đây cho avr-8bit:

int v1=1;
int v2=2;
v2=v2+v1;

Tôi mong đợi sự tháo gỡ sau đây

ldi r18, 1;
ldi r19, 2;
add r19, r18;

nhưng sau khi tôi chạy:

avr-gcc -mmcu=atmega2560 Test.c -o Test.elf

avr-objdump -S Test.elf > Test.lss

Tôi đã tháo rời sau đây

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0
    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01
    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03
    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02
    add r24, r18
    adc r25, r19
    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

Có ai có thể giúp tôi hiểu kết quả của trình dịch ngược không?

Chỉnh sửa: Sử dụng char lắp ráp trở thành:

ldi r24, 0x01
std Y+1, r24
ldi r24, 0x02
std Y+2, r24
ldd r25, Y+2
ldd r24, Y+1
add r24, r25
std Y+2, r24

Khi nào có hướng dẫn std?

Câu trả lời:


20

Câu trả lời ngắn: các thanh ghi của bạn là 8 bit và giá trị của bạn là 16 bit. Do đó, nó xử lý chúng thành hai mảnh.

Câu trả lời dài:

    ldi r24, 0x01   ; 1
    ldi r25, 0x00   ; 0

Lưu trữ giá trị 16 bit 1 trong các thanh ghi 8 bit r24, r25.

    std Y+2, r25    ; 0x02
    std Y+1, r24    ; 0x01

Lưu trữ nó tại các vị trí ngăn xếp Y + 1, Y + 2.

    ldi r24, 0x02   ; 2
    ldi r25, 0x00   ; 0

Lưu trữ giá trị 16 bit 2 trong các thanh ghi 8 bit r24, r25.

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

Lưu trữ nó tại các vị trí ngăn xếp Y + 3, Y + 4.

    ldd r18, Y+3    ; 0x03
    ldd r19, Y+4    ; 0x04
    ldd r24, Y+1    ; 0x01
    ldd r25, Y+2    ; 0x02

Sao chép chúng trở lại từ ngăn xếp vào (r18, r19) và (r24, r25)

    add r24, r18
    adc r25, r19

Thêm (r18, r19) vào (r24, r25), bao gồm cả việc mang theo bổ sung thứ hai

    std Y+4, r25    ; 0x04
    std Y+3, r24    ; 0x03

Lưu trữ nó trở lại trên ngăn xếp.

Để có được bản lắp ráp ban đầu của bạn, hãy thử hai điều:

  • sử dụng biến "char"
  • sử dụng tùy chọn trình biên dịch "-O2"

Chỉnh sửa : lý do trình biên dịch lưu trữ các biến vào ngăn xếp thay vì giữ chúng trong các thanh ghi là vì chúng được lưu trữ với loại lưu trữ "tự động" mặc định. Nó có thể tối ưu hóa chúng thành các thanh ghi nhưng nó không phải, ngay cả khi bạn khai báo chúng với lớp lưu trữ "đăng ký".

Mặc dù đây không phải là một yêu cầu nghiêm ngặt của ngôn ngữ, đó là hành vi trình biên dịch bình thường. Nếu tại một thời điểm nào đó, bạn lấy địa chỉ của v1 thì nó phải được chỉ định một vị trí lưu trữ và được lưu lại ở đó bất cứ khi nào giá trị của "v1" thay đổi. Vì vậy, để lưu sổ sách kế toán cho dù v1 nên được lưu trữ trong một thanh ghi hoặc trên ngăn xếp, nó sẽ giữ nó trên ngăn xếp và xử lý riêng từng dòng mã.


Cảm ơn bạn! Bây giờ thì rõ hơn rồi! Xin vui lòng tìm chỉnh sửa của tôi trong câu hỏi.
DarkCoffee

1
Xem chỉnh sửa của tôi. Hãy thử -O2. Có lẽ -O3, mặc dù điều đó có thể tạo ra mã bị hỏng.
pjc50

3
Rất nhiều mã nhúng tôi làm việc với định nghĩa các loại bổ sung dành riêng cho kích thước của chúng, chẳng hạn như "uint8, uint16, uint32" cho các số nguyên không dấu, chẳng hạn. Bằng cách đó, bạn luôn biết chính xác loại biến mình đang xử lý. Đặc biệt là trong nhúng nhỏ, đã ký, thả nổi, "int" có kích thước / độ ký không xác định sẽ khiến bạn mất hết chu kỳ CPU và gây ra các lỗi nghiêm trọng ở mức tồi tệ nhất.
John U

Trình biên dịch thực sự đã ngừng hành xử như thế này khoảng 10-15 năm trước. Vấn đề phân bổ đăng ký chủ yếu được giải quyết và trình biên dịch bị nguyền rủa tốt về nó. Họ biết chính xác khi nào một biến phải nằm trên ngăn xếp và khi nào nó có thể nằm trong một thanh ghi, liệu nó có xứng đáng với nỗ lực di chuyển nó hay không và khi nào nên làm như vậy. Việc ghi sổ được thực hiện vào thời gian biên dịch và bản thân trình biên dịch có bộ nhớ gigabyte. Ngoại lệ lớn là chế độ gỡ lỗi, vì lý do rõ ràng, nhưng sau đó mọi thứ đều nằm trên ngăn xếp.
MSalters

@ pjc50 -O3có thể sản xuất mã bị hỏng? [cần dẫn nguồn] (và không, mã C gọi ra Hành vi không xác định và sau đó ngắt với một số cài đặt tối ưu hóa không được tính)
marcelm

4

Khi tôi tìm thấy một số mã ví dụ, tôi sẽ đưa ra nhận xét của mình một câu trả lời - những người khác đã giải thích vấn đề.

Rất nhiều mã nhúng tôi làm việc với định nghĩa các loại bổ sung dành riêng cho kích thước của chúng, chẳng hạn như "uint8, uint16, uint32" cho các số nguyên không dấu, chẳng hạn. Bằng cách đó, bạn luôn biết chính xác loại biến mình đang xử lý. Đặc biệt là trong nhúng nhỏ, đã ký, thả nổi, "int" có kích thước / chữ ký không xác định sẽ khiến bạn mất hết chu kỳ CPU và gây ra lỗi nghiêm trọng nhất.

Đây là #defines hiện tại của chúng tôi:

/*
 * Example - the basic data types from our embedded code
 */
typedef unsigned char       uint8;  /*  8 bits */
typedef unsigned short int  uint16; /* 16 bits */
typedef unsigned long int   uint32; /* 32 bits */

typedef char                int8;   /*  8 bits */
typedef short int           int16;  /* 16 bits */
typedef int                 int32;  /* 32 bits */

typedef volatile int8       vint8;  /*  8 bits */
typedef volatile int16      vint16; /* 16 bits */
typedef volatile int32      vint32; /* 32 bits */

typedef volatile uint8      vuint8;  /*  8 bits */
typedef volatile uint16     vuint16; /* 16 bits */
typedef volatile uint32     vuint32; /* 32 bits */

3
Ý tưởng tốt; uint8_t và bạn bè hiện là một phần của tiêu chuẩn: stackoverflow.com/questions/16937459/iêu
pjc50

Thật tiện dụng làm sao! Chúng tôi đã kế thừa những người có dự án là C89, vì vậy thật tốt khi biết có phiên bản chính thức.
John U

2

Mã C của bạn sử dụng biến số nguyên 16 bit (int). Trình biên dịch không thể đọc được suy nghĩ của bạn, vì vậy nó biên dịch chính xác những gì có trong tệp nguồn. Vì vậy, nếu bạn muốn các biến 8 bit, bạn phải sử dụng loại tương ứng.

Kết quả là bạn vẫn sẽ lưu trữ các giá trị trong bộ nhớ (mặc dù đơn giản hơn). Tôi không giỏi về C, nhưng IMHO, có một số tùy chọn để gán biến cho một số thanh ghi, nếu bạn muốn một số biến nằm trong thanh ghi thay vì RAM. Cái gì đó như:

register unsigned char VARNAME asm("r3");

Lưu ý rằng không phải tất cả các thanh ghi có sẵn cho các thủ thuật như vậy.

Vậy, kết luận thế nào? Viết chương trình của bạn trong hội đồng. Chúng sẽ luôn nhỏ hơn, nhanh hơn và dễ dàng để đọc / hỗ trợ.


Hội dễ đọc hơn C?
dext0rb

@ dext0rb - Có. Tất nhiên nếu bạn biết cả hai đủ tốt. Nếu bạn chỉ biết C, thì lắp ráp và bất kỳ ngôn ngữ nào khác sẽ khó đọc.
johnfound

Tôi không đồng ý với điểm cuối cùng, các chương trình được viết bằng trình biên dịch chương trình khó đọc hơn nhiều. Chỉ cần so sánh mã nguồn được đưa ra ở trên. Mã C rõ ràng hơn và ngắn hơn nhiều, cũng như ý định của nó. Sự khác biệt này chỉ phát triển khi các cấu trúc được sử dụng.
soandos

@soandos - Mã C ngắn hơn, vâng. Rõ ràng hơn? Tôi không chắc. Nếu nó là như vậy, câu hỏi trên sẽ không phải hỏi gì cả. Trên thực tế, giá của "độ ngắn" là "độ mờ" của các chi tiết.
johnfound

Tất nhiên, anh chàng nói "Tôi không giỏi về C" sẽ tuyên bố những ưu điểm của lắp ráp thuần túy. : D
dext0rb
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.