Các lệnh JXX (jump conditional) là xương sống để điều khiển luồng thực thi trong lập trình Assembly. Chúng dựa vào trạng thái các cờ trong thanh ghi EFLAGS để quyết định rẽ nhánh, từ đó hiện thực hóa các cấu trúc điều kiện như if, while, hay for ở cấp độ máy.
Cơ chế hoạt động
Mọi lệnh JXX đều tuân theo cú pháp:
JXX destination_label ; Nhảy tới nhãn nếu điều kiện XX đúng
; Ngược lại, tiếp tục dòng lệnh kế tiếp
Việc nhảy được thực hiện bằng cách thay đổi giá trị của con trỏ lệnh EIP. Đây là kiểu nhảy tương đối — mã máy lưu trữ độ lệch so với lệnh tiếp theo.
Phân loại lệnh theo chức năng
1. Dựa trên cờ đơn lẻ
| Lệnh | Điều kiện | Cờ kiểm tra | Sử dụng phổ biến |
JZ / JE | Bằng 0 / Bằng nhau | ZF = 1 | Kiểm tra kết quả phép so sánh hoặc phép toán bằng 0. |
JNZ / JNE | Khác 0 / Không bằng | ZF = 0 | Kiểm tra khác biệt hoặc giá trị khác 0. |
JC / JB / JNAE | Có mượn / Nhỏ hơn | CF = 1 | So sánh số không dấu: nhỏ hơn thì nhảy. |
JNC / JNB / JAE | Không mượn / Lớn hơn hoặc bằng | CF = 0 | So sánh số không dấu: không nhỏ hơn thì nhảy. |
JO | Tràn số | OF = 1 | Kiểm tra tràn sau phép toán có dấu. |
JNO | Không tràn | OF = 0 | Đảm bảo tính an toàn cho phép toán có dấu. |
JS | Âm | SF = 1 | Kết quả âm sau phép toán có dấu. |
JNS | Dương hoặc 0 | SF = 0 | Kết quả không âm. |
JP / JPE | Số bit 1 chẵn | PF = 1 | Ít dùng trong lập trình hiện đại. |
JNP / JPO | Số bit 1 lẻ | PF = 0 | Ít dùng trong lập trình hiện đại. |
2. So sánh số không dấu
| Lệnh | Điều kiện | Cờ liên quan | Gợi nhớ |
JA / JNBE | Lớn hơn | CF=0 và ZF=0 | Above (cao hơn) |
JAE / JNB | Lớn hơn hoặc bằng | CF=0 | Above or Equal |
JB / JNAE | Nhỏ hơn | CF=1 | Below (thấp hơn) |
JBE / JNA | Nhỏ hơn hoặc bằng | CF=1 hoặc ZF=1 | Below or Equal |
3. So sánh số có dấu
| Lệnh | Điều kiện | Cờ liên quan | Gợi nhớ |
JG / JNLE | Lớn hơn | SF=OF và ZF=0 | Greater |
JGE / JNL | Lớn hơn hoặc bằng | SF=OF | Greater or Equal |
JL / JNGE | Nhỏ hơn | SF≠OF | Less |
JLE / JNG | Nhỏ hơn hoặc bằng | SF≠OF hoặc ZF=1 | Less or Equal |
4. Lệnh vòng lặp đặc biệt
| Lệnh | Hoạt động | Tương đương |
JCXZ | Nhảy nếu CX = 0 | CMP CX, 0; JZ label |
JECXZ | Nhảy nếu ECX = 0 | TEST ECX, ECX; JZ label |
LOOP | ECX--; nhảy nếu ECX ≠ 0 | DEC ECX; JNZ label |
LOOPE / LOOPZ | ECX--; nhảy nếu ECX ≠ 0 và ZF = 1 | DEC ECX; JZ label (kết hợp với CMP trước đó) |
Mẫu sử dụng phổ biến
Mẫu 1: Rẽ nhánh if-else
CMP EAX, EBX
JNE branch_else ; Nếu không bằng → nhảy
MOV DWORD PTR [result], 1
JMP after_if
branch_else:
MOV DWORD PTR [result], 0
after_if:
Mẫu 2: Vòng lặp while/for
MOV ECX, 5
MOV EDI, OFFSET data_array
XOR EAX, EAX ; Khởi tạo tổng = 0
sum_loop:
ADD EAX, DWORD PTR [EDI]
ADD EDI, 4
LOOP sum_loop ; Tự giảm ECX và nhảy nếu ≠0
Mẫu 3: Chọn giá trị nhỏ nhất
CMP EAX, EBX
JBE keep_eax ; Nếu EAX ≤ EBX → giữ nguyên
MOV EAX, EBX ; Ngược lại → gán EBX vào EAX
keep_eax:
Lưu ý quan trọng khi sử dụng
- Phân biệt "lớn hơn" có dấu và không dấu:
MOV AL, 0xFF ; 255 (không dấu) hoặc -1 (có dấu)
MOV BL, 1
CMP AL, BL
JA unsigned_ok ; Nhảy vì 255 > 1
JG signed_ok ; Không nhảy vì -1 < 1
- Giới hạn khoảng nhảy: Hầu hết
JXX chỉ hỗ trợ nhảy ngắn (-128 đến +127 byte). Nếu vượt quá, cần dùng JMP gián tiếp.
- Hiệu năng: CPU hiện đại dự đoán nhánh. Thiết kế để đường đi thường xuyên không cần nhảy sẽ tối ưu hơn.
- An toàn cờ: Chỉ các lệnh như
CMP, TEST, ADD, SUB... mới ảnh hưởng cờ. Lệnh MOV không làm thay đổi cờ.
- Lưu kết quả điều kiện: Dùng
SETcc để ghi trực tiếp kết quả logic vào thanh ghi byte.
CMP EAX, EBX
SETG DL ; DL = 1 nếu EAX > EBX (có dấu), ngược lại DL = 0