You are on page 1of 83

Bài 3:

 Hầu hết các hệ điều hành máy tính hiện nay, đặc biệt là các hệ điều hành của
Microsoft, đều hỗ trợ hai dạng cấu trúc tập tin thực thi có thể hoạt động trên nó, đó
là tập tin cấu trúc dạng COM và tập tin cấu trúc dạng EXE. Có nhiều điểm khác
nhau giữa hai cấu trúc chương trình này, nhưng điểm khác biệt lớn nhất là: Các
chương trình cấu trúc dạng EXE gồm 3 đoạn: Mã lệnh (Code), dữ liệu (Data) và
Ngăn xếp (Stack). Khi hoạt động, 3 đoạn này sẽ được nạp vào 3 đoạn (Segment)
bộ nhớ tách biệt trên bộ nhớ;
             Các chương trình dạng COM thì ngược lại, nó chỉ có 1 đoạn mã lệnh, trong
đó chứa cả mã lệnh và ngăn xếp. Vì thế, khi được nạp vào bộ nhớ để hoạt động nó
chỉ được cấp phát một đoạn bộ nhớ. Rõ ràng kích thước của một chương trình dạng
COM không thể vượt quá giới hạn của một đoạn bộ nhớ (với Intel 8088/80286 và
MSDOS, 1 Segment bộ nhớ = 64KB).
             Trong khi đó một chương trình dạng EXE có thể lớn hơn 3 Segment bộ
nhớ. Do đó, khi thiết kế các chương trình lớn, với chức năng phức tạp, trong đó có
liên kết giữa các modun chương trình khác nhau thì ta phải thiết kế theo cấu trúc
chương trình dạng EXE.
             Hợp ngữ hỗ trợ thiết kế cả hai dạng cấu trúc chương trình EXE và COM,
mỗi dạng phù hợp với một nhóm trình biên dịch nào đó. Muốn biên dịch một
chương trình hợp ngữ sang dạng EXE thì ngoài việc nó phải được viết theo cấu
trúc dạng EXE ta còn cần phải sử dụng một trình biên dịch phù hợp. Điều này cũng
tương tự với việc muốn có một chương trình thực thi dạng COM.
             Văn bản của một chương trình hợp ngữ dạng EXE cũng cho thấy rõ nó
gồm 3 đoạn: Code, Data và Stack. Tương tự, văn bản của chương trình hợp ngữ
dạng COM cho thấy nó chỉ có 1 đoạn: Code, cả Data và Stack (không tường minh)
đều nằm ở đây.
             Một chương trình hợp ngữ gồm hai thành phần chính: phần lệnh hợp ngữ
và phần chỉ dẫn biên dịch. Chỉ có các lệnh là được biên dịch thành ngôn ngữ máy.
Phần hướng dẫn biên dịch không được dịch sang ngôn ngữ máy, nó chỉ có tác dụng
với các trình biên dịch. Thông thường mỗi chương trình biên dịch có một nhóm
hướng dẫn biên dịch phù hợp với nó, những với các hướng dẫn biên dịch cơ bản và
đơn giản thì nó phù hợp với hầu hết các trình biên dịch hợp ngữ hiện nay. Trong tài
liệu này chúng tôi sử dụng các hướng dẫn biên dịch phù hợp với trình biên dịch
Microsoft Macro Assembler (MASM).
               Cấu trúc chương trình được giới thiệu sau đây sử dụng các hướng dẫn
biên dịch định nghĩa đoạn đơn giản (.Model, .Code, .Stack, .Data) phù hợp với
MASM, TASM (Turbo Macro Assembler), A86. Việc sử dụng định nghĩa đoạn
đơn giản sẽ làm cho văn bản chương trình sáng sủa và dễ đọc hơn. Với các định
nghĩa đoạn đơn giản ta cũng có thể xây dựng được các chương trình từ đơn giản
đến phức tạp.
Cấu trúc chương trình dạng COM:
.Model           <Chế độ bộ nhớ>
.Code
                        ORG   100h
            <Nhãn chính>:        
                        JMP    <Thủ tục chính>
       <Khai báo dữ liệu đặt tại đây>
<Thủ tục chính>       PROC
 
       <Các lệnh của chương trình đặt tại đây>        
         
<Thủ tục chính>       Endp
<Các thủ tục khác đặt tại đây>
            End     <Nhãn chính>
Trong cấu trúc chương trình trên các từ khóa Model, Code, ORG, Proc, Endp, End
là các hướng dẫn biên dịch. <Nhãn chính> là nhãn của lệnh Jmp.
Cấu trúc này cho thấy rõ, một chương trình hợp ngữ dạng COM chỉ có 1 đoạn, đó
chính là đoạn Code (đoạn mã lệnh), trong này bao gồm cả phần khai báo dữ liệu.
Các khai báo dữ liệu trong chương trình dạng COM có thể đặt ở đầu hoặc ở cuối
chương trình, nhưng với việc sử dụng định nghĩa đoạn đơn giản các khai báo dữ
liệu phải đặt ở đầu chương trình.   
Chỉ dẫn ORG   100h và lệnh JMP   <Thủ tục chính> sẽ được đề cập trở lại ở
các phần sau đây của tài liệu này.
Cấu trúc chương trình dạng EXE:
.Model           <Chế độ bộ nhớ>
.Stack           100h
.Data   
      <Khai báo dữ liệu đặt tại đây>
.Code                        
<Thủ tục chính>       PROC
 
      <Các lệnh của chương trình đặt tại đây>     
             
<Thủ tục chính>       Endp
<Các thủ tục khác đặt tại đây>
                        END
Trong cấu trúc chương trình trên các từ khóa Model, Code, Data, Stack, Proc,
Endp, End là các hướng dẫn biên dịch.
Cấu trúc này cho thấy rõ, một chương trình hợp ngữ dạng gồm 3 đoạn: đoạn Code,
chứa toàn bộ mã lệnh của chương trình. Đoạn Data, chứa phần khai báo dữ liệu của
chương trình. Đoạn Stack, nơi chứa stack (ngăn xếp) của chương trình khi chương
trình được nạp vào bộ nhớ để hoạt động. 
Chỉ dẫn .Stackđặt ở đầu chương trình với mục đích khai báo kích thước của Stack
dùng cho chương trình sau này. Kích thước thường được chọn là 100h (256) byte. 
Chỉ dẫn .Model được đặt ở đầu cả cấu trúc chương trình dạng COM và EXE với
mục đích khai báo chế độ bộ nhớ mà chương trình sử dụng.
Ví dụ: Sau đây là hai chương trình hợp ngữ đơn giản, dạng COM và dạng EXE,
cùng thực hiện nhiệm vụ in ra màn hình 3 dòng văn bản như sau :
                 Nguyen Kim Le       Tuan
 Nguyen Le Tram     Thanh
 Nguyen Le Tram     Uyen
Hai chương trình dưới đây chỉ có tác dụng minh họa cho việc sử dụng các hướng
dẫn biên dịch định nghĩa đoạn đơn giản và giúp các bạn thấy được những điểm
giống nhau, khác nhau giữa hai dạng cấu trúc chương trình dạng COM và EXE, vì
vậy, ở đây các bạn chưa cần quan tâm đến ý nghĩa của các lệnh và các hàm/ngắt
trong nó. Phần lệnh hợp ngữ và các hàm/ngắt sẽ được trình bày ngay sau đây.
Chương trình viết theo cấu trúc dạng COM:
.Model           Small
.Code
                        ORG   100h
            Start:
                        Jmp     Main
           MyChildren        DB      ‘Nguyen Kim Le     Tuan’,0Ah,0Dh
                                     DB      ‘Nguyen Le Tram   Thanh’,0Ah,0Dh
                                     DB      ‘Nguyen Le Tram   Uyen’,’$’
Main               PROC
                    ;------- in ra mot xau voi ham 09/21h -------
                    Mov            Ah, 09h
                    Lea              Dx, MyChildren
                    Int               21h
                    ;------- ket thuc chuong trinh -------
                   Int               20h
Main            Endp
            End     Start
Chương trình này chọn chế độ bộ nhớ Small. Tên thủ tục chính là Main (tên thủ
tục chính là tùy ý). Nhãn chính của chương trình là Start (tên thủ tục chính là tùy
ý), đó chính là nhãn của lệnh Jmp. Phần khai báo dữ liệu chỉ khai báo một biến, đó
là MyChildren.
Chương trình này gọi hàm 4Ch của ngắt 21h để kết thúc chương trình. Có thể gọi
ngắt 20h để kết thúc các chương trình dạng COM.  
Chương trình viết theo cấu trúc dạng EXE:
.Model           Small
.Stack            100h
.Data
      MyChildren            DB      ‘Nguyen Kim Le      Tuan’,0Ah,0Dh
                                    DB      ‘Nguyen Le Tram   Thanh’,0Ah,0Dh
                                    DB      ‘Nguyen Le Tram   Uyen’,’$’
.Code                        
Main               PROC
                    ;------- khởi tạo DS -------
                    Mov            Ax, @Data
                    Mov            DS, Ax
                    ;------- in ra mot xau voi ham 09/21h -------
                    Mov            Ah, 09h
                    Lea              Dx, MyChildren
                    Int               21h
                    ;------- ket thuc chuong trinh -------
                    Mov            Ah, 4Ch
                    Int               21h
Main               Endp
            END   Main
Chương trình này chọn chế độ bộ nhớ Small. Khai báo kích thước Stack là 100h
byte. Phần khai báo dữ liệu được đặt trong đoạn Data, ở đây chỉ khai báo một biến,
đó là MyChildren. Tên thủ tục chính là Main (tên thủ tục chính là tùy ý).
Thao tác đầu tiên của chương trình là trỏ thanh ghi đoạn DS về đầu đoạn Data, hay
còn gọi là khởi tạo thanh ghi đoạn DS:
        Mov      Ax, @Data
        Mov     DS, Ax
thao tác này được xem như là bắt buộc đối với cấu trúc chương trình dạng EXE sử
dụng định nghĩa đoạn đơn giản. Các chương trình viết theo cấu trúc dạng EXE phải
gọi hàm 4Ch của ngắt 21h để kết thúc.
Có thể thấy, cấu trúc chương trình dạng COM và cấu trúc chương trình dạng EXE
chỉ khác phần hướng dẫn biên dịch, phần khai báo biến và phần lệnh thao tác chính
hoàn toàn giống nhau. Hai chương trình đơn giản ở trên hoàn toàn giống nhau ở
biến là MyChildren và các lệnh gọi hàm 09h của ngắt 21h để in ra màn hình một
xâu kí tự (xâu này chính là giá trị khởi tạo của biến MyChildren).
Chú ý 1: Trình biên dịch hợp ngữ (Macro Assembler) cho phép các chương trình
được dịch bởi nóc họn sử dụng một trong các chế độ bộ nhớ sau:
- Small: Đoạn mã lệnh (Code) và đoạn dữ liệu (Data) của chương trình đều chỉ có
thể chứa trong một đoạn (segment) bộ nhớ. Tức là, kích thước của chương trình chỉ
có thể tối đa là hai đoạn bộ nhớ. Tuy vậy chế độ bộ nhớ này đủ dùng cho hầu hết
các chương trình hợp ngữ.
- Medium: Đoạn Code của chương trình có thể chiếm nhiều hơn một đoạn bộ nhớ.
Trong khi đó, đoạn Data chỉ có thể chiếm 1 đoạn bộ nhớ.
- Compact: Đoạn Data của chương trình có thể chiếm nhiều hơn một đoạn bộ nhớ.
Trong khi đó, đoạn Code chỉ có thể chiếm 1 đoạn bộ nhớ.
- Large: Đoạn Code và đoan Data của chương trình đều có thể chiếm nhiều hơn
một đoạn bộ nhớ. Nhưng trong trường hợp này không thể định nghĩa một mảng dữ
liệu có kích thước lớn hơn 64 Kbyte.
- Huge: Tương tự như Large, nhưng trong trường hợp này có thể định nghĩa một
mảng dữ liệu có kích thước lớn hơn 64 Kbyte.
Chế độ bộ nhớ Small là đơn giản nhất, được hầu hết các chương trình lựa chọn.
Chú ý 2: Với các chương trình hợp ngữ sử dụng định nghĩa đoạn đơn giản: Khi
được nạp vào bộ nhớ để hoạt động thì các thanh ghi đoạn sẽ tự động trỏ về các
đoạn chương trình tương ứng. Cụ thể: Thanh ghi đoạn CS chứa địa chỉ segment
của đoạn bộ nhớ chứa đoạn Code của chương trình. Thanh ghi đoạn DS (và có thể
cả ES) chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Data của chương trình.
Thanh ghi đoạn SS chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Stack của
chương trình.     
Tuy nhiên, trong thực tế, khi nạp chương trình EXE vào bộ nhớ DOS luôn dành ra
256 byte đầu tiên của vùng nhớ, mà DOS cấp phát cho chương trình, để chứa PSP
(Program Segment Prefix) của chương trình. PSP chứa các thông tin cần thiết mà
trình biên dịch chuyển đến cho DOS để hỗ trợ DOS trong việc thực hiện chương
trình này, đặc biệt, chương trình cũng có thể truy xuất vùng nhớ PSP. Do đó, DOS
phải đưa địa chỉ segment của vùng nhớ chứa PSP vào cả DS và ES trước khi
chương trình được thực hiện. Tức là, ngay khi chương trình được nạp vào bộ nhớ
DS không phải chứa địa chỉ segment của đoạn Data của chương trình mà chứa địa
chỉ segment của PSP.
Vì vậy, để trỏ DS về lại đoạn Data chương trình chúng ta phải đặt ngay hai lệnh
sau đây ở đầu chương trình viết theo cấu trúc EXE:
            Mov        Ax, @Data
            Mov        DS, Ax       
Với việc khởi tạo thanh ghi đoạn DS ở trên, địa chỉ segment của tất cả các biến
khai báo trong đoạn Data đều được chứa trong thanh ghi DS, do đó, trong các thao
tác xử lý biến sau này chương trình không cần quan tâm đến địa chỉ segment của
nó nữa.       
Chú ý 3: Hợp ngữ còn cho phép các chương trình sử dụng các hướng dẫn biên
dịch định nghĩa đoạn toàn phần, các định nghĩa này phù hợp với hầu hết các trình
biên dịch hợp ngữ hiện nay. Định nghĩa đoạn toàn phần giúp cho việc viết chương
trình hợp ngữ trở nên mềm dẻo và linh hoạt hơn, nó giúp người lập trình có thể
điều khiển thứ tự các đoạn chương trình, kết hợp các đoạn chương trình, liên kết
các đoạn chương trình trong bộ nhớ,... , ngay trong khi lập trình.
Chi tiết về cách sử dụng và mục đích sử dụng của các hướng dẫn biên dịch nói
chung và các định nghĩa đoạn toàn phần nói riêng dễ dàng tìm thấy trong rất
nhiều tài liệu về lập trình hợp ngữ [1], [2]. Ở đây chúng tôi chỉ giới thiệu sơ lược
về nó  thông qua ví dụ dưới đây.            
Ví dụ: Sau đây là một chương trình dạng EXE sử dụng các hướng dẫn biên dịch
định nghĩa đoạn toàn phần (phù hợp với Macro Assembler):
S_Seg             Segment        Stack
            DB      100h   DUP (?)
S_Seg             Ends
            D_Seg             Segmet
MyChildren        DB      ‘Nguyen Kim Le      Tuan’,0Ah,0Dh
                          DB      ‘Nguyen Le Tram   Thanh’,0Ah,0Dh
                          DB      ‘Nguyen Le Tram   Uyen’,’$’
D_Seg             Ends
C_Seg             Segment
            ASSUME       CS:C_Seg, SS:S_Seg, DS:D_Seg                           
Main           PROC
                    ;------- khởi tạo DS -------
                    Mov            Ax, D_Seg
                    Mov            DS, Ax
                    Mov            Ah, 09h
                    Lea              Dx, MyChildren   ; địa chỉ offset của biến MyChildren
                    Int               21h
                    Mov            Ah, 4Ch
                    Int               21h
Main           Endp
C_Seg             Ends
            END     Main
Điều dễ nhận thấy đầu tiên là phần khai báo biến và phần lệnh chính trong chương
trình này hoàn toàn giống như trong chương trình sử dụng định nghĩa đoạn đơn
giản (hai chương trình ví dụ ở trên).
Chương trình này sử dụng hướng dẫn biên dịch định nghĩa đoạn toàn
phần Segment ... Ends để định nghĩa 3 đoạn chương trình với tên lần lượt là:
S_Seg (đoạn stack), D_Seg (đoạn Data), C_Seg (đoạn Code). Tên của các đoạn
được định nghĩa ở đây là tùy ý.
Hướng dẫn biên dịch Assume được sử dụng để báo cho trình biên dịch biết 
chương trình muốn chứa địa chỉ segment của các đoạn chương trình trong các
thanh ghi đoạn nào (trỏ thanh ghi đoạn về đoạn chương trình). Cụ thể ở đây là:
Thanh ghi đoạn CS chứa địa chỉ segment của đoạn Code (CS:C_Seg). Thanh ghi
đoạn SS chứa địa chỉ segment của đoạn Stack (SS:S_Seg). Thanh ghi đoạn DS
chứa địa chỉ segment của đoạn Data (DS:C_Seg). Tuy nhiên, trong thực tế Assume
DS:D_Seg không tự động nạp địa chỉ segment của D_Seg vào DS, do đó chương
trình phải nạp trực tiếp bằng các lệnh:
            Mov    Ax, D_Seg
            Mov    DS, Ax
Nên nhớ, hướng dẫn biên dịch Segment ... Ends chỉ có tác dụng định nghĩa đoạn,
nó  không thể báo cho trình biên dịch biết đoạn được định nghĩa thuộc loại đoạn
chương trình nào (Code, Data, Stack, Extra). Chỉ có định nghĩa Segment   Stack ...
Ends là báo cho trình biên dịch biết đoạn được định nghĩa là đoạn Stack, nhờ đó,
khi chương trình được nạp vào bộ nhớ thanh ghi đoạn SS sẽ được trỏ về đoạn này.

Bài 4:
MỘT SỐ LỆNH ASSEMBLY CƠ SỞ
Cú pháp lệnh:
     Một lệnh hợp ngữ đầy đủ gồm bốn thành phần sau đây:
    [Nhãn lệnh:]         <Tên lệnh>        [Các toán hạng]       [;Lời giải thích]
Trong đó:
-     [Nhãn lệnh:]: Là một dãy các kí tự đứng trước câu lệnh (kết thúc bởi dấu hai
chấm (:)), nó được chỉ định thay thế cho địa chỉ của câu lệnh trong các đoạn lệnh
lặp, rẽ nhánh,... Do đó, nó chỉ được sử dụng khi cần.
Trong một chương trình hợp ngữ không thể có hai nhãn lệnh trùng tên, tên của các
nhãn cũng không thể trùng với tên của các thủ tục trong chương trình.
-     <Tên lệnh>: Là một trong các lệnh thuộc tập lệnh hợp ngữ (lệnh gợi nhớ:
Mnemonic) của vi xử lý trên máy tính thực hiện lệnh này.
Lệnh hợp ngữ không phân biệt chữ hoa hay chữ thường. Trong chương trình hợp
ngữ mỗi dòng chỉ có thể chứa một lệnh và mỗi lệnh phải được đặt trên một dòng.
-     [Các toán hạng]: Là đối tượng mà lệnh tác động vào. Một lệnh hợp ngữ của
Intel 8088/8086 có thể không có toán hạng, có một toán hạng, hoặc có hai toán
hạng. Nếu có hai toán hạng thì toán hạng đứng trước gọi là [Toán hạng đích], toán
hạng đứng sau gọi là [Toán hạng nguồn]. [Toán hạng đích] không thể là một hằng
số.
Một số lệnh hợp ngữ của các Intel 80286/80386/... có thể có đến 3 toán hạng, trong
trường hợp này cũng chỉ có một [Toán hạng đích].
-     [;Lời giải thích]: Chỉ có tác dụng với người viết và người đọc chương trình, nó
không có ý nghĩa với trình biên dịch, tức là, không được dịch sang mã máy. Lời
giải thích thường được sử dụng để làm rõ ý nghĩa của câu lệnh (khi người viết thấy
cần). Lời giải thích phải nằm sau dấu chấm phảy (;).
Ví dụ 1: Xét lệnh sau đây:
        Lenh_VD:    Mov    AX,BX        ; đặt giá trị thanh ghi BX vào thanh ghi AX
Trong đó:

 Lenh_VD: Trong trường hợp này dãy kí tự Lenh_VD được sử dụng làm


nhãn lệnh cho lệnh Mov.

 Mov: Là tên lệnh.


 AX và BX: Là các toán hạng (đích và nguồn). Trong trường hợp này toán
hạng là các thanh ghi đa năng 16 bít.

 “đặt giá trị thanh ghi BX vào thanh ghi AX”: Là lời giải thích cho lệnh
này. Trong thực tế lời giải thích thường là tiếng Việt không dấu.

Ví dụ 2: Xem các lệnh sau đây:


-             NOP                             ; đây là lệnh không có toán hạng
-             Mov        Ax, Bl             ; lệnh này có hai toán hạng, [Toán hạng đích] là
thanh
                                                   ; ghi 16 bít, [Toán hạng nguồn] là thanh ghi 8 bít
-             Add         Cl, Spt         ; lệnh này có hai toán hạng, [Toán hạng đích] là
thanh
                                                   ; ghi 8 bít, [Toán hạng nguồn] là một biến byte
-             Mov        Ax, [SI]           ; lệnh này có hai toán hạng, [Toán hạng đích] là
thanh
                                                   ; ghi 16 bít, [Toán hạng nguồn] là một ô nhớ
-              Sub        Dl, ‘a’ – ‘A’     ; lệnh này có hai toán hạng, [Toán hạng đích] là
thanh
                                                  ; ghi  8 bít, [Toán hạng nguồn] là một hằng
số           
-             IMul        Ax, Bx, 20      ; lệnh này có ba toán hạng, [Toán hạng đích] là
thanh
                                                 ; ghi 16 bit (Ax), [Toán hạng nguồn] là thanh ghi 16
bít
                                                 ; (Bx) và một hằng số (20)
Lệnh Imul ở trên là một lệnh nhân mới của vi xử lý Intel 80286. Lệnh này thực
hiện như sau: lấy nội dung/giá trị hai [Toán hạng nguồn] nhân với nhau, kết quả
chứa ở [Toán hạng đích] (trong lệnh trên là: Bx*20, tích kết quả chứa ở thanh ghi
Ax (chỉ lấy 16 bít thấp của tích để đưa vào Ax)).  
1. Lệnh Mov (Move):
Cú pháp lệnh:      
              Mov      [Toán hạng đích], [Toán hạng nguồn]
Trong đó:
-     [Toán hạng đích]: Có thể là thanh ghi (8 bít hay 16 bít), ô nhớ (chính xác hơn
là địa chỉ của một ô nhớ) hay một biến nào đó. [Toán hạng đích] không thể là hằng
số.   
-     [Toán hạng nguồn]: Có thể là hằng số, biến, thanh ghi, ô nhớ (chính xác hơn là
địa chỉ của một ô nhớ) nào đó.
Tác dụng: Lấy nội dung (giá trị) của [Toán hạng nguồn] đặt vào [Toán hạng đích].
Nội dung của [Toán hạng nguồn] không bị thay đổi.
Ví dụ 1:
-             Mov        Ax, 5                ; Ax ß 5: đặt giá trị 5 vào thành ghi Ax
-             Mov        Ax, 5*2             ; Ax ß 5*2: đặt giá trị 10 vào thành ghi Ax
-             Mov        Bx, (80*(Dong - 1) + (Cot - 1))*2         
                                                     ; Dong, Cot là các biến
-             Mov        Dl, ‘A’              ; Dl = 41h: đặt mã ASCII của ‘A’ vào thanh ghi
Dl
-             Mov        Cx, Var1         ; Cx = Var1: đặt giá trị của biến Var1 vào thanh
ghi Cx
-             Mov        Ax, Bx             ; Ax = Bx: đặt giá trị của thanh ghi Bx vào Ax
-             Mov        Ax, Dl             ; Ax = Dl: đặt giá trị của Dl (8 bít) vào Ax (16
bít)
-             Mov        Bl, Dx             ; Bl = Dx: không hợp lệ, vì: Dx (16 bít) mà Bl (8
bít)
-             Mov        Dl, 300           ; Dl = 300: không hợp lệ, vì 300 vượt giới hạn 1
byte
Ví dụ 2: Giả sử DI = 100; Ô nhớ tại địa chỉ offset 100 trong đoạn nhớ Data (được
chỉ bởi DS) chứa kí tự B. Thì :
-            Mov        Ax, DI             ; (1) đặt giá trị thanh ghi DI vào thanh ghi Ax: Ax
= 100
-            Mov        Ax, [DI]           ; (2) Ax = <nội dung của ô nhớ được chỉ bởi DI
(DI
                                                   ; chứ địa chỉ offset của ô nhớ)>. Tức là, đặt nội
dung của 
                                                   ; ô nhớ được chỉ bởi DI vào thanh ghi Ax: Ax =
41h
Hãy phân biệt sự khác nhau giữa hai lệnh trên: Lệnh (1) sử dụng chế độ địa chỉ
thanh ghi. Lệnh (2) sử dụng chế độ địa chỉ gián tiếp thanh ghi.
Nhớ lại rằng: Trong chế độ địa chỉ gián tiếp thanh ghi, các thanh ghi chỉ có thể là
BX, DI, SI (địa chỉ đoạn chứa trong DS) hay BP (địa chỉ đoạn chứa trong SS). Như
vậy lệnh (2) tương đương với lệnh (3) nhưng khác lệnh (4):
-             Mov        Ax, DS:[DI]                ; (3)     
-             Mov        Ax, ES:[DI]                ; (4)
Ví dụ 3:
-              Mov        Ax, [SI]          ; đặt nội dung ô nhớ được chỉ bởi SI vào thanh
ghi Ax
-              Mov        [DI], Bx          ; đặt giá trị của thanh ghi bx vào ô nhớ được chỉ
bởi DI
-              Mov        [DI], [SI]         ; [DI] ß [SI] : lệnh không hợp lệ, vì: không thể
chuyển
                                                     ; nội dung của ô nhớ vào một ô nhớ một cách trực
tiếp
-             Mov        Var1, Ax        ; Var1 ß Ax : đặt giá trị t/ghi Ax vào biến word
Var1
Chú ý:
-              Lệnh Mov không làm ảnh hưởng đến các cờ.
-              Mov        DS:[DI], ES:[SI]        ; lệnh không hợp lệ, vì: không thể chuyển
dữ liệu
                                                                 ; trực tiếp giữa hai toán hạng bộ nhớ với
nhau
-              Mov        DS, ES                      ; DS ß ES: lệnh không hợp lệ,
-              Mov        ES, 0100                  ; lệnh không hợp lệ, vì:  không thể chuyển
                                                                 ; trực tiếp một hằng số vào thanh ghi đoạn.
Để chuyển giá trị của hai thanh ghi đoạn hay nội dung của hai ô nhớ ta có thể
mượn một thanh ghi đa năng làm trung gian:
-                   Mov        Ax, ES            ; hai lệnh này chuyển nội dung của thanh ghi
đoạn ES
                    Mov        DS, Ax            ; vào thanh ghi đoạn DS thông qua thanh ghi
Ax
Theo cách thông thường, để hoán đổi giá trị của hai thanh ghi đoạn hay nội dung
của hai ô nhớ người ta thường sử dụng hai thanh ghi đa năng làm trung gian:
-                   Mov        Ax, [DI]           ; lưu tạm nội dung ô nhớ được chỉ bởi DI và
Ax
                    Mov        Bx, [SI]           ; lưu tạm nội dung ô nhớ được chỉ bởi SI và
Bx
                    Mov        [DI], Bx          ; chuyển giá trị của t/ghi Bx và ô nhớ được chỉ
bởi DI
                    Mov        [SI], Ax          ; chuyển giá trị của t/ghi Ax và ô nhớ được chỉ
bởi SI
Bốn lệnh trên có tác dụng hoán đổi nội dung của hai ô nhớ trong đoạn Data (DS)
được chỉ bởi DI và SI (DI và SI chứa địa chỉ Offset của các ô nhớ).
-             Không thể dùng thanh ghi đoạn CS làm [Toán hạng đích] trong lệnh Mov.
2. Các lệnh Inc – Dec – Add và Sub
Cú pháp lệnh:

 Inc        [Toán hạng đích]

 Add       [Toán hạng đích],[Toán hạng nguồn]

 Dec       [Toán hạng đích]

 Sub       [Toán hạng đích],[Toán hạng nguồn]

Trong đó: [Toán hạng đích], [Toán hạng nguồn]: tương tự lệnh Mov.
Tác dụng:      

 Lệnh Inc (Increment): làm tăng giá trị của [Toán hạng đích] lên 1 đơn vị.
 Lệnh Dec (Decrement): làm giảm giá trị của [Toán hạng đích] xuống 1 đơn
vị.

 Lệnh Add (Addition): lấy giá trị/nội dung của [Toán hạng nguồn] cộng vào
giá trị/nội dung của [Toán hạng đích], kết quả này đặt vào lại [Toán hạng
đích].       

 Lệnh Sub (Subtract): lấy giá trị/nội dung của [Toán hạng đich] trừ đi giá
trị/nội dung của [Toán hạng nguồn], kết quả này đặt vào lại [Toán hạng đích]. 

Ví dụ 1:
                    Mov        Ax, 121             ; đặt giá trị 121 vào thanh ghi Ax
                    Mov        Bx, 223             ; đặt giá trị 232 vào thanh ghi Bx
                    Inc           Ax                    ; Ax = Ax + 1: tăng Ax lên 1 đơn vị (Ax =
122)
                    Dec         Bx                    ; Bx = Bx + 1: giảm Bx xuống 1 đơn vị (Bx =
222)
                    Sub         Ax, Bx              ; Ax = Ax – Bx : Ax = -100
                    Add         Ax, 120            ; Ax = Ax + 120 : Ax = 20
                    Mov        Cx, Ax              ; Cx= Ax : Cx = 20
Dãy lệnh trên, đặt giá trị cuối cùng của thanh ghi Ax vào thanh ghi Cx (Cx = 20).
Ví dụ 2:
-              Inc           Spt                         ; Spt = Spt + 1; tăng giá trị biến Spt lên 1
đơn vị
-              Inc           DS:[SI]                    ; tăng ndung ô nhớ được chỉ bởi DS:SI lên
1 đơn vị
-              Add         Ax, Var1                 ; Ax = Ax + Var1; cộng giá trị biến Var1
vào Ax
-              Add         Var2, Dx                 ; Var2 = Var2 + Dx. Biến Var2 là biến
dạng word
-              Add         Dx, [SI]                    ; cộng thêm nội dung ô nhớ được chỉ bởi
SI vào Dx
-              Add         [DI], [SI]                   ; [DI] = [DI] + [SI] : lệnh không hợp lệ,
vì: không thể
                                                                ; cộng trực tiếp nội dung hai ô nhớ với
nhau.
                                                               ; Yêu cầu của lệnh trên có thể được viết lại
như sau:
                    Mov        Ax, [SI]               ; lưu tạm nội dung ô nhớ được chỉ bởi SI
và Ax
                    Mov        Bx, [DI]               ; lưu tạm nội dung ô nhớ được chỉ bởi DI
và Bx
                    Add         Bx, Ax               ; cộng Ax và Bx, kết quả chứa ở Bx
                    Mov        [DI], Bx               ; đặt kết quả phép cộng vào lại ô nhớ được
chỉ bởi DI
Ví dụ 3: Cộng thêm giá trị của thanh ghi Ax vào nội dung của ô nhớ tại địa chỉ
offset 0100 trong đoạn DS:
                    Mov        DI, 0100                       ; trỏ DI về ô nhớ offset 0100
                    Mov        Bx, DS:[DI]                  ; lưu tạm ndung ô nhớ DS:DI vào
thanh ghi Bx
                    Add        Bx, Ax                         ; cộng thêm Ax vào Bx
                    Mov        DS:[DI], Bx                   ; đặt kết quả vào lại ô nhớ DS:DI
(DS:0100)
Trong trường hợp này ta có thể sử dụng lệnh Add    DS:[DI],Ax.
Ví dụ 4: Giả sử tại ô nhớ 0B800:0100 trong bộ nhớ có chứa một word dữ liệu.
Hãy tăng nội dung của ô nhớ này lên một đơn vị.
                    Mov        Ax, 0B800h        ; mượn thanh ghi Ax làm trung gian để
chuyển
                    Mov        ES, Ax        ; địa chỉ đoạn của ô nhớ cần truy xuất vào ES
                    Mov        DI, 01         ; đặt địa chỉ offset của ô nhớ cần truy xuất vào
DI
                    ;-----------------------; (gọi ngắn gọn: trỏ ES:DI về ô nhớ cần truy xuất)
                    Mov        Dx, ES:[DI]        ; chuyển tạm nội dung ô nhớ cần tăng vào
Dx
                    Inc         Dx                      ; tăng giá trị thanh ghi Dx lên 1 đơn vị
                    Mov        ES:[DI], Dx        ; đặt giá trị Dx đã tăng vào lại ô nhớ cần
tăng
Ví dụ 5: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một byte dữ liệu. Hãy
chuyển nội dung của ô nhớ này vào thành ghi AL.
                    Mov        Ax, 0A00h                    ; (1); Các lệnh (1), (2), (3) trỏ cặp
thanh
                    Mov        ES, Ax                           ; (2); ghi ES:DI về ô nhớ có địa chỉ
0A00:0100
                    Mov        DI, 0100h                    ; (3); trong đó 0A00 là địa chỉ
Segment và
                    ;-------------------------                  ; 0100 là địa chỉ Offset. Lệnh (4)
chuyển nội
                    Mov        Al, ES:[DI]                    ; (4); dung ô nhớ được chỉ bởi ES:DI
vào Al.
Ví dụ 6: Giả sử tại địa chỉ 0100:0100 trong bộ nhớ có chứa 2 word dữ liệu liên
tiếp (hai ô nhớ liên tiếp). Hãy tính tổng nội dung hai word nhớ này, rồi lấy kết quả
ghi vào ô nhớ tại địa chỉ 0100:0120.
                    Mov        Ax, 0100                   
                    Mov        ES, Ax                          ; trỏ cặp thanh ghi ES:SI về đầu vùng
nhớ
                    Mov        SI, 0100                      ; cần truy xuất.
                    Mov        DI,0120                       ; trỏ cặp thanh ghi ES:DI về ô nhớ
chứa kết quả
                    ;-------------------------                  ; các ô nhớ này ở trong cùng một
Segment
                    Mov        Ax, ES:[SI]                   ; lưu tạm nội dung ô nhớ đầu tiên vào
Ax
                    Add         Ax, ES:[SI+2]              ; cộng nội dung ô nhớ kế tiếp vào Ax
                    Mov        ES:[DI], Ax                    ; ghi kết quả vào ô nhớ 0100:0120
Lệnh Add       Ax, ES:[SI+2] ở trên sử dụng chế độ định địa chỉ bộ nhớ gián tiếp,
cụ thể là định địa chỉ chỉ mục (sử dụng thanh ghi chỉ mục SI).
Qua 3 ví dụ 4, 5, 6 ta có thể rút ra nguyên tắc cơ bản khi truy xuất dữ liệu/nội dung
của một ô nhớ là: Sử dụng một cặp thanh ghi thích hợp (DS:DI, DS:SI, ES:DI,
ES:SI,...) để chứa địa chỉ logic (gồm cả Segment và Offset) của ô nhớ cần truy
xuất. Thao tác này thường gọi là trỏ về ô nhớ cần truy xuất. Sau đó sử dụng cặp
thanh ghi này để ghi/đọc nội dung của ô nhớ đã được trỏ tới.
Ngoài ra, khi truy xuất ô nhớ cần phải xác định dữ liệu/nội dung tại đó là một Byte
hay một Word và nếu là truy xuất đọc thì kết quả sẽ được lưu vào đâu (thanh ghi
hay ô nhớ).
Chú ý 1:

 Không thể cộng trực tiếp hai thanh ghi đoạn. Trong trường hợp này phải sử
dụng các thanh ghi đa năng làm trung gian.
 Lệnh Add thực hiện phép cộng không nhớ. Để thực hiện phép cộng có nhớ
(cộng thêm giá trị của cờ nhớ (CF) hiện tại vào kết quả) phải sử dụng
lệnh ADC (ADD with Carry) [2 - 171]. Tương tự với lệnh Sub và SBB [2 -
180].
 Để thực hiện phép cộng trên các số/giá trị BCD (Binary Coded Decimal) ta
phải sử dụng các lệnh cộng AAA (Ascii Adjust for Addition) và DAA (Decimal
Adjust for Addition) để điều chỉnh (adjust) kết quả cuối cùng [2 - 172]. Tương
tự, với phép trừ trên các số BCD phải sử dụng lệnh AAS và DAS [2 - 183].

Chú ý 2:

 Các thanh ghi của vi xử lý Intel 8086/8088 đều là 16 bít, nên để chứa một
đại lượng dữ liệu 32 bít nó phải dùng 2 thanh ghi, thường là các thanh ghi đa
năng (thanh ghi tích lũy): Ax, Bx, Cx, Dx. Cặp thanh ghi Dx:Ax thường được
sử dụng nhất, khi đó Ax chứa 16 bít thấp, Dx chứa 16 bít cao của đại lượng 32
bít.
 Để cộng/trừ trên các số 32 bít ta không thể sử dụng Add/Sub theo cách
thông thường, mà phải thực hiện như sau: Cộng/Trừ 16 bít thấp, sau đó
Cộng/Trừ 16 bít cao. Nếu phép Cộng/Trừ trên 16 bít thấp xuất hiện bít nhớ/bít
mượn thì phải tiến hành điều chỉnh kết quả, nếu không kết quả sẻ sai. Sử dụng
các phép kiểm tra cờ để biết phép Cộng/Trừ có xuất hiện bít nhớ/bít mượn hay
không [1 - 477].
 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 =

                     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 dungtoá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].
5. Lênh
̣ Mul và Div
Cú pháp:

 Mul     [Toán hạng nguồn]

 IMul   [Toán hạng nguồn]

 Div      [Toán hạng nguồn]

 IDiv    [Toán hạng nguồn]

Trong đó: [Toán hạng nguồn]có thể là thanh ghi hay ô nhớ. Với các lê ̣nh nhân:
[Toán hạng đích] ngầm định là thanh ghi Al hoă ̣c Ax. Với các lê ̣nh chia: [Toán
hạng đích] là mô ̣t trong các thanh ghi đa năng Ax, Bx,...
Tác dụng:
-     Lê ̣nh Mul (Multiply): Thực hiê ̣n phép nhân trên số không dấu. Nếu [Toán
hạng nguồn] là toán hạng 8 bít thì lê ̣nh sẽ nhân nô ̣i dung của [Toán hạng nguồn]
với giá trị thanh ghi AL, kết quả 16 bít chứa ở thanh ghi Ax.   
Nếu [Toán hạng nguồn] là toán hạng 16 bít thì lê ̣nh sẽ nhân nô ̣i dung của [Toán
hạng nguồn] với giá trị thanh ghi Ax, kết quả 32 bít chứa ở că ̣p thanh ghi Dx:Ax,
phần thấp ở Ax, phần cao ở Dx. Nếu phần cao của kết quả (AH hoă ̣c DX) bằng 0
thì các cờ CF = 0 và OF = 0.
-     Lê ̣nh IMul (Interger Multiply): Tương tự lê ̣nh Mul nhưng thực hiê ̣n phép nhân
trên hai số có dấu. Kết quả cũng là mô ̣t số có dấu.  
     -     Lê ̣nh Div (Divide): Thực hiê ̣n phép chia trên số không dấu. Nếu [Toán hạng
nguồn] là toán hạng 8 bít thì lê ̣nh sẽ lấy giá trị của thanh ghi Ax (số bị chia) chia
cho [Toán hạng nguồn] (số chia), kết quả thương số chứa trong thanh ghi Al, số dư
chứa trong thanh ghi Ah.      
Nếu [Toán hạng nguồn] là toán hạng 16 bít thì lê ̣nh sẽ lấy giá trị của că ̣p thanh ghi
Dx:Ax (số bị chia) chia cho [Toán hạng nguồn] (số chia), kết quả thương số chứa
trong thanh ghi Ax, số dư chứa trong thanh ghi Dx.
Nếu phép chia cho 0 xảy ra hay thương số vượt quá giới hạn của thanh ghi AL
(chia 8 bít) hay Ax (chia 16 bít) thì CPU sẽ phát sinh lỗi “Divice overflow”.
-     Lê ̣nh Idiv (Integer Divide): Tương tự lê ̣nh  Div nhưng thực hiê ̣n phép chia trên
hai số có dấu. Kết quả cùng là các số có dấu.  
Ví dụ 1:
-             Mul         Bl                    ; Ax ßAL * Bl: số bị nhân ngầm định trong
Al       
-             Mul         Bx                   ; Dx:Ax ßAx * Bx: số bị nhân ngầm định trong
Ax
-             Idiv         Bl                    ; Ax/Bl, thương số chứa trong Al, số dư chứa
trong Ah
-             Idiv         Bx                   ; Dx:Ax/Bx, thương số chứa trong Ax, số dư
trong Dx
Ví dụ 2: Dãy các lê ̣nh dưới đây sẽ thực hiê ̣n phép gán A = 4*A – 3*B, trong đó A
và B là các biến kiểu word:
                    Mov        Ax, 4                ; số nhân phải được chứa trong Ax
                    IMul        A                      ; thực hiê ̣n phép nhân
                    Mov        Bx, Ax              ; lưu tạm kết quả vào Bx        
                    Mov        Ax, 3                ; Ax = 3
                    Imul        B                     ; Ax = Ax * B
                    Sub         Bx, Ax           
                    Mov        A, Bx                ; đă ̣t kết quả cuối cùng vào A
Trong trường hợp này ta đã giả sử hiê ̣n tượng tràn đã không xảy ra và kết quả phép
nhân chỉ chứa trong thanh ghi Ax.    
Ví dụ 3: Các lê ̣nh sau đây thực hiê ̣n phép: chia -123 cho 24:
                    Mov        Ax, -123         ; đă ̣t số bị chia vào Ax
                    Mov        Bl, 24              ; đă ̣t số chia vào Bl (thanh ghi 8 bít)
                    Idiv         Bl                    ; chia Ax cho Bl, kết quả chứa ở Al và Ah
Ví dụ 4: Dãy lê ̣nh dưới đây sẽ thực hiê ̣n phép gán A = N! (tính N giai thừa). A là
mô ̣t biến word:
                    Mov        Ax, 1               ; chuẩn bị Ax để lưu kết quả
                    Mov        Cx, N              ; tính N!
               LapNhan:                          
                    Mul         Cx                   ; Ax ßAx * Cx
                    Loop       LapNhan      
                    ;---------------------------
                    Mov        A, Ax              ; trả kết quả vào biến A
Trong trường hợp này chúng ta giả sử kết quả không vượt quá gới hạn 16 bít.
Chúng ta đã biết: N! = 1 nếu N = 1, N! = N*(N-1)*(N-2)*...*1 nếu N>1, điều này
hoàn toàn phù hợp với sự thay đổi của thanh ghi CX trong lê ̣nh lă ̣p Loop. Do đó, ở
đây ta có thể sử dụng CX như là N trong biểu thức tính giai thừa.    
Chú ý: Hợp ngữ cung cấp lê ̣nh AAM (Ascii Adjust for Multiple) để điều chỉnh kết
quả phép nhân trên 2 số BCD dạng không dồn. Và lê ̣nh AAD (Ascii Adjust for
Division) để điều chỉnh kết quả phép chia trên 2 số BCD dạng không dồn. Ngoài ra
còn có lê ̣nh CBW (Convert Byte to Word) và lê ̣nh CWD (Convert Word to
Doubleword) để hỗ trợ cho phép chia trên các số có dấu [2 – 187-200].
Lê ̣nh IMul của vi xử lý Intel 80286 cho phép ghi rõ [Toán hạng đích], [Toán hạng
nguồn] trong câu lê ̣nh, các lê ̣nh này có thể có đến 3 toán hạng [1 - 541].  
6. Lênh
̣ logic: NOT – AND – OR – XOR – TEST
Trước khi tìm hiểu về các lê ̣nh logic chúng ta xem lại kết quả thực hiê ̣n các phép
tính logic trên 2 bít nhị phân A và B thông qua bảng sau đây:

A B A And B A Or B A Xor B NotA

0 0 0 0 0 1

0 1 0 1 1 1

1 0 0 1 1 0
1 1 1 1 0 0
 
Bảng trên cho thấy: Với phép And: kết quả = 1 chỉ khi cả hai bít = 1; Với phép Or:
kết quả = 0 chỉ khi cả hai bít = 0; Với phép Xor: kết quả = 0 khi hai bít giống nhau,
kết quả = 1 khi hai bít khác nhau. Với phép Not: 0 thành 1, 1 thành 0.     
Cú pháp:

 Not     [Toán hạng đích]

 And     [Toán hạng đích], [Toán hạng nguồn]

 Or       [Toán hạng đích], [Toán hạng nguồn]

 Xor     [Toán hạng đích], [Toán hạng nguồn]

 Test    [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 số (trực hằng),
biến, thanh ghi hay địa chỉ ô nhớ. [Toán hạng đích] không thể là hằng số.
Tác dụng: Mỗi lê ̣nh logic thực hiê ̣n phép tính logic tương ứng trên các bít (tương
ứng về vị trí) của [Toán hạng đích] và [Toán hạng nguồn], kết quả được ghi vào lại
[Toán hạng đích]. Riêng lê ̣nh Not, thực hiê ̣n phép đảo bít ngay trên các bít của
[Toán hạng đích]. Hầu hết các lê ̣nh logic đều ảnh hưởng đến các cờ CF, OF, ZF,...
-     Lê ̣nh Not (Logical Not): Thực hiê ̣n viê ̣c đảo ngược từng bít trong nô ̣i dung của
[Toán hạng đích]. Lê ̣nh này không làm ảnh hưởng đến các cờ.
Lê ̣nh Not thường được sử dụng để tạo dạng bù 1 của [Toán hạng đích].        
-     Lê ̣nh And (Logical And): Thực hiê ̣n phép tính logic And trên từng că ̣p bít
(tương ứng về vị trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào
[Toán hạng đích].
Lê ̣nh And thường được sử dụng để xóa (= 0) mô ̣t hoă ̣c nhiều bít xác định nào đó
trong mô ̣t thanh ghi.
-     Lê ̣nh Or (Logical Inclusive Or):Thực hiê ̣n phép tính logic Or trên từng că ̣p bít
(tương ứng về vị trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào
[Toán hạng đích].
Lê ̣nh Or thường dùng để thiết lâ ̣p (= 1) mô ̣t hoă ̣c nhiều bít xác định nào đó trong
mô ̣t thanh ghi.
-     Lê ̣nh Xor (eXclusive OR):Thực hiê ̣n phép tính logic Xor trên từng că ̣p bít
(tương ứng về vị trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào
[Toán hạng đích].
Lê ̣nh Xor thường dùng để so sánh (bằng nhau hay khác nhau) giá trị của hai toán
hạng, nó cũng giúp phát hiê ̣n ra các bít khác nhau giữa hai toán hạng này.  
-     Lê ̣nh Test: Tương tự như lê ̣nh And nhưng không ghi kết quả vào lại [Toán
hạng đích], nó chỉ ảnh hưởng đến các cờ CF, OF, ZF,...
Ví dụ 1:
                    Mov        Al,0                 ; Al ß0
                    Not          Al                    ; Al = Not Al. Tức là Al = 0FFh
Ví dụ 2: Cho AL = (10010011)2, BL = (11001100)2.
-             And         Al, Bl              ; Al ß 10010011 And 11001100. Al =
-             And         Al, 0                ; Al ß 10010011 And 0. Al =
-             Or            Bl, Al              ; Bl ß 11001100 Or 10010011. Al =
-             Or           Bl, 4                ; Bl ß 11001100 Or 100. Al =
-             Xor         Al, Bl              ; Al ß 10010011 Xor 11001100. Al =
-              Xor         Bl, Bl              ; Bl ß 11001100 Xor 11001100. Bl = 00000000
Ví dụ 3: Để xóa nô ̣i dung thanh ghi nào đó, trong hợp ngữ ta có thể sử dụng mô ̣t
trong các lê ̣nh sau đây:
-             Mov        Ax, 0
-             Sub         Ax, Ax
-             Xor         Ax, Ax            ; các că ̣p bít giống nhau thì đều = 0 
Ví dụ 5: Lê ̣nh sau đây sẽ xóa (= 0) các bít 3 và 6 của thanh ghi AL, các bít khác
giữ nguyên giá trị:
-             And         AL, 10110111b        ; AL ßAL And 10110111
Trong trường hợp này: dãy bít  10110111 được gọi là dãy bít mă ̣t nạ, các bít 3 (=
0) và 6 (= 0) được gọi là các bít mă ̣t nạ. Như vâ ̣y muốn làm cho bít nào = 0 ta cho
bít mă ̣t nạ tương ứng với nó = 0, các bít còn lại trong dãy bít mă ̣t nạ đều = 1.       
Ví dụ 6:  Lệnh sau đây sẽ thiết lâ ̣p (= 1) các bít 3 và 6 của thanh ghi AL, các bít
khác giữ nguyên giá trị:
-             Or            AL, 01001000b        ; AL ßAL Or 01001000
Trong trường hợp này: dãy bít 01001000 được gọi là dãy bít mă ̣t nạ, các bít 3 (= 1)
và 6 (= 1) được gọi là các bít mă ̣t nạ. Như vâ ̣y muốn làm cho bít nào = 1 ta cho bít
mă ̣t nạ tương ứng với nó = 1, các bít còn lại trong dãy bít mă ̣t nạ đều = 0.       
Ví dụ 7: Lê ̣nh sau đây sẽ kiểm tra bít 12 của thanh ghi AX là = 0 hay = 1:
-             And         AX, 0001000000000000b              ; AX ßAX And
0001000000000000
Với dãy bít mă ̣t nạ như trên, nếu bít 12 của Ax = 0 thì kết quả: Ax = 0, nếu bít 12
của Ax = 1 thì kết quả: Ax <> 0.
Cách dùng lê ̣nh And như trên để kiểm tra bít ít được sử dụng, vì nó làm thay đổi
giá trị của thanh ghi cần kiểm tra (điều này có thể khắc phục bằng lệnh Test) và
phải thêm bước kiểm tra giá trị của Ax (= 0 hay <> 0) mới biết được kết quả kiểm
tra. Ngoài ra, nó cũng chỉ kiểm tra được 1 bít.
Trong thực tế người ta thường sử dụng kết hợp giữa các lê ̣nh dịch bít, lê ̣nh quay
bít, lê ̣nh nhảy,... để kiểm tra các bít trong mô ̣t thanh ghi.
7. Lênh
̣ chuyển dữ liêụ qua cổng: IN và OUT
Cú pháp:

 IN           AL, <Địa chỉ cổng>        

 OUT       <Địa chỉ cổng>, AL

Trong đó:<Địa chỉ cổng> chính là số hiệu cổng (port) mà lệnh nhận nhiệm vụ trao
đổi dữ liệu qua nó. Địa chỉ cổng có thể được ghi trực tiếp dưới dạng một hằng số
hoặc được ghi thông qua thanh ghi Dx.
Tác dụng:
-     LênhIn (Input): Đọc một lượng dữ liệu 8 bít từ cổng được chỉ ra ở <Địa chỉ
cổng> đưa vào lưu trữ trong thanh ghi AL.  
Nếu địa chỉ cổng nằm trong giới hạn từ 0 đến FF (hệ thập lục phân) thì có thể viết
trực tiếp trong câu lệnh, nếu địa chỉ cổng lớn hơn FF thì ta phải dùng thanh ghi Dx
để chỉ định địa chỉ cổng.
-     LệnhOut (Output): Gởi một lượng dữ liệu 8 bít từ thanh ghi AL ra cổng được
chỉ ra ở <Địa chỉ cổng>. Tương tự lệnh In, địa chỉ cổng có thể được viết trực tiếp
trong câu lệnh hoặc thông qua thanh ghi Dx.
Ví dụ 1:
-             In             Al, 40h                       ;                      
-             Mov        Dx, 3B0h                   ;
               In             Al, Dx                         ;
Ví dụ 2:
-             Out          40h, Al                       ;                      
-             Mov        Dx, 3B0h                   ;
               Out          Dx, Al                         ;
Ví dụ 3:
Các khai báo hằng:
                    DAT        EQU                13h     ;
                    POR        EQU                7Ch     ;
Các lệnh:
        Mov        Al, POR          ;
        Mov        Bl, DAT          ;
        Out          Bl, Al              ;
Mô ̣t số ví dụ minh họa:
Ví dụ 1: Giả sử tại địa chỉ 0100:0120 trong bô ̣ nhớ có chứa mô ̣t mảng dữ liê ̣u, gồm
100 ô nhớ, mỗi ô là 1 word. Hãy tính tổng nô ̣i dung của các ô nhớ này, kết quả
chứa trong thanh ghi Dx.
                    Mov        Ax, 0100                   
                    Mov        DS, Ax                          ; trỏ că ̣p thanh ghi DS:DI về
                    Mov        DI, 0120                       ; đầu vùng nhớ cần truy xuất
(0100:0120)
                    ;------------------------              
                    Mov        Dx, 0                            ; chuẩn bị Dx để lưu tổng
                    Mov        Cx, 100                         ; lă ̣p 100 lần vì mảng gồm 100 ô nhớ
             Lap_TT:
                    Add         Dx, DS:[DI]                           ; cô ̣ng thêm n/dung ô nhớ chỉ
bởi DS:DI vào Dx
                    Add         DI, 2                                      ; chỉ đến ô nhớ kế tiếp (vì ô nhớ
word nên tăng 2)
                    Loop       Lap_TT                      ; lă ̣p lại đủ 100 lần (duyê ̣t qua đủ 100
ô nhớ)
                    ;-----------------------------------
Kết thúc đoạn lê ̣nh trên tổng nô ̣i dung của 100 word nhớ bắt đầu tại địa chỉ
0100:0120 trong bô ̣ nhớ được lưu vào thanh ghi Dx. Ở đây chúng ta bỏ qua khả
năng tràn dữ liê ̣u trong thanh ghi kết quả Dx.
Ví dụ 2: Các lệnh sau đây sẽ copy 100 word dữ liệu từ vùng nhớ bắt đầu tại địa chỉ
0100:0120 sang vùng nhớ bắt đầu tại 0100:0500:
                    Mov        Ax, 0100
                    Mov        DS, Ax
                    Mov        SI, 0120                      ; trỏ DS:SI về vùng nhớ nguồn
0100:0120
                    Mov        DI, 0500                     ; trỏ DS:DI về vùng nhớ địch
0100:0500
                    ;--------------------
                    Mov        Cx, 100
            Lap_Copy:
                    Mov        Ax, DS:[SI]                  ; lưu tạm nội dụng word nhớ tại
DS:SI  vào Ax
                    Mov        DS:[DI], Ax                  ; ghi giá trị Ax và word nhớ tại
DS:DI
                    Add         SI, 2                            ; đến word nhớ tiếp theo
                    Add         DI, 2
                    Loop       Lap_Copy
             ;-------------------------
Hai vùng bộ nhớ đã cho đều ở trong cùng một segment nên ở đây ta chỉ cần sử
dụng một thanh ghi đoạn dữ liệu DS.      
Ví dụ 3: Các lệnh sau đây sẽ tính tổng nội dung của 100 ô nhớ (100 byte nhớ)
trong bộ nhớ, bắt đầu tại địa chỉ 0A00:0120. Kết quả được lưu vào word nhớ ngay
trước vùng nhớ này:
                    Mov        Ax, 0A00h
                    Mov        ES, Ax
                    Mov        SI, 0120h                    ; trỏ DS:SI về vùng nhớ nguồn
0A00:0120
                    Mov        DI, SI                          ; trỏ DS:DI về vùng nhớ nguồn
0A00:0120
                    Sub         DI,2                             ; trỏ DS:DI về word nhớ trước vùng
nhớ nguồn
                    ;------------------------
                    Mov        Cx, 100         
                    Mov        Dx, 0                                            ; DX chứa tổng
              TTong:
                    Add         Dx, Byte PTR ES:[SI]                  ; cộng n.d của byte nhớ tại
ES:SI vàoDX
                    Inc           SI                                               ; ô nhớ byte
                    Loop       TTong
                    ;----------------------------------------  
                    Mov        Word PTR ES:[DI], DX
                    ;---------------------------------------- 
Trong đoạn lệnh trên chúng ta sử dụng toán tử PTR để định kiểu ô nhớ cần truy
xuất. Lệnh Add    Dx, Byte PTR ES:[SI]: lấy nội dung của byte nhớ tại ô nhớ
được chỉ bởi ES:SI cộng thêm vào thanh ghi DX. Nếu ta viết Add    Dx, ES:
[SI] thì hệ thống lấy giá trị cả 1 word tại ES:SI cộng thêm vào DX (vì DX là thanh
ghi word (16 bít), điều này không đúng với thực tế, vì chúng ta đang cần truy xuất
đến các byte nhớ. Trong trường hợp này, nếu không dùng toán tử PTR thì lệnh
Add phải viết như sau: Add    DL, ES:[SI], khi đó hệ thống chỉ lấy giá trị cả 1
byte tại ES:SI cộng thêm vào DL (vì DL là thanh ghi byte: 8 bít),  
Ví dụ 4: Các lệnh sau đây sẽ copy toàn bộ 20 kí tự Ascii từ biến Xau1 vào biến
Xau2. Giả sử Xau1 và Xau2 đã được khai báo trước như sau:
Xau1               DB      ‘Khoa CNTT – DHKH Hue’
Xau2               DB      20 Dup (‘ ’)
Các lệnh:
                    Mov        Ax, @Data
                    Mov        DS, Ax
                    Lea          SI, Xau1
                    Lea          DI, Xau2
                    ;------------------------
                    Mov        Cx, 20
            Lap_Copy:
                    Mov        Al, Byte PTR DS:[SI]
                    Mov        Byte PTR DS:[DI], Al
                    Inc           SI
                    Dec         DI
                    Loop       Lap_Copy     
                    ;-------------------------------------
Các biến của chương trình hợp ngữ được khai báo trong đoạn Data. Hai lệnh đầu
tiên ở trên có tác dụng lấy địa chỉ segment của đoạn Data đưa vào thanh ghi đoạn
DS. Do đó, DS sẽ chứa địa chỉ segment của hai biến Xau1 và Xau2. Hay cụ thể:
DS:SI trỏ về biến Xau1 và DS:DI trỏ về Xau2.
Ví dụ 5: Chương trình sau đây: Để in một xâu kí tự từ trong chương trình ra màn
hình văn bản đơn sắc (môi trường MSDOS).
Chú ý:
-     BIOS và MSDOS đã cung cấp nhiều hàm/ngắt để chương trình hợp ngữ ghi
một kí tự hoặc một xâu kí tự ra màn hình, tại vị trí hiện tại của con trỏ màn hình
hoặc tại một tọa độ màn hình xác định nào đó. Kỹ thuật này được gọi là kỹ thuật
ghi ra màn hình gián tiếp thông qua các hàm/ngắt màn hình.
-     Biết rằng, tất cả những thông tin mà chúng ta thấy được trên màn hình của một
máy tính đều được lưu trữ trong bộ nhớ màn hình của máy tính đó, theo một cách
nào đó. Tức là, nọi thao tác ghi/đọc trên màn hình đều phải thông qua bộ nhớ màn
hình. Như vậy: Muốn ghi một xâu kí tự ra màn hình chương trình có thể ghi nó vào
bộ nhớ màn hình. Muốn đọc một xâu kí tự từ màn hình chương trình có thể đọc nó
từ bộ nhớ màn hình. Kỹ thuật này được gọi là kỹ thuật truy xuất trực tiếp bộ nhớ
màn hình.
-     Mỗi hệ điều hành, mỗi chế độ màn hình sử dụng một đoạn bộ nhớ xác định
(thường là khác nhau) cho bộ nhớ màn hình. Và cách tổ chức lưu trữ thông tin trên
màn hình trong bộ nhớ màn hình cũng khác nhau với các hệ điều hành, các chế độ
màn hình khác nhau.
-     Trên môi trường hệ điều hành MSDOS, bộ nhớ màn hình của chế độ nàm hình
đơn sắc 25 dòng (0 đến 24) x 80 cột (0 đếm 79) được lưu trữ tại segment nhớ
0B800, bắt đầu tại offset 0000.
-     Tổ chức lưu trữ thông tin trên màn hình trong bộ nhớ màn hình loại này như
sau: Mỗi kí tự trên nàm hình chiếm 1 word trong bộ nhớ màn hình: byte thấp chứa
mã ASCII của kí tự, byte cao chứa thuộc tính (màu chữ, màu nền,...) của kí tự đó.
Từng dòng kí tự trên màn hình, từ trái sang phải (từ cột 0 đến cột 79), được lưu tữ
lần lượt tại các offset liên tiếp nhau trong bộ nhớ màn hình: 80 kí tự của dòng 0
được lưu trữ tại 80 word đầu tiên, 80 kí tự của dòng 1được lưu trữ tại 80 word tiếp
theo,... , 80 kí tự của dòng 79ược lưu trữ tại 80 word cuối cùng trong bộ nhớ nàm
hình. Như vậy ta dễ dàng tính ra được offset trong bộ nhớ màn hình, tương ứng với
một kí tự trên màn hình khi ta biết được tọa độ (dòng, cột) của nó. Cụ thể, offset
của kí tự tạo tọa độ (Dòng, Cột) được tính theo công thức sau:
                                    (80*(Dong - 1) + (Cot - 1))*2       
-      Bộ nhớ màn hình loại này có kích thước là 25 x 80 x 2 (byte) = 40000 byte.
Bạn đọc tự lập trình thử nhé.

Bài 5:
NGẮT TRONG CHƯƠNG TRÌNH ASSEMLBLY
Ngắt (Interrupt) là các tín hiệu mà các thành phần trong hệ thống, như: thiết bị
ngoại vi, hệ điều hành, chương trình người sử dụng, ..., gửi đến vi xử lý (họ Intel)
mỗi khi nó cần trao đổi thông tin với vi xử lý hay cần được sự phục vụ từ vi xử lý.
Ngắt cũng có thể phát sinh từ chính bên trong vi xử lý khi nó phát hiện một lỗi
nghiêm trong xảy ra trong quá trình xử lý của nó. Khi nhận được một tín hiệu yêu
cầu ngắt vi xử lý sẽ dừng ngay thao tác (lệnh) hiện tại để xem xét và đáp ứng yêu
cầu ngắt đó, sau đo mới tiếp tục lại từ thao tác (lệnh) mà nó bị dừng trước đó.

Các máy IBM_PC, sử dụng vi xử lý Intel, bao gồm nhiều loại ngắt: Ngắt phần
cứng, ngắt phần mềm, ngắt bên trong, ngắt không chắn được (ngắt có độ ưu tiên
cao nhất). Trong đó ngắt phần mềm là các ngắt được phát sinh từ hệ điều hành và
chương trình của người sử dụng, điều này thường thấy trong các chương trình hợp
ngữ.

Hệ thống các ngắt mềm bên trong máy tính IBM_PC được cung cấp bởi BIOS và
hệ điều hành, gồm 256 ngắt, chúng được đánh số từ 0 đến 255 và được gọi là số
hiêu ngắt. Mỗi ngắt mềm tương ứng với một chương con được gọi là chương trình
con phục vụ ngắt, tức là, để đáp ứng một yêu cầu ngắt mềm thì hệ thống sẽ phải
thực hiện một chương trình con phục vụ ngắt tương ứng.

Hợp ngữ cung cấp lệnh Int để các chương trình gọi một ngắt mềm khi cần. Khi gọi
ngắt mềm chương trình phải chỉ ra số hiệu ngắt cần gọi, khi đó hệ thống sẽ phải
gọi thực hiện chương trình con phục vụ ngắt tương ứng để đáp ứng yêu cầu gọi
này.

Trong môi trường hệ điều hành 16 bít MSDOS, ngay sau khi khởi động, hệ điều
hành nạp tất cả 256 chương trình con phục vụ ngắt vào bộ nhớ. Khi đó 256 địa chỉ
(Segment:Offset) các vùng nhớ, nơi định vị của 256 chương trình con phục vụ
ngắt, sẽ được lưu trữ tại một không gian nhớ đặc biệt trong bộ nhớ, vùng nhớ này
được gọi là bảng vector ngắt (mỗi địa chỉ định vị của một chương trình con phục
vụ ngắt được gọi là một vector ngắt.

Như vậy khi hệ thống nhận được một yêu cầu ngắt n (Int n) từ chương trình thì nó
phải: 1. Dừng lệnh hiện tại, đưa giá trị con trỏ lệnh CS:IP hiện tại (đó chính là địa
chỉ của lệnh sau lệnh gọi ngắt) vào lưu trữ ở hai phần tử trên đỉnh Stack; 2. Xác
định phần tử (địa chỉ) trong bảng vector ngắt chứa vector ngắt của ngắt n, lấy giá
trị tại đây (2 phần tử liên tiếp) để đưa vào cặp thanh ghi con trỏ lệnh CS:IP (đây
chính là địa chỉ của lệnh đầu tiên của chương trình con phục vụ ngắt n trong bộ
nhớ); 3. Bắt đầu thực hiện chương trình con phục vụ ngắt cho đến khi gặp lệnh kết
thúc chương trình này, đó là lệnh Iret; 4. Lấy nội dung của hai phần tử trên đỉnh
Stack đặt vào lại cặp thanh ghi con trỏ lệnh để quay trở lại tiếp tục thực hiện lệnh
sau lệnh gọi ngắt trong chương trình.

Những vấn đề liên quan đến Stack và cơ chế của một lời gọi ngắt sẽ được chúng
tôi làm rõ hơn ở phần sau của tài liệu này.

Lê ̣nh Int

Cú pháp:

Int <n>

Trong đó: Trong đó <n> là số hiệu ngắt của một ngắt mềm cần gọi. Tức là, n có thể
là một trong các giá trị từ 0 đến 255, đó chính là số hiệu ngắt của 256 ngắt mềm
mà BIOS và MSDOS cung cấp.

Tác dụng: Lệnh Int (Interrupt) được sử dụng để gọi một ngắt mềm (của BIOS hoặc
MSDOS) trong chương trình hợp ngữ. Khi một ngắt mềm được gọi thì hệ thống sẽ
thực hiện chương trình con phục vụ ngắt tương ứng với nó.

Ví dụ 1:

Int 10h ; gọi ngắt 10h của BIOS

Int 20h ; gọi ngắt 20h của MSDOS


Một ngắt mềm, hay chính xác hơn là một chương trình con phục vụ ngắt, có thể
thực hiện nhiều chức năng khác nhau, mỗi chức năng này được thể hiện thông qua
một con số, được gọi là số hiệu hàm của ngắt. Do đó, trước khi gọi ngắt chương
trình phải chỉ rõ gọi ngắt với hàm nào, bằng cách đặt số hiệu hàm cần gọi vào
thanh ghi AH.

Ví dụ 2:

Mov Ah, 01 ; gọi ngắt 21h với hàm 01, Hay nói ngược lại:
gọi hàm

Int 21h ; 01 của ngắt 21h

Trước khi gọi hàm/ngắt chương trình cần cung cấp đầy đủ dữ liệu vào (nếu có) cho
nó, sau khi hàm/ngắt được thực hiện chương trình cần xác định rõ nó có trả về dữ
liệu kết quả (dữ liệu ra) hay không, nếu có thì chứa ở đâu: thanh ghi hay ô nhớ, và
có tác động đến các cờ hiệu hay không.

Ví dụ 3:

Mov Ah, 02 ; đặt số hiệu hàm cần gọi vào AH

Mov DH, 10 ; cung cấp dữ liệu vào thứ nhất vào DH

Mov DL, 20 ; cung cấp dữ liệu vào thứ hai vào DL


Int 10h ; gọi ngắt 10h với hàm 02. Hàm/ngắt này
không

; trả về dữ liệu kết quả.

Dãy lệnh trên thực hiện việc gọi hàm 02 của ngắt 10h (ngắt của BIOS), nó thực
hiện việc dịch chuyển con trỏ đến dòng 10 cột 20 của màn hình văn bản.

Ví dụ 4:

Mov Ah, 2Bh ; gọi ngắt 21h với

Int 21h ; hàm 2Bh

Hàm này trả về ngày-tháng-năm hiện tại (theo đồng hồ hệ thống trên máy tính). Cụ
thể: Thanh ghi CX (1980-2099) chứa năm hiện tại, thanh ghi DH (1-12) chứa
tháng hiện tại, thanh ghi DL (1-31) chứa ngày hiện tại. Đồng thời AL cho biết ngày
trong tuần (0 : chủ nhật, 6 : thứ 7).

Mô ̣t số hàm của ngắt 21h (MSDOS)

Ngắt 21h của MSDOS là ngắt thường được sử dụng nhất, nên ở đây chúng tôi chọn
giới thiệu về ngắt này, nhưng chỉ với các hàm vào/ra kí tự/xâu kí tự cơ bản. Chức
năng của mỗi ngắt, chức năng của các hàm trong một ngắt, các yêu cầu dữ liệu
vào/ra của từng hàm trong mỗi ngắt,... dễ dàng tìm thấy trong các tài liệu về lập
trình hệ thống.

Hàm 02 của ngắt 21h:


Tác dụng: In một kí tự ra màn hình. Kí tự (hoặc mã ASCII của kí tự) cần in được
đặt trước trong thanh ghi DL. Kí tự ở đây có thể là kí tự thường hoặc kí tự điều
khiển.

Sử dụng:

Vào: Ah = 02

Dl = <Kí tự cần in ra>

Ra: Không có

Ví dụ 1: Các lệnh sau đây in ra màn hình kí tự A:

Mov Ah, 02

Mov Dl, ‘A’ ;có thể viết lệnh Mov Dl, 41h

Int 21h ; 41h là mã ASCII của kí tự A

Ví dụ 2: Các lệnh sau đây in ra màn hình 10 kí tự, bắt đầu từ kí tự A:

Mov Cx, 10

Mov Ah, 02

Mov Dl, ‘A’


Lap_In:

Int 21h

INC DL

Loop Lap_In

Ví dụ 3: Các lệnh sau đây in xâu kí tự từ trong biến TieuDe ra màn hình. Giả sử
rằng biến TieuDe đã được khai báo như sau:

TieuDe DB ‘Khoa CNTT Truong DHKH Hue’

Các lệnh:

Lea DI, TieuDe

Mov Cx, 25

Mov Ah, 02

Lap_In:

Mov Dl, Byte PTR [DI]

Int 21h

INC DI
Loop Lap_In

;---------------------------

Ví dụ 4: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một xâu kí tự ASCII,
gồm 50 kí tự. Các lệnh sau đây sẽ in xâu kí tự nói trên ra màn hình.

Mov Ax, 0A00h

Mov ES, Ax

Mov DI, 0100h

;-------------------------

Mov Cx, 50

Mov Ah, 02

Lap_In:

Mov Dl, Byte PTR ES:[DI]

Int 21h

INC DI
Loop Lap_In

;--------------------------------------

Hàm 09 của ngắt 21h:

Tác dụng: In một dãy kí tự (một xâu kí tự) ra màn hình. Địa chỉ của xâu cần in này
phải được chỉ bởi cặp thanh ghi DS:DX và xâu phải được kết thúc bởi dấu $.

Sử dụng:

Vào: Ah = 09

DS:DX = <Segment:Offset của xâu cần in ra>

Ra: Không có

Ví dụ 1: Giả sử chương trình đã khai báo biến TieuDe. Viết lệnh in nội dung của
biến TieuDe ra màn hình:

- Trong trường hợp này biến TieuDe phải được khai báo trước như sau:

TieuDe DB ‘Truong DH Khoa hoc Hue$’

- Và đoạn lệnh gồm các lệnh sau:

Mov Ah, 09
Mov Ax, Seg TieuDe

Mov DS, Ax

Mov Dx, Offset TieuDe ; có thể dùng lệnh Lea TieuDe

Int 21h

Trong thự tế, với các chương trình hợp ngữ viết ở dạng đoạn giản đơn, thì không
cần đưa địa chỉ Segment của biến cần in vào DS. Bởi vì:

- Đối với các chương trình dạng COM:

- Đối với các chương trình dạng EXE:

Ví dụ 2: Giả sử biến TieuDe đã được khai báo như sau:

TieuDe DB ‘Khoa CNTT Truong DHKH Hue$’

Các lệnh sau chỉ in các kí tự “Truong DHKH Hue” từ biến TieuDe ra màn hình:

Mov Ax, Segment TieuDe

Mov DS, Ax

Mov Dx, TieuDe

Add Dx, 11
Mov Ah, 09

Int 21h

Các lệnh sau chỉ in các kí tự “Khoa CNTT” từ biến TieuDe ra màn hình:

Mov Ax, Segment TieuDe

Mov DS, Ax

Mov Dx, TieuDe

Mov DI, Dx

Add DI, 10

Mov Byte PTR DS:[DI], ‘$’

Mov Ah, 09

Int 21h

Ví dụ 3: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một xâu kí tự ASCII,
gồm 50 kí tự. Các lệnh sau đây sẽ in xâu kí tự nói trên ra màn hình.

Mov Ax, 0A00h


Mov DS, Ax

Mov Dx, 0100h

;-------------------------

Mov DI, Dx

Mov Cx, 50

Add DI, Cx

Mov Byte PTR DS:DX, ‘$’

;-------------------------------------

Mov Ah, 09

Int 21h

;-------------------

Hàm 01 của ngắt 21h:

Tác dụng:Nhập một kí tự từ bàn phím vào lưu trong thanh ghi AL. Cụ thể là, AL sẽ
chứa mã ASCII của kí tự ghi trên phím nhập.

Sử dụng:
Vào: Ah = 01

Ra: Al = 0: nếu phím nhập là một trong các phím chức năng

Al = < mã ASCII của kí tự ghi trên phím nhập>

Cụ thể như sau: Khi chương trình gọi ngắt 21h với hàm 01 thì màn hình sẽ xuất
hiện một con trỏ nhập, để người sử dụng nhập vào một kí tự từ bàn phím. Khi đó,
nếu người sử dụng gõ một trong các phím chức năng thì AL nhận được giá trị 0,
nếu người sử dụng gõ một phím kí tự nào đó thì AL nhận được mã ASCII của kí tự
đó.

Chú ý: Hàm 08 của ngắt 21h có chức năng tương tự hàm 01 ngắt 21h nhưng kí tự
trên phím gõ không xuất hiện trên màn hình, tất cả đều được xuất hiện bởi kí tự
‘*’.

Ví dụ 1:

Mov Ah, 01 ; với hai lệnh này màn hình sẽ xuất hiện con
trỏ

Int 21h ; nhập để người sử dụng nhập một kí tự vào


AL

Ví dụ 2: Các lệnh sau đây cho phép nhập một xâu kí tự, với số lượng kí tự được ấn
định trước, vào biến LuuXau đã khai báo trước trong chương trình

Giả sử biến LuuXau được khai báo như sau:


LuuXau DB 30 Dup (‘ ‘)

Các lệnh:

Mov Ax, Seg LuuXau

Mov DS, Ax

Lea DI, LuuXau

;--------------------------------

Mov Cx, 30

Mov Ah, 01

Nhap_Xau:

Int 21h

Mov Byte PTR DS:[DI], Al

INC DI

Loop Nhap_Xau

;--------------------------------------
Trong trường hợp này chúng ta đã giả sử: Người sử dụng chỉ nhập các kí tự (gõ
phím kí tự để nhập), không nhập các phím chức năng.

Trong thực tế, không bao giờ chúng ta sử dụng hàm 01 ngắt 21h để nhập xâu, vì nó
tồn tại hai hạn chế: không thể kết thúc nhập bằng cách gõ phím Enter; số kí tự của
xâu nhập phải được ấn định trước trong chương trình. Để khắc phục, MSDOS cung
cấp hàm 0Ah của ngắt 21h để hỗ trợ nhập xâu kí tự.

Hàm 0Ah của ngắt 21h:

Tác dụng:Nhập một xâu kí tự vào một biến đệm cho trước trong chương trình, biến
này phải được chỉ bởi cặp thanh ghi DS:DX. Và biến đệm phải có dạng như sau:

- Byte 0: chứa số kí tự tối đa của xâu nhập vào

- Byte 1: chứa một trị không (= 0)

- Byte 2, byte 3, byte 4, ... chứa một trị rỗng (để trống), để chứa các kí tự sẽ
được nhập vào sau này (khi chương trình thực hiện).

Để có được một biến như trên chương trình phải khai báo biến (tên biến là
Xau_Nhap) như sau:

Xau_Nhap DB 256, 0, 256 Dup (‘ ‘)

Như vậy Xau_Nhap là một biến kiểu byte, gồm 258 byte. Byte đầu tiên (byte) chứa
trị 256, byte 1 chứa trị 0, 256 byte tiếp theo chứa kí tự trống, được sử dụng để chứa
các kí tự sẽ được nhập sau này. Xau_Nhap chứa tối đa 256 kí tự.
Cũng có thể sử dụng hàm 0Ah/21h để nhập một xâu kí tự vào vùng nhớ có địa chỉ
xác định trong bô nhớ.

Sử dụng:

Vào: Ah = 0Ah

DS:DX = <Địa chỉ Segment:Offset của xâu nhập>

Ra: DS:DX không thay đổi

Biến đệm bây giờ có dạng như sau:

- Byte 0: không thay đổi

- Byte 1: chứa tổng số kí tự đã được nhập vào

- Byte 2, byte 3, byte 4, ... chứa các kí tự đã được nhập vào.

Ví dụ 1: Với khai báo biến đệm Xau_Nhap như trên, nếu sau này chương trình
nhập vào xâu: “Tin hoc” thì:

- Byte 0: vẫn chứa số 256

- Byte 1: chứa giá trị 7, đó chính là 7 kí tự trong xâu “Tin hoc”

- Từ byte 2 đến 8 chứa lần lượt các kí tự trong xâu “Tin hoc.
Ví dụ 2: Giả sử chương trình đã khai báo xâu TieuDe như sau:

TieuDe DB 100, 0, 100 Dup (‘ ‘)

Các lệnh sau đây sử dụng hàm 0Ah/21h để nhập một xâu kí tự vào biến
TieuDe:

Mov Ax, Seg TieuDe

Mov Ds, Ax

Lea Dx, TieuDe

Mov Ah, 0Ah

Int 21h

Các lệnh sau đây lấy số kí tự thực sự nhập đưa vào lưu trữ trong thanh ghi Cx:

Mov Cx, 0

Mov Cl, TieuDe[1]

Các lệnh sau đây sử dụng hàm 02/21h để in xâu kí tự vừa nhập ra lại màn hình:

Lea DI, TieuDe

Mov Cx, 0
Mov Cl, TieuDe[1]

Add DI, 2

Mov Ah, 02

Lap_In:

Mov Dl, DS:[DI]

Int 21h

INC DI

Loop Lap_In

Các lệnh sau đây sử dụng hàm 09/21h để in xâu kí tự vừa nhập ra lại màn hình:

Mov Ax, Seg TieuDe

Mov Ds, Ax

Lea Dx, TieuDe

Add Dx, 2
Mov DI, Dx

Mov Cx, 0

Mov Cl, TieuDe[1]

Add DI, Cx

Mov Byte PTR DS:[DI], ‘$’

Mov Ah, 09h

Int 21h

Ví dụ 3: Chương trình dạng COM sau đây sử dụng hàm 0Ah ngắt 21h để nhập một
xâu kí tự vào biến Buff. Sau đó sử dụng hàm 09h ngắt 21h để in xâu kí tự vừa nhập
ra lại màn hình.

Để đơn giản, chương trình khai báo biến Buff gồm toàn kí tự ‘$’, nhờ đó, khi dùng
hàm 09h/21h để in ra chương trình không cần đưa thêm kí tự ‘$’ vào cuối xâu
nhập, mà chỉ cần trỏ DS:DX về đầu vùng/xâu kí tự cần in ra.

.Model small

.Code

ORG 100h

Start:
JMP Main

TBN DB 'Nhap vao mot xau ki tu: $'

TBX DB 0Ah,0Dh,'Xau vua nhap: $'

Buff DB 100,0,100 Dup ('$')

Main Proc

Mov Ah, 09h

Lea Dx, TBN

Int 21h

;--------------------------

Mov Ah, 0Ah

Lea Dx, Buff

Int 21h

;-------------------------
Mov Ah, 09h

Lea Dx, TBX

Int 21h

;-------------------------

Mov Ah, 09h

Lea Dx, Buff

Add Dx, 2

Int 21h

;-------------------------

Int 20h

Main Endp

End Start

Một số ví dụ:
Ví dụ 1: Giả sử hàm X của ngắt Y khi được gọi sẽ trả về trong cặp thanh ghi ES:SI
địa chỉ của vùng nhớ chứa tên của nhà sản xuất (nhãn hiệu) của vi xử lý đang sử
dụng trên máy tính hiện tại, tên này dài không quá 8 kí tự.

Các lệnh sau đây sẽ in tên nhà sản xuất nói trên ra màn hình:

Mov Ah, X ; hàm cần gọi được đưa vào thanh ghi
ah

Int Y ; gọi ngắt Y với hàm X

;--------------------

Mov Cx, 8 ; tên dài không quá 8 kí tự

Mov Ah, 02 ; dùng hàm 02/21h để in kí tự


ra nàm hình

LapIn:

Mov Dl, Byte PTR ES:[SI]

Int 21h

INC SI ; đến kí tự tiếp theo

Loop LapIn

;------------------------------------
Ví dụ 2: Giả sử hàm X của ngắt Y khi được gọi với AL = 1 (được gọi là hàm con)
sẽ trả về trong cặp thanh ghi ES:DI địa chỉ của vùng nhớ chứa ngày-tháng-năm sản
xuất ROM-BIOS đang sử dụng trên máy tính hiện tại. Đồng thời hàm/ngắt này
cũng cho biết số kí tự trong xâu ngày-tháng-năm trong thanh ghi BX.

Các lệnh sau đây sẽ in xâu ngày-tháng-năm nói trên ra màn hình:

Mov Ah, X ; hàm cần gọi được đưa vào thanh ghi
ah

Mov Al, 1 ; gọi hàm X với Al = 1

Int Y ; gọi ngắt Y với hàm X

;--------------------

Mov Cx, Bx ; đưa số kí tự của xâu ngày-tháng-


nămvào Cx

Mov Ah, 02 ; dùng hàm 02/21h để in kí tự


ra nàm hình

LapIn:

Mov Dl, Byte PTR ES:[DI]

Int 21h
INC DI ; đến kí tự tiếp theo

Loop LapIn

;-------------------------------------

Ví dụ 3: Hàm 39h của ngắt 21h được sử dụng để tạo thư mục con trên đĩa. Hàm
này quy định: DS:DX chứa xâu tên thư mục cần tạo, bao gồm cả đường dẫntìm đến
thư mục này, xâu này kết thúc bởi trị 0. Nếu việc tạo không thành công thì Cf = 1,
khi đó thanh ghi Ax sẽ chứa mã lỗi.

Các lệnh sau đây sẽ tạo ra thư mục con BTCB trong thư mục ASSEM trên thư mục
gốc ổ đĩa D.

Chương trình phải khai báo biến TenTM, chứa xâu tên thư mục cần tạo như sau:

TenTM DB ‘D:\ASSEM\BTCB’,0

Các lệnh:

Mov Ax, Seg TenTM

Mov DS, Ax

Mov Dx, Offset TenTM

Mov Ah, 39h

Int 21h
;-------------------------

Jc TB_Loi ; nếu CF = 1 thì việc tạo bị lỗi

<In ra thong bao hoan thanh>

Jmp KetThuc

TB_Loi:

<In ra thong bao khong tao duoc thu muc>

KetThuc:

...

;----------------------------------------------------
MỘT SỐ VÍ DỤ
Ví dụ 1: Giả sử hàm X của ngắt Y khi được gọi sẽ trả về trong cặp thanh ghi ES:SI
địa chỉ của vùng nhớ chứa tên của nhà sản xuất (nhãn hiệu) của vi xử lý đang sử
dụng trên máy tính hiện tại, tên này dài không quá 8 kí tự.
Các lệnh sau đây sẽ in tên nhà sản xuất nói trên ra màn hình:
                    Mov        Ah, X                             ; hàm cần gọi được đưa vào thanh
ghi ah
                    Int           Y                                  ; gọi ngắt Y với hàm X
                    ;--------------------        
                    Mov        Cx, 8                              ; tên dài không quá 8 kí tự
                    Mov        Ah, 02                            ; dùng hàm 02/21h để in kí tự ra
nàm hình
             LapIn:
                    Mov        Dl, Byte PTR ES:[SI]
                    Int           21h
                    INC         SI                                 ; đến kí tự tiếp theo
                    Loop       LapIn 
                    ;------------------------------------
Ví dụ 2: Giả sử hàm X của ngắt Y khi được gọi với AL = 1 (được gọi là hàm con)
sẽ trả về trong cặp thanh ghi ES:DI địa chỉ của vùng nhớ chứa ngày-tháng-năm sản
xuất ROM-BIOS đang sử dụng trên máy tính hiện tại. Đồng thời hàm/ngắt này
cũng cho biết số kí tự trong xâu ngày-tháng-năm trong thanh ghi BX.
Các lệnh sau đây sẽ in xâu ngày-tháng-năm nói trên ra màn hình:
                    Mov        Ah, X                            ; hàm cần gọi được đưa vào thanh
ghi ah
                    Mov        Al, 1                             ; gọi hàm X với Al = 1
                    Int           Y                                 ; gọi ngắt Y với hàm X
                    ;--------------------
                    Mov        Cx, Bx                          ; đưa số kí tự của xâu ngày-tháng-
nămvào Cx
                    Mov        Ah, 02                          ; dùng hàm 02/21h để in kí tự ra nàm
hình
             LapIn:
                    Mov        Dl, Byte PTR ES:[DI]
                    Int           21h
                    INC         DI                                ; đến kí tự tiếp theo
                    Loop       LapIn 
                    ;-------------------------------------
Ví dụ 3: Hàm 39h của ngắt 21h được sử dụng để tạo thư mục con trên đĩa. Hàm
này quy định: DS:DX chứa xâu tên thư mục cần tạo, bao gồm cả đường dẫntìm đến
thư mục này, xâu này kết thúc bởi trị 0. Nếu việc tạo không thành công thì Cf = 1,
khi đó thanh ghi Ax sẽ chứa mã lỗi.
Các lệnh sau đây sẽ tạo ra thư mục con BTCB trong thư mục ASSEM trên thư mục
gốc ổ đĩa D. Chương trình phải khai báo biến TenTM, chứa xâu tên thư mục cần
tạo như sau:
            TenTM           DB      ‘D:\ASSEM\BTCB’,0
Các lệnh:
            Mov    Ax, Seg TenTM
            Mov    DS, Ax
            Mov    Dx, Offset TenTM
            Mov    Ah, 39h
            Int       21h   
            ;-------------------------
            Jc        TB_Loi                                   ; nếu CF = 1 thì việc tạo bị lỗi
            <In ra thong bao hoan thanh>
            Jmp     KetThuc
       TB_Loi:
            <In ra thong bao khong tao duoc thu muc>
        KetThuc:
            ...
            ;----------------------------------------------------    
Ví dụ 4: Sau đây là chương trình dạng COM: In ra tất cả (256) các kí tự có trong
bảng mã ASCII:
.Model       Small
.Code
      ORG       100h
          Start:
                    Jmp         Main
    TB       DB    'Noi dung Bang ma ASCII:',0Ah,0Dh,'$'   
      Main           Proc
                    Mov        Ah, 09h
                    Lea          Dx, TB
                    Int           21h
                    ;--------------------------
                    Mov        Cx, 256
                    Mov        Ah, 02
                    Mov        Dl, 0
            LapIn:
                    Int           21h
                    ;-----------------
                    Mov        Bl, Dl
                    Mov        Dl, ' '
                    Int           21h
                    Mov        Dl, Bl
                    ;-----------------
                    INC         Dl
                    Loop       LapIn
                    ;-------------------------
                    Int           20h
    Main        Endp
                    End         Start   
 
Ví dụ 5.1: Sau đây là chương trình dạng COM: Nhập vào một xâu kí tự bất kỳ.
Sau đó in ra lại chính xâu kí tự vừa được nhập vào nhập. Sử dụng hàm 09/21h để
in ra.
.Model       Small
.Code
                    ORG       100h
    Start:
                        JMP    Main
              TBN                DB    'Nhap vao mot xau ki tu: $'
              TBX                DB    0Ah,0Dh,'Xau vua nhap: $'
              Buff                DB    100,0,100 Dup (' ')  
   Main           Proc
                    Mov        Ah,  09h
                    Lea          Dx, TBN
                    Int           21h
                    ;--------------------------
                    Mov        Ah, 0Ah
                    Lea          Dx, Buff
                    Int           21h
                    ;-------------------------
                    Mov        Cx, 0
                    Mov        Cl,  Buff[1]
                    Add         Dx, 2
                    Mov        DI, Dx
                    Add         DI, Cx
                    Mov        Byte PTR [DI], ‘$’
                    ;-------------------------
                    Mov        Ah,  09h
                    Lea          Dx, TBX
                    Int           21h
                    ;-------------------------
                    Mov        Ah,  09h
                    Lea          Dx, Buff
                    Add         Dx, 2
                    Int           21h
                    ;-------------------------
                    Int           20h
     Main           Endp
                    End         Start
 
Ví dụ 5.2: Sau đây là chương trình dạng EXE: Nhập vào một xâu kí tự bất kỳ. Sau
đó in ra lại chính xâu kí tự vừa được nhập vào nhập. Sử dụng hàm 02/21h để in ra.
.Model       Small
.Stack        100h
.Data
              TBN                DB    'Nhap vao mot xau ki tu: $'
              TBX                DB    0Ah,0Dh,'Xau vua nhap: $'
              Buff                DB    100,0,100 Dup (' ')  
.Code
          Main           Proc
                    Mov        Ax,@Data
                    Mov        DS, Ax
                    ;-------------------------
                    Mov        Ah,  09h
                    Lea          Dx, TBN
                    Int           21h
                    ;--------------------------
                    Mov        Ah, 0Ah
                    Lea          Dx, Buff
                    Int           21h
                    ;-------------------------
                    Mov        Cx, 0
                    Mov        Cl,  Buff[1]
                    Add         Dx, 2
                    Mov        DI, Dx
                    ;--------------------------
                    Mov        Ah,  09h
                    Lea          Dx, TBX
                    Int           21h
                    ;-------------------------
                    Mov        Ah, 2
       Lap_In:                          
                    Mov        Dl, [DI]
                    Int           21h
                    INC         DI
                    Loop       Lap_In
                    ;-------------------------
                    Mov        Ah, 4Ch
                    Int           21h
   Main           Endp
            End    
Ví dụ 6: Sau đây là chương trình dạng COM: Nhập vào một kí tự thường, chương
trình sẽ in ra kí tự in hoa tương ứng.
.Model       Small
.Code
            ORG   100h
Start:
            Jmp     Main
              TB1                 DB    'Nhap vao mot ki tu thuong: $'      
              TB2                 DB    0Ah,0Dh,'Ki tu hoa tuong ung: $'
 Main           Proc
                    Mov        Ah, 09h
                    Lea          Dx, TB1
                    Int           21h
                    ;----------------------
                    Mov        Ah, 01
                    Int           21h
                    Mov        Bl, Al
                    ;-----------------------
                    Mov        Ah, 09h
                    Lea          Dx, TB2
                    Int           21h
                    ;----------------------
                    Mov        Ah, 02
                    Mov        Dl, Bl
                    Sub         Dl, 20h
                    Int           21h
                    ;-------------------------
                    Int           20h
 Main           Endp
         End        Start   
Ví dụ 7: Sau đây là chương trình dạng COM: Nhập vào một xâu kí tự sau đó in ra
lại xâu đã nhập theo thứ tự đảo ngược.
.Model       Small
.Code
            ORG   100h
Start:
            JMP    Main
              TBN                DB    'Nhap vao mot xau ki tu: $'
              TBX                DB    0Ah,0Dh,'Xau vua nhap: $'
              Buff                DB    100,0,100 Dup (' ')  
 Main           Proc
                    Mov        Ah,  09h
                    Lea          Dx, TBN
                    Int           21h
                    ;--------------------------
                    Mov        Ah, 0Ah
                    Lea          Dx, Buff
                    Int           21h
                    ;-------------------------
                    Mov        Cx, 0
                    Mov        Cl,  Buff[1]
                    ;---------------------------
                    Mov        Ah,  09h
                    Lea          Dx, TBX
                    Int           21h
                    ;-------------------------
                    Lea          Dx, Buff
                    Add         Dx, 2
                    Mov        DI, Dx
                    Add         DI, Cx
                    Dec         DI
                    Mov        Ah, 02
        Lap_In_Nguoc:
                    Mov        Dl, [DI]
                    Int           21h
                    DEC        DI
                    Loop       Lap_In_Nguoc
                    ;--------------------------------
                    Int           20h
 Main           Endp
            End     Start
 
Ví dụ 8:,Sau đây là chương trình dạng COM: Nhập vào hai số (số thứ nhất: nhỏ
hơn 5; số thứ hai: nhỏ hơn hoặc bằng 5), sau đó in ra tổng của hai số vừa nhập.
.Model       Small
.Code
        ORG       100h
Start:
        Jmp         Main
              TBN1              DB        'Nhap so hang thu nhat (nho hon 5): $'
              TBN2              DB        0Ah,0Dh,'Nhap so hang thu hai (nho hon bang 5):
$'
              TBX                DB        0Ah,0Dh,'Tong cua hai so la: $'
 Main           Proc
        Mov        Ah, 09h
                    Lea          Dx, TBN1
        Int           21h
        Mov        Ah, 01
        Int           21h
        Mov        Bl, Al
        Sub         Bl, 30h
        ;-------------------------
        Mov        Ah, 09h
        Lea          Dx, TBN2
        Int           21h
        Mov        Ah, 01
        Int           21h
        Sub         Al, 30h
        Add         Bl, Al
        ;-------------------------
        Mov        Ah, 09h
        Lea          Dx, TBX
        Int           21h                            
        Mov        Ah, 02
        Mov        Dl, Bl
        Add         Dl, 30h
        Int           21h
        ;------------------------
        Int           20
Main           Endp
         End              Start          

Bài 6:
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.
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.
Các lệnh Dịch bít – Quay bít
Các lệnh dịch bít là các lệnh làm cho các bít trong một thanh ghi bị dịch về bên trái
(lệnh ShR) hoặc về bên phải (lệnh ShL) một hoặc nhiều bít. Lệnh quay bít làm cho
các bít trong một thanh ghi quay theo “chiều trái” hoặc theo “chiều phải” một hoặc
nhiều bít. Thông thường các bít được dịch hay được quay đều lần lượt được đưa
qua cờ CF. Do đo, các lệnh dịch bít và quay bít thường được sử dụng để kiểm tra
giá trị bít (= 0 hay = 1) của các bít trong thanh ghi.
Hợp ngữ cung cấp hai dạng lệnh quay bít, quay không qua cờ CF (lệnh RoL và
RoL) và quay có qua cờ CF (lệnh RcL và RcR).
Cú pháp:

 Shr      [Toán hạng đích], <n>


 Shl      [Toán hạng đích], <n>
 Rcr     [Toán hạng đích], <n>
 Rcl      [Toán hạng đích], <n>

Trong đó: [Toán hạng đích] là một thanh ghi 8 bít hoặc 16 bít. <n> là số bít
cần dịch, nếu <n> = 1 thì chỉ định trực tiếp trong câu lệnh, nếu <n> lớn hơn 1
phải chỉ định thông qua thanh ghi CL.
Tác dụng:

 Lệnh ShR (Shift Logical Right): Dịch chuyển các bít trong thanh ghi [Toán
hạng đích] sang phải một hoặc nhiều bít. Các bít được dịch lần lượt được đưa
vào cờ CF, cờ CF sẽ chứa bít của lần dịch cuối cùng. Sau khi dịch các bít bị
khuyết (ở bên đối diện) sẽ được thay bằng các bít có trị 0. Tức là, với thanh ghi
8 bít thì sau 8 lần dịch nó sẽ nhận được một dãy 8 bít = 0, tương tự với thanh
ghi 16 bít thì sau 16 lần dịch nó sẽ nhận được một dãy 16 bít = 0. Nếu thanh ghi
AL = 01001001 thì sau khi bị dịch về bên trái 2 bít nó sẽ như sau: AL =
00100100, khi đó CF = 1.
 Lệnh ShL (Shift Logical Left): Tương tự như lệnh ShR nhưng các bít được
dịch về phía bên trái.
 Lệnh RCR (Rotate through Carry Right): Tương tự như lệnh ShR, nhưng
bít được dịch sẽ được đặt vào lại bít bị khuyết ở bên đối diện. Tức là, với thanh
ghi 8 bít thì sau 8 lần dịch nó sẽ nhận lại dãy bít ban đầu, tương tự với thanh ghi
16 bít thì sau 16 lần dịch nó sẽ nhận lại dãy bít ban đầu. Nếu thanh ghi AL
= 01001001 thì sau khi bị quay về bên trái 2 bít nó sẽ như sau: AL = 00100101,
khi đó CF = 1.
 Lệnh RCL (Rotate through Carry Left): Tương tự như lệnh RCR nhưng các
bít được quay về phía bên trái.

Ví dụ 1:

 Shr          Al, 1
 Mov        Cl, 2

             Shr          Al, CL


             Shl          Bl, CL
              Rcl          AL, CL

 Rcr          Dl, 1               

Ví dụ 2: Các lệnh sau đây đếm số bít bằng 1 trong thanh ghi BX, kết quả chứa ở
thanh ghi Al mà không làm thay đổi giá trị của nó:
        Mov        Al, 0   
        Mov        Cx, 16
   DemBit1:
        Rol          Bx, 1
        Jnc          TiepTuc
        Inc           Al
   TiepTuc:
        Loop       DemBit1
  ;--------------------------     
Ví dụ 3: Các lệnh sau đây đếm số bít giống nhau (tương ứng) giữa hai thanh ghi
Ax và Bx. Kết quả chứa trong biến Dem (Dem    DB      0):
        Mov        Dem, 0 
        Xor         Ax, Bx
        Mov        Cx, 16
   KTra:
        Shl          Ax, 1
        Jnc          Bit_0         
        Jmp         TiepTuc
  Bit_0:
        Inc           Dem
  TiepTuc:
        Loop       KTra  
  ;------------------------
Ví dụ 4: Các lệnh sau đây in nội dung của thanh ghi BX ra màn hình dưới dạng sô
nhị phân:
                    Mov        Cx, 16             ; lặp in đủ 16 bít
                    Mov        Ah, 02             ; in ra kí tự với hàm 02/21h
            In_NhiPhan:
                    Shl          Bx, 1       ; dịch trái để bít cần in rơi vào cờ CF
                    Jc            In_1       ; lệnh JC nhảy khi cờ CF = 1
                    Mov        Dl, ‘0’      ; CF = 0 tức là bít cần in có trị = 0, nên in ra kí tự
‘0’
                    Int           21h
                    Jmp         In_Tiep         
            In_1:
                    Mov        Dl, ‘1’     
                    Int           21h
            In_Tiep:
                    Loop       In_NhiPhan
                    ;-------------------------
Ví dụ 5: Các lệnh sau đây nhập một số nhị phân từ bàn phím đưa vào lưu trữ trong
thanh ghi BX:
        Mov        Bx, 0
        Mov        Cx, 0
        Mov        Ax, 0
        Mov        Ah, 1
  Nhap:
        Int           21h
        Cmp        Al, 0Dh             ; nếu nhập Enter thì kết thúc
        Je            KetThuc
        ;--------------------
        Cmp        Al, ‘1’
        Je            Them_Bit
        Cmp        Al, ‘0’
        Je            Them_Bit
        ;----------------------
        Mov        Ah,09
        Mov        Dx, Seg TB    ; Biến TB được khai báo như sau:
        Mov        DS, Dx            ; TB     DB    ‘Ban phai nhap 0/1 hoac Enter$’
        Mov        Dx, Offset TB
        Int           21h
        Jmp         Nhap
  Them_Bit:
        Sub         Al, 30h                       ; có thẻ sử dụng lệnh And     Al, 0Fh
        Shl          Bx, 1
        Or            Bx, Al
        Inc           Cx
        Cmp        Cx, 17
        Jne          Nhap
  KetThuc:
        Mov        4Ch
        Int           21h
;----------------------------------
Ví dụ 6: Giả sử tại địa chỉ 0B000:0010 trong bộ nhớ có chứa một byte dữ liệu, nó
cho biết một số thông tin liên quan đến cấu hình hệ thống. Byte 3 cho biết máy tính
hiện tại có (byte 3 = 1) hay không có (byte 3 = 0) cổng cắm USB.
Các lệnh sau đây cho biết máy tính hiện tại (máy thực hiện đoạn lệnh) có hay
không có cổng cắm USB. Kết quả được thông báo thông qua thanh ghi Cx: Cx = 0:
không có cổng cắm USB; Cx = 0FFFFh: có cổng cắm USB.
        Mov        Ax, 0B000h
        Mov        DS, Ax
        Mov        DI, 0010h
        ;-------------------------
        Mov        Ax, 0
        Mov        Al, byte PTR DS:[DI]                     
        ;-------------------------------------
        Mov        Cl, 4
        Shr          Al, Cl
        Jc            CoUSB                                   ; co cong USB
        Mov        Cx, 0                                       ; khong co cong USB
        Jmp         KetThuc
  CoUSB:
        Mov        Cx, 0FFFFh
  KetThuc:
        ...            
        ;-------------------------
Trong thực tế, kết quả kiểm tra này thường được trả lời thông qua ngay chính cờ
CF. CF = 0: không có cổng USB, CF = 1: có cổng USB.
Chú ý 1: Có thể sử dụng lệnh dịch bít ShR/ShL để thực hiện phép chia/phép nhân
giá trị của một thanh ghi (chứa số nguyên không dấu) với một số là bội số của 2.
Ví dụ: Hai lệnh sau đây sẽ dịch AL sang trái 3 bít, tức là nhân AL với 8:
        Mov        Cl, 3
        Shl          Al, Cl              ; Al ß AL * 8, 8 = 23.
Ví dụ: Hai lệnh sau đây sẽ dịch AL sang phải 3 bít, tức là chia AL với 8:
        Mov        Cl, 3
        Shr          Al, Cl              ; Al ß AL * 8, 8 = 23.
Chú ý 2: Hợp ngữ còn cung cấp các lệnh dịch chuyển số học SAL (Shift
Arithmetic Left) và SAR (Shift Arithmetic Right) . SAL tương tự hoàn toàn ShL,
có thể sử dụng để thực hiện nhân 2 với các số âm. SAR tương tự ShR nhưng bít
cuối cùng của [Toán hạng đích] không bị thay bằng bít 0 mà vẫn giữ nguyên giá trị
cũ, có thể sử dụng để thực hiện chia 2 với các có dấu.
Các lệnh dich bít, quay bít của các vi xử lý Intel 80286/80386/.... cho phép viết số
bít cần dịch, trong trường hợp lớn hơn một, trực tiếp trong lệnh dịch, quay mà
không cần thông qua thanh ghi Cl [1 – 540]

You might also like