- Ngôn ngữ hợp ngữ sắp xếp các lệnh theo mục đích: truyền tải, tính toán, kiểm soát luồng, v.v.
- Mỗi lệnh bao gồm một mã lệnh và các toán hạng, với các định dạng khác nhau tùy thuộc vào kiến trúc.
- Có một số chế độ địa chỉ để truy cập dữ liệu, chẳng hạn như trực tiếp, trực tiếp và gián tiếp.
Ngôn ngữ hợp ngữ đã là một trong những trụ cột cơ bản của phát triển phần mềm kể từ khi máy tính ra đời. Mặc dù ngôn ngữ cấp cao chiếm ưu thế hiện nay, trình biên dịch vẫn được sử dụng rộng rãi và phù hợp trong các môi trường mà việc kiểm soát phần cứng, tối ưu hóa hoặc hạn chế tài nguyên là ưu tiên hàng đầu.
Trong bài viết này, chúng ta sẽ đi sâu vào các loại lệnh ngôn ngữ hợp ngữ, phân tích cấu trúc, mục đích, chế độ địa chỉ và cách chúng được tổ chức trên các kiến trúc khác nhau. Bạn cũng sẽ tìm thấy các ví dụ, định dạng và tổng quan toàn diện cho phép bạn hiểu đầy đủ logic đằng sau các lệnh này, vốn hoạt động rất chặt chẽ với phần cứng.
Lệnh trong ngôn ngữ lắp ráp là gì?
Lệnh hợp ngữ là một lệnh mà CPU có thể hiểu trực tiếp (sau khi được chương trình hợp ngữ dịch sang mã máy). Mỗi lệnh có một mục đích cụ thể và thường bao gồm một từ viết tắt đại diện cho thao tác và một hoặc nhiều toán hạng cho biết dữ liệu được lấy ở đâu và/hoặc kết quả được lưu trữ ở đâu.
Các lệnh này phụ thuộc rất nhiều vào kiến trúc bộ xử lý, do đó mỗi loại CPU (dù là x86, ARM, MIPS, v.v.) đều có bộ lệnh riêng với cú pháp và khả năng cụ thể.
Cấu trúc của một hướng dẫn
Hướng dẫn thường bao gồm hai phần:
- Mã hoạt động (opcode): Chỉ ra hành động cần thực hiện, chẳng hạn như thêm, di chuyển dữ liệu, so sánh, v.v.
Toán hạng cung cấp thông tin cần thiết để thực hiện phép toán và có thể được lưu trữ trong thanh ghi, bộ nhớ hoặc là các hằng số. Không phải tất cả các lệnh đều yêu cầu toán hạng, và trong một số trường hợp, chúng có thể được ngầm định trong lệnh hoặc được lưu trữ trong các thanh ghi được định nghĩa trước.
Các loại lệnh trong ngôn ngữ lắp ráp
Tùy thuộc vào chức năng chúng thực hiện trong chương trình, các lệnh ngôn ngữ lắp ráp có thể được phân loại thành nhiều nhóm. Những thông tin chi tiết nhất được trình bày dưới đây:
1. Hướng dẫn chuyển dữ liệu
Các lệnh này cho phép sao chép dữ liệu từ vị trí này sang vị trí khác mà không làm thay đổi nội dung của dữ liệu tại nguồn. Chúng rất cần thiết để di chuyển thông tin giữa các thanh ghi, bộ nhớ và thiết bị ngoại vi.
Một số hướng dẫn phổ biến nhất trong nhóm này là:
- PHIM: Sao chép nội dung của một toán hạng sang toán hạng khác. Ví dụ,
MOV AX, BXsao chép nội dung của BX vào AX. - MOVS / MOVSB / MOVSW: Di chuyển chuỗi từ địa chỉ nguồn (SI) đến địa chỉ đích (DI), tự động hóa quá trình chuyển giao.
- LODS / LODSB / LODSW: Tải giá trị từ địa chỉ được chỉ định bởi thanh ghi SI vào bộ tích lũy (AL hoặc AX).
- ĐỌC: Tải địa chỉ có hiệu lực thay vì giá trị. Hữu ích để lấy địa chỉ của một biến hoặc cấu trúc.
2. Hướng dẫn sử dụng pin
Ngăn xếp là một cấu trúc LIFO (vào sau, ra trước) được sử dụng để lưu trữ dữ liệu tạm thời, truyền tham số hoặc lưu trữ ngữ cảnh thực thi.
- XÔ: Lưu trữ giá trị trên ngăn xếp, giảm con trỏ SP.
- NHẠC POP: Truy xuất giá trị gần đây nhất được lưu trữ trên ngăn xếp, tăng SP.
- ĐẨY / POPF: Chúng lưu trữ và truy xuất trạng thái của các cờ CPU trên ngăn xếp.
3. Hướng dẫn số học và logic
Chúng cho phép thực hiện các phép tính và thao tác trên các bit. Các lệnh này rất cần thiết để thao tác dữ liệu số và đưa ra quyết định trong luồng chương trình.
Trong số những thứ được sử dụng nhiều nhất chúng ta thấy:
- THÊM / ĐĂNG KÝ: Phép cộng và phép trừ.
- MUL / IMUL: Phép nhân nhị phân.
- DIV / IDIV: Phép chia (số nguyên hoặc có dấu).
- VÀ / HOẶC / XOR / KHÔNG: Các phép toán logic bitwise.
- SHL / SHR / ROL / ROR: Sự dịch chuyển và quay bit.
4. Hướng dẫn kiểm soát luồng
Chúng cho phép bạn sửa đổi trình tự thực thi của chương trình , thêm các lệnh nhảy hoặc các lệnh gọi có điều kiện và không điều kiện đến các lệnh khác.
- JMP: Nhảy vô điều kiện đến một địa chỉ khác trong chương trình.
- GỌI / GỌI LẠI: Gọi đến một chương trình con và quay lại.
- JE/JNE/JG/JL/JZ/JNZ: Nhảy có điều kiện, lệnh này được thực hiện nếu đáp ứng được một số điều kiện nhất định (sau khi so sánh).
5. Hướng dẫn vào/ra (I/O)
Chúng cho phép giao tiếp giữa bộ xử lý và các thiết bị bên ngoài. Các lệnh này cho phép đọc và ghi dữ liệu đến/từ các cổng (phần cứng).
- VÀO: Đọc dữ liệu từ cổng đầu vào.
- NGOÀI: Gửi dữ liệu tới cổng đầu ra.
6. Hướng dẫn về dấu chấm động
Nhiều CPU có các bộ xử lý phụ toán học (hoặc các lệnh đặc biệt) dành riêng cho việc xử lý các số thập phân (số dấu phẩy động). Các lệnh này cho phép thực hiện các phép toán như:
- Phép cộng, trừ, nhân và chia số thực.
- Các phép toán lượng giác (sin, cos, tan).
- Các phép toán logarit, mũ và căn bậc hai.
Các hướng dẫn này thường tuân thủ tiêu chuẩn IEEE 754 về độ chính xác số học.
Chế độ địa chỉ
Một phần cơ bản của thiết kế lệnh là cách xác định vị trí của các toán hạng. Chế độ định địa chỉ xác định cách CPU truy cập dữ liệu.
1. Địa chỉ ngay lập tức
Dữ liệu được đưa trực tiếp vào lệnh. Phương pháp này nhanh, nhưng không cho phép sửa đổi giá trị mà không thay đổi lệnh.
MOV AX, 5
2. Địa chỉ trực tiếp
Địa chỉ bộ nhớ chứa dữ liệu được chỉ định rõ ràng.
MOV AX,
3. Địa chỉ gián tiếp
Địa chỉ hiệu lực được lấy từ nội dung của bản ghi. Rất linh hoạt và được sử dụng cho cấu trúc và danh sách liên kết.
MOV AX,
4. Địa chỉ được lập chỉ mục
Địa chỉ được tính bằng cách cộng cơ số và chỉ số (ví dụ, để duyệt mảng). Nó sử dụng các thanh ghi đặc biệt như SI, DI, hoặc thanh ghi cơ số + độ lệch.
MOV AX,
Định dạng hướng dẫn: các loại khác nhau tùy thuộc vào kiến trúc
Cấu trúc của một hướng dẫn có thể thay đổi tùy thuộc vào kiến trúc. Các định dạng phổ biến bao gồm:
Định dạng 4 chiều
Nó bao gồm hai toán hạng, vị trí kết quả và địa chỉ của lệnh tiếp theo. Hiện nay nó đã lỗi thời do độ phức tạp của nó.
Định dạng 3 chiều
Nó chỉ định hai toán hạng và một địa chỉ đích. Đây là kiểu cấu trúc phổ biến trong các kiến trúc RISC hiện đại.
Định dạng 2 chiều
Sử dụng toán hạng nguồn và toán hạng đích (toán hạng đích cũng có thể đóng góp giá trị). Phổ biến trên kiến trúc x86 và các kiến trúc CISC khác.
Định dạng 1 chiều
Chỉ có một toán hạng được chỉ định; toán hạng còn lại được giả định là bộ tích lũy. Thường gặp trong các bộ xử lý cũ và các hệ thống đơn giản.
Định dạng 0 địa chỉ (dựa trên ngăn xếp)
Các phép toán sử dụng ngăn xếp để lấy và lưu trữ toán hạng và kết quả. Rất hiệu quả đối với các máy tính dựa trên ngăn xếp, chẳng hạn như một số máy tính bỏ túi lập trình được.
Hướng dẫn macro và mã lệnh giả
Để nâng cao năng suất, nhiều trình biên dịch hợp ngữ cung cấp các lệnh macro , giúp mở rộng một dòng lệnh thành nhiều lệnh thực tế. Chúng rất hữu ích để tránh lặp lại các mẫu phức tạp hoặc để tạo ra các cấu trúc cấp cao như vòng lặp hoặc câu lệnh điều kiện.
Các mã lệnh giả Chúng là những hướng dẫn mà trình biên dịch dịch nội bộ thành một chuỗi hướng dẫn thực sự. Ví dụ, NOP (không có hoạt động) có thể được dịch là XCHG AX, AX trên CPU x86.
Chỉ thị của trình biên dịch
Các chỉ thị ( còn được gọi là các thao tác giả ) không phải là các lệnh theo nghĩa chặt chẽ. Chúng dùng để kiểm soát quá trình lắp ráp, định nghĩa dữ liệu, các đoạn mã, macro, điều kiện biên dịch, v.v.
Các ví dụ phổ biến bao gồm:
- .dữ liệu / .mã: Chúng chỉ ra phần dữ liệu hoặc mã.
- .org: Đặt địa chỉ mà từ đó lắp ráp sẽ bắt đầu.
- .equ: Định nghĩa hằng số tượng trưng.
Các trường hợp sử dụng ngôn ngữ lắp ráp
Mặc dù sự phát triển mạnh mẽ của các ngôn ngữ lập trình bậc cao, ngôn ngữ hợp ngữ vẫn giữ vai trò quan trọng trong một số lĩnh vực:
- Phát triển hệ thống nhúng và vi điều khiển.
- Trình điều khiển lập trình, bộ nạp khởi động và BIOS.
- Tối ưu hóa tối đa trong các ứng dụng như trò chơi điện tử hoặc mật mã.
- Kỹ thuật đảo ngược, gỡ lỗi và hack cấp thấp.
Hơn nữa, việc đào tạo ngôn ngữ lập trình hợp ngữ vẫn chiếm một vị trí quan trọng trong các chương trình đào tạo khoa học máy tính và điện tử, vì nó cung cấp sự hiểu biết sâu sắc về cách thức hoạt động của bộ xử lý, cách dữ liệu được lưu trữ và xử lý, cũng như cách các lệnh được thực thi ở cấp độ nhị phân.
Hiểu được tất cả các loại lệnh lắp ráp, cấu trúc, chức năng và chế độ địa chỉ của chúng sẽ cung cấp nền tảng vững chắc cho bất kỳ lập trình viên nào muốn tìm hiểu sâu hơn về lập trình cấp thấp hoặc hiểu cách các ngôn ngữ cấp cao cuối cùng được dịch thành mã máy. Mặc dù việc sử dụng nó đã trở nên cụ thể hơn nhưng nó vẫn là một công cụ thiết yếu trong nhiều lĩnh vực quan trọng của phát triển phần mềm và hệ thống.