3. Lệnh LOOP
Cú pháp:
Loop <Nhãn đích>
Trong đó: <Nhãn đích> là một nhãn lệnh và nó phải đứng trước lệnh lặp Loop không quá 126 byte.
Tác dụng: Khi gặp lệnh này chương trình sẽ lặp lại việc thực hiện các lệnh sau <Nhãn lệnh> đủ n lần, với n được đặt trước trong thanh ghi CX. Sau mỗi lần lặp CX tự động giảm 1 đơn vị (Cx = Cx - 1) và lệnh lặp sẽ dừng khi Cx = 0.
Lệnh Loop thường được sử dụng để cài đặt các đoạn chương trình lặp với số lần lặp xác định, được cho trước trong thanh ghi Cx (tương tự các vòng lặp For trong các ngôn ngữ lập trình bậc cao).
Ví dụ 1: Xem đoạn lệnh sau đây:
Mov Ax, 6
Mov Cx, 4 ; lặp lại 4 lần
Lap: Add Ax, 10 ; cộng thêm 10 vào Ax
Loop Lap ; lặp lại việc cộng 10 vào Ax đủ 4 lần
Kết thúc đoạn lệnh trên: Ax = 46 (cụ thể: Lần 1: Ax = 6 + 10; Lần 2: Ax = 16 + 10; Lần 3: Ax = 26 + 10; Lần 4: Ax = 36 + 10 = 46).
Ví dụ 2: Xem đoạn lệnh sau đây:
Mov Ax, 6
Mov Bx, 3
Mov Cx, 4 ; lặp lại 4 lần
Lap_TT:
Add Ax, Bx ; cộng thêm giá trị thanh ghi Bx vào thanh ghi Ax
Inc Bx ; tăng Bx lên 1 đơn vị
Loop Lap_TT ; lặp lại các lệnh sau nhãn lệnh Lap_TT đủ 4 lần
;----------------------------- ; sau lệnh lặp này Ax = 24, Bx = 7
Mov Dx, Ax ; Dx ß Ax
Mov Cx, Bx ; Cx = 7, sau Loop Cx = 0, được thay bằng Bx = 7
Kết thúc đoạn lệnh trên: Ax = 24 (Lần 1: Ax = 6 + 3;Lần 2: Ax = 9 + 4; Lần 3: Ax = 13 + 5; Lần 4: Ax = 18 + 6) và Dx = Ax = 24.
Khi gặp lệnh Loop Lap_TT chương trình sẽ quay lại (nếu Cx <> 0) thực hiện lệnh Add Ax, Bx (Lap_TT là nhãn của lệnh này), tất nhiên khi đó nó cũng phải thực hiện lại lệnh Inc Bx. Dó đó, có thể nói lệnh Loop này thực hiện vòng lặp cho cả hai lệnh Add và Inc. Đó cũng chính là lý do mà người ta thường viết nhãn của các lệnh phải được lặp theo kiểu như trên (nhãn lệnh và lệnh không cùng trên một dòng).
Ví dụ 3: Xem đoạn lệnh sau đây:
Mov Dx, 6
Mov Cx, 5 ; lặp lại 5 lần
TT:
Add Dx, Cx ; cộng thêm giá trị thanh ghi Cx vào thanh ghi Dx
Loop TT ; lặp lại các lệnh sau nhãn lệnh TT đủ 5 lần
;-----------------------------;
Mov Bx, Cx
Kết thúc đoạn lệnh trên Bx = Cx = 0 (khi Cx = 0 thì vòng lặpLoop TT kết thúc) và Dx = 21 (Lần 1: Dx = Dx + Cx = 6 + 5;Lần 2: Dx = Dx + Cx = 11 + 4; Lần 3: Dx = Dx + Cx = 15 + 3; Lần 4: Dx = Dx + Cx = 18 + 2;Lần 5: Dx = Dx + Cx = 20 + 1 = 21).
Ví dụ 4: Các lệnh sau đây thực hiện phép gán:
Ax = 2 + 4 + ...+ 100
Mov Ax, 0
Mov Bx, 2
Mov Cx, 50
Lap_TT:
Add Ax, Bx
Add Bx, 2
Loop Lap_TT
Ví dụ 5: Giả sử tại địa chỉ offset 100 trong đoạn nhớ Data (được chỉ bởi thanh ghi đọan DS) có chứa một mảng dữ liệu, gồm 100 ô nhớ, mỗi ô là một byte. Hãy cộng thêm 50 đơn vị vào tất cả các ô nhớ trong mảng này.
Mov DI, 0100 ; trỏ cặp thanh ghi DS:DI về
; vùng nhớ cần truy xuất (DS:0100)
;------------------------
Mov Cx, 100 ; lặp 100 lần vì mảng gồm 100 ô nhớ
Lap_TangThem:
Mov Dl, DS:[DI] ; lấy nôi dung ô nhớ chỉ bởi DS:DI lưu vào DL
Add Dl, 50 ; cộng thêm 50 vào Dl
Mov DS:[DI], Dl ; đặt giá trị đã tăng thêm vào lại ô nhớ DS:DI
Inc DI ; chỉ đến ô nhớ kế tiếp (vì ô nhớ byte nên tăng 1)
Loop Lap_TangThem ; lặp lại đủ 100 lần (duyệt qua đủ 100 ô nhớ)
Trong trường hợp này ta có thể sử dụng lệnh Add DS:[DI], 50 để tăng trực tiếp nội dung của ô nhớ, hợp ngữ cho phép điều này. Nhưng cách đã viết thường được áp dụng hơn, vì tính tổng quát của nó. Nói chung, chúng ta nên hạn chế tác động trực tiếp lên nôi dung của ô nhớ.
Ví dụ 6: Giả sử tại địa chỉ 0100:0C00 trong bộ nhớ có chứa một xâu kí tự gồm 50 kí tự (tức là, gồm 50 ô nhớ, mỗi ô 1 byte). Hãy copy xâu kí tự này sang vùng nhớ bắt đầu tại địa chỉ 0200:0100.
Mov Ax, 0100
Mov DS, Ax ; trỏ cặp thanh ghi DS:SI về
Mov SI, 0C00 ; đầu vùng nhớ chưa xâu cần copy (0100:0C00)
Mov Ax, 0200
Mov ES, Ax ; trỏ cặp thanh ghi ES:DI về
Mov DI, 0100 ; vùng nhớ chứa xâu kết quả copy 0200:0100
;------------------------
Mov Cx, 50 ; lặp 50 lần vì xâu gồm 50 kí tự
Lap_Copy:
Mov Bl, DS:[SI] ; mượn Bl để chuyển tường kí tự từ ô nhớ được
Mov ES:[DI], Bl ; chỉ bởi DS:SI sang ô nhớ được chỉ bởi ES:DI
Inc SI ; chuyển đến kí tự tiếp theo
Inc DI
Loop Lap_Copy ; lặp lại đủ 50 lần (để copy đủ 50 kí tự)
Trong ví dụ này, vùng nhớ chứa xâu kí tự cần copy (vùng nhớ nguồn) và vùng nhớ chứa kết quả copy (vùng nhớ đích) nằm ở hai đoạn (segment) nhớ khác nhau, do đó, ta phải sử dụng hai thanh ghi đoạn dữ liệu khác nhau: DS và ES.
Qua 2 ví dụ 5 và 6 ta có thể rút ra nguyên tắc cơ bản để truy xuất dữ liệu trên một vùng nhớ/mảng nhớ:
Chú ý: Lệnh Loop thực hiện vòng lặp cho đến khi Cx = 0, vì thế nó được xem như lệnh lặp không điều kiện. Thực tế, hợp ngữ còn có các lệnh lặp có điều kiện, nó cho phép kết thúc vòng lặp trước khi Cx = 0 dựa vào một điều kiện nào đó. Cụ thể: lệnh LoopE (Loop while Equal): Chỉ lặp lại khi Cx <> 0 và cờ ZF = 1; lệnh LoopZ (Loop while Zero): tương tự LoopE; LoopNE (Loop while Not Equal): Chỉ lặp lại khi Cx <> 0 và cờ ZF = 0;... [2 - 154].
4. Lệnh LEA (LoadEffectiveAddress)
Cú pháp:
LEA [Toán hạng đích],[Toán hạng nguồn]
Trong đó: [Toán hạng đích]: Là các thanh ghi 16 bít. [Toán hạng nguồn]: Là địa chỉ của một vùng nhớ hay tên của một biến.
Tác dụng: Lệnh LEA có tác dụng chuyển địa chỉ offset của [Toán hạng nguồn] vào [Toán hạng đích]. Lệnh này thường được sử dụng để lấy địa chỉ offset của một biến đã được khai báo trong chương trình. Thanh ghi được sử dụng trong trường hợp này là thanh ghi cơ sở (BX) và thanh ghi chỉ mục (SI và DI).
Ví dụ 1:
Lea Bx, DS:[0100] ; chuyển thành phần địa chỉ offset (0100) vào Bx
Lea DI, XauKT ; chuyển địa chỉ offset của biến XauKT vào DI
; thao tác này thường được gọi là trỏ DI và đầu
; biến XauKT
Khi chương trình được nạp vào bộ nhớ để hoạt động thì các biến được khai báo trong chương trình sẽ được định vị (cấp phát vùng nhớ) tại một địa chỉ xác định trong vùng nhớ Data. Từ đây, để thao tác đến dữ liệu trên các biến của chương trình thì chương trình phải xác định được địa chỉ segment vào offset (hai thành phần của địa chỉ logic) của biến. Lệnh LEA ở trên chỉ lấy được địa chỉ offset của biến, để lấy được địa chỉ segment của nó ta có thể sử dụng lệnh Mov với toán tử Seg (tương tự có thể sử dụng lệnh Mov với toán tử Offset để lấy địa chỉ offset của biến). Ví dụ: Các lệnh sau lấy địa chỉ Segment:Offset của biến XauKT (hay trỏ DS:SI về đầu biến XauKT):
Mov Ax, Seg XauKT ; đưa địa chỉ Segment của biến XauKT
Mov DS, Ax ; vào thanh ghi DS
Mov SI, Offset XauKT ; đưa địa chỉ Offset của biến XauKT vào SI
Ví dụ 2: Giả sử biến TenGom (là biến kiểu byte) đã được khai báo như sau:
TenGom DB ‘Nguyen Kim Le Tuan’
Xem các lệnh sau đây (1):
Mov Bx, 0
Mov Al, TenGom[Bx] ; Al =‘N’
Add Bx, 7 ;
Mov Bl, TenGom[Bx] ; Bl =‘K’
Xem các lệnh sau đây (2):
Lea DI, TenGom
Mov Al, [DI] ; Al =‘N’
Mov Bl, [DI + 7] ; Bl =‘K’
Ta có thể thấy, nhóm các lệnh (1) và nhóm các lệnh (2) là tương đương nhau về tác dụng của nó, nhưng (1): sử dụng trực tiếp tên biến để truy xuất đến các phần tử của nó; (2): sử dụng thanh ghi chỉ mục DI để truy xuất đến các phần tử của biến. Trong trường hợp này địa chỉ segment mặc định được chỉ bởi DS, điều này phù hợp với việc sử dụng địa chỉ gián tiếp thanh ghi chỉ mục.
Ví dụ 3: Giả sử tại địa chỉ 0100:0C00 trong bộ nhớ có chứa một xâu kí tự gồm 50 kí tự (tức là, gồm 50 ô nhớ, mỗi ô 1 byte). Hãy copy xâu kí tự này vào một biến trong chương trình.
Với yêu cầu này chương trình phải khai báo một biến byte có độ lớn 50 byte:
LuuTru DB 50 Dup (‘ ‘)
Mov Ax, 0100
Mov DS, Ax ; trỏ cặp thanh ghi DS:SI về
Mov SI, 0C00 ; đầu vùng nhớ chưa xâu cần copy (0100:0C00)
;--------------------------------
Mov Ax, Seg LuuTru ; trỏ cặp thanh ghi ES:DI về
Mov ES, Ax ; đầu biến LuuTru
Lea DI, LuuTru
;--------------------------------
Mov Cx, 50
Lap_Copy:
Mov Bh, DS:[SI] ; mượn Bh để chuyển tường kí tự từ ô nhớ được
Mov ES:[DI], Bh ; chỉ bởi DS:SI sang ô nhớ được chỉ bởi ES:DI
Inc SI ; chuyển đến kí tự tiếp theo
Inc DI
Loop Lap_Copy ; lặp lại đủ 50 lần (để copy đủ 50 kí tự)
Chú ý: Hợp ngữ còn cung cấp các lệnh LDS (Load Pointer use DS) để lấy nội dung toán hạng bộ nhớ 32 bít đưa vào các thanh ghi 16 bít (mặc định 16 bít cao vào thanh ghi đoạn dữ liệu DS); và lệnh LES (Load Pointer use DS) tương tự LDS nhưng mặc định 16 bít cao vào thanh ghi đoạn dữ liệu (thứ hai) ES [2 - 137].
Có thể bạn quan tâm: Beginning x64 Assembly Programming: From Novice to AVX Professional 1st ed. Edition