Lệnh so sánh:
Cú pháp: Cmp [Toán hạng đích], [Toán hạng nguồn]
- Trong đó: [Toán hạng đích], [Toán hạng nguồn] có thể là hằng, biến, thanh ghi hay ô nhớ. [Toán hạng đích] không thể là hằng số. [Toán hạng đích] và [Toán hạng nguồn] không thể đồng thời là ô nhớ.
- Tác dụng: Lệnh Cmp (Compare) được sử dụng để so sánh giá trị/nội dung của [Toán hạng đích] so với [Toán hạng nguồn]. Tương tự như lệnh Sub, nó lấy [Toán hạng đích] trừ đi [Toán hạng nguồn] nhưng kết quả không làm thay đổi [Toán hạng đích] mà chỉ làm thay đổi giá trị của một số cờ hiệu: CF, ZF, OF,...
Kết quả so sánh của lệnh Cmp là: [Toán hạng đích] > [Toán hạng nguồn]; [Toán hạng đích] ≥[Toán hạng nguồn]; [Toán hạng đích] < [Toán hạng nguồn]; [Toán hạng đích] ≤[Toán hạng nguồn]; [Toán hạng đích] = [Toán hạng nguồn]; [Toán hạng đích] ≠ [Toán hạng nguồn];... mỗi kết quả sẽ tác động (0 →1, 1→0) đến một cờ tương ứng cụ thể nào đó.
Do đó, để biết được kết quả so sánh chương trình phải sử dụng các lệnh kiểm tra cờ (đó là cá lệnh nhảy), và chúng phải được đặt ngay sau lệnh so sánh. Như vậy lệnh Cmp sẽ không có ý nghĩa khi nó đứng độc lập.
Có thể nói ngược lại, lệnh Cmp được sử dụng để cung cấp điều kiện nhảy (thay đổi giá trị các cờ) cho các lệnh nhảy có điều kiện.
Ví dụ 1:
- Cmp Ax, Bx ; so sánh giá tị thanh ghi Ax với Bx
- Cmp Ax, 20 ; so sánh giá trị thanh ghi Ax với 20
- Cmp Ax, [SI] ; so sánh Ax với nội dung ô nhớ được chỉ bởi SI
Ví dụ 2:
- Cmp Al, ‘A’ ; so sánh giá trị thanh ghi Al với ‘A’
- Cmp Al, Var1 ; so sánh giá trị thanh ghi Al với giá trị biến Var1
Tất cả cá lệnh Cmp ở trên điều không có ý nghĩa, vì nó không cho biết kết quả so sánh một cách trực tiếp mà phải ánh thông qua các cờ.
Lệnh Cmp không thể sử dụng để so sánh hoặc kiểm tra giá trị của các cờ.
Các lệnh nhảy
Lệnh nhảy không điều kiện:
Cú pháp: Jmp <Vị trí đích>
- Trong đó: <Vị trí đích> có thể là nhãn của một lệnh, tên của một thủ tục hoặc có thể là một thanh ghi, một ô nhớ (đã được định nghĩa) nào đó. <Vị trí đích> cũng có thể là một biến nào đó, giá trị của nó thường là địa chỉ của một ô nhớ trong đoạn Code.
- Tác dụng: Khi gặp lệnh này chương trình chuyển điều khiển (nhảy đến) đến thực hiện lệnh sau <Nhãn đích> mà không phụ thuộc vào bất kỳ điều kiện nào.
Cơ chế thực hiện của lệnh Jmp là thay đổi nội dung của cặp thanh ghi con trỏ lệnh CS:IP. CS:IP mới sẽ là địa chỉ của lệnh sau <Nhãn đích> trong bộ nhớ.
Lệnh Jmp có 3 dạng: Short, Near và Far. Đối với dạng Short và Far thì chỉ có thanh ghi IP bị thay đổi khi lệnh thực hiện, ngược lại, với dạng Far, khi lệnh thực hiện thì cả thanh ghi IP và thanh ghi đoạn CS đều bị thay đổi. Hay nói cách khác: Đối với dạng Short và Near thì lệnh Jmp và <Vị trí đích> phải nằm trong cùng Segment nhớ, ngược lại, với dạng Far thì lệnh Jmp và <Vị trí đích> có thể nằm ở các Segment nhớ khác nhau.
Ví dụ 1:
Start:
Jmp Main
TieuDe DB ‘Khoa CNTT – DHHH’
Main Proc
..................
Main Endp
End Start
Ví dụ 2:
- Jmp short Main
- Jmp Ax
- Jmp word PTR [BX]
- Jmp dword PTR [BX]
Ví dụ 3:
Reset DD 5BE000F0h
Jmp Reset
Ví dụ 4:
Mov Ax, 15
Mov Bx, 20
Jmp TTong
Add Ax, Bx
TTong:
Sub Ax, Bx
Mov Cx, Ax
Kết thúc đoạn lệnh trên Cx = Ax = -5, vì lệnh Add Ax, Bx không được thực hiện. Khi gặp lệnh Jmp TTong chương trình nhảy đến thục hiện lệnh sau nhãn TTong, đó chính là lệnh Sub Ax, Bx.
Lệnh nhảy có điều kiện:
Cú pháp chung: <Lệnh nhảy có điều kiện> <Vị trí đích>
- Trong đó: <Vị trí đích>: Tương tự như lệnh Jmp.
- Tác dụng: Khi gặp một lệnh nhảy có điều kiện, đầu tiên chương trình sẽ kiểm tra điều kiện nhảy của nó, nếu thỏa mãn thì sẽ nhảy đến thực hiện lệnh ở <Vị trí đích>, nếu không thì bỏ qua không thực hiện lệnh nhảy này.
Điều kiện nhảy của các lệnh nhảy này chính là sự thay đổi giá trị của các cờ hiệu, do đó để tạo điều kiện nhảy cho một lệnh nhảy xác định thì chương trình phải làm thay đổi giá trị của cờ hiệu tương ứng với nó. Chương trình thường dùng các lệnh địch bít, quay bít, so sánh,... để làm thay đổi giá trị các cờ hiệu để tạo điều kiện nhảy cho các lệnh nhay. Cách đơn giản nhất là sử dụng lệnh Cmp ngay trước lệnh nhảy.
Sau đây là các lệnh nhảy có điều kiện với dữ liệu có dấu:
- Lệnh JG: Nhảy nếu [Đích] > [Nguồn] ; (SF = 0F và ZF = 0)
- Lệnh JL: Nhảy nếu [Đích] < [Nguồn] ; (SF <> 0F)
- Lệnh JGE: Nhảy nếu [Đích] ≥[Nguồn] ; (SF = 0F)
- Lệnh JLE: Nhảy nếu [Đích] ≤[Nguồn] ; (CF <> 0F và ZF = 1)
- ... <còn nhiều lệnh khác>
Trong đó: [Đích] và [Nguồn] chính là hai toán hạng: [Toán hạng đích] và [Toán hạng nguồn] trong lệnh Cmp đứng ngay trước lệnh nhảy. Tức là, chương trình sử dụng lệnh Cmp để tạo điều kiện nhảy cho các lệnh này. Cụ thể: lệnh nhảy có thực hiện được hay không (có chuyển điều khiển đến <Vị trí đích> hay không) phụ thuộc vào giá trị của [Đích] và [Nguồn] trong lệnh Cmp đứng ngay trước nó.
Với việc sử dụng lệnh Cmp để tạo điều kiện nhảy cho các lệnh nhảy thì ta không cần quan tâm đến các cờ điều kiện nhảy của chúng.
Sau đây là các lệnh nhảy có điều kiện với dữ liệu không dấu:
- Lệnh JA: Nhảy nếu [Đích] > [Nguồn] ; (CF = 0 và ZF = 0)
- Lệnh JB: Nhảy nếu [Đích] < [Nguồn] ; (CF = 0)
- Lệnh JNA: Nhảy nếu [Đích] không lớn hơn [Nguồn]; (CF =1 or ZF =1)
- Lệnh JNB: Nhảy nếu [Đích] không nhỏ hơn [Nguồn] ; (CF = 0)
Các lệnh nhảy với dữ liệu có dấu có thể áp dụng với các dữ liệu không dấu.
Sau đây là các lệnh nhảy có điều kiện dùng chung:
- Lệnh JC: Nhảy nếu cờ CF = 1
- Lệnh JNC: Nhảy nếu cờ CF = 0
- Lệnh JZ: Nhảy nếu cờ ZF = 1
- Lệnh JNZ: Nhảy nếu cờ ZF = 0
- Lệnh JE: Nhảy nếu [Đích] = [Nguồn]; Tương tự JZ; (ZF = 1)
- Lệnh JNE: Nhảy nếu [Đích] ≠[Nguồn]; Tương tự JNZ; (ZF = 0)
- ... <còn nhiều lệnh khác> [2 - 150]
Với các lệnh này, chương trình thường sử dụng các lệnh dịch bít hoặc lệnh quay bít để tạo điều kiện nhảy nó.
Ví dụ 1a: Dãy lệnh sau đây thực hiện việc gán giá trị cho thanh ghi Cx dựa vào giá trị của thanh ghi Ax và Dx:
Mov Ax, 12
Mov Dx, 25
;-------------------
Cmp Ax, Dx ; Ax ? Bx
Jg Nhan1 ; nếu Ax > Dx
Jle Nhan2
Nhan1:
Mov Cx, Ax
Jmp Tiep_Tuc
Nhan2:
Mov Cx, Dx
Jmp Tiep_Tuc
Tiep_Tuc:
Mov Bx, Cx
;------------------------
Có thể thấy, ở đây không cần dùng lệnh Jle Nhan2, vì nếu Ax không lớn hơn Dx thì chắc chẵn nó sẽ nhỏ hơn hoặc bằng Dx. Ngoài ra cũng không cần dùng lệnh Jmp Tiep_Tuc sau nhãn Nhan2, vì việc chuyển đến lệnh sau nhãn Tiep_Tuc ở đây là tất nhiên. Vì thế đoạn lệnh trên có thể được viết rút gọn như trong Ví dụ 1b sau đây.
Ví dụ 1b: Dãy lệnh sau đây là trường hợp rút gọn của dãy lệnh trên:
Mov Ax, 12
Mov Dx, 25
;------------------
Cmp Ax, Dx
Jg Nhan1 ; nếu Ax > Dx
Mov Cx, Dx ; khi Ax ≤Dx
Jmp Tiep_Tuc
Nhan1:
Mov Cx, Ax ; khi Ax > Dx
Tiep_Tuc:
Mov Bx, Cx
;------------------------
Trong cả hai ví dụ trên: khi kết thúc, Bx = Cx = Dx = 25. Nhưng nếu cho
Ax = 120 (Ax > Bx) thì Bx = Cx = Ax = 120.
Ax = 120 (Ax > Bx) thì Bx = Cx = Ax = 120.
Ví dụ 2: Giả sử tại địa chỉ 0A000:0100 trong bộ nhớ có chứa một mảng các số nguyên kiểu byte, gồm 100 phần tử (100 byte).
Các lệnh sau đây tính tổng của các phần tử trong mảng này mà giá trị của nó lớn hơn 123. Kết quả chứa ở thanh ghi Dx.
Mov Ax, 0A000h
Mov DS, Ax
Mov DI, 0100h
;------------------------
Mov Dx, 0
Mov Cx, 100
Lap_TT:
Mov Al, Byte PTR DS:[DI]
Cmp Al, 123
Jle Tiep_Tuc
Add Dx, Al
Tiep_Tuc:
INC DI ; trỏ đến phần tử kế tiếp
Loop Lap_TT ; lặp lại: kiển tra và tính tổng
;---------------------
Ví dụ 3: Giả sử tại địa chỉ 0C000:00120 trong bộ nhớ có chứa một xâu kí tự, xâu này được kết thúc bởi giá trị 0 (số 0).
Các lệnh sau đây sẽ đếm xem xâu nói trên gồm bao nhiêu kí tự. Kết quả ghi vào ô nhớ ngay trước vùng nhớ chứa xâu này:
Mov Ax, 0C000h
Mov ES, Ax
Mov DI, 00120h
Mov SI, DI
;------------------------
Mov Dx, 0
Lap_Dem:
Mov Al, Byte PTR ES:[DI]
Cmp Al, 0 ; so sánh Al với 0
Je KetThuc ; nếu Al = 0: đã đến cuối xâu
INC Dx ; khi Al <> 0: đếm
INC DI ; trỏ đến kí tự kế tiếp
Jmp Lap_Dem ; lặp lại: kiển tra và đếm
KetThuc:
Mov Byte PTR DS:[SI - 1], Dx
;------------------------------------------
Ví dụ 4: Các lệnh sau đây in nội dung bản bản ASCII ra màn hình, nhưng không in ra các kí tự có mã 07h, 0Ah, 0Dh.
Mov Cx, 256
Mov Ah, 02
Mov Dl, 0
Lap_In:
Cmp Dl, 07h
Je TTuc
Cmp Dl, 0Ah
Je TTuc
Cmp Dl, 0Dh
Je TTuc
Int 21h
TTuc:
INC DL
Loop Lap_In
;-----------------------
Ví dụ 5: Các lệnh sau đây cho phép nhập một xâu kí tự bất kỳ, dài không quá 200 kí tự, từ bàn phím vào biến XauNhap. Sau đó copy các kí tự là chữ cái in hoa trong xâu vừa nhập vào biến XauHoa.
Trước hết chương trình phải khai báo các biến:
XauNhap DB 200, 0, 200 Dup (‘ ’)
XauHoa DB 200 Dup (‘ ’)
Các lệnh:
Mov Ax, Seg XauNhap
Mov DS, XauNhap
Mov Dx, Offset XauNhap
Mov Ax, Seg XauHoa
Mov ES, XauHoa
Mov DI, Offset XauHoa
;----------------------------------
Mov Ah, 0Ah
Int 21h
;---------------------
Mov Cx, 0
Mov Cl, XauNhap[1]
Mov SI, Dx
Add SI, 2
;------------------------------
Lap_Copy:
Mov Al, DS:[SI]
Cmp Al, ‘A’
Jl TTuc
Cmp Al, ‘Z’
Jg TTuc
Mov ES:[DI], Al
INC DI
TTuc:
INC SI ;
Loop Lap_Copy
;-----------------------
Nên nhớ, trong bảng mã ASCII các kí tự là chữ cái in hoa nằm ở những vị trí liên tiếp nhau: A, B, C, ..., Z, chúng có mã lần lượt là 65, 66, 67, ..., 90.
Ví dụ 6: Giả sử tại địa chỉ 0F000:FFFE trong bộ nhớ ROM-BIOS có chứa một byte dữ liệu. Byte này cho biết loại của máy tính đang sử dụng. Cụ thể, nếu byte này: chứa trị 0FBh: máy PC/XT; chứa trị 0FCh: máy PC/AT; chứa trị 0FFh: máy PC classic;...
Các lệnh sau đây cho biết máy tính đang sử dụng thuộc loại máy nào:
Trước hết chương trình phải khai báo các biến trả lời:
TB1 DB ‘Day la may PC/XT.$’
TB2 DB ‘Day la may PC/AT.$’
TB3 DB ‘Day la may PC classic.$’
Các lệnh:
Mov Ax, 0F000h
Mov ES, Ax
Mov SI, 0FFFEh
;------------------------
Mov Al, Byte PTR ES:[SI]
Cmp Al, 0FBh
Je TraLoi1
Cmp Al, 0Fch
Je TraLoi2
Cmp Al, 0Ffh
Je TraLoi3
...
TraLoi1:
Mov Ax, Seg TB1
Mov DS, Ax
Lea Dx, Offset TB1
Mov Ah, 09
Int 21h
Jmp KetThuc
TraLoi2:
Mov Ax, Seg TB2
Mov DS, Ax
Lea Dx, Offset TB2
Mov Ah, 09
Int 21h
Jmp KetThuc
TraLoi1:
Mov Ax, Seg TB3
Mov DS, Ax
Lea Dx, Offset TB3
Mov Ah, 09
Int 21h
Jmp KetThuc
.....
KetThuc:
Mov Ah, 4Ch
Int 21h
;----------------------
Có thể nói, ví dụ trên đây là một thao tác điển hình trong lập trình hợp ngữ. Nó cũng cho thấy chức năng và thế mạnh của ngôn ngữ này. Đây cũng là mục tiêu mà người lập trình hợp ngữ nhắm tới. Việc truy xuất vào các vùng nhớ dữ liệu để lấy các byte/word thông tin cấu hình hệ thống là một yêu cầu cơ bản với các ngôn ngữ lập trình cấp thấp, và nó được thực hiện một cách khá đơn giản trong ngôn ngữ hợp ngữ. Ví dụ trên đây cũng cho thấy nguyên tắc để làm việc này.
4 Comment:
rất hay :))))
rất hay <3
rất hay <3
thanks
Đăng nhận xét
Thank you for your comments!