Hướng dẫn toàn diện về lệnh nhảy điều kiện trong Assembly

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ệnCờ kiểm traSử dụng phổ biến
JZ / JEBằng 0 / Bằng nhauZF = 1Kiểm tra kết quả phép so sánh hoặc phép toán bằng 0.
JNZ / JNEKhác 0 / Không bằngZF = 0Kiểm tra khác biệt hoặc giá trị khác 0.
JC / JB / JNAECó mượn / Nhỏ hơnCF = 1So sánh số không dấu: nhỏ hơn thì nhảy.
JNC / JNB / JAEKhông mượn / Lớn hơn hoặc bằngCF = 0So sánh số không dấu: không nhỏ hơn thì nhảy.
JOTràn sốOF = 1Kiểm tra tràn sau phép toán có dấu.
JNOKhông trànOF = 0Đảm bảo tính an toàn cho phép toán có dấu.
JSÂmSF = 1Kết quả âm sau phép toán có dấu.
JNSDương hoặc 0SF = 0Kết quả không âm.
JP / JPESố bit 1 chẵnPF = 1Ít dùng trong lập trình hiện đại.
JNP / JPOSố 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ệnCờ liên quanGợi nhớ
JA / JNBELớn hơnCF=0 và ZF=0Above (cao hơn)
JAE / JNBLớn hơn hoặc bằngCF=0Above or Equal
JB / JNAENhỏ hơnCF=1Below (thấp hơn)
JBE / JNANhỏ hơn hoặc bằngCF=1 hoặc ZF=1Below or Equal

3. So sánh số có dấu

LệnhĐiều kiệnCờ liên quanGợi nhớ
JG / JNLELớn hơnSF=OF và ZF=0Greater
JGE / JNLLớn hơn hoặc bằngSF=OFGreater or Equal
JL / JNGENhỏ hơnSF≠OFLess
JLE / JNGNhỏ hơn hoặc bằngSF≠OF hoặc ZF=1Less or Equal

4. Lệnh vòng lặp đặc biệt

LệnhHoạt độngTương đương
JCXZNhảy nếu CX = 0CMP CX, 0; JZ label
JECXZNhảy nếu ECX = 0TEST ECX, ECX; JZ label
LOOPECX--; nhảy nếu ECX ≠ 0DEC ECX; JNZ label
LOOPE / LOOPZECX--; nhảy nếu ECX ≠ 0 và ZF = 1DEC 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

Thẻ: x86-assembly conditional-jump eflags

Đăng vào ngày 25 tháng 6 lúc 09:14