Tập lệnh assembly của Intel 8086/8088 (Phần 2)

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].

Mới hơn Cũ hơn

Biểu mẫu liên hệ