You are on page 1of 172

GV.

Vương Quốc Dũng


1

------

Giáo trình

Lập trình hợp


ngữ

1
GV. Vương Quốc Dũng

Ch¬ng 1. MÔ HÌNH LẬP TRÌNH CỦA 8086


1.1. Giới thiệu chung về máy tính và sơ đồ cấu trúc của IBM-PC
1.1.1. Giới thiệu chung về máy tính:
* Máy tính ngày nay là máy tính số, thực chất là thiết bị điện tử thực hiện:
Nhận thông tin vào đã dược số hóa.
Xử lý thông tin theo dãy các lệnh nhớ sẵn bên trong.
Đưa thông tin ra.
Dãy các lệnh nhớ sẵn bên trong chính là các chương trình.
Máy tính gồm 2 phần:
Phần cứng (Hardware): Gồm các linh kiện vật lý.
Phần mềm (Software): Gồm các chương trình và dữ liệu.
* Các loại máy tính:
Máy tính điện tử đầu tiên ra đời vào năm 1946, rất cồng kềnh, nó chiếm khoảng
1500 m2 . Hiện nay người ta chia máy tính ra làm 2 nhóm lớn:
+ Máy vi tính (MicroComputer):
Ra đời những năm 70 và phát triển rầm rộ vào những năm 80.
Đặc điểm: Máy tính được chế tạo ra cho 1 người sử dụng, còn gọi là máy tính cá
nhân (Personal Computer - PC).
Máy vi tính chia làm 4 loại:
. Desktop: Máy để bàn.
. WorkStation: Máy trạm làm việc. Đầu tiên khái niệm này xuất hiện với
máy mạng, xong ngày nay đây là loại máy PC có cấu hình mạnh.
Hai loại trên có đặc điểm chung là đặt cố định.
. NoteBook: Cỡ bỏ vừa cặp sách.
. Parmitop: Cỡ bé cầm tay.
+ Máy tính lớn:
Ra đời sớm hơn máy vi tính, hiện nay vẫn tồn tại và phát triển, xong không
nhiều vì đắt tiền và đã có mạng máy tính. Mạng máy tính thay cho máy tính lớn
hiệu quả hơn.
Đặc điểm: Hỗ trợ cho nhiều người sử dụng. Có 2 loại:
. MainFram.
. SuperComputer.
Những máy này sản xuất ra làm 2 mục đích:
. Làm máy chủ trong mạng máy tính lớn.
. Giải các bài toán phức tạp yêu cầu tốc độ nhanh.
1.1.2. Biểu diễn thông tin
1.1.2.1. Các số nhị phân (số hệ hai)
Trong máy tính để biểu diễn một giá trị số, chúng ta dùng hệ cơ số 2 hoặc nói ngắn gọn
hơn là hệ hai (Binary number system, viết tắt là hệ B). Trong đó chỉ tồn tại 2 chữ số 0 và 1

2
GV. Vương Quốc Dũng
để biểu diễn các giá trị số (ứng với 2 trạng thái: không có điện và có điện). 0 và 1 cũng là
các giá trị có thể có của một chữ số hệ hai (Binary digit, viết tắt là bit).
Bit
Bit là thuật ngữ được viết tắt của cụm từ binary digit. Mỗi một bit còn cho ta biết được
trạng thái của một tín hiệu điện trên một đường dây tại một thời điểm: điện áp ở mức cao
(có điện) là 1, điện áp ở mức thấp (không có điện) 0.

1.1.2.2. Biểu diển dữ liệu


Hệ đếm
Hệ đếm thập phân
Trong cuộc sống hằng ngày chúng ta dùng hệ cơ số mười hoặc nói ngắn gọn là hệ mười
(Decimal number system, viết tắt là hệ D) để biểu diễn các giá trị số. điều này là rất tự
nhiên vì từ xa xưa một con người bình thường đã biết dùng 10 ngón tay để đếm. Trong hệ
thống này, chúng ta dùng tổ hợp của các chữ số 0..9 để biểu diễn các giá trị số, đi kèm theo
tập hợp có thể dùng thêm chữ D hoặc d ở cuối để chỉ ra rằng đó là hệ 10 (một chữ số
không có ký hiệu chữ đi kèm ở sau thì ta ngầm hiểu đó là số hệ 10)
Hệ đếm nhị phân
Số nhị phân hay còn gọi là số hệ hai là hệ đếm dùng trong máy tính. Một số hệ hai gồm
các bit được đánh dấu bằng chữ B hoặc b đi kèm ở cuối để phân biệt với các hệ khác khi ta
làm việc cùng một lúc với các hệ đếm khác nhau. Một cụm 4 bit sẽ tạo thành 1 nibble, một
cụm 8 bit tạo thành 1 byte, một cụm 16 bit tạo thành 1 word (một từ), một cụm 32 bit tạo
thành 1 double word (từ kép). Chữ số dầu tiên bên trái trong dãy các số hệ hai gọi là bit có
ý nghĩa lớn nhất (Most significant bit – MSB), còn bit cuối cùng bên phải (bit 0) trong dãy
gọi là à bit có ý nghĩa bé nhất (Least significant bit – LSB). Ứng với thứ tự đếm 1, 2, 3 …
ở hệ 10 thì ở hệ hai ta có 1, 10, 11, …
3 0
nibble

7 0

Byte
15 0

Word

31 0

Double Word
Hình 1.1. Các đơn vị đo độ dài của số hệ hai dẫn xuất từ bit
Hệ đếm thập lục phân

3
GV. Vương Quốc Dũng
Nếu ta dùng số hệ hai để biểu diễn các số có giá trị lớn, ta sẽ gặp điều bất tiện là số hệ
hai thu được quá dài. Ví dụ để biểu diễn số 255 ta cần 8 bit viết như sau:
255 = 11111111b
Trong thực tế để viết kết quả cho gọn lại, người ta tìm cách nhóm 4 số hệ hai
(1 nibble) thành một số hệ mười sáu. Hệ 16 dùng tất cả 4 bit để biểu diễn các giá trị cho
một chữ số, một chữ số có giá trị từ 0..15. Để làm được điều này người ta sử dụng các chữ
số có sẵn ở hệ 10 (0..9) để biểu diễn các giá trị số tương ứng 0..9, còn các số 10..15, dùng
thêm các chữ cái A..F hoặc a..f để biểu diễn các giá trị còn lại. Để phân biệt với các số hệ
khác, ta kèm theo chữ H hoặc h ở cuối.
Ví dụ: 255 = 11111111b = FFh
79 = 01001111b = 4Fh
Chuyển đổi giữa các hệ đếm
Ta gọi a là giá trị cơ số của hệ đếm (a = 2, 8, 10 hoặc 16), n là số chữ số biểu diễn giá trị
cho một số P, trong đó có k chữ số phần nguyên, chỉ số vị trí của các chữ số phần nguyên
trong P là 0..k-1 tính từ dấu phảy thập phân sang trái, chỉ số vị trí các chữ số phần thập
phân trong P là -1 ..-n+k, mi là giá trị của chữ số có chỉ số vị trí i trong P (0 < mi < a, i =
-n+k..k-1).
Ta có công thức chuyển đổi giữa các số hệ đếm cơ số a về số P hệ 10 như sau:

P = ak-1 x mk-1 + ak-2 x mk-2 + … + a1 x m1 + m0 + a-1 x m-1 + … + a-n+k x m-n+k (1)


Đổi số hệ 2 sang hệ 10
Muốn đổi một số từ hệ 2 sang số hệ 10 tương ứng, ta áp dụng công thức (1) ở trên với
cơ số a = 2, có nghĩa là ta chỉ cần tính các giá trị 2 i tương ứng với các chữ số khác 0 thứ i
của số hệ 2 rồi cộng chúng lại.

Ví dụ: 10111.11b = 24 + 22 + 21 + 1 + 2-1 + 2-2 = 25.75


Đổi số hệ 16 sang hệ 10
Tương tự như số hệ 2 sang hệ 10. Ta xét ví dụ sau:

1AF7h = 1 * 163 + 10 x 162 + 15 * 161 + 7 = 6903


Ta lưu ý các chữ số là chữ cái trong số hệ 16 có các giá trị tương ứng như sau:
A 10
B 11
C 12
D 13
E 14
F 15

Đổi số hệ 10 về hệ 2

4
GV. Vương Quốc Dũng
Cách 1: Lấy số hệ mười cần đổi trừ đi 2x (với x là giá trị lớn nhất của số mũ chọn sao
cho 2x nhỏ hơn hoặc bằng số hệ mười cần đổi), ghi lại giá trị 1 cho chữ số hệ 2 ứng với 2 x.
tiép tục làm như vậy đối với hiệu số vừa tạo ra và các chữ sô 2 i bậc thấp hơn cho tới khi
đạt tới 20 và ghi lại các giá trị 0 hoặc 1 cho chữ số hệ hai thứ i tùy theo quan hệ giữa số dư
và lũy thức tương ứng.

= 1 khi số dư ≥ 2i
= 0 khi số dư ≤ 2i
Ví dụ đổi 34 sang số hệ 2:
- Các giá trị 2i cần tính đến (25 = 32 là giá trị 2x sát dưới nhất so với 34):
25 24 23 22 21 20
- Các chữ số hệ hai tính được:
1 0 0 0 1 0
Như vậy 34 = 100010b
Cách 2: Lấy số cần đổi chia cho 2 và ghi nhớ phần dư, tiếp theo lấy thương của phép
chia vừa nhận được chia cho 2 và ghi nhớ phần dư. Cứ như vậy cho đến khi thương bằng
0. Đảo ngược thứ tự dãy các số dư, ta sẽ được các chữ số của số hệ hai cần tìm.
Ví dụ đổi số 34 sang số hệ hai ta thực hiện như sau:

34 2
0 17 2
1 8 2
0 4 2
0 2
2
0 1 2
1

Các số dư trong khung sẽ được sắp xếp theo chiều mũi tên. Ta có kết quả là 100010b.
Trong trường hợp số hệ mười có thêm phần lẻ sau dấu thập phân cần đổi sang số hệ hai
thì ta phải thực hiện đổi riêng rẽ từng phần rối sau đó ghép kết quả lại.
Riêng với phần lẻ sau dấu phảy thập phân ta làm như sau:
Lấy số cần đôi nhân với 2, tích nhận được sẽ gồm phần nguyên và phần lẻ thập phân,
ghi nhớ phần nguyên, lấy phần lẻ thập phân của tích vừa thu được nhân với 2. Cứ tiếp tục
như vậy cho đến khi tích được chẵn bằng 1 (không còn phần lẻ thập phân). Lấy các phần
nguyên đã lưu, sắp xếp lại ta được các chữ số sau dấu phảy nhị nhân (phần lẻ nhị phân) cần
tìm.

5
GV. Vương Quốc Dũng

Ví dụ đổi 0.125 ra số hệ hai


0.125 x 2 = 0 .250
0.250 x 2 = 0 .500
0.500 x2 = 1 .000
Kết quả ta thu được số nhị phân: 0.001b (như thứ tự phần được đóng khung)
Kết hợp 2 ví dụ ta có ví dụ đổi số 34.125 ra số nhị phân, kết quả là 100010.001b
Đổi số hệ 10 về hệ 16
Thực hiện tương tự như đổi số hệ 10 về số hệ 2, ta thực hiện chia kết quả phần thương
liên tiếp cho 16, lấy giá trị phần dư sắp xếp theo thứ tự ngược lại…
Tóm lại: để việc chuyển đổi thực hiện nhanh chóng, tránh thực hiện quá nhiều phép
chia, để đổi một số hệ mười ra số hệ hai, ta thực hiện thao thứ tự:
Số hệ mười ---> số hệ mười sáu ---> số hệ hai ---> số hệ tám
Vì việc thực hiện đổi số hệ mười sáu về số hệ hai rất đơn giản, chỉ việc đổi từng chữ số
hệ 16 ra hệ 2 rồi ghép chúng lại theo thứ tự. Từ số hệ 2 về số hệ 8 ta lấy từng cụm 3 bit
một bắt đầu từ LSB để đổi ra các số từ 0 ÷ 7 sau đó ghép đúng theo thứ tự.
Ví dụ: đổi số 125 ra hệ 16, ta lấy 125 chia 16 được 7 dư 13 = C, nhớ C. Lấy 7 chia 16
được 0, dư 7. Kết quả ta được 7Ch
Trong đó: Ch = 1101b
7h = 0111b
Ghép lại ta được số nhị phân: 01111101b = 175 (8)

1.1.2.3. Các phép toán số học với số hệ hai


Phép cộng
Phép cộng các số hệ hai giống như khi ta thực hiện với số hệ mười. Quy tắc phép cộng
số hệ 2 được chỉ ra trong bảng 1.1.
Bảng 1.1. y=a+b
A b y Nhớ
0 0 0 0
0 1 1 0
1 0 1 0
1 1 0 1

6
GV. Vương Quốc Dũng

Bảng 1.2. Mở rộng bảng 1.1, y = a + b + c + d + e + f + g


a b c d e f g y Nhớ
0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 0
0 0 0 0 0 1 0 1 0
0 0 0 0 0 1 1 0 1

0 0 0 1 1 1 1 0 2
0 0 1 1 1 1 1 1 2
1 0 1 1 1 1 1 0 3
1 1 1 1 1 1 1 1 3
Nhận xét: theo bảng 1.2 ta thấy kết quả tổng chia cho 2 (cơ số hệ đếm nhị phân) là giá
trị cần nhớ, còn phần dư chính là y. Như vậy cách thực hiện không khác gì với số hệ mười,
ví dụ ta cộng nhiều số thập phân được giá trị là 49, ta lấy 49 chia cho 10 (cơ số hệ đếm
thập phân), được 4 dư 9, viết 9 và nhớ 4. Giá trị nhớ sẽ được dùng để cộng với cột bit ở vị
trí tiếp theo.
Bảng 1.1 chính là cách mà các bộ cộng trong các khối tính toán số học của máy tính
thực hiện.
Phép trừ
Phép trừ các số hệ 2 giống như làm với số hệ 10. Bảng 1.3 là qui tắc thực hiện phép trừ
số nhị phân.
Bảng 1.3. Qui tắc trừ số nhị phân y = a - b
a b y Mượn
0 0 0 0
0 1 1 1
1 0 1 0
1 1 0 0
Số bù 2
Trong thực tế, việc thực hiện phép trừ chính là cộng số bị trừ với số đảo dấu của số trừ,
điều này cũng được ứng dụng trong các khối tính toán số học của máy tính để tận dụng các
bộ cộng đã có sẵn. Vậy số đảo dấu của số trừ chính là một số âm. Khi thực hiện phép trừ
trong máy tính cũng tất yếu sẽ có kết quả là một số âm. Vấn đề đặt ra đối với số hệ hai là ta
phải biểu diễn số âm nhị phân như thế nào sao cho thích hợp để đáp ứng được việc sử

7
GV. Vương Quốc Dũng
dụng các bộ cộng trong máy tính. Việc sử dụng số bù 2 chính là cách biểu diễn số có dấu
trong máy tính ngày nay.
Vậy số bù 2 của số A chính là số đảo dấu của số A đó. Ví dụ số bù 2 của 5 là -5 và
ngược lại, số bù 2 của –5 là 5.

Sau đây là phương pháp xây dựng số bù 2:


- Trước hết ta xây dựng số bù 1 của A. Số bù một của A chính là số nhận được khi ta
đảo tất cả các bit của số A.
Ví dụ 5 = 00000101b, số bù 1 của 5 là 11111010b
- Số bù 2 của A = số bù 1 của A + 1
Ví dụ: số bù 2 của 5 = số bù 1 của 5 + 1 = 11111010b + 1 = 11111011b = -5
Bảng 1.4. Biểu diễn các số theo số hệ 2, số hệ hai có dấu và số bù 2
Số hệ hai 8 bit Số hệ mười tương Số hệ mười tính
đương theo số bù 2
0000 0000 0 0
0000 0001 1 +1
0000 0010 2 +2
… … …
0111 1101 125 +125
0111 1110 126 +126
0111 1111 127 +127
1000 0000 128 -128
1000 0001 129 -127
1000 0010 130 -126
… … …
1111 1101 253 -3
1111 1110 254 -2
1111 1111 255 -1
Phép nhân
Phép nhân số hệ hai thực hiện giống như ta làm với số hệ mười. Chỉ cần chú ý khi cộng
kết quả tuân thủ theo bảng 1.2. qui tắc cộng mở rộng.
Qui tắc thực hiện phép nhân hệ 2 được chỉ ra trong bảng 1.5 .
Bảng 1.5. Qui tắc thực hiện phép nhân y = a x b
a b y
0 0 0
0 1 0

8
GV. Vương Quốc Dũng

1 0 0
1 1 1
Trên cơ sở quy tắc vừa nêu và để đơn giản ta thực hiện ví dụ một phép nhân 2 số hệ 2
với độ dài 4 bit để làm sáng tỏ thuật toán nhân thực hiện trong máy tính.

1001 Số bị nhân = 9
x
0110 Số nhân = 6
0000 Thành phần 1 của tổng tích lũy
1001 Thành phần 2 của tổng tích lũy
1001 Thành phần 3 của tổng tích lũy
0000 Thành phần 4 của tổng tích lũy
0110110 Tổng tích lũy bằng 54
Độ dài cực đại của kết quả trong trường hợp này là 8 bit. Nếu ta nhân các số 8 bit (hoặc
16) bit thì độ dài cực đại của kết quả là 16 bit (hoặc 32 bit). Mỗi lần nhân một bit khác 0
của số nhân với số bị nhân, ta thu được chính số bị nhân, nếu dịch trái nó một số lần tương
ứng với cị trí các bit khác 0, ta tạo ra 1 thành phần của tổng tích lũy. Tổng của các thành
phần như trên là kết quả của phép nhân.
Phân tích quá trình trên, ta thấy phép nhân có thể thực hiện theo thuật toán cộng và dịch
như sau:
- Thành phần đầu tiên của tổng tích lũy thu được là tích của LSB trong số nhân với số
bị nhân. Nếu LSB = 0 thì thành phần này bằng 0, Nếu LSB = 1 thì thành phần này =
chính số bị nhân.
- Mỗi thành phần thứ i tiếp theo của tổng tích lũy sẽ tính được bằng cách tương tự,
nhưng phải dịch trái đi i bit (có thể bỏ qua các thành phần = 0)
- Tổng của các thành phần là tích cần tìm.
Để minh họa thuật toán trên, ta dùng luôn nó để rút gọn ví dụ đã làm ở trên:
1001 Số bị nhân = 9
x
0110 Số nhân = 6
1001 Số bị nhân dịch trái 1 lần
1001 Số bị nhân dịch trái 2 lần
0110110 Tổng tích lũy bằng 54
Trong máy tính, phép nhân được thực hiện bởi bộ cộng và bộ dịch trái theo như thuật
toán cộng và dịch vừa trình bày.
Phép chia
Phép chia là phép tính ngược của phép nhân. Từ đó suy ra phép chia có thể thực hiện
bằng các phép trừ và phép dịch liên tiếp cho tới khi không thể trừ được nữa (do không còn
gì để trừ hoặc số bị trừ nhỏ hơn số chia). Sau đây là thuật toán thực hiện phép chia thông
qua ví dụ cụ thể:

9
GV. Vương Quốc Dũng
Ví dụ: 35/7 = 7
Ta hãy quan sát kỹ các bước phải thực hiện khi thực hiện phép chia bằng tay
100011 101
000 0111
1000
101
0111
101
0101
101
000
Trong các phép tính ở trên ta liên tục cần phải dự đoán và kiểm tra để tìm ra kết quả
đúng. Công việc này rất khó khăn với các mạch điện tử của khối tính toán số học (vốn là
các phần tử để thực hiện phép cộng và dịch trong máy tính).
Trong máy tính:
- Nếu số chia là 1 byte, thì số bị chia phải là 2 byte, do vậy khi số bị chia là 1 byte ta
phải mở rộng bit dấu cho số bị chia lên 8 bit cao.
- Nếu số chia là 2 byte, thì số bị chia phải là 4 byte, do vậy số bị chia là 1 word ta phải
mở rộng bit dấu cho số bị chia lên 16 bit cao.
Sau đây là một thuật toán khắc phục nhược điểm trên nêu trên:
1. Kiểm tra số bị chia, mở rộng bit dấu nếu cần
2. Đổi số chia ra số bù 2 của nó (để bước tiếp theo làm phép cộng với số bị chia thay cho
phép trừ). K = số bù 2 của số chia dịch trái đi 8 bit (nếu số chia là số 8 bit) hoặc 16 bit
nếu số chia là số 16 bit. Q = số bị chia
3. H = Q + K.
- Nếu H có MSB = 0 (có nghĩa là phần này của số bị chia chia được cho số chia) thì
bit tương ứng của thương bằng 1. Q = H
- Nếu kết quả này có MSB = 1 (có nghĩa là phần này của số bị chia không chia được
cho số chia) thì bit tương ứng của thương bằng 0.
4. Số K = số K dịch phải đi 1 bit, và làm lại bước 3 cho đến khi nhận được H = 0 (chia
hết) hoặc H số chia (chia còn dư).
5. Kết quả của thương số là các bit 0, 1 ghép theo trình tự các bit tương ứng của
thương.
Ví dụ: 103/6 = 17 dư 1. Thực hiện phép chia này ở hệ 2 theo thuật trên.
Ta có: Số chia = 00000110b
Số bù 2 của số chia = 11111010b
Số bị chia = 01100111b = 0000000001100111b (mở rộng bit dấu)

10
GV. Vương Quốc Dũng

Thuật toán chia số nhị phân số 103/6:


Tên toán hạng, thao tác Giá trị Thương
Q = Số bị chia 0000000001100111
K = Số bù 2 của số chia << 8 bit 1111101000000000
H=Q+K 1111101001100111 0
Q 0000000001100111
K = K >> 1 bit (giữ nguyên bit dấu) 1111110100000000
H=Q+K 1111110111100111 0
Q 0000000001100111
K = K >> 1 bit (giữ nguyên bit dấu) 1111111010000000
H=Q+K 1111111011100111 0
Q 0000000001100111
K = K >> 1 bit (giữ nguyên bit dấu) 1111111101000000
H=Q+K 1111111110100111 0
Q 0000000001100111
K = K >> 1 bit (giữ nguyên bit dấu) 1111111110100000
H=Q+K>6 0000000000000111 1
Q=H 0000000000000111
K = K >> 1 bit (giữ nguyên bit dấu) 1111111111010000
H=Q+K 1111111111010111 0
Q 0000000000000111
K = K >> 1 bit (giữ nguyên bit dấu) 1111111111101000
H=Q+K 1111111111101011 0
Q 0000000000000111
K = K >> 1 bit (giữ nguyên bit dấu) 1111111111110100
H=Q+K 1111111111111011 0
Q 0000000000000111
K = K >> 1 bit (giữ nguyên bit dấu) 1111111111111010
H=Q+K<6 0000000000000001 1
Vậy ta có kết quả = 10001b = 17, số dư H = 1

1.1.2.4. Biểu diễn số trong máy tính

11
GV. Vương Quốc Dũng
Biểu diễn số nguyên
Phần cứng của máy tính cần giới hạn độ lớn của một số để có thể lưu nó trong các thanh
ghi hay các ô nhớ. Do vậy các số nguyên được chia thành các số 8 bit, 16 bit, 32 bit, 64…
tương ứng chúng có MSB là bit 7, 15, 31, 63 và MSB đều là bit 0.
Số nguyên không dấu
Số nguyên không dấu (unsigned integer) biểu diễn các số nguyên dương. Số nguyên
không dấu rất thích hợp để biểu diễn các đại lượng luôn dương, chẳng hạn địa chỉ của ô
nhớ, bộ đếm hay, mã ASCII của ký tự (chúng ta sẽ thấy sau này). Bởi vì các số nguyên
không dấu được định nghĩa là các số nguyên dương, nên không cần dùng bit nào để biểu
diễn bit dấu, do đó 8 bit của một byte hay 16 bit của một word đều được dùng để biểu diễn
giá trị.
- Số nguyên không dấu lớn nhất có thể chứa trong một byte là:
11111111b = FFh = 255.
- Số nguyên không dấu lớn nhất có thể chứa trong một word là: 1111111111111111b
= FFFFh = 65535.
Chú ý rằng LSB = 1 thì số nguyên là số lẻ và ngược lại nó là số chẵn
Số nguyên có dấu
Số nguyên có dấu (signed integer) có thể là số dương hoặc âm. MSB được dùng để biểu
diễn dấu của số, MSB = 1 cho biết đó là số âm, MSB = 0 cho biết đó là số dương. Các số
nguyên có dấu được lưu trữ trong máy tính dưới dạng số bù 2.
Một ưu điểm của việc biểu diễn số âm trong máy tính bằng số bù 2 là phép trừ có thể
thực hiện bằng phương pháp lấy bù và phép cộng, các vi mạch thực hiện phép cộng và bù
các bit được con người thiết kế tương đối dễ dàng.
Biểu diễn số thực
Trong máy tính các số thực được biểu diễn và được viết dưới dạng dấu phảy
động (dạng có phần số mũ). Còn trong thực tế con người biểu diễn số thực ở 2
dạng:
- Dạng bình thường: 3.14, 3.0, -24.1234567, - 0.0002
Việc dùng dấu chấm trong cách viết số thực là cách viết của các nước Anh,
Mỹ.
- Dạng viết có phần mũ.
Để hiện thị được ra màn hình một số thực dạng bình thường hay dạng viết dấu
phảy động, phải thông qua giả lập bằng phần mềm.
Các dạng số dấu chấm động:
Đổi phần thập phân thành dạng nhị phân
Giả sử có phần thập phân là 0,d1d2d3…dn. Có dạng biểu diễn nhị phân là
0,b1b2b3…bm.
Bit b1 tương ứng với phần nguyên của tích: 0,d1d2d3…dn*2
Sau đây là thuật toán để đổi phần thập phân ra dạng nhị phân m chữ sô
X chứa phần thập phân cần đổi

12
GV. Vương Quốc Dũng
FOR i = 1 to m DO
Begin
Y=X*2
X = phần thập phân của Y
Bi = Phần nguyên của Y
End;
Các số dấu chấm (phảy) động
Dạng biểu diễn dấu phảy động, mỗi số được biểu diễn bằng 2 phần:
- Phần định trị (mantissa) chứa các bit của số
- Phần mũ (exponent) dùng để điều chỉnh lại vị trí của dấu phảy nhị phân
trong số đó.
Ví dụ : số thập phân 2,5 biểu diễn dạng nhị phân là 1,01b
Phần biểu diến dấu phảy động của nó có phần định trị là 1,01
Phần mũ là 1
Vì 10,1b có thể viết là 1,01b*21
Đối với các số khác 0, phần định trị được lưu như là một trị số <1 và < 2. Phần
định trị như vậy được gọi là phần định trị đã được chuẩn hóa
Đối với các số < 1, nếu chúng ta chuẩn hóa phần định trị thì phần mũ của chúng sẽ âm.
chẳng hạn số 0,0001b có dạng biểu diễn dấu phảy động
-4
là 1*2 . Vì phần mũ âm không được biểu diễn như số có dấu, nên với tất cả các số phần
mũ sẽ được tính lại thông qua các số có tên gọi là bias. bias sẽ được cộng thêm vào để tạo
7
ra phần mũ là một số dương. Ví dụ nếu ta chọn 8 bits cho phần mũ thì số 2 -1 = 127 sẽ
được chọn làm bias
Short real – dùng 4 byte để lưu trữ, 1 bit dấu, 8 bit phần mũ, 23 bit phần định trị. Không
chứa phần nguyên của phần định trị. Bias = 127.
Long real – dùng 8 byte để lưu trữ, 1 bit dấu, 11 bit phần mũ, 52 bit phần định trị.
Không chứa phần nguyên của phần định trị. Bias = 1023.
Temporary real – dùng 10 byte để lưu trữ, 1 bit dấu, 15 bit phần mũ, 64 bit phần định
trị. Gồm cả phần nguyên của phần định trị. Bias = 16383.
Ta xem cụ thể một loại số chấm động kiểu short real sau để thấy rõ.
Số dấu chấm (phảy) động kiểu short real
. Để biểu diễn số 0,0001b chúng ta có phần định trị là 1 và phần mũ là -4, Sau
khi cộng thêm 127, chúng ta nhận được phần mũ là 123 = 011111101b
Hình sau mô tả sắp xếp của một dấu phảy động 32 bits, trong đó S là một bit
dấu, exponent (phần mũ) chiếm 8 bits, mantissa (phần định trị) chiếm 23 bits

31 30 23 22 0
S Exponent Mantissa
Hình 1.1. Biểu diễn số dấu chấm (phảy) động 32 bit trong máy tính

13
GV. Vương Quốc Dũng
Ví dụ biểu diễn số 4,9 ở dạng dấu phảy động 32 bit (short real):
Áp dụng cách biểu diễn số dấu chấm động ở hình 1.1, chúng ta có:
4.9 = 100.1110011001100…
Sau khi chuẩn hóa ta có phần định trị là: 1.00111001100110011001100 và phần mũ
là 2. Cộng thêm bias vào phần mũ, ta nhận được 129 = 10000001b. Do phần nguyên trong
phần định trị không được lưu nên ta được số biểu diễn trên máy cho giá trị 4.9 là:
0 10000001 00111001100110011001100 hay bằng 409CCCCCh
Ví dụ biểu diễn số -0.75 ở dạng dấu phảy động 32 bit (short real):
Chúng ta có biểu diễn nhị phân của 0.75 = 0.11b, do đó –0.75 = -0.11b
Phần định trị sẽ được chuẩn hóa bằng 1.1
Phần mũ là -1, cộng với bias = 127, ta được phần mũ là 126 = 01111110b
Phần nguyên không được lưu nên ta có dạng short real của -0.75 là
1 01111110 10000000000000000000000 hay bằng BF400000h
Biểu diễn ký tự
Không phải mọi số liệu mà máy tính xử lý dều là các con sô. các thiết bị ngoại vi như
màn hình hay máy in đều so xu hướng làm việc với các ký tự. Ngoài ra các chương trình
như chương trình xử lý văn bản chuyên làm việc với dữ liệu kiểu ký tự. Cũng như tất cả
các dữ liệu khác, các ký tự cũng cần phải được mã hóa thành dạng nhị phân để máy tính có
thể xử lý chúng. Một kiểu mã hóa thông dụng nhất cho các ký tự đó là mã ASCII
(American Standard Code For Infermation Interchange – Mã chuẩn của Mỹ dùng để trao
đổi thông tin). Trước đây mã ASCII được sử dụng trong thông tin với các thiết bị teletype,
ngày nay mã ASCII được sử dụng trên tất cả các máy tính cá nhân.
Mã một byte

Hệ thống mã ASCII sử dụng 7 bit để mã hóa cho một ký tự, đo đó có tổng cộng 2 7 =
128 mã ASCII. Bảng 1.6 trình bày các mã ASCII và các ký tự tương ứng với chúng.
Chú ý rằng chỉ có 95 ký tự ứng với 95 mã ASCII từ 32 ÷ 126 là có khả năng hiển thị (in
được), các mã từ 0 ÷ 31 và mã 127 được dùng đến với các mục đích điều khiển quá trình
truyền thông, do đó không có khả năng in được. Hầu hết các máy vi tính chỉ sử dụng các
ký tự in được và một số ký tự điều khiển như CR (về đầu dòng), LF (xuống dòng), HT
(Tab), BS (xóa lùi), BEL (Đưa tiếng bip ra loa).

14
GV. Vương Quốc Dũng

Bảng 2.6. Bảng mã ASCII với 128 ký tự đầu.


Hexa- 0 1 2 3 4 5 6 7
decimal
0 <NUL> <DLE>
0 @ P ` p
0 16 32 48 64 80 96 112
1 <DC1>
! 1 A Q a q
1 17 33 49 65 81 97 113
2 <DC2>
“ 2 B R b r
2 18 34 50 66 82 98 114
3 ♥ <DC3>
# 3 C S c s
3 19 35 51 67 83 99 115
4 ♦ <DC4>
$ 4 D T d t
4 20 36 52 68 84 100 116
5 ♣
21 % 5 E U e u
5 37 53 69 85 101 117
6 ♠ <SYN>
& 6 F V f v
6 22 38 54 70 86 102 118
7 <BEL>
23 ‘ 7 G W g w
7 39 55 71 87 103 119
8 <BS> <CAN>
( 8 H X h x
8 24 40 56 72 88 104 120
9 <HT> <EM>
) 9 I Y i y
9 25 41 57 73 89 105 121
A <LF>
26 * : J Z j z
10 42 58 74 90 106 122
B <VT> <ESC>
+ ; K [ k {
11 27 43 59 75 91 107 123

15
GV. Vương Quốc Dũng
C <FF> <FS>
, < L \ l |
12 28 44 60 76 92 108 124
D <CR>
29 - = M ] Mm }
13 45 61 77 93 109 125
E <SO>
30 . > N ^ n ~
14 46 62 78 94 110 126
F <SI> <DEL>
31 / ? O _ o
15 47 63 79 95 111 127

Bảng 2.7. Bảng mã ASCII với 128 ký tự nửa sau.


Hexa- 8 9 A B C D E F
decimal
0 Ç É á α ≡
128 144 160 176 192 208 224 112
1 ü æ í β ±
129 145 161 177 193 209 225 241
2 é Æ ó γ ≥
130 146 162 178 194 210 226 242
3 â ô ú π ≤
131 147 163 179 195 211 227 243
4 ä ö Ñ ∑ ⌠
132 148 164 180 196 212 228 244
5 à ò Ñ σ ⌡
133 149 165 181 197 213 229 245
6 å Û ª µ ÷
134 150 166 182 198 214 230 246
7 ç ù º τ ≈
135 151 167 183 199 215 231 247
8 ê Ÿ ¿ Φ •
136 152 168 184 200 216 232 248
9 ë Ö θ ⋅
137 153 169 185 201 217 233 249
A è Ü ¬ Ω −
138 154 170 186 202 218 234 250

16
GV. Vương Quốc Dũng
B ï ¢ ½ δ √
139 155 171 187 203 219 235 251
C î £ ¼ ∞ ∧
140 156 172 188 204 220 236 252
D ì ¥ i φ ²
141 157 173 189 205 221 237 253
E Ä « ∈ ■
142 158 174 190 206 222 238 254
F Ă ƒ » ∩ 255
143 159 175 191 207 223 239

Vì mỗi ký tự ASCII được mã hóa bằng 7 bit nên mã của một ký tự chứa vừa vặn trong
một byte với MSB = 0. Các ký tự in được có thể hiển thị ra màn hình hoặc ra máy in, trong
khi đó các ký tự điều khiển lại dùng để điều khiển các thiết bị này. Ví dụ để hiển thị ký tự
A trên màn hình, chương trình sẽ gửi mã ASCII 41h đến màn hình, còn để lùi con trỏ trở
lại đầu dòng, chương trình sẽ gửi mã ASCII có giá trị 13 (0Dh – mã ASCII của ký tự điều
khiển CR) đến màn hình.
Ngoài ra máy tính cũng hiển thị một số ký tự đặc biệt ứng với một số mã ASCII không
in được. Như ta sẽ thấy bộ điều khiển màn hình của IBM PC có thể hiển thị một bộ ký tự
mở rộng 256 ký tự (bảng 2.7)

1.1.2.5. Mã hai byte


Unicode là viết tắt của từ Universal Code, tức là bộ mã vạn năng, có thể dùng để mã hoá
tất cả các ngôn ngữ chính trên toàn thế giới (trong đó có tiếng Việt), được dùng chủ yếu
trong trao đổi hiển thị dữ liệu trong các hệ thống công nghệ thông tin. Unicode còn là cộng
động của một số công ty hàng đầu trong lĩnh vực công nghệ thông tin như Microsoft, IBM,
Sun... được thành lập từ năm 1991 nhằm tạo ra một bộ mã dùng chung cho toàn thế giới.
Song song với tổ chức Unicode còn có tổ chức ISO (Tổ chức chuẩn quốc tế chính thống)
cũng nghiên cứu một bộ mã đa ngữ dùng trong CNTT là ISO /IEC 10646. Unicode và ISO
từ năm 1993 đã thống nhất cùng nhau phát triển và đồng nhất 2 bộ mã ở miền 16-Bit.
Unicode là bộ mã 16-Bit (có 65.536 ô mã)
Các định dạng biến đổi tập kí tự Unicode (UTF-Unicode Transformation Format) thực
hiện biến đổi mỗi giá trị Unicode thành một dãy giá trị mã duy nhất. Một UTF có thể xác
định một thứ tự byte cho việc tuần tự hoá các giá trị mã trong byte. UTF cũng có thể xác
định việc dùng dấu thứ tự byte. Unicode định nghĩa ra các định dạng biến đổi tập kí tự là
UTF-8, UTF-16, UTF-16LE, UTF-16BE, UTF-32.
Dạng mã hoá mặc định của chuẩn Unicode là 16-bit: các kí tự được gán một giá trị 16-
bit duy nhất, ngoại trừ các kí tự được mã hoá bằng các thay thế bao gồm một cặp hai giá trị

17
GV. Vương Quốc Dũng
16-bit. Dạng mã hoá 16-bit này chính là định dạng UTF-16. Trong UTF-16, các kí tự cho
tới 65 535 được mã hoá thành các giá trị 16-bit; các kí tự trên 65535 được mã hoá thành
các cặp giá trị 16-bit (thay thế).

1.1.3. Sơ đồ khối cấu trúc của IBM-PC.


Gồm 5 thành phần cơ bản:
Bộ xử lý (Processor).
Hệ thống nhớ (Memory).
Hệ thống vào ra (I/O System).
Bus liên kết hệ thống.
Chương trình.

Sơ đồ khối chung:

CPU Bộ nhớ chính

Bus liên kết hệ thống

Hệ thống vào ra
(Bàn phím , màn hình, ổ đĩa, chuột, ...
và các mạch ghép nối)

Máy tính có 1 bộ xử lý trung tâm (CPU) thì máy tính đó gọi là máy tính tuần
tự, hay còn gọi là máy tính Ven Newmann.
Máy tính có nhiều bộ xử lý gọi là máy tính song song.
Đơn vị xử lý trung tâm:
Chức năng: - Điều khiển hoạt động của hệ thống.
- Xử lý dữ liệu.
Nguyên tắc hoạt động:
Hoạt động theo chương trình nằm trong bộ nhớ, nhận lần lượt từng lệnh từ bộ nhớ,
giải mã để phát tín hiệu điều khiển thực hiện lệnh. Trong quá trình thực hiện
chương trình, nó trao đổi dữ liệu với bộ nhớ và các thiết bị vào ra.
Các thành phần cơ bản:

18
GV. Vương Quốc Dũng
Đơn vị điều khiển (Control Unit): điều khiển hoạt động của CPU và các thành
phần khác của máy tính.
Đơn vị số học và logic (Arithmetic & Logic Unit - ALU): thực hiện các chức năng
xử lý dữ liệu.
Các thanh ghi: Là các ngăn nhớ đặc biệt nằm trong CPU để chứa các thông tin tạm
thời phục vụ cho quá trình thực hiện chương trình.
Bus bên trong: Dùng để kết nối và trao đổi thông tin giữa các thành phần với nhau.
Hệ thống nhớ:
Chức năng: Dùng để nhớ chương trình và dữ liệu.
Chương trình là những lệnh yêu cầu máy tính phải thực hiện.
Dữ liệu là những gì mà chương trình tác động vào.
Các thành phần cơ bản:
Bộ nhớ chính: (Main Memory): Là thành phần nhớ được nối trực tiếp với CPU và
được điều khiển bởi CPU. Các chương trình đang thực hiện phải nằm trong bộ nhớ
chính.
. Bộ nhớ chính gồm các ngăn nhớ và mỗi ngăn nhớ có 1 địa chỉ xác định,
các ngăn nhớ được tổ chức theo Byte.
. Bộ nhớ chính có tốc độ cao, dung lượng nhỏ. Gồm:
ROM (Read Only Memory): Chứa thông tin cố định trong hệ thống.
RAM (Random Access Memory): Bộ nhớ tạm thời.
Bộ nhớ ngoài: Để lưu trữ các tài nguyên phần mềm của máy tính.
Về mặt tổ chức: được tổ chức như 1 thiết bị vào ra.
Bộ nhớ Cache: Bộ nhớ đệm truy nhập nhanh. Được đặt xen giữa bộ nhớ chính và
CPU để tăng tốc độ trao thông tin giữa CPU và hệ thống nhớ.
Hệ thống vào ra:
Chức năng:
Dùng để trao đổi thông tin giưa máy tính với thế giới bên ngoài.
Các thành phần:
Các thiết bị ngoại vi (các thiết bị vào ra): làm nhiệm vụ chuyển đổi thông tin ở
dạng vật lý nào đó về dạng dữ liệu phù hợp với máy tính.
Các mạch ghép nối vào ra: Các thiết bị ngoại vi không được nối ghép trực tiếp với
CPU mà phải thông qua các mạch ghép nối vào ra. Trong các mạch ghép nối vào
ra có các cổng vào ra và được đánh địa chỉ xác định. Các thiết bị vào ra được ghép
nối thông qua cổng.
Bus liên kết hệ thống:
* Định nghĩa Bus:
Là tập hợp các đường dây để vận chuyển thông tin (các Bit) từ phần mạch này đến
các phần mạch khác trong phạm vi máy tính.
Bit là từ viết tắt của ‘BInary digiT’.
Bản chất vật lý: Không có điện áp  truyền 0
Có điện áp  truyền 1

1 Bit (tại 1 thời điểm)

19
GV. Vương Quốc Dũng

Tập các đường dây vận chuyển thông tin đồng thời được gọi là độ rộng của
Bus (Ví dụ: 8 đường dây thì độ rộng là 8 Bit)
* Chức năng của Bus:
Bus chia thành 3 loại: - Bus địa chỉ.
- Bus dữ liệu.
- Bus điều khiển
Chỉ có Bus địa chỉ và Bus dữ liệu mới có khái niệm độ rộng.
Bus địa chỉ

CPU Bộ Mạch Các thiết


nhớ ghép nối bị vào ra
chính vào ra

Bus dữ liệu

Bus điều khiển

Lý do tồn tại của các loại Bus:


- Bus địa chỉ:
. CPU muốn trao đổi dữ liệu với ngăn nhớ nào, với cổng vào ra nào thì cần
phải có Bus địa chỉ.
. Bus địa chỉ vận chuyển địa chỉ từ CPU đến bộ nhớ hay cổng vào ra để xác
định ngăn nhớ nào hay cổng vào ra nào cần trao đổi thông tin.
. Bus địa chỉ nói tổng quát gồm n đường dây A 0 ÷ An-1 thì gọi độ rộng Bus
là n Bit và n Bit này được dùng để đánh địa chỉ, do đó có khả năng quản lý tối đa
n n
2 địa chỉ ngăn nhớ hay 2 Byte nhớ (vì bộ nhớ chính quản lý theo Byte).
Ví dụ:
8088/8086: Bộ vi xử lý có n = 20  quản lý tối đa 220 Byte = 1MB
80286:n = 24  quản lý tối đa 224 Byte = 24 x 220 = 16 MB.
80386:n = 32  quản lý tối đa 232 Byte = 22 x 230 = 4 GB.
Pentum II:n = 36  quản lý tối đa 236 Byte = 26 x 230 = 64 GB.
- Bus dữ liệu:
. Vận chuyển dữ liệu từ bộ nhớ đến CPU.
. Vận chuyển dữ liệu giữa các thành phần với nhau.
. Bus dữ liệu ký hiệu D0 ÷ Dm-1 thì độ rộng Bus là m Bit.
m thường là các giá trị: 8, 16, 32, 64.
Ví dụ:

20
GV. Vương Quốc Dũng
8088/86: m = 8 tức là vận chuyển 1 lúc 1 Byte.
80286: m = 16 tức là vận chuyển 1 lúc 2 Byte.
80386/486: m = 32 tức là vận chuyển 1 lúc 4 Byte.
Pentum II: m = 64 tức là vận chuyển 1 lúc 8 Byte.
- Bus điều khiển:
. Là tập hợp các tín hiệu điều khiển hoặc là phát ra từ CPU để điều khiển bộ
nhớ hay hệ thống vào ra, hoặc là từ bộ nhớ hay hệ thống vào ra đến yêu cầu CPU.
1.2. Tổ chức các thanh ghi của vi xử lý 8086.

Để làm việc được thì trong CPU cần có các thanh ghi. Thanh ghi chính là bộ nhớ
có tốc độ truy nhập cực nhanh, dùng để lưu trữ các giá trị địa chỉ và dữ liệu phục
vụ cho nhiệm vụ điều khiển và xử lý dữ liệu. Trong 8086 có:
4 thanh ghi đoạn.
3 thanh ghi con trỏ và 2 thanh ghi chỉ số
4 thanh ghi đa năng.
1 thanh ghi cờ
1.2.1. Các thanh ghi đoạn:
Khối BIU đưa ra trên Bus địa chỉ 20 Bit địa chỉ, như vậy 8086 có khả năng
phân biệt được 220 = 1.048.576 = 1M ô nhớ = 1 Mbyte.
Nói cách khác: Không gian địa chỉ của 8086 là 1 Mbyte. Trong không gian 1
Mbyte này, bộ nhớ cần được chia thành các vùng khác nhau (điều này có lợi
khi làm việc ở chế độ nhiều người sử dụng hoặc đa nhiệm) dành riêng để:
Chứa mã chương trình.
Chứa dữ liệu và kết quả trong không gian của chương trình.
Tạo ra vùng nhớ đặc biệt gọi là ngăn xếp (Stack) dùng vào việc quản lý các thông
số của bộ VXL khi gọi chương trình con hoặc trở về từ chương trình con.
Bộ vi xử lý 8086 có các thanh ghi 16 Bit liên quan đến địa chỉ đầu của các
vùng (các đoạn) kể trên và chúng được gọi là các thanh ghi đoạn (Segment
Registers). Đó là:
. CS - Code Segment: Thanh ghi đoạn mã.
. DS - Data Segment: Thanh ghi đoạn dữ liệu.
. SS - Stack Segment: Thanh ghi đoạn ngăn xếp.
. ES - Extra Segment: Thanh ghi đoạn dữ liệu phụ.
Các thanh ghi đoạn sẽ xác định địa chỉ của các ô nhớ nằm ở đầu đoạn. Địa
chỉ này gọi là địa chỉ cơ sở.
1.2.2. Các thanh ghi con trỏ và chỉ số:
Trong 8086 có 3 thanh ghi con trỏ và 2 thanh ghi chỉ số 16 Bit, các thanh ghi
này được ngầm định như là thanh ghi lệch cho các đoạn tương ứng, cụ thể:
. IP - Instruction Pointer (Con trỏ lệnh): IP luôn trỏ vào lệnh tiếp theo sẽ
được thực hiện nằm trong đoạn mã CS. Địa chỉ đầy đủ của lệnh tiếp theo
này ứng với: CS : IP.

21
GV. Vương Quốc Dũng
. BP - Base Pointer (Con trỏ cơ sở): BP luôn trỏ vào 1 dữ liệu nằm trong
đoạn ngăn xếp SS. Địa chỉ đầy đủ của 1 phần tử trong đoạn ngăn xếp ứng
với: SS : BP.
. SP - Stack Pointer (Con trỏ ngăn xếp): SP luôn trỏ vào đỉnh hiện thời của
ngăn xếp SS. Địa chỉ đầy đủ của đỉnh ngăn xếp ứng với: SS : SP.
. SI - Source Index (Chỉ số gốc/nguồn): SI trỏ vào 1 dữ liệu nằm trong đoạn
dữ liệu DS. Địa chỉ đầy đủ của 1 phần tử trong đoạn DS: DS : SI
. DI - Destination Index (Chỉ số đích): DI trỏ vào 1 dữ liệu nằm trong đoạn
dữ liệu DS. Địa chỉ đầy đủ cụ thể ứng với:
ES : DI

*Địa chỉ của các ô nhớ khác nằm trong đoạn được tính bằng cách cộng thêm
vào địa chỉ cơ sở 1 giá trị gọi là địa chỉ lệch hay độ lệch (địa chỉ offset /địa
chỉ tương đối). Độ lệch này được xác định bởi các thanh ghi 16 Bit khác
đóng vai trò thanh ghi lệch (offset register) - được nói rõ trong phần thanh
ghi con trỏ và chỉ số.
Cụ thể, để xác định địa chỉ vật lý 20 Bit của 1 ô nhớ cụ thể nào đó trong 1
đoạn bất kỳ, CPU 8086 phải dùng đến 2 thanh ghi 16 Bit (1 thanh ghi chứa
địa chỉ cơ sở, 1 thanh ghi chứa địa chỉ lệch). Từ nội dung của cặp thanh ghi
trên, tạo ra địa chỉ vật lý theo công thức sau:
Địa chỉ vật lý = thanh ghi đoạn *16 +thanh ghi lệch.
= segment * 16 + offset

Và địa chỉ logic được ký hiệu như sau:


Thanh ghi đoạn : thanh ghi lệch
Hay:
Segment : offset.
Địa chỉ kiểu Segment : offset là địa chỉ Logic vì nó tồn tại dưới dạng giá trị
của các thanh ghi cụ thể bên trong CPU. Khi cần thiết truy nhập ô nhớ nào đó,
CPU phải đổi địa chỉ logic này ra địa chỉ vật lý để rồi đưa lên Bus địa chỉ. Việc
chuyển đổi này do 1 bộ tạo địa chỉ thực hiện (Khối ∑ trong hình vẽ)
Riêng trong các lệnh thao tác với dữ liệu kiểu chuỗi thì cặp:
. ES:DI luôn ứng với phần tử thuộc chuỗi đích.
. DS:SI luôn ứng với phần tử thuộc chuỗi gốc (nguồn).
Ví dụ:
Cặp CS:IP sẽ chỉ ra địa chỉ của lệnh sắp thực hiện trong đoạn mã.
Nếu tại 1 thời điểm nào đó ta có: CS = F000h và IP = FFF0h thì:
địa chỉ logic CS : IP , tương ứng ta có địa chỉ vật lý:
F000h*16 + FFF0h = F0000h + FFF0h = FFFF0h.
Chú ý: Một ô nhớ có duy nhất một địa chỉ vật lý, nhưng ta có thể dùng nhiều địa
chỉ logic khác nhau để định vị cùng một ô nhớ.
Ví dụ: Hai địa chỉ logic FFF0:00F0

22
GV. Vương Quốc Dũng
FF00:0FF0
cùng xác định ô nhớ có địa chỉ vật lý là FFFF0h.
1.2.3. Các thanh ghi đa năng:
Trong khối EU có 4 thanh ghi đa năng 16 Bit: AX, BX, CX, DX, điều đặc
biệt là khi chứa các dữ liệu 8 Bit thì mỗi thanh ghi này có thể tách ra thành 2
thanh ghi 8 Bit cao và 8 Bit thấp để làm việc độc lập. Đó là các cặp thanh
ghi AH & AL, BH & BL, CH & CL, DH & DL.
(trong đó H - chỉ phần cao ; L chỉ phần thấp).
. AX (Accumulater, Acc - thanh chứa): Các kết quả của thao tác được chứa
ở đây (Kết quả của phép *, / ,...).
. BX (Base - thanh ghi cơ sở): Thường chứa địa chỉ cơ sở của 1 bảng dùng
trong lệnh XLAT.
. CX (Count - Bộ đếm): CX thường được dùng để chứa số lần lặp trong
trường hợp các lệnh LOOP (lặp). Còn CL chứa số lần dịch hoặc quay trong
các lệnh dịch hoặc quay thanh ghi.
. DX (Data - thanh ghi dữ liệu): DX cùng AX tham gia vào các thao tác của
phép * hoặc / các số 16 Bit.
DX còn dùng để chứa địa chỉ của các cổng trong các lệnh vào/ra dữ liệu
trực tiếp (IN/ OUT).
1.2.4. Thanh ghi cờ (Flag Register - FR):
Đây là thanh ghi khá đặc biệt trong CPU, mỗi Bit của nó dùng để phản ánh 1
trạng thái nhất định của kết quả phép toán do ALU thực hiện hoặc một trạng
thái hoạt động của EU gọi là các Bit cờ.
Dựa vào các cờ này người lập trình có thể có các lệnh thích hợp tiếp theo
cho bộ vi xử lý (các lệnh có điều kiện). Thanh ghi cờ gồm 16 Bit nhưng
người ta chỉ dùng hết 9 Bit của nó để làm các Bit cờ.

x x x x O D I T S Z x A x P x C

x : Bit không được định nghĩa.


Các cờ cụ thể:
. C hoặc CF (Carry Flag): Cờ nhớ.
CF = 1 khi có nhớ hoặc mượn từ MSB sang.
(MSB - Most Significant Bit: Bit có ý nghĩa nhất).
. P hoặc PF (Parity Flag): Cờ chẵn lẻ, phản ánh tính chẵn lẻ của tổng số Bit 1
trong kết quả.
PF = 1 khi tổng số Bit 1 trong kết quả là chẵn (even parity).
. A hoặc AF (Auxiliary Flag): Cờ nhớ phụ, rất có ý nghĩa khi ta làm việc với
1 số BCD.
AF = 1 khi có nhớ hoặc mượn từ 1 số BCD thấp (4 Bit thấp) sang 1 số
BCD cao (4 Bit cao).
. Z hoặc ZF (Zero Flag): Cờ Zero, ZF = 1 khi kết quả = 0.
. S hoặc SF (Sign Flag): Cờ dấu, SF = 1 khi kết quả âm.

23
GV. Vương Quốc Dũng
. O hoặc OF (Overflow Flag): Cờ tràn, OF = 1 khi kết quả là 1 số bù 2
vượt ra ngoài giớ hạn biểu diễn dành cho nó.
Trên là 6 Bit cờ trạng thái phản ánh các trạng thái khác nhau của kết quả
sau một thao tác nào đó. Chúng được lập hoặc xóa tùy theo điều kiện cụ thể
sau các thao tác của ALU. Ngoài ra 8086 còn có 3 cờ điều khiển (các cờ này
được lập, xóa bằng các lệnh riêng):
. T hoặc TF (Trap Flag): Cờ bẫy, TF = 1 khi CPU làm việc ở chế độ chạy
từng lệnh (Chế độ này dùng khi cần tìm lỗi của 1 chương trình).
. I hoặc IF (Interrupt enable Flag): Cờ cho phép ngắt, IF = 1 thì CPU cho
phép các yêu cầu ngắt (ngắt che được) được tác động.
. D hoặc DF (Direction Flag): Cờ hướng, DF = 1 khi CPU làm việc với 1
chuỗi ký tự từ phải sang trái (DF còn gọi là cờ lùi).
* ý nghĩa của các cờ khá rõ ràng; Riêng cờ tràn cần làm rõ hơn để hiểu rõ
bản chất và cơ chế làm việc của nó.
Giả thiết ta làm việc với số bù 2 dài 8 Bit, kết quả để ở AL. Gọi C 6,7 là cờ nhớ từ
Bit 6 lên Bit 7 (b7), trong đó b7 là MSB và cũng chính là Bit dấu (SF) của AL
CF b7 b6 AL b0

C6,7

Quan hệ giữa cờ OF với cờ C6,7 tuân theo phương trình sau:


OF = CF ⊕ C6,7
Nghĩa là: khi thực hiện các phép toán với số tính theo mã bù 2 (số có dấu),
hiện tượng tràn sẽ xảy ra (Cờ OF = 1) nếu có nhớ từ MSB (tức là SF) sang CF
nhưng ngược lại không có nhớ vào chính nó (SF) hoặc ngược lại.
Ví dụ:

01111111 = 127 10000000 = - 128


+ +
00000001 = 1 10000001 = - 127
10000000 = - 128 (Kq sai) 1 00000000 = 000 (Sai)

Overflow Carry Out Overflow


(Nhớ vào SF, xong không có (Nhớ vào CF, xong không có
nhớ vào CF - Kết quả sai: Tổng nhớ vào SF - Kết quả sai:
hai số dương bằng số âm) Tổng hai số âm bằng 1 số
ở đây: C6,7 = 1 (có nhớ từ bit 6 dương)
lên bit 7) ở đây: C6,7 = 0 (không có nhớ
CF = 0 (không có nhớ từ bit 6 lên bit 7)
từ MSB sang) CF = 1 (có nhớ từ
do vậy: OF = CF ⊕ C6,7 = 1 MSB sang)

24
GV. Vương Quốc Dũng
do vậy: OF = CF ⊕ C6,7 = 1

1.3. Cơ chế quản lý bộ nhớ.


1.3.1. Tổng quan về hệ thống nhớ:
1.3.1.1. Các đặc trưng của hệ thống nhớ:
+ Vị trí:
Bên trong CPU: Các thanh ghi mà người lập trình có thể can thiệp được.
Bộ nhớ trong: . Bộ nhớ chính.
. Bộ nhớ Cache.
Bộ nhớ ngoài:
+ Dung lượng:
Độ dài của từ nhớ (Bit): 8 Bit hay 16 Bit (có thể là Byte).
Số lượng từ nhớ.
+ Đơn vị trao đổi:
Theo từ nhớ.
Theo Block nhớ.
+ Phương pháp truy cập:
Tuần tự.
Trực tiếp.
Ngẫu nhiên.
+ Hiệu năng (Performance):
Thời gian truy nhập.
Chu kỳ truy nhập.
Tốc độ truyền (Bps - Bit/sec).
+ Các loại bộ nhớ vật lý:
Bộ nhớ từ.
Bộ nhớ quang.
Bộ nhớ bán dẫn.
+ Các đặc trưng vật lý:
Xoá được và không xoá được.
Khả biến và không khả biến.
. Khả biến: Mất nguồn thì mất thông tin.
. Không khả biến: Mất nguồn thì không mất thông tin (VD: Đĩa từ).
+ Tổ chức bộ nhớ.
+ Giá thành.
1.3.1.2. Mô hình phân cấp hệ thống nhớ:
DRAM
Bộ vi xử lý (CPU) SRAM

Cache Bộ nhớ Bộ nhớ


Tập Cache L2 chính ngoài
thanh L1 (Thứ cấp)
ghi (Sơ cấp)
25
GV. Vương Quốc Dũng

Đường chấm gạch thể hiện: Cache L2 có thể đưa 1 phần vào bộ vi xử lý,
hoặc có thể đưa toàn bộ vào bộ vi xử lý.
Qui luật mô hình phân cấp hệ thống nhớ:

Dung lượng , thời gian truy nhập (tốc độ )


Tần xuất truy nhập bởi CPU , giá thành 1 Bit

1.3.2. Bộ nhớ bán dẫn:


Bộ nhớ bán dẫn gồm 2 loại là ROM và RAM.
1.3.2.1. ROM - Read Only Memory:
Có 5 loại khác nhau.
ROM mặt nạ: Khi chế tạo ghi luôn thông tin cho nó và vĩnh viễn nội dung thông
tin đó không thay đổi được.
PROM - Programmable ROM: Khi chế tạo, không có thông tin, xong có thể ghi
được thông tin vào 1 lần bằng thiết bị chuyên dùng, nếu ghi sai thì vứt bỏ.
EPROM - Erasable PROM:
. Ghi thông tin được nhiều lần bằng thiết bị chuyên dùng.
. Trước khi ghi phải xóa nội dung cũ bằng tia cực tím.
EEPROM - Electrically EPROM:
. Giống RAM nhưng mất nguồn thì không mất thông tin.
. Cần đọc hay ghi thông tin thì đều được ngay. Nó ghi thông tin theo từng
Byte. Thông tin ghi lần cuối không bị mất khi mất nguồn.
Flash Memory (bộ nhớ tia chớp): Giống EEPROM, chỉ cho phép đọc cả khối, ghi
cả khối, chế tạo với dung lượng lớn - ngày nay ít sử dụng. Có 1 thời kỳ làm bộ nhớ
ngoài cho máy tính xách tay.
1.3.2.2. RAM - Random Access Memory:
Đã là bộ nhớ bán dẫn thì đều là bộ nhớ truy nhập ngẫu nhiên, tức là thời gian
truy nhập trên tất cả các ô nhớ đều bằng nhau khi có địa chỉ phát ra.
Bản chất của bộ nhớ này là Read Write Memory tức là bộ nhớ ghi dọc chứ
không phải là bộ nhớ truy nhập ngẫu nhiên.
RAM có 2 loại chính:
SRAM (Static RAM - RAM tĩnh):
. Thông tin ổn định.
. Tốc độ nhanh.

26
GV. Vương Quốc Dũng
. Dung lượng IC nhỏ.
. Giá thành cao.
Trong máy tính nó được dùng làm Cache L2.
DRAM (Dynamic RAM - RAM động):
. Thông tin không ổn định, do đó cần có mạch làm tươi để ổn định (mạch
bù đắp điện áp bị sụt).
. Tốc độ chậm.
. Dung lượng IC lớn.
. Giá thành thấp.
DRAM được dùng để thiết kế ra bộ nhớ chính, nó có 2 loại phổ biến:
. EDO-DRAM (Extended Data Output DRAM): được cải tiến ở mạch ra
dữ liệu để truy nhập nhanh.
. SDRAM (Symchronous DRAM): Loại này có chân để đưa tín hiệu
xung Clock vào nhằm tạo ra sự đồng bộ theo đầu vào của tín hiệu Clock,
do đó phân mức thực hiện đồng thời kiểu Pipeline.

Clock

1.3.2.3. Bộ nhớ Cache:


Nguyên tắc chung: Cache được đặt giữa 2 mức nhớ có tốc độ khác nhau để
nhằm tăng tốc độ xử lý.

Mức nhớ A Cache Mức nhớ B

Tốc độ 2 mức nhớ: A >> B


Tốc độ: A >> Cache >> B
Dunglượng: Cache << B
Vị trí đặt Cache:
Cache có thể ở giữa CPU và bộ nhớ chính.
Cache có thể ở giữa bộ nhớ chính và bộ nhớ ngoài.
Trường hợp Cache giữa CPU và bộ nhớ chính:
- Tốc độ: CPU >> Cache >> bộ nhớ ngoài.
- Dung lượng: Cache << bộ nhớ ngoài.
1.3.2.4. Bộ nhớ ngoài:
Đĩa từ: Đặc trưng bởi các thông số sau.
- Đầu từ: Tiếp xúc hay không tiếp xúc.
Số lượng đĩa: 1 đĩa hay nhiều đĩa.
Số lượng đầu từ trên một mặt:
. 1 đầu từ trên 1mặt.

27
GV. Vương Quốc Dũng
. Nhiều đầu từ trên 1mặt: tăng tốc độ truy nhập vì giảm hành trình dịch
chuyển của đầu từ.
Số track trên 1 mặt.
Số sector trên 1 track.
Dung lượng của 1 sector.
Tốc độ quay (vòng/ph).
Tốc độ truyền: Tốc độ trao đổi thông tin với bên ngoài.
Đĩa quang:
CD-ROM.
CD-WORM: Có khả năng ghi 1 lần/1 đĩa và không xóa được.
Đĩa Magneto-Optical CD: Đĩa quang từ, có thể ghi đọc nhiều lần nên được dùng
tương tự như đĩa cứng hoặc đĩa mềm.

1.3.3. Cơ chế quản lý bộ nhớ của 8086.


Bộ nhớ chính của 8086/8088 là một dãy các bytes liên tiếp nhau được đánh số
từ 0 ÷ 220-1 (00000h ÷ FFFFFh). Chỉ số thứ tự đó được gọi là địa chỉ tuyệt đối
và sử dụng 20 bits địa chỉ để đánh địa chỉ. Bộ VXL 80286 có 24 bits địa chỉ,
cho phép bộ nhớ lên tới 16 MB. Bộ VXL 80386 có 32 bits địa chỉ cho phép bộ
nhớ lên tới 4GB.
Để tận dụng bộ nhớ và thuận lợi trong xử lý, người ta chia bộ nhớ thành từng
đoạn (segment), mỗi đoạn bắt đầu từ địa chỉ chia hết cho 16 và có chiều dài tối
đa là 64 KB, địa chỉ trong một đoạn được đánh số từ 0 ÷ 216-1 và gọi là địa chỉ
offset. Lúc đó vị trí của một byte trong bộ nhớ được xác định bằng cặp giá trị
segment:offset, cặp giá trị này gọi là địa chỉ logic, trong đó segment số nhị
phân 16 bits biểu diễn địa chỉ tuyệt đối cho đầu của mỗi đoạn là segment*16,
offset cũng là 1 số nhị phân 16 bits biểu diễn địa chỉ tương đối (địa chỉ lệch /
địa chỉ hiệu dụng) trong mỗi đoạn.
Bộ Địa
VXLchỉ8086 có khả năng định
vật lý Địa địa
chỉ chỉ
đoạntới(segment)
1 MB bộ nhớ, tuy nhiên toàn bộ bộ
nhớ không phải chỉ giành cho các chương trình ứng dụng. Chẳng hạn:
1 KB đầuFFFFFh
tiên giành cho bảng vector ngắt.
Một số vùng nhớ ≠ giành riêng cho các mục đích Mỗiđặcđoạn có chứa
biệt như khôngcác chương
F0000hcủa bộ nhớ mànF000h
trình của BIOS, gian nhớ
hình (Video display là 64- Bộ
memory) KBnhớvì màn hình
chứa các EFFFFh
thông tin sẽ được hiển thị lên màn hình.các thanh ghi biểu diễn
Để trình bày bản đồ bộ nhớ của 8086, ta phân bộgiá nhớtrị địacác
thành chỉđoạn
lệch
tách rời nhau,
E0000h E000h
mỗi đoạn có không gian nhớ là 64 KB, Ví dụ: trong đoạn (offset) là
DFFFFh các thanh ghi 16 bit,
nên có chỉ có thể đánh
địa chỉ trong một đoạn
từ 0 ÷ FFFFh, tức tối đa
là FFFFh Bytes = 64
20000h 2000h
KB
1FFFFh

10000h 1000h
0FFFFh 28

00000h 0000h
GV. Vương Quốc Dũng

Địa chỉ bắt đầu có thể cho mỗi đoạn:

Hoặc Đoạn 1

Đoạn 1
48

32

16

Ta thấy các đoạn có thể chồng lên nhau, có thể nối tiếp nhau, có thể tách rời nhau.
Việc phân bổ bộ nhớ thành từng đoạn làm cho việc tổ chức chương trình và dữ liệu
khá mềm dẻo. Chương trình không cần phải viết như một dãy liên tục các chỉ thị
(instruction) và dữ liệu, mà có thể là các đơn thể (module) gồm mã và dữ liệu.
Dữ liệu cũng có thể tổ chức thành các cấu trúc dữ liệu khác nhau: dữ liệu dùng
riêng, dữ liệu dùng chung với các chương trình khác trong hệ thống. Mỗi đơn thể
mã và dữ liệu này có thể có kích thước khác nhau.

29
GV. Vương Quốc Dũng

Bản đồ bộ nhớ của 8086:

Vùng nhớ Địa chỉ vật lý


BIOS F0000h
Để giành E0000h
Để giành D0000h
Để giành C0000h
Video B0000h
Video A0000h

Vùng giành cho các ứng dụng khác

DOS
BIOS & dữ liệu của DOS 00400h
Các véc tơ ngắt 00000h

1.4. Cơ chế thực hiện chương trình


Trong hệ thống 8086, CPU có 3 hoạt động cơ bản là:
- Thực hiện chương trình
- Xử lý ngắt.
- Chuyển nhượng BUS
Nội dung chính của phần này giúp sinh viên nắm được hoạt động thực hiện
chương trình của CPU, xong tài liệu cũng đưa thêm nội dung hoạt động xử lý
ngắt và chuyển nhượng BUS nhằm giúp sinh viên nắm được tổng quan về hoạt
động của CPU.
1.4.1. Thực hiện chương trình:
+ Nhận lệnh (Instruction Fetch):
Địa chỉ của lệnh cần nhận nằm ở PC sẽ được chuyển qua Bus địa chỉ để đưa đến bộ
nhớ tìm ra ngăn nhớ chứa lệnh.
CPU phát ra tín hiệu điều khiển đọc ngăn nhớ (MEMR).
Nội dung của lệnh được chuyển qua Bus dữ liệu đưa vào thanh ghi lệnh (có thể chỉ
nhận mã lệnh).
+ Giải mã lệnh (Instruction Decode):
Mã lệnh từ thanh ghi lệnh được chuyển đến đơn vị điều khiển và ở đó lệnh được
giải mã để xác định hành động của lệnh.

30
GV. Vương Quốc Dũng
+ Nhận dữ liệu (Data Fetch):
Lệnh có thể yêu cầu nhận dữ liệu từ bộ nhớ hay cổng vào ra đưa vào CPU.
+ Xử lý dữ liệu (Data Processing):
Nếu lệnh yêu cầu thực hiện 1 phép toán nào đó thì dữ liệu sẽ chuyển vào ALU. Ở
đó phép toán sẽ được thực hiện và kết quả tạm thời sẽ được cất trong thanh ghi dữ
liệu.
+ Cất dữ liệu (Data Saving):
Phát ra được tìm ra nơi cất.
Đưa dữ liệu ra ngoài.
Phát ra tín hiệu điều khiển ghi.
Nếu hệ thống xử lý tuần tự thì tốc độ chậm, do đó ngày nay áp dụng kỹ thuật
Pipeline (Kỹ thuật xử lý đường ống trong các bộ vi xử lý) để nâng cao tốc
độ xử lý.
1.4.2. Xử lý ngắt.
1.4.2.1. Ngắt (Interrupt):
Ngắt là cơ chế CPU tạm dừng công việc hiện tại để sang thực hiện công việc
khác (với những sự kiện không biết trước).
Trong thực tế có 2 loại ngắt sau:
+ Ngắt do tín hiệu điều khiển từ bên ngoài gửi đến CPU (ngắt cứng)
Ngắt không che được: Bắt buộc CPU phải ngắt (VD: báo lỗi phần cứng).
Ngắt che được (ngắt vào ra): Còn tùy thuộc vào số hiệu ngắt hay mức độ ưu tiên
mà CPU có chấp nhận ngắt hay không.
+ Ngắt do lỗi khi thực hiện chương trình (ngoại lệ):
Ví dụ: chia chi 0, tràn số,...
Lúc này máy chuyển sang chương trình con báo lỗi.
1.4.2.2. Đáp ứng của CPU khi có yêu cầu ngắt.
Khi có yêu cầu ngắt kiểu N (N là số hiệu ngắt) đến CPU và nếu yêu cầu đó
được phép, CPU thực hiện các công việc sau:
1. Giảm giá trị con trỏ ngăn xếp đi 2 và nạp thanh ghi cờ vào mảng nhớ ngăn
xếp có địa chỉ trên.
(* SP← SP- 2, {SP}← FR: Trong đó {SP} ô nhớ do SP chỉ ra * )
Tức là chỉ ra đỉnh mới của ngăn xếp và cất thanh ghi cờ vào đỉnh ngăn xếp.
2. Xóa cờ ngắt và xóa cờ bẫy
(* IF ← 0, TF ← 0 * )
Tức là cấm các ngắt khác tác động vào CPU, cho CPU chạy ở chế độ bình
thường.
3. Giảm giá trị con trỏ ngăn xếp đi 2 và nạp (PUSH) nội dung của thanh ghi
đoạn mã (CS) hiện hành vào mảng nhớ ngăn xếp có địa chỉ trên.
(* SP ← SP – 2, {SP} ← CS * )

31
GV. Vương Quốc Dũng
Tức là chỉ ra đỉnh mới của ngăn xếp, cất địa chỉ đoạn của địa chỉ trở về vào
đỉnh ngăn xếp.
4. Giảm giá trị con trỏ ngăn xếp đi 2 và nạp (PUSH) nội dung của thanh ghi
con trỏ lệnh (IP) hiện hành vào mảng nhớ ngăn xếp có địa chỉ trên (để nhớ
nơi chương trình bị gián đoạn).
(* SP ← SP – 2, {SP} ← IP * )
Tức là chỉ ra đỉnh mới của ngăn xếp, cất địa chỉ lệch của địa chỉ trở về vào
đỉnh ngăn xếp.
5. Lấy lệnh tại địa chỉ mới của chương trình con phục vụ ngắt kiểu N tương
ứng trong bảng véc tơ ngắt
{N* 4} → IP, {N* 4 + 2} → CS
6. Tại cuối chương trình phục vụ ngắt, khi gặp lệnh IRET bộ vi xử lý 8086
quay trở lại chương trình chính tại địa chỉ trở về và với giá trị cũ của thanh
ghi cờ được lấy ra từ ngăn xếp:
- Lấy lại địa chỉ lệch của địa chỉ trở về từ đỉnh ngăn xếp
{SP} → IP
- Lần lượt tăng giá trị con trỏ ngăn xếp lên 2 (* SP← SP + 2 *)
-- {SP}→ CS, SP ← SP + 2
-- {SP}→ FR, SP ← SP + 2
* Về mặt cấu trúc chương trình, khi có ngắt xảy ra thì CTC (Chương trình
chính) liên hệ với CTcPVN (chương trình con phục vụ ngắt) được mô tả như
sau:
CTC
(Thân CTC)
CPU tự + Cất thanh ghi FR CTcPVN
+ Xóa IF và TF Lệnh cất các thanh ghi vì không
+ Cất CS và IP muốn bị thay đổi bởi CTc
+ Lấy địa chỉ chương
trình con phục vụ ngắt (Thân CTcPVN)
CPU tự + Lấy lại IP, CS
+ Lấy lại thanh ghi FR Lệnh lấy lại các thanh ghi đã cất

IRET

Trong thực tế các ngắt mềm INT N đã bao trùm các loại ngắt khác bởi Intel đã
qui định 1 số kiểu ngắt đặc biệt được xếp vào đầu dãy ngắt mềm INT N như
sau:
+ INT 0: Ngắt mềm do phép chia cho số 0 gây ra.
+ INT 1: Ngắt mềm để chạy từng lệnh ứng với trường hợp cờ TF = 1

32
GV. Vương Quốc Dũng
+ INT 2: Ngắt mềm do tín hiệu tích cực tại chân NMI gây ra.
+ INT 3: Ngắt mềm để đặt điểm dừng của chương trình tại 1 địa chỉ nào đó.
+ INT 4: (hoặc lệnh INTO): ngắt mềm ứng với trường hợp cờ tràn OF = 1.
Các kiểu ngắt khác còn lại thì được giành cho Intel và cho user (IBM không
hoàn toàn tuân thủ các qui định này khi chế tạo máy PC/XT và PC/AT)
+ INT 5 – INT 1FH: dành riêng cho Intel trong các bộ vi xử lý cao cấp khác.
+ INT 20H- INT 0FFH: dành cho user.
Các kiểu ngắt N trong INT N đều tương ứng các địa chỉ xác định của
CTcPVN mà ta có thể tra được trong bảng các véc tơ ngắt.

03FEH – 03FFH CS của CTcPVN INT FFH


03FCH – 03FDH IP của CTcPVN INT FFH

0082H – 0083H CS của CTcPVN INT 20H


0080H – 0081H IP của CTcPVN INT 20H

000AH – 000BH CS của CTcPVN INT2


0008H – 0009H IP của CTcPVN INT2
0006H – 0007H CS của CTcPVN INT1
0004H – 0005H IP của CTcPVN INT 1
0002H – 0003H CS của CTcPVN INT0
0000H – 0001H IP của CTcPVN INT0
(Xem phụ lục A: các ngắt của DOS và BIOS/ 320 sách Kỹ thuật VXL của
Thầy Văn Thế Minh_ĐHBKHN).
Intel qui định bảng này nằm trong RAM bắt đầu từ địa chỉ 0000H đến địa
chỉ 0400H (dài 1 KB).
(Vì 8086 có 256 kiểu ngắt, mỗi kiểu ứng 1 véc tơ ngắt, 1 véc tơ ngắt cần 4
byte để chứa địa chỉ đầy đủ cho SC:IP của CTcPVN:
4 x 256 = 1024 byte = 1 KB)
1.4.2.3. Xử lý ưu tiên ngắt.

Trong thực tế tại cùng 1 thời điểm có nhiều yêu cầu ngắt thuộc các loại ngắt
khác nhau cùng đòi hỏi CPU phục vụ thì CPU xử lí các yêu cầu ngắt đó như thế
nào? CPU phải có chính sách xử lí ngắt theo luật ưu tiên.
- Các yêu cầu ngắt được sắp xếp theo 1 thứ tự ưu tiên với nguyên tắc: ngắt
nào có mức ưu tiên cao nhất sẽ được CPU nhận biết và phục vụ trước.
Ngay từ khi chế tạo (thường được gọi là ngầm định) CPU 8086 có khả năng
phân biệt các mức ưu tiên khác nhau cho các loại ngắt (theo thứ tự từ cao
xuống thấp như sau):

+ Ngắt nội bộ: INT 0 (phép chia 0) Cao nhất

33
GV. Vương Quốc Dũng
INTN, INTO
+ Ngắt không che được NMI
+ Ngắt che được INTR
+ Ngắt để chạy từng lệnh INT 1 } Thấp nhất

Để thấy rõ hiện tượng của CPU trong cơ chế ngắt ưu tiên này ta có thể lấy 1 ví dụ
cụ thể sau:
Giả thiết tại 1 thời điểm nào đó, trong khi CPU (ở trạng thái cho phép ngắt
với cờ IF = 1) đang thực hiện phép chia và có lỗi xảy ra do
số chia = 0, đúng lúc đó CPU cùng nhận được yêu cầu ngắt từ đầu vào
INTR, CPU sẽ xử lí như sau:
Theo thứ tự ưu tiên ngầm định trong việc xử lý ngắt của CPU 8086 thì INT
0 có mức ưu tiên cao hơn INTR vì vậy đầu tiên CPU sẽ thực hiện chương
trình con phục vụ ngắt INT 0 để đáp ứng với lỗi đặc biệt do phép chia cho 0
gây ra và cờ IF bị xóa về 0. Yêu cầu ngắt INTR sẽ tự động bị cấm cho tới
khi chương trình con phục vụ ngắt INT 0 được hoàn tất và trở về nhờ lệnh
IRET, cờ IF cũ được trả lại (IF = 1). Tiếp theo đó CPU sẽ đáp ứng yêu cầu
ngắt INTR bằng cách thực hiện chương trình con phục vụ ngắt dành cho
INTR.

1.4.3. Chuyển nhượng Bus:


Là trường hợp CPU trao quyền điều khiển hệ thống cho 1 mạch điều khiển
khác và khi đó CPU thả nổi Bus (Mạch CPU ở trạng thái trở kháng cao về
mặt logic). CPU chỉ giữ lại một số BUS điều khiển cơ bản, để khi cần thì
phát tín hiệu điều khiển lấy lại quyền sử dụng BUS.

CÂU HỎI VÀ BÀI TẬP


1. Nêu chức năng của:
- Bộ vi xử lý
- Các loại BUS
2. Trình bày đặc điểm chức năng của các thanh ghi con trỏ và chỉ số trong vi xử lý
8086.
3. Trình bày đặc điểm, chức năng của các thanh ghi đoạn trong vi xử lý 8086.
4. Trình bày đặc điểm, chức năng của các thanh ghi đa năng trong vi xử lý 8086.
5. Trình bày đặc điểm, chức năng của thanh ghi cờ cùng các bít cờ.
6. Giả sử các byte trong bộ nhớ có địa chỉ từ 0 đến 4 có nội dung như sau:

Địa chỉ Nội dung


0 01101010
1 11011011
2 11100001
3 00011110

34
GV. Vương Quốc Dũng
4 10001101
a. Cho biết nội dung của word (từ nhớ) ở địa chỉ 2
b. Cho biết nội dung của word (từ nhớ) ở địa chỉ 3
c. Cho biết nội dung của word (từ nhớ) mà byte cao của nó ở địa chỉ 2
d. Cho biết giá trị của bit 7 của byte 2.
e. Cho biết giá trị của bit 4 của byte 2.
f. Cho biết giá trị của bit 4 của byte 3.
g. Cho biết giá trị của bit 11 của word 2.
7. Sử dụng số liệu của bài 6, cho biết nội dung của:
a. nibble thấp của byte 1
b. nibble cao của byte 4
8. Giả sử một lệnh chuyển nội dung của thanh ghi AX vào một từ nhớ trong bộ
nhớ máy tính. Những gì sẽ xảy ra trong chu kỳ:
- nhận lệnh
- thực hiện lệnh
9. Có 2 loại bộ nhớ ROM và RAM, cho biết loại bộ nhớ nào:
a. Chứa chương trình của người sử dụng.
b. Chứa chương trình để khởi động máy.
c. Có thể thay đổi bởi người sử dụng.
d. Giữ được dữ liệu kể cả khi đã tắt máy.
10. Đổi các số nhị phân và số hex sau ra số thập phân:
a. 1110b
b. 100100111110b
c. 46Ah
d. FEA7Dh
11. Đổi các số thập phân sau ra số nhị phân và hex: 97, 629, 973, 1026.
12. Đổi các số nhị phân sau ra các số hệ10 và hex:
a. 111001010101
b. 111001010101111001010101
c. 101000101101
d. 1011001101001101
13. Thực hiện phép cộng sau:
a. 10010101b + 01110110b
b. 111001010101111001010101b + 10010101b
c. B23CDh + 9F7Eah
d. FEEFCh + ABCDEh
14. Thực hiện các phép trừ sau:
a. 10010101b - 01110110b
b. 111001010101111001010101b - 10010101b
c. B23CDh - 9F7Eah
d. FEEFCh - ABCDEh
15. Đổi các số nguyên thập phân sau ra số hex 16 bit: 234, -16, -32216, 31634,
7899, 65356.

35
GV. Vương Quốc Dũng
16. Thực hiện phép trừ các số nhị phân và số hex dưới đây bằng cách cộng với số
bù 2 của số trừ:
a. 10010101b - 01110110b
b. 111001010101111001010101b - 10010101b
c. B23CDh - 9F7Eah
d. FEEFCh - ABCDEh
17. Đổi các số hệ hex sau ra số thập phân có dấu và không dấu:
a. 7FFEh
b. 8543h
c. FEh
d. 7Fh
18. Đổi số -120 ra số nhị phân 16 bit và số nhị phân 8 bit.
19. Cho biết các số thập phân sau, có thể lưu được hay không trong (a) như một số
16 bit; (b) như một số 8 bit
a. 32767
b. -40000
c. 65536
d. 257
e. -128
20. Cho biết các số có dấu sau, số nào là âm hay dương:
a. 1010101010100010b
b. 789Ah
c. F765h
d. 897Fh
e. 9DCBh
21. Giả sử chuỗi “S12.75” đang lưu trong bộ nhớ bắt đầu tại địa chỉ 0, cho biết nội
dung của các byte từ 0 đến 5 dưới dạng số hex.
22. Hãy dịch thông điệp đã mã hóa dưới dạng mã ASCII sau đây:
41 74 74 61 63 6B 20 61 77 6E
23. Giả sử một byte có mã ASCII của một chữ hoa, hỏi phải cộng thêm một số hex
là bao nhiêu để đổi nó thành chữ thường.
24. Giả sử một byte có mã ASCII của một chữ số thập phân từ 0 đến 9, hỏi phải trừ
đi một số hex là bao nhiêu để đổi nó thành chứa chính chữ số đó.
25. Trình bày những điểm khác nhau giữa thanh ghi và ô nhớ.
25. Hãy xác định địa chỉ vật lý của ô nhớ có địa chỉ logic 0A51h:ADBFh.
26. Hãy chỉ ra 5 cặp địa chỉ logic của ô nhớ có địa chỉ vật lý A7627h.
27. Hãy giải thích tại sao với vi xử lý 8086, độ dài một đoạn nhớ không quá 64 KB.

Ch¬ng 2. TỔNG QUAN VỀ HỢP NGỮ


2.1. Giới thiệu hợp ngữ
Cũng như các ngôn ngữ lâp trình khác, để học hợp ngữ, trước tiên ta phải học
cú pháp, cách khai báo biến, các lệnh số học và dịch chuyển cơ bản và cuối
cùng phải nắm được cách tổ chức trương trình.

36
GV. Vương Quốc Dũng
Trong chương trình hợp ngữ bao gồm mã lệnh, số hiệu và ngăn xếp giống như
chương trình mã máy. Việc thực hiện các thao tác vào/ ra trong hợp ngữ khó
khăn hơn nhiều so với các ngôn ngữ bậc cao bởi vì các lệnh của hợp ngữ quá
cơ bản và gần với ngôn ngữ máy. Tuy nhiên chúng ta có thể sử dụng các hàm
của DOS cho các thao tác vào ra vì chúng khá rõ ràng và đủ nhanh cho hầu hết
các yêu cầu ứng dụng.
Một chương trình hợp ngữ để có thể thực hiện được thì vẫn phải chuyển sang
dạng mã máy.
Để có thể soạn thảo 1 chương trình hợp ngữ và dịch sang dạng mã máy cần:
1 text editor (y/c chuẩn ASCII) (of DOS or NC or WINDOWS)
Masm.exe/ Tasm.exe (chương assemble): là chương trình chạy trên máy PC. Đây
là công cụ Assemple để kiểm tra cú pháp và dịch.
Link.exe : dùng để liên kết các modull chương trình “*.obj” thành 1 file duy nhất
có dạng “*.exe”
Có thể cần tới file exe2bin.exe để đổi file “*.exe” thành file “*.com” hoặc cần
“Tlink/t”.
Các file khi dịch ra đều có dạng “*.exe”. nếu có thể đổi sang file “*. Com” thì
có thể nó sẽ không chạy được ở file “*.exe”, do đó phảI đổi sang dạng “*.com”
Ngoài ra nếu có các công cụ sau càng tốt:
. codeview.exe (dùng để debug 1 cách logic 1 file)
. lip.exe (tạo ra 1 hệ thống thư viện)
2.2. Cú pháp dòng lệnh trong chương trình hợp ngữ:
Các chương trình hợp ngữ được dịch ra các lệnh mã máy bằng một chương
trình biên dịch vì vậy chúng cần được viết ra sao cho phù hợp với các khuôn
mẫu của trình biên dịch đó.
Ở đây chúng ta sẽ sử dụng trình biên dịch MICROSOFT MACRO
ASSEMBLER (MASM) hoặc TURBO ASSEMBLER (TASM). Mã lệnh hợp
ngữ nói chung không phân biệt chữ hoa hay chữ thường. Chúng ta thống nhất sử
dụng chữ hoa để phân biệt mã lệnh với phần còn lại của chương trình.
Một chương trình hợp ngữ bao gồm nhiều dòng lệnh, mỗi dòng lệnh viết ở 1
dòng. Có 2 dạng:
Lệnh thật: dưới dạng ký hiệu (Symbolic), đôi khi còn gọi là dạng gợi nhớ
(mnemonic) của bộ VXL.
Hoặc là 1 hướng dẫn cho chương trình dịch (assembler directive), lệnh thật sẽ được
dịch ra mã máy.
Hướng dẫn chương trình dịch không được dịch ra mã máy, nó có tác dụng
chỉ dẫn riêng cho chương trình dịch thực hiện công việc.
Một dòng lệnh có thể có các trường sau (không nhất thiết phải có đủ tất cả các
trường):
Tên mã lệnh các toán hạng chú giải

Ví dụ 1 S1: MOV CX, 5 ; khởi tạo bộ đếm.

37
GV. Vương Quốc Dũng
Trường tên:
Để chứa các nhãn, tên biến hoặc tên thủ tục. Các tên và nhãn này sẽ được chương
trình dịch gán bằng địa chỉ cụ thể của ô nhớ.
Tên và nhãn có thể có độ dài từ 1->31 ký tự, không được chứa dấu cách và không
được bắt đầu bằng một con số.
Có thể dùng một số ký tự đặc biệt trong tên, nhãn, đó là: ?, ., @._,$,%. Nếu là dấu
“.” được dùng thì bắt buộc phải ở vị trí đầu tiên của tên hoặc nhãn .
Một nhãn thường kết thúc bằng dấu (“:”)nói chung nên đặt tên bình thường, có ý
nghĩa gợi nhớ là tốt nhất .
Ví dụ trên trường tên là nhãn “S1”
Trường mã lệnh:
Trong trường mã lệnh nói chung sẽ có 2 dạng:
Lệnh thật: Chứa các mã lệnh gợi nhớ, mã lệnh gợi nhớ, mã lệnh này sẽ
được định ra mã máy. ở ví dụ trên là MOV
Hướng dẫn chương trình dịch: Chứa các lệnh giả và không được dịch ra
mã máy.
Trường toán hạng:
Với lệnh thật: Chứa các toán hạng của lệnh, số toán hạng có thể có
là 0, 1 hoặc 2 toán hạng trong 1 một lệnh.
Nếu là 1 toán hạng: Có thể là toán hạng đích hoặc toán hạng gốc.
Nếu là 2 toán hạng: gồm có toán hạng đích và toán hạng gốc
Ví dụ 1 ở trên gồm 2 toán hạng là CX và 5
Trong đó: CX – toán hạng đích.
5 – toán hạng gốc
Toán hạng đích: có thể là 1 thanh ghi hoặc 1 ô nhớ là nơi chứa kết quả.
Toán hạng nguồn: cũng có thể là 1 thanh ghi hoặc 1 ô nhớ, các chỉ thị
thường không làm thay đổi toán hạng nguồn.
Ví dụ 2: NOP ; không có toán hạng, dây là lệnh không làm gì cả.
INC AX ; 1 toán hạng, đây là lệnh tăng AX lên 1
ADD X1, 2 ;2 toán hạng, đây là lệnh cộng 2 vào ô nhớ X1
Trường giải thích:
Để cho người lập trình ghi chú lệnh đó để làm gì, mở đầu trường này là
dấu “;”. Trình biên dịch bỏ qua không dịch từ sau dấu “;” này của dòng
lệnh. Lời giải thích ghi chú có thể có hoặc không có.
Xong đây là ngôn ngữ bậc thấp, ta khó có thể hiểu được một chương trình
viết bằng hợp ngữ khi không có lời bình. Trong thực tế điền các lời giải
thích vào hầu hết các dòng lệnh là một phương pháp học lập trình tốt .
2.2.1. Dữ liệu chương trình
Dữ liệu cho chương trình hợp ngữ rất đa dạng. Các dữ liệu có thể được cho
dưới các dạng sau:
Dạng số hệ 2: 0011B hoặc 0111b
Dạng số hệ 10: 1234 hoặc 1234D hoặc 1234d
Dạng số Hex (hệ 16) 0BBAh, 1EF1h, 1EACh...
38
GV. Vương Quốc Dũng
Kí tự, chuỗi kí tự: ‘A’, “A”, “abc”, ‘abc’...
Tóm lại:
Số cho ở hệ nào phải được kèm đuôi của hệ đó như đã học ở tin học căn bản. Số hệ
10 có thể không cần vì đây là trường hợp ngầm định của Assembler.
Số Hex bắt buộc phải bắt đầu bằng một chữ số thập phân đứng trước, nếu bắt đầu
bằng các chữ a,...,f hoặc A,...,F thì phải thêm số 0 ở đầu.
Kí tự và chuỗi kí tự phải được đặt trong cặp nháy đơn hoặc cặp nháy kép. Các kí tự
sẽ được trình biên dịch dịch ra mã ASCII của chúng.
2.2.2. Các biến
Biến trong chương trình hợp ngữ có vai trò như nó có ở ngôn ngữ bậc cao. Mỗi
biến phải được gán 1 kiểu dữ liệu và được chương trình dịch gán cho 1 địa chỉ
nhất định trong bộ nhớ.
Để định nghĩa các kiểu dữ liệu khác nhau ta thường dùng các lệnh giả sau (các
toán tử giả định nghĩa số liệu).
DB (define byte) : định nghĩa biến kiểu byte.
DW (define word) : định nghĩa biến kiểu từ (word- 2byte).
DD (define double word) : định nghĩa biến kiểu từ kép (4 byte).
a. Các biến kiểu byte:
Biến kiểu byte chiếm 1 byte trong bộ nhớ .
Hướng dẫn chương trình dịch để định nghĩa biến kiểu byte có dạng tổng quát
sau:
Tên DB giá_trị_khởi_đầu.
Trong đó: toán tử giả DB được hiểu là “Định nghĩa kiểu byte”
Ví dụ:
X1 DB 4
định nghĩa biến kiểu byte có tên X1 và dành 1 byte trong bộ nhớ để
chứa giá trị khởi đầu là 4.
X2 DB ?
Định nghĩa biến kiểu byte có tên X2 được dành 1 byte trong bộ nhớ và
không được gán giá trị khởi đầu
C1 DB ‘$’
Định nghĩa biến C1 là 1 biến ký tự, ‘$’ - một trường hợp đặc biệt của
biến kiểu byte.
b. Các biến kiểu từ
Hướng dẫn chương trình dịch để định nghĩa biến kiểu từ có dạng sau:
Tên DW giá_trị_khởi_đầu.
Ví dụ:
W1 DW 50 ;biến kiểu từ W1 dành 2 byte trong bộ nhớ...
W2 DW ? ;biến kiểu từ W2 dành 2 byte trong bộ nhớ....

39
GV. Vương Quốc Dũng
c. Biến kiểu từ kép:
(đọc tài liệu phần: 18.1 các số có độ chính xác kép - chương 18 các phép tính
số học nâng cao - giáo trình: lập trình hợp ngữ (assembler) & máy tính IBM –
PC).
Hướng dẫn chương trình dịch để định nghĩa biến kiểu từ kép có dạng sau:
Tên DD giá_trị_khởi_đầu.
Ví dụ:
X1 DD 50 ;biến kiểu từ kép X1 dành 4 byte trong bộ nhớ...
X2 DD ? ;biến kiểu từ kép X2 dành 4 byte trong bộ nhớ....
c. Biến mảng.
Là biến hình thành từ 1 dãy liên tiếp các phần tử cùng loại byte hoặc từ (Word)
hoặc từ kép (Double Word) trong bộ nhớ cùng với các giá trị ban đầu tương ứng.
Ví dụ: R1 DB 7, 8, 9, 4, 2, 3
Định nghĩa biến mảng có tên R1 gồm 6 byte & chiếm chỗ trong bộ nhớ từ
địa chỉ ứng với R1 để chứa các giá trị khởi đầu 7, 8, 9, 4, 2, 3
phần tử thứ nhất trong mảng có giá trị là 7, có địa chỉ tại địa chỉ R1
phần tử thứ 2 trong mảng có giá trị là 8, có địa chỉ tại địa chỉ R1+1
Khi muốn khởi đầu các phần tử của mảng với cùng 1 giá trị chúng ta có thể dùng
thêm toán tử DUP trong lệnh.
Ví dụ:
R2 DB 5 dup(4)  R2 DB 4, ,4, 4, 4, 4.
R3 DB 5 dup(?): mảng gồm 5 byte chứa 5 phần tử nhưng chưa được
khởi gán giá trị.
Toán tử DUP có thể lồng nhau để định nghĩa ra 1mảng.
Ví dụ: R4 Db 4, 3, 2 Dup(1, 2 Dup(7),5)
Tương đương: R4 Db 4, 3, 1, 7, 7, 5, 1, 7, 7 ,5

Chú ý:
Đối với bộ VXL của Intel: nếu ta có 1 từ (Word) để trong bộ nhớ thì:
byte thấp của nó được để trong ô nhớ có địa chỉ thấp
byte cao của nó được để trong ô nhớ có địa chỉ cao
cách lưu giữ số liệu kiểu này thường gọi là “qui ước đầu bé”.
Đối với bộ VXL của Motorola: cách cất số liệu theo thứ tự ngược lại hay còn gọi là
“qui ước đầu to”
Ví dụ:
Sau khi định nghĩa biến kiểu từ có tên W1 như sau
w1 DW 0FFEEh
thì byte thấp là EEh được để tại ô nhớ có địa chỉ tại địa chỉ của w1
byte cao là FFH sẽ được để ở địa chỉ tiếp theo tức
tại địa chỉ = (địa chỉ của w1+1).
d. Biến kiểu xâu ký tự:

40
GV. Vương Quốc Dũng
Biến kiểu xâu ký tự là 1 trường hợp đặc biệt của biến mảng, trong đó các phần
tử của mảng là các ký tự. Một xâu ký tự có thể được định nghĩa bằng các ký tự
hoặc bằng mã ASCII của các ký tự đó.
Ví dụ:
S1 DB ‘string’
Tương dương với : S1 DB 73h, 74h, 72h, 69h, 6Eh, 67h
Và cũng là: S1 DB 73h, 74h, ‘r’,’i’, 6Eh, 67h
Ba câu lệnh trên đều định nghĩa cho cùng 1 xâu ký tự nhưng gán cho nó ở các
dạng dữ liệu khác nhau.
2.2.3. Các hằng có tên
Các hằng trong chương trình hợp ngữ thường được gán tên để làm cho chương
trình trở nên dễ đọc hơn.
Hằng có thể là kiểu số hay kiểu ký tự.
Để gán tên cho hằng, chúng ta có thể sử dụng toán tử (lệnh) giả EQU (equate)
như sau:
Cú pháp: tên EQU hằng số
ví dụ: CR EQU ODh ; CR là carriage return
LF EQU OAh ; LF là line feed
ở ví dụ trên lệnh giả EQU gán giá trị số 13 (mã ASCII của ký tự trở về đầu
dòng) cho tên hằng là CR và gán giá trị số 10 (mã ASCII của ký tự thêm
dòng mới) cho tên LF
Hằng cũng có thể là 1 chuỗi ký tự
Ví dụ: S1 EQU “hello!”
Sau khi đã gán 1 chuỗi cho tên S1, ta có thể sử dụng hằng có tên S1 để định
nghĩa 1 biến mảng khác
Ví dụ: MSG DB S1, ‘LAN’ ; mảng MSG gồm 2 phần tử
; kiểu xâu ký tự.
Lệnh giả EQU không giành chỗ của bộ nhớ cho tên của hằng nên ta có thể đặt
nó khá tự do tại những chỗ thích hợp bên trong chương trình. Tuy nhiên trong
thực tế nên đặt các định nghĩa này trong đoạn dữ liệu.
2.3. Các chỉ dẫn dùng trong Assembler
2.3.1. Các chỉ dẫn đoạn:
Trình hợp dịch Turbo Assembler cho phép sử dụng 2 loại chỉ dẫn đoạn: chỉ dẫn
đoạn giản đơn và chỉ dẫn đoạn chuẩn. Chỉ dẫn đoạn giản đơn dễ dùng hơn
nhưng có một số hạn chế so với chỉ dẫn chuẩn. Chỉ dẫn chuẩn là loại chỉ dẫn
phức tạp khi sử dụng.
1) Chỉ dẫn đoạn giản đơn:
Các chỉ dẫn này gồm •STACK, •CODE, •DATA, •MODEL và DOSSEG
•STACK

41
GV. Vương Quốc Dũng
Chỉ dẫn này qui định kích thước của ngăn xếp (stack).
Ví dụ: •STACK 200h
sẽ qui định kích thước stack là 512 bytes
Nếu chỉ ghi •STACK không ghi gì theo sau thì mặc nhiên stack là 1 KB.
Dùng •STACK khi viết chương trình hợp ngữ một mình, nếu chương trình
hợp ngữ được gọi từ một chương trình khác thì không cần.
•CODE
Đánh dấu điểm bắt đầu đoạn mã chương trình. Ta có thể nghĩ tất cả các lệnh
của chương trình nằm trong một đoạn mã, nhưng thực ra với chỉ dẫn đoạn
chuẩn ta có thể có nhiều đoạn mã.
•DATA
Chỉ dẫn này chỉ nơi bắt đầu đoạn dữ liệu. Ta nên đặt các biến nhớ trong đoạn
này.
Ví dụ:
•DATA
Nume1 DB 3Ch
Nume2 DB 00h
Trước khi sử dụng các biến trong đoạn dữ liệu ta phải nạp @DATA là hằng địa
chỉ đoạn dữ liệu vào đoạn dữ liệu Near (đoạn dữ liệu gần) DS. Vì không thể
đưa trực tiếp hằng vào DS nên người ta dùng 2 lệnh sau:
MOV AX, @data
MOV DS, AX
Ví dụ: Chương trình sau sẽ hiển thị chuỗi str lên màn hình
DOSSEG
•MODEL Small
•STACK 200h
•DATA
Str DB ‘Chao cac ban$’
•CODE
Begin:
MOV AX, @data
MOV DS, AX
LEA DX, str
MOV AH, 9
INT 21h
MOV AH, 4Ch
INT 21h
END Begin
Đến dây ta có thể hỏi tại sao không đặt địa chỉ cho CS và SS?
- Với CS ta không cần vì DOS đã làm thay cho việc đó, dĩ nhiên là địa chỉ
đầu của chương trình phải đưa vào CS thì chương trình mới thực hiện
được.
- SS cũng được DOS đặt giá trị trước khi chương trình bắt đầu. Ta có thể
thay đổi nội dung của SS khi thực hiện chương trình nhưng ít khi, nếu

42
GV. Vương Quốc Dũng
muốn thao tác trực tiếp trên stack ta cần hiểu rõ cơ chế hoạt động của
stack.
Cách sử dụng DS có khác với CS và SS. Thông thường là chương trình tác
động trên các đoạn dữ liệu thông qua DS. Với các giá trị khác nhau của DS,
cho phép thao tác trên các đoạn dữ liệu khác nhau. Trong các tất cả vừa hoặc
nhỏ, một đoạn dữ liệu là đủ, nhưng trong chương trình lớn có thể cần nhiều
đoạn dữ liệu.
Thanh ghi ES hoạt động tương tự như DS. Thường ta không quan tâm đến
thanh ghi này, trừ khi muốn truy cập dữ liệu thông qua thanh ghi này. Khi đó ta
cần khởi tạo thêm thanh ghi đoạn dữ liệu phụ ES.
Ví dụ: MOV AX, @data
MOV DS, AX
MOV ES, AX
Hoặc MOV AX, @data
MOV ES, AX
Ví dụ: Chương trình sau đây đưa •DATA vào ES và in ký tự trong đoạn dữ liệu
thông qua ES
DOSSEG
•MODEL Small
•STACK 200h
•DATA
Ch DB ‘A’
•CODE
Begin:
MOV AX, @data
MOV ES, AX
MOV BX, offset Ch
MOV DL, ES:[BX] ; lấy nội dung của biến Ch
MOV AH, 2
INT 21h ; Hiển thị nội dung biến Ch ra màn hình
MOV AH, 4Ch
INT 21h
END Begin
•MODEL
Chỉ dẫn này xác định kiểu kích thước bộ nhớ của đơn thể hợp ngữ dùng các chỉ
dẫn đoạn giản đơn. Chú ý khi dùng lệnh nhảy kiểu mã gần (near) thì chỉ cần
nạp giá trị cho IP, còn khi dùng lệnh nhảy kiểu mã xa (far) thì phải nạp giá trị
cho cả CS và IP. Tương tự với dữ liệu gần chỉ cần truy cập qua địa chỉ offset,
còn dl xa thì phải có đủ segment và offset.
Sau đây là các kiểu kích thước bộ nhớ khác nhau được dùng:

Kiểu kích thước mô tả

TINY (hẹp) Mã lệnh &DL gói gọn trong 1 đoạn, cả mã lệnh và dữ

43
GV. Vương Quốc Dũng

liệu đều gần (near)


SMALL (nhỏ) Mã lệnh gói gọn trong 1 đoạn, dữ liệu nằm trong 1
đoạn khác, cả mã lệnh và dữ liệu đều gần (near)
Medium (trung bình) Mã lệnh không gói gọn trong 1 đoạn, dữ liệu nằm
trong 1 đoạn, mã thì xa, dữ liệu thì gần
Compact (gọn) Mã lệnh gói gọn trong 1 đoạn, dữ liệu có thể lớn hơn
1 đoạn, mã thì gần, dữ liệu thì xa, một mảng dữ liệu
liên tục không lớn hơn 64 KB
Large (lớn) Cả mã lệnh và dữ liệu đều có thể lớn hơn 1 đoạn,
nhưng một mảng dữ liệu liên tục không lớn hơn 64
KB. Cả mã lệnh và dữ liệu đều xa.
Huge(đồ sộ) Cả mã lệnh và dữ liệu đều có thể lớn hơn 1 đoạn,
một mảng dữ liệu liên tục có thể lớn hơn 64 KB. Cả
mã lệnh và dữ liệu đều xa. Các con trỏ chỉ đến phần
tử mảng đều xa.
Thường ít có chương trình nào có mã hoặc dữ liệu lớn hơn 64 KB nên kiểu
SMALL là đủ. Ta nên dùng kiểu SMALL nếu có thể được, vì mã xa (các kiểu
MEDIUM, LARGE, HUGE) làm cho việc thực hiện chương trình bị chậm và
các dữ liệu xa (các kiểu COMPACT, LARGE, HUGE) thì khó quản lý.
Các kiểu bộ nhớ vừa nói tương ứng với các kiểu bộ nhớ dùng trong Turbo C
(và các trình biên dịch khác). Khi liên kết đơn thể hợp ngữ với ngôn ngữ cấp
cao cần xác định đúng •MODEL vì như vậy các đoạn trong hợp ngữ mới khớp
với ngôn ngữ cấp cao.
Cần dùng •MODEL thì dùng các chỉ dẫn đoạn giản đơn, vì nếu không trình hợp
dịch sẽ không định được các đoạn cho •STACK, •DATA, •CODE. Vì vậy
•MODEL phải đặt trước tất cả các chỉ dẫn khác.
DOSSEG
Chỉ dẫn này qui định các đoạn trong chương trình được sắp xếp theo qui ước
của Microsoft. Khi ta chỉ viết chương trình hợp ngữ thì luôn dùng câu lệnh này
(chỉ dẫn này), còn khi liên kết chương trình hợp ngữ với chương trình ngôn ngữ
cấp cao thì không cần vì chương trình ngôn ngữ cấp cao tự động chọn thứ tự
đoạn theo Microsoft.
2) Chỉ dẫn đoạn chuẩn:
Các chỉ dẫn đoạn chuẩn là SEGMENT, ENDS, ASSUME, GROUP.
2a) Chỉ dẫn SEGMENT
SEGMENT định nghĩa đoạn với các thuộc tính cần thiết, lệnh có cú pháp như
sau:
Name SEGMENT [align] [combine] [use] [‘class’]
Trong đó:
name : tên đoạn. Các đoạn có thể trùng tên.
align : Xác định biên vùng nhớ bắt đầu của đoạn, là một trong các giá trị sau

44
GV. Vương Quốc Dũng
BYTE Bắt đầu ở tại một vị trí bất kỳ trong bộ nhớ
WORD Bắt đầu ở địa chỉ chẵn (địa chỉ chia hết cho 2)
DWORD Bắt đầu ở địa chỉ chia hết cho 4
PARA Bắt đầu ở địa chỉ paragraph (địa chỉ chia hết cho 16)
PAGE Bắt đầu ở địa chỉ trang (địa chỉ chia hết cho 256, giá trị
mặc định).
combine : Xác định cách thức liên kết các đoạn cùng tên trong các đơn thể
khác nhau, là một trong các dạng sau

AT expression Xác định vị trí của đoạn tại một địa chỉ đoạn tuyệt đối
theo trị của expression. Expression không được chứa
một tên khác. Trình liên kết không phát sinh mã hoặc dữ
liệu tại đoạn định bởi AT. Thường dùng AT để truy cập
một vùng nhớ cố định màn hình hoặc ROM BIOS.
Ví dụ: ColorTextSeg SEGMENT AT 0B800h

COMMON Vị trí của đoạn này và các đoạn khác cùng tên ở cùng
một chỗ. Chiều dài chung là chiều dài đoạn dài nhất.
MEMORY Nối tất cả các đoạn cùng tên thành một đoạn liên tục
PRIVATE Không ghép đoạn này với các đoạn khác (mặc định)
PUBLIC Tương tự như MEMORY
STACK Nối tất cả các đoạn cùng tên thành một đoạn liên tục.
Trình liên kết gán giá trị đầu cho SS là địa chỉ của
đoạn này và cho SP là chiều dài của đoạn. Tổng
chiều dài của đoạn là tổng chiều dài của tất cả các
đoạn.

Use : Xác định kích thước đoạn khi dùng các chỉ dẫn P386 hoặc P386N,
có một trong các dạng sau

USE16 Trong trường hợp này kích thước đoạn tối đa là


64 KB (mặc định)
USE32 Kích thước đoạn tối đa là 4 GB

Class : Điều khiển thứ tự các đoạn khi liên kết. Các đoạn cùng tên class
được nạp vào một chỗ trong bộ nhớ bất chấp thứ tự xuất hiện
trong chương trình nguồn. Tên class phải nằm giữa 2 dấu nháy
đơn hoặc 2 dấu nháy kép.
Chú ý: Chỉ dẫn này chỉ khai báo đoạn nhưng nó không báo cho Assembler biết
loại segment nào được định nghĩa (code, data, extra, stack). Chỉ có SEGMENT
STACK là cho Assembler biết đoạn vừa khai báo là đoạn stack được chỉ bởi SS.
2b) Chỉ dẫn ENDS
Chỉ định nơi kết thúc đoạn, có dạng:

45
GV. Vương Quốc Dũng
[segmentname] ENDS
Với segmentname là tên đã khai báo ở trước SEGMENT.
Ví dụ:
- Định nghĩa đoạn dữ liệu
Dseg SEGMENT
A DB 0
B DB 0
C DW ?
Dseg ENDS
- Định nghĩa đoạn mã lệnh
Cseg SEGMENT
……
MOV CL, 2
SHL AX, CL
MOV BX, AX
……
Cseg ENDS
2c) Chỉ dẫn ASSUME
Chỉ định thanh ghi nào được dùng tương ứng với đoạn nào, cú pháp như sau:
ASSUME segmentreg:name[,segmentreg:name….]
ASSUME segmentreg:Nothing
ASSUME Nothing
Trong đó:
- segmentreg là một trong các thanh ghi CS, DS, ES, SS
- name là tên đã khaibáo ở SEGMENT
- Nothing : trường hợp thanh ghi đoạn không chỉ đến đoạn được đặt tên thì
dung Nothing. Nếu chỉ ghi Nothing sau ASSUME thì huỷ bỏ mọi liên kết
giữa đoạn với thanh ghi đoạn.
Ví dụ: ASSUME CS:Cseg, DS:Dseg
sẽ báo cho Assembler biết các nhãn và các biến trong đoạn Cseg sẽ dùng
thanh ghi đoạn CS, còn các nhãn và các biến trong đoạn Dseg sẽ dùng thanh
ghi đoạn DS.
Chỉ dẫn này thường dùng ngay sau chỉ dẫn khai báo đoạn kiểu •CODE.
Chú ý: ASSUME DS:Cseg không tự động nạp địa chỉ của đoạn vào thanh ghi
DS, ta phải nạp trực tiếp bằng lệnh:
MOV AX, Dseg
MOV DS, AX
2c) Chỉ dẫn GROUP
GROUP gom các đoạn khác nhau vào một đoạn, cú pháp như sau:
groupname GROUP segname1, segname2,…
groupname là tên nhóm bao gồm các đoạn segname1, segname2, …
Khi dùng GROUP thì tất cả các nhãn và biến định nghĩa trong các đoạn của
GROUP sẽ bắt đầu từ địa chỉ đầu của GROUP.
Chú ý:

46
GV. Vương Quốc Dũng
- Các đoạn trong một GROUP có thể không nằm liên tục nhau. Các đoạn
không nămg trong GROUP có thể nằm xen kẽ giữa các đoạn trong
GROUP.
- Khoảng cách giữa byte đầu tiên trong đoạn đầu tiên và byte cuối cùng
trong đoạn cuối cùng của GROUP không thể vượt quá 65535 bytes.
Ví dụ:
Code GROUP Cseg, Aseg, Bseg
Cseg SEGMENT WORD PUBLIC ‘Code’

Cseg ENDS
Aseg SEGMENT WORD PUBLIC ‘Data’
DB ‘Aseg’
Aseg ENDS
Bseg SEGMENT WORD PUBLIC ‘Data’
DB ‘Bseg’
Bseg ENDS
Ví dụ : chương trình Hello.ASM được viết lại với các chỉ dẫn đoạn chuẩn
StackSeg SEGMENT PARA STACK ‘Stack’
DB 100 dup(?)
StackSeg ENDS
DataSeg SEGMENT WORD ‘Data’
Message DB ‘Hello World’, 10, 13,’$’
DataSeg ENDS
CodeSeg SEGMENT WORD ‘Code’
ASSUME CS:CodeSeg, DS:DataSeg
Begin:
MOV AX, DataSeg
MOV DS, AX
MOV DX, offset Message
MOV AH, 9
INT 21h
MOV AH, 4Ch
INT 21h
StackSeg ENDS
END Begin
3) Nhóm chỉ dẫn điều khiển:
3a) Chỉ dẫn END
Chỉ dẫn này báo cho Assembler biết chương trình kết thúc tại vị trị END này.
Cú pháp: END [start_address]
start_address là một nhãn xác định điểm bắt đầu của chương trình mà DOS sẽ
thi hành.
Chú ý:
- Mỗi tập tin hợp ngữ nguồn phải chỉ một chỉ dẫn END.

47
GV. Vương Quốc Dũng
- Nếu chương trình gồm nhiều phần nằm tách rời trên các tập tin khác
nhau, thì ta chỉ có thể định nghĩa start_address dứng sau END trong phần
chính (main module).
3b) Chỉ dẫn ORG
Chỉ dẫn này dùng để báo cho Assembler biết các mã lệnh và dữ liệu ngay sau
ORG sẽ được nạp vào bộ nhớ bắt đầu tại địa chỉ offset mới xác định bởi
expression.
Ví dụ : ORG 100h
3c) Chỉ dẫn EVEN
Chỉ dẫn này dùng để báo cho Assembler biết các dữ liệu được khai báo ngay
sau EVEN sẽ được nạp vào bộ nhớ tại địa chỉ chẵn.
Chỉ dẫn này làm cho chương trình chạy nhanh hơn. CPU 80286 dùng BUS dữ
liệu 16 bits, nên nó có thể vận chuyển 16 bits dữ liệu cùng một lúc. Tuy nhiên
việc chuyển 16 bits dữ liệu bắt đầu tại địa chỉ lẻ sẽ thực hiện chậm hơn tại địa
chỉ chẵn. Vì vậy muốn chương trình thực hiện nhanh hơn, các dữ liệu cần truy
xuất nên lưu tại địa chỉ chẵn. Thường trong chương trình ta nên khai báo các dữ
liệu dạng doubleword trước tiên, sau đó đến dữ liệu dạng word rồi cuối cùng
mới đến các dữ liệu dạng byte.
4) Chỉ dẫn ghi chú:
Dùng để ghi chú, giải thích ý nghĩa của một đoạn chương trình, Assembler khi
dịch sẽ bỏ qua phần này.
COMMENT * [text]
text
* [text]
Tất cả các văn bản text giữa 2 dấu sao (*) và văn bản trên dòng chứa dấu * thứ 2
sẽ là phần ghi chú. Assembler bỏ qua phần này khi dịch.
5) Chỉ dẫn Mode:
Dùng để báo cho Assembler biết chương trình muốn dịch đối với CPU loại nào,
nếu không dùng chỉ dẫn này thì Assembler sẽ dịch chương trình đối với CPU
8086/8088, chỉ dẫn này gồm:
•8086
Chỉ cho phép dịch các lệnh của CPU 8086/8088 và bộ vi xử lý toán học
8087
•286
Chỉ cho phép dịch các lệnh của CPU 8086/8088/80286 và bộ vi xử lý
toán học 80287
•386
Chỉ cho phép dịch các lệnh của CPU 8086/8088/80286/80386 và bộ vi
xử lý toán học 80387
Chú ý: Chỉ dẫn này phải để ở bên ngoài đoạn
Ví dụ: Muốn dịch các lệnh mới của CPU 80386 ta phải dùng như sau:
•386
đặt ở đầu chương trình.

48
GV. Vương Quốc Dũng
6) Chỉ dẫn chọn cơ số:
Cơ số mặc nhiên dùng trong chương trình là 10. Tuy nhiên ta có thể chỉ định
khác đi bằng chỉ dẫn:
•RADIX num
Trong đó num là 2, 8, 10, 16
Ví dụ:
•RADIX 16 ; chọn cơ số 16
MOV AX, 100 ; AX = 100h = 256
•RADIX 10 ; chọn cơ số 10
SUB AX, 100 ; AX = AX - 100 = 156
•RADIX 2 ; chọn cơ số 2
ADD AX, 100 ; AX = AX + 100b = 156 + 4 = 160
7) Chỉ dẫn định nghĩa dữ liệu:
Chỉ dẫn này cho phép khai báo vùng nhớ dành cho các dữ liệu cần sử dụng
trong chương trình. Các dữ liệu có thể là số, chuỗi hay một biểu thức có trị xác
định.
[name] DB expression1[, expression2…]
[name] DW [type PTR] expression1[, expression2…]
[name] DD expression1[, expression2…]
[name] DF [type PTR] expression1[, expression2…]
[name] DQ expression1[, expression2…]
[name] DT expression1[, expression2…]
Trong đó:
name là tên vùng dữ liệu (tên biến, tên mảng,…), nếu không ghi thì
không gán tên cho vùng dữ liệu này.
Các dạng này có ý nghĩa như sau:
7a) [name] DB expression1[, expression2…]
Mỗi expression sẽ được cấp phát một byte, là một trong các trường hợp sau:
- Biểu thức hằng có giá trị từ -128 đến 255
- ? thì không gán giá trị đầu cho vùng nhớ được cấp phát (trị bất ky)
- Chuỗi ký tự
- Dùng với toán tử DUP có tác dụng lặp lại biểu thức với số lần là count
count DUP(expression1[, expression2…])
Toán tử này có thể lồng nhau.
Ví dụ:
Num DB -128
Buff DB 80 dup(?) ; mảng Buff gồm 80 phần tử chưa khởi
; gán giá trị
Msg DB ‘Hello$’
7b) [name] DW [type PTR] expression1[, expression2…]
Nếu dùng type PTR thì trình hợp dịch thêm một số thông tin về chẩn lỗi cho
name đã định nghĩa và nhờ vậy mà Turbo Debugger sẽ hiển thị nội dung
của name chính xác. Việc thêm thông số này không ảnh hưởng tới mã phát
sinh khi hợp dịch.

49
GV. Vương Quốc Dũng
type có thể là BYTE, WORD, DWORD, FWORD, QWORD, TBYTE
hoặc tên của một cấu trúc.
Mỗi expression được cấp phát một word, là một trong các trường hợp sau:
- Biểu thức hằng có giá trị từ -32767 đến 65535
- ? thì không gán giá trị đầu cho vùng nhớ được cấp phát (trị bất ky)
- Biểu thức địa chỉ xác định địa chỉ gần (offset) trong đoạn 64 KB
- Dùng với toán tử DUP
Ví dụ:
DW 12345
Wbuff DB 6 dup(0)
WPtr DB Wbuff ; WPtr chứa địa chỉ offset của Wbuff
; (WPtr đóng vai trò con trỏ trỏ tới Wbuff)
7c) [name] DD expression1[, expression2…]
Mỗi expression được cấp phát 2 word, là một trong các trường hợp sau:
- Biểu thức hằng có giá trị từ -2147483648 đến 4294967295
- Là số chấm động ngắn 32 bits
- ? thì không gán giá trị đầu cho vùng nhớ được cấp phát (trị bất ky)
- Biểu thức địa chỉ xác định địa chỉ xa (segment:offset) trong đoạn 16 bits
hoặc địa chỉ gần trong đoạn 32 bits (offset 32 bits)
- Dùng với toán tử DUP
Ví dụ:
Data16 SEGMENT USE16
Xarray DD 0, 1, 2, 3, 4
Data16 ENDS

Data32 SEGMENT USE32


Const DD 3.141, 2.718 ; Hằng chấm động
DblPtr DW Const ; DblPtr là con trỏ xa 16 bits
NrPtr DD Xarray ; NrPtr là con trỏ gần 32 bits
BigInt DD 12345678 ; Số nguyên lớn
Darray DD 4 dup(?) ; mảng 4 phần tử chưa khởi gán.
Data32 END
7d) [name] DF [type PTR] expression1[, expression2…]
Chỉ dẫn này thường dùng cho 80386. Mỗi expression được cấp phát 6 bytes,
là một trong các trường hợp sau:
- Biểu thức hằng có giá trị từ -140737488355328 đến 281474976710655
- ? thì không gán giá trị đầu cho vùng nhớ được cấp phát (trị bất ky)
- Biểu thức địa chỉ xác định địa chỉ xa (segment:offset) trong đoạn 48 bits
- Dùng với toán tử DUP
Nếu dùng type PTR thì trình hợp dịch thêm một số thông tin về chẩn lỗi cho
name đã định nghĩa và nhờ vậy mà Turbo Debugger sẽ hiển thị nội dung
của name chính xác. Việc thêm thông số này không ảnh hưởng tới mã phát
sinh khi hợp dịch.

50
GV. Vương Quốc Dũng
type có thể là BYTE, WORD, DWORD, FWORD, QWORD, TBYTE
hoặc tên của một cấu trúc.
Ví dụ:
•386
Person Struc
Name DB 32 dup(?)
Age DB ?
Person ENDS
Data32 SEGMENT USE32
Pptr DF Person PTR 0 ; con trỏ xa 48 bits trỏ đến cấu trúc.
Msg DB ‘Hello$’
PtrMsg DF Msg ; con trỏ xa trỏ tới Msg
Data32 ENDS
7e) [name] DQ expression1[, expression2…]
Mỗi expression được cấp phát 4 words, là một trong các trường hợp sau:
- Biểu thức hằng có giá trị từ -263 đến 264
- Là dấu chấm động ngắn 64 bits
- ? thì không gán giá trị đầu cho vùng nhớ được cấp phát (trị bất ky)
- Biểu thức địa chỉ xác định địa chỉ xa (segment:offset) trong đoạn 48 bits
- Dùng với toán tử DUP
7f) [name] DT expression1[, expression2…]
Mỗi expression được cấp phát 5 words, là một trong các trường hợp sau:
- Biểu thức hằng thập phân nén có giá trị từ 0 đến 99999999999999999999
(20 số 9)
- Là dấu chấm động 80 bits
- ? thì không gán giá trị đầu cho vùng nhớ được cấp phát (trị bất ky)
- Biểu thức địa chỉ xác định địa chỉ xa (segment:offset) trong đoạn 48 bits
- Dùng với toán tử DUP

Nhiều trị có thể định nghĩa bằng một chỉ dẫn định nghĩa dữ liệu (ví dụ dùng cho
khai báo mảng: Xarray DW 0, 1, 2, 3, 4)
Trong trường hợp muốn định nghĩa một mảng lớn không thể viết trên một dòng
thìcó thể viết trên nhiều dòng, như sau:
Arr DB 10, 6, 19, 7, 1
DB 25, 5, 7, 20, 8
Có thể định nghĩa khối dữ liệu bằng toán tử DUP. Ví dụ sau khai báo mảng Arr
có 255 phần tử kiểu word khởi gán giá trị đầu bằng 0
Arr DW 255 dup(0)
Trị khởi đầu cho biển có thể là biểu thức hoặc nhãn
Ví dụ:
X DW 955/3+3
Buff DW 60 dup(0)
BuffPtr DW Buff ; Chứa địa chỉ của Buf
8) Chỉ dẫn đặt tên cho vị trí nhớ:

51
GV. Vương Quốc Dũng
Chỉ dẫn LABEL để đặt tên cho một vị trí nhớ. LABEL dùng để đặt tên và kiểu
nhãn nhưng không cấp phát vùng nhớ, có cú pháp như sau:
name LABEL type
Trong đó:
name là tên nhãn
type là một trong các giá trị sau:
BYTE Nhãn kiểu byte
WORD Nhãn kiểu word
DWORD Nhãn kiểu Double Word
FWORD Nhãn kiểu con trỏ xa
QWORD Nhãn kiểu bốn word
TBYTE Nhãn kiểu 10 byte
NEAR Nhãn kiểu gần
FAR Nhãn kiểu xa
PROC Nhãn kiểu gần/xa, tương đương với
NEAR khi kiểu bộ nhớ là TINY, SMALL, COMPACT.
FAR khi kiểu bộ nhớ là MEDIUM, LARGE, HUGE
UNKNOWN Nhãn không xác định
Ví dụ:
Định nghĩa một biến mảng kiểu byte 2 phần tử có thể truy xuất một lúc như
một word
WordVar LABEL WORD
DB 1, 2

MOV AX, WordVar ; AH = 2, AL = 1
Định nghĩa một biến mảng gồm 100 bytes có thể truy xuất theo 3 dạng dữ
liệu byte, word, double word
WArray LABEL WORD
DWArray LABEL DWORD
BArray DB 100 dup(?)
Theo khai báo ở trên, ta có một vùng nhớ có 3 tên là WArray (nếu xem
vùng nhớ này gồm 50 word), DArray (nếu xem vùng nhớ này gồm 25
double word) và BArray (nếu xem vùng nhớ này gồm 100 bytes)
Định nghĩa nhãn FarLabel là nhãn xa và NearLabel là nhãn gần
FarLabel LABEL FAR
NearLabel LABEL NEAR ; FarLabel và NearLabel ở cùng một vị trí

JMP FarLabel ; Nhảy xa (nạp cả CS và IP)

JMP NearLabel; Nhảy gần (Chỉ nạp IP)
Định nghĩa TempVar theo kiểu UNKNOWN để sau đó có thể truy xuất như
byte hay word
TempVar LABEL UNKNOWN
DB ?, ?

52
GV. Vương Quốc Dũng

MOV TempVar, AX ; TempVar là một word
MOV DL,TempVar ; TempVar là một byte
Có thể dùng để xác định độ dài của một mảng tính theo byte
Arr DW 60 dup(0)
ArrEnd LABEL WORD
ArrLength DB ArrEnd - Arr
9) Các chỉ dẫn thay thế
Gồm 2 chỉ dẫn EQU và = dùng để gán trị số và chuỗi cho nhãn.
9a) Chỉ dẫn EQU
Dùng để gán giá trị số hoặc chuỗi cho nhãn, có cú pháp như sau:
name EQU text
name EQU expression
Trong đó :
text nằm giữa 2 dấu <>
Expression có thể là một hằng số 16 bits, một nhãn, một tên biến, một
toán hạng, một từ khóa hợp ngữ.
Ví dụ:
Table EQU [BX+SI] ; một toán hạng
F10 EQU 68 ; một hằng số
move EQU MOV ; Một từ khóa
S1 EQU <run time> ; Một chuỗi
9b) Chỉ dẫn =
name = expression
Tương tự như EQU nhưng không được dùng cho định nghĩa hằng chuỗi, tuy
nhiên lại được phép dùng để định nghĩa lại nhãn đã được định nghĩa trước đó
(với EQU thì không định nghĩa lại được)
Ví dụ:
Row = 10

MOV AH, Row

Row = 20
10) Các chỉ dẫn Listing
Khi Assembler dịch một tập tin nguồn •ASM sang tập tin đối tưộng •OBJ. Lúc
đó nó có thể thông báo kết quả của quá trình dịch theo nhiều cách như sau:
- Có thể ghi thêm thông tin vào một tập tin listing hay tập tin cross-
reference, các thông tin cần thiết khi sửa sai đối với các chương trình lớn
và phức tạp.
- Có thể xuất các thông báo về kết quả của quá trình dịch ra thiết bị xuất
chuẩn (thường là màn hình).
Chỉ dẫn này dùng để điều khiển sự định dạng từng trang của tập tin listing khi ta
muốn in tập tin này ra máy in.
10a) Chỉ dẫn PAGE

53
GV. Vương Quốc Dũng
PAGE [lines][, columns]
Định chiều dài, chiều rộng của một trang giấy in trong tập tin listing.
Trị định sẵn là lines = 50, columns = 80
10b) Chỉ dẫn TITLE
TITLE text
Định tiêu đề text sẽ được in ra trên dòng 2 của mỗi trang
10c) Chỉ dẫn SUBTITLE
SUBTITLE text
Định tiêu để phụ text sẽ được in ra trên dòng 3 của mỗi trang.
2.4. Các toán tử dùng trong Assembler
Điểm quan trọng để phân biệt sự khác nhau giữa toán tử (operator) và lệnh là:
- Toán tử điều khiển sự tính toán các trị hằng xác định lúc dịch
- Lệnh điều khiển sự tính toán các trị không xác định được cho đến khi
chương trình dịch thực hiện.
Ví dụ: Toán tử + điều khiển phép cộng khi dịch và lệnh ADD điều khiển phép
cộng khi chương trình thực hiện.
2.4.1. Các toán tử số học

Toán tử Cú pháp Ý nghĩa


+ + expression Dương
- - expression Âm
* expression1 * expression2 Nhân
/ expression1 / expression2 Chia
MOD expression1 MOD expression2 Phần dư
+ expression1 + expression2 Cộng
- expression1 - expression2 Trừ
SHL Expression SHL count Dịch expession sang trái count
bits, count là số nguyên
SHR Expression SHR count Dịch expession sang phải count
bits, count là số nguyên

Ví dụ:
MOV BX, (80 * 4 + 10) * 2
MOV AX, 01110100b SHL 2
MOV DL, 300 MOD 8
2.4.2. Các toán tử logic

Toán tử Cú pháp
NOT NOT expression
AND expression1 AND expression2

54
GV. Vương Quốc Dũng
OR expression1 OR expression2
XOR expression1 XOR expression2

Ví dụ:
MOV AL, 8 OR 4 AND 2
MOV AL, NOT (20 XOR 0011100b)
2.4.3. Các toán tử quan hệ
Các toán tử quan hệ so sánh 2 biểu thức và cho giá trị true (-1) nếu điều kiện của
toán tử thỏa mãn, ngược lại cho trị false (0)

Toán tử Cú pháp Ý nghĩa


EQ expression1 EQ expression2 =
NE expression1 NE expression2 ≠
LT expression1 LT expression2 <
LE expression1 LE expression2 ≤
GT expression1 GT expression2 >
GE expression1 GE expression2 ≥
Ví dụ:
MOV AX, 4 EQ 3 ; AX = 0
MOV AX, 4 NE 3 ; AX = -1
MOV AX, 4 LT 3 ; AX = 0

2.4.4. Các toán tử cung cấp thông tin về biển và nhãn


1) Toán tử SEG
SEG expression
Cho địa chỉ đoạn của expression. Expression có thể là một biến, nhãn, tên
SEGMENT, tên GROUP hay các toán hạng bộ nhớ khác.
2) Toán tử OFFSET
OFFSET expression
Cho địa chỉ offset của expression. Expression có thể là một biến, nhãn, tên
SEGMENT, tên GROUP hay các toán hạng bộ nhớ trực tiếp khác.
Ví dụ: Chuyển segment và offset của biến Table vào cặp thanh ghi DS:DX
Table DB ?
MOV AX, SEG Table
MOV DS, AX
MOV DX, OFFSET Table
2.4.5. Toán tử chỉ số []

55
GV. Vương Quốc Dũng
[expression]
Toán tử chỉ số thường dùng chung với toán hạng bộ nhớ trực tiếp hoặc gián tiếp
Ví dụ:
Var DB 3Ch
MOV AL, [Var]
2.4.6. Toán tử :
segment:expression
segment có thể là tên một trong các thanh ghi đoạn DS, CS, ES, SS hay tên
SEGMENT (đoạn), tên GROUP đã khai báo
Toán tử quy định cách tính địa chỉ của một biến hay một nhãn đối với đoạn
SEGMENT được chỉ. Toán tử này dùng chung với toán tử chỉ số [] thì segment
này phải được đặt ngoài toán tử []
Ví dụ:
Toán hạng [ES:DI] ; viết sai
Toán hạng ES:[DI] ; viết đúng
2.4.7. Toán tử $
Cho địa chỉ offset hiện hành mà Assembler đang dịch. Nói khác đi $ là trị của
địa chỉ offset hiện hành. $ có thể dùng trong biểu thức hoặc bất kỳ vị trí nào
thích hợp. Thường toán tử $ dùng để tính độ dài của một chuỗi.
Ví dụ:
Str DB ‘Hello’
lenStr EQU $-Str
2.4.8. Toán tử TYPE
TYPE expression
Cho trị biểu thị dạng của expression
- Nếu expression là biến
+ nếu trị là 1 thì biểu thị biến dạng BYTE.
+ nếu trị là 2 thì biểu thị biến dạng WORD
+ nếu trị là 4 thì biểu thị biến dạng DWORD
+ nếu trị là 6 thì biểu thị biến dạng FWORD
+ nếu trị là 8 thì biểu thị biến dạng QWORD
+ nếu trị là 10 thì biểu thị biến dạng TBYTE
- Nếu expression là nhãn
+ nếu trị là 0FFFFh thì biểu thị nhãn dạng NEAR.
+ nếu trị là 0FFFEh thì biểu thị nhãn dạng FAR.
- Nếu expression là hằng thì trị là 0
Ví dụ:
Var DW ?
Arr DD 10 dup(?)
Str DB ‘This is string’
MOV AX,Type Var ; AX = 2
MOV AX, Type Arr ; AX = 4
MOV AX, Type Str ; AX = 1

56
GV. Vương Quốc Dũng
2.4.9. Toán tử LENGTH
LENGTH Var
Cho số đơn vị mà biến Var xin cấp.
Ví dụ:
Table DW 50 dup(?)
MOV CX, Length Table ; CX = 50
2.4.10. Toán tử SIZE
SIZE Var
Cho số bytes mà biến Var xin cấp.
Ví dụ:
Table DW 50 dup(?)
MOV CX, size Table ; CX = 100
2.4.11. Các toán tử thuộc tính
1) Toán tử PTR
type PTR expression
Cho phép thay đổi dạng của expression
- Nếu expression là một biến hay một toán hạng bộ nhớ thì type có thể là
BYTE, WORD, DWORD, FWORD, QWORD, TBYTE.
- Nếu expression là nhãn thì type có thể là NEAR hay FAR
Ví dụ: Lệnh sau làm cho Assembler nhập nhằng
MOV [BX], 0
Lệnh trên nạp 0 vào vùng nhớ có địa chỉ DS:BX, vùng nhớ này có thể có
chiều dài là 1 byte, 2 bytes, 4 bytes,… Assembler không biết đưa trị 0 vào 1
ô nhớ, 2 ô nhớ, hay 4 ô nhớ …. Để tránh tình trạng này, ta dùng toán tử PTR
và lệnh trên có thể sửa lại như sau:
MOV BYTE PTR [BX], 0 ; Nạp 0 vào 1 ô nhớ có địa chỉ offset là
; nội dung của BX
MOV WORD PTR [BX], 0 ; Nạp 0 vào 2 ô nhớ liên tiếp có địa chỉ
; offset là nội dung của BX
2) Toán tử PTR
HIGH expression
LOW expression
Cho trị của byte cao và byte thấp của expression. Expression phải là một trị
hằng xác định khi dịch.
Ví dụ:
X EQU 0ABCDh
MOV AL, LOW X ; AL = 0CDh
MOV AH, HIGH X ; AH = 0ABh
2.5. Giới thiệu một số lệnh cơ bản.
1. MOV
Cú pháp: MOV đích, nguồn
Giải thích:

57
GV. Vương Quốc Dũng
Dùng để chuyển 1 word hay 1 byte dữ liệu từ toán hạng nguồn về toán
hạng đích.
Toán hạng nguồn và toán hạng đích có thể tìm được theo các chế độ địa
chỉ khác nhau nhưng phải cùng độ dài và không được phép đồng thời là
2 ô nhớ hoặc 2 thanh ghi đoạn.
Bảng các khả năng kết hợp cho phép của các toán hạng trong lệnh MOV:

Toán hạng Thanh ghi công Thanh ghi đoạn ô nhớ


Toán đích dụng chung
hạng nguồn
Thanh ghi công yes yes yes
dụng chung
Thanh ghi đoạn yes no yes
ô nhớ yes yes no
Hằng số yes no yes

2. XCHG đích, nguồn


giải thích:
lệnh này được dùng để hoán chuyển nội dung của 2 thanh ghi hay giữa thanh
ghi và ô nhớ.
Toán hạng đích và toán hạng nguồn có thể tìm theo các chế độ địa chỉ khác
nhau, nhưng phải chứa dữ liệu có cùng độ dài, không được phép đồng thời là
2 ô nhớ và toán hạng không được phép là thanh ghi đoạn.
Sau lệnh này toán hạng này chứa nội dung cũ của toán hạng kia và ngược
lại.
Bảng các khả năng kết hợp cho phép của các toán hạng trong lệnh XCHG:

Toán hạng Thanh ghi công dụng ô nhớ


Toán đích
hạng nguồn
Thanh ghi công dụng yes yes
ô nhớ yes no

3. ADD (cộng 2 toán hạng)


Cú pháp: ADD đích, nguồn
Giải thích:
Lệnh này cộng 2 toán hạng đích và nguồn, kết quả cất vào toán hạng
đích. Sau phép toán, toán hạng nguồn không thay đổi.
Toán hạng đích & gốc có thể tìm được theo các chế độ địa chỉ khác
nhau, nhưng phải chứa dữ liệu có cùng độ dài & không được phép đồng
thời là 2 ô nhớ và cũng không được phép đồng thời là thanh ghi đoạn.
Ví dụ:
ADD word1, AX

58
GV. Vương Quốc Dũng
Lệnh này cộng nội dung của thanh ghi AX với nội dung của từ nhớ
word1, kết quả được chứa trong word1. AX không bị thay đổi.
4. SUB (sub stract) – trừ 2 toán hạng
Cú pháp: SUB đích, nguồn
giải thích:
Lệnh này Lệnh này lấy toán hạng đích trừ toán hạng nguồn, kết quả cất vào
toán hạng đích. Sau phép toán, toán hạng nguồn không thay đổi.
Toán hạng đích và nguồn như lệnh ADD.
Sau đây là bảng các khả năng kết hợp cho phép của các toán hạng trong lệnh
ADD & SUB:

Toán hạng Thanh ghi công dụng ô nhớ


Toán đích chung
hạng nguồn
Thanh ghi công dụng yes yes
chung
ô nhớ yes no
Hằng số yes yes
5. INC (increment Destination Register or momery)
Cú pháp: INC đích
Giải thích:
đích đích + 1, tức:
Lệnh này tăng toán hạng đích lên 1, kết quả cất vào toán hạng đích.
Toán hạng đích có thể là 1 thanh ghi hay 1 ô nhớ và có thể tìm được theo
các chế độ địa chỉ khác nhau.
Lưu ý: nếu đích = FFH thì (đích + 1) = 00H
đích = FFFFH thì (đích + 1) = 0000H
mà không ảnh hưởng đến cờ CF
lệnh này  lệnh ADD đích,1 nhưng chạy nhanh hơn.
6. DEC - Decrement Destination Register or momery
cú pháp: DEC đích
Giải thích: đích đích -1
Toán hạng đích như lệnh INC.
Lưu ý:
nếu đích = 00H thì (đích-1) = FFH
đích = 0000H thì (đích-1) = FFFFH
mà không ảnh hưởng đến cờ CF
7. PUSH (push word on the stack).
Cất 1 từ vào ngăn xếp
Cú pháp : Push nguồn
Giải thích: SP←SP-2

59
GV. Vương Quốc Dũng
Nguồn →SP
Trong đó toán hạng nguồn có thể tìm được theo các chế độ dịa chỉ khác
nhau : có thể là thanh ghi đa năng, thanh ghi đoạn hoặc là ô nhớ. Lệnh này
thường dùng với lệnh POP như là một cặp đối ngẫu để xử lý các dữ liệu và
trạng thái của chương trình chính (CTC) khi vào/ra chương trình con(CTc)
Vi dụ:
PUSH Bx ; cất BX vào ngăn xếp tại vị trí do SP chỉ ra
PUSH Table[Bx} ; cất 2 byte của vùng dữ liệu DS có địa chỉ đầu tại
[Table+Bx}.
8. POP - (Pop word from top of stack)
Lấy 1 từ vào thanh ghi từ đỉnh của ngăn xếp.
Cú pháp : POP đích
Giải thích: Đích ← {SP}
Minh hoạ: Trước lệnh
SP ← SP +2
POP.
Trong
Offset đó các toán hạng đích có thể tìm được theo 00FCh
các chế độ địa chỉ khác
SP
nhau : có thể là thanh ghi đa năng, ô nhớ, thanh ghi đoạn ( nhưng không được
00FAh
phép là thanh ghi đoạn mã CS). Dữ liệu để tại ngăn xếp không thay đổi.
5678h FFFFh CX
Ví dụ: POP CX; lấy 2 byte từ đỉnh
00FCh SP ngăn xếp → CX
POP DX 1234h; lấy 2 byte từ đỉnh ngăn xếp → DX
00FEh 0001h DX

0100h
Stack

Sau lệnh POP CX

00FAh
5678h 00FEh SP
00FCh
1234h
00FEh SP 5678h CX

0100h
0001h DX
stack

sau lệnh POP DX

00FAh

5678h 0100h SP
00FCh
1234h
00FEh 5678h CX
0100h SP 60
1234h DX
Stack
GV. Vương Quốc Dũng

9. NEG - Negate a operand


Cú pháp: NEG đích
Giải thích:
Toán hạng đích có thể là 1 thanh ghi hay 1 ô nhớ
Lệnh này sẽ thay thế nội dung của toán hạng đích bằng số bù 2 của nó.

10. LEA - Load Effective Address (nạp địa chỉ hiệu dụng vào thanh ghi)
Cú pháp: LEA đích, nguồn
Giải thích: Đích điạ chỉ lệch của nguồn / địa chỉ tương đối của nguồn.
(hoặc địa chỉ hiệu dụng của nguồn)
ở đây:
Đích thường là các thanh ghi BX, CX, DX, BP, SI, DI
Nguồn là tên biến trong đoạn DS được chỉ rõ trong lệnh hoặc ở ô nhớ cụ thể.
Ví dụ:
LEA DX, M1 ; nạp địa chỉ lệnh của biến M1vào DX
LEA CX, [BX][DI] ; nạp vào CX địa chỉ hiệu dụng do BX và DI chỉ ra
; tức CX = BX+DI
11. LOOP
Cú pháp: LOOP nhãn

61
GV. Vương Quốc Dũng
Giải thích:
Lệnh này dùng để lặp lại đoạn chương trình (gồm các lệnh nằm trong
khoảng từ nhãn đến hết lệnh LOOP nhãn) cho đến khi CX=0 (CX chứa số
lần lặp, sau mỗi lần lặp CX tự động giảm đi 1, tức thực hiện lệnh LOOP
nhãn thì CX tự động giảm đi 1).
Chú ý: nhãn nằm ở đầu vòng lặp LOOP và không được cách lệnh LOOP quá
126 bytes.
12. JMP
Cú pháp: JMP nhãn Hoặc Jmp offset
Lệnh này khiến cho bộ VXL bắt đầu thực hiện 1 lệnh mới tại địa chỉ được
chỉ ra ứng với địa chỉ của nhãn hoặc lệnh mới tại địa chỉ CS:offset
Lệnh này được gọi là lệnh nhảy vô điều kiện.
13. XOR
(hoặc loại trừ những bít tương ứng của 2 toán hạng)
cú pháp: XOR đích, nguồn
giải thích đích đích ⊕ nguồn
toán hạng đích & nguồn có thể tìm được theo các chế độ địa chỉ khác nhau,
nhưng phải chứa dữ liệu cùng độ dài & không được phép đồng thời là 2 ô
nhớ, cũng không thể là 2 thanh ghi đoạn
tính chất của phép OR loại trừ cho kết quả = 0 nếu toán hạng đích trùng
toán hạng gốc, do đó lệnh này còn được dùng để xoá về 0 một thanh ghi nào
đó kèm theo các cờ CF & OF cũng bị xoá
ví dụ:
XOR AL, BL ; AL AL ⊕ BL
XOR BH, BH ; xoá BH, xoá các cờ CF&OF.

14. INT - Interrupt Program Execution


Cú pháp: INT N;
N: số hiệu ngắt, có giá trị từ 00H đến FFH. Mỗi số hiệu ngắt ứng với 1
chương trình phục vụ ngắt, có địa chỉ lấy từ bảng vectơ ngắt.
Chương trình phục vụ ngắt còn gọi là chương trình con phục vụ ngắt vì
cách thức tổ chức và quan hệ giữa nó với chương trình bị ngắt cũng giống
như cách tổ chức và quan hệ giữa chương trình chính và chương trình con.
Lệnh này để gọi CTcPVN (Chương trình con phục vụ ngắt).
2.6. Giới thiệu qua về ngắt 21h của DOS
Ngắt 21h được dùng để gọi rất nhiều hàm của DOS. Mỗi hàm được gọi bằng
cách đặt số hàm (số hiệu của hàm) vào trong thanh ghi AH và gọi INT 21h.
ta xem xét các hàm sau:

62
GV. Vương Quốc Dũng
số hiệu ngắt chương trình
1 vào 1 phím
2 đưa 1 ký tự ra màn hình
9 đưa ra 1 chuỗi ký tự
• hàm 1: vào ký tự từ bàn phím
vào: AH = 1
ra: AL = ký tự từ thiết bị vào chuẩn (mã ASCII của ký tự)
AL = 0 nếu là phím điều khiển hay phím chức năng
hàm này nhận dữ liệu trong thanh ghi AH và trả về kết quả trong thanh ghi
AL.
để gọi CTc phục vụ này ta viết lệnh sau:
MOV AH, 1
INT 21h
Bộ VXL sẽ đợi user ấn 1 phím nếu cần thiết.
Nếu phím ký tự được ấn, AL nhận mã ASCII của phím đó và ký tự được hiện nên
màn hình.
Nếu 1 phím điều khiển hay phím chức năng được ấn thì AL = 0.
• hàm 2: hiển thị 1 ký tự hay thi hành 1 chức năng điều khiển
vào: AH = 2
DL = mã ASCII của ký tự hiển thị hay ký tự điều khiển
Ra: AL = mã ASCII của ký tự hiển thị hay ký tự điều khiển
Dùng hàm này để hiển thị 1 ký tự mà ta đặt mã ASCII của nó trong DL.
Ví dụ: lệnh sau sẽ xuất hiện dấu “?” – màn hình:
MOV AH, 2
MOV DL, ’?’
INT 21h
• hàm 9: hiển thị 1 chuỗi ký tự tại thiết bị ta chuẩn.
Vào: AH = 9
DX = địa chỉ tương đối (offset) của chuỗi.
Chuỗi phải kết thúc bằng ký tự ‘$’
Ví dụ chuỗi được định nghĩa:
S1, DB ‘Hello!$’
Ký tự $ đánh dấu kết thúc chuỗi & không được hiển thị. Nếu chuỗi chứa mã
ASCII của ký tự điều khiển thì chức năng điều khiển sẽ được thi hành.
2.7. Giới thiệu qua về ngắt 10h của BIOS
• hàm 2: dịch chuyển con trỏ (định vị con trỏ)
vào: AH = 2
DH = dòng
DL = cột
BH = trang

63
GV. Vương Quốc Dũng
Ra: không
• hàm 8: đọc ký tự với thuộc tính tại vị trí con trỏ
(nhận mã ASCII của ký tự với thuộc tính tại vị trí con trỏ)
vào: AH = 8
BH = trang số
Ra: AH = thuộc tính
AL = ký tự
• hàm 9: hiển thị ký tự với thuộc tính tại vị trí con trỏ
(viết 1 ký tự ASCII & thuộc tính của nó tại vị trí con trỏ)
Vào: AH = 09h
BH = trang
AL = ký tự
CX = số lần viết ký tự
BL = thuộc tính (chế độ văn bản)
hay màu (chế độ đồ hoạ)
Ra: không.
2.8. Cấu trúc chương trình nguồn hợp ngữ
Chương trình viết bằng hợp ngữ phải có cấu trúc tương tự như 1 chương trình
mã máy nghĩa là sử dụng bộ nhớ theo các vùng khác nhau bao gồm: vùng để
chứa dữ liệu & 1 vùng nhớ khác được dùng làm ngăn xếp phục vụ hoạt động
của chương trình.
2.8.1. Khai báo mô hình bộ nhớ
Kích thước của bộ nhớ dành cho đoạn mã & đoạn dữ liệu trong 1 chương trình
được xác định nhờ hướng dẫn chương trình dịch .MODEL với cú pháp như sau:
.MODEL Kiểu_kích_thước_bộ_nhớ
Hướng dẫn này phải đặt trước các hướng dẫn khác & phải được đưa vào
trước bất kỳ 1 đoạn định nghĩa nào.
Có nhiều kiểu kích thước bộ nhớ khác nhau tuỳ theo sự đòi hỏi dung
lượng bộ nhớ khác nhau của từng chương trình khác nhau. Thông thường:
- Mã chương trình dài nhất chỉ cần 1 đoạn (64 KB) để chứa.
-Và dữ liệu chương trình nhiều nhất chỉ cần 1 đoạn (64 KB) để chứa.
Thích hợp nhất nên chọn Kiểu_kích_thước_bộ_nhớ là SMALL hoặc nếu
tất cả mã & DL có thể gói gọn trong 1 đoạn thì chọn
Kiểu_kích_thước_bộ_nhớ là TINY.
2.8.2. Khai báo ngăn xếp
Việc khai báo ngăn xếp là cốt để dành ra 1 vùng nhớ đủ lớn dùng làm ngăn xếp
phục vụ cho hoạt động của chương trình khi có chương trình con.
Việc khai báo thực hiện nhờ hướng dẫn chương trình dịch .Stack
Cú pháp: .Stack kích_thước

64
GV. Vương Quốc Dũng
Kích thước quyết định số byte trong bộ nhớ giành cho ngăn xếp
Nếu không khai báo, chương trình dịch tự động gán cho kích thước giá trị 1 KB,
đây là giá trị quá lớn với 1 ứng dụng thông thường
Trong thực tế thường lấy kích thước từ 100 đến 256 byte
ví dụ:
.Stack 100 (kích thước = 100 byte)
.Stack 100h (kích thước = 256 byte)
2.8.3. Khai báo đoạn dữ liệu:
Đoạn dữ liệu: chứa toàn bộ các định nghĩa cho các biến của chương trình.
Các hằng cũng nên định nghĩa ở đây để đảm bảo tính hệ thống mặc dù ta có thể để
chúng ở bên trong chương trình như đã nói ở phần trên.
Việc khai báo đoạn DL được thực hiện nhờ hướng dẫn chương trình dịch .DATA,
việc khai báo biến & hằng được thực hiện tiếp ngay sau đó bằng các lệnh giả.
Ví dụ
.DATA
MSG DB ‘Hello!’
CR EQU 13
LF EQU 10
X1 DB ?
2.8.4. Khai báo mã
Đoạn mã chứa mã lệnh của chương trình, việc khai báo đoạn mã được thực hiện
nhờ những hướng dẫn chương trình dịch .CODE
Bên trong đoạn mã các dòng lệnh phải được tổ chức 1 cách hợp lý, đúng ngữ pháp
dưới dạng 1 chương trình chính & nếu cần thiết thì kèm theo các chương trình con.
Các chương trình con sẽ được gọi ra bằng các lệnh CALL có mặt bên trong
chương trình chính.
1 thủ tục được định nghĩa nhờ các lệnh giả PROC & ENDP
Lệnh giả PROC để bắt đầu 1 thủ tục
ENDP để kết thúc 1 thủ tục
Vậy chương trình chính có thể được định nghĩa bằng các lệnh giả PROC &
ENDP theo mẫu sau:
Tên_CTC PROC
; các lệnh của thân CTC
CALL tên _ CTc; gọi CTc
; các lệnh của thân CTC
Tên_CTC Endp
Giống như chương trình chính, 1 chương trình con cũng được định nghĩa dưới
dạng 1 thủ tục nhờ các lệnh giả PROC & ENDP theo mẫu sau
Tên_CTc PROC
; các lệnh của thân CTc
RET
Tên_CTC Endp

65
GV. Vương Quốc Dũng
chú ý trong mẫu chương trình nói trên, ngoài các lệnh giả có tính nghi thức bắt
buộc. Ta cần chú ý đến sự bố trí của lệnh gọi chương trình con (CTc) -(CALL)
trong chương trình chính (CTC) & lệnh RET (trở về) trong CTc.
2.8.5. Khung của chương trình hợp ngữ tổng quát
.MODEL Kiểu_kích_thước_bộ_nhớ
.STACK kích_thức
.DATA
; các định nghĩa cho biến và hằng để tại đây
.CODE
Tên_CTC Proc
; các lệnh của thân CTC
CALL tên_CTc; gọi CTc nếu có
; các lệnh của thân CTC
Tên_CTC Endp
; các CTc nếu có
END tên_CTC

DOS thi hành được 2 loại tập tin: dạng •EXE và •COM. Tập tin dạng •EXE
thường dùng để xây dựng các chương trình lớn, còn tập tin dạng •COM để xây
dựng các chương trình có dung lượng nhỏ hơn 64 KB. Các bộ hợp dịch TASM và
MASM cho phép tạo cả 2 loại tập tin •EXE và •COM, cách viết một chương trình
hợp ngữ đối với từng loại •EXE hay •COM là khác nhau.

2.8.6. Khung của chương trình hợp ngữ dạng tập tin .exe:
a. Đặc điểm của tập tin • EXE:
Chương trình có thể khai báo nhiều đoạn khác nhau, mỗi chương trình có thể có
nhiều đoạn chương trình (đoạn mã lệnh) và nhiều đoạn dữ liệu tuỳ theo qui mô
khai báo mô hình bộ nhớ.
Có thể gọi chương trình con dạng NEAR hay FAR.
Kích thước tập tin có thể lớn tùy ý thùy thuộc vào kích thước bộ nhớ của máy tính.
Thường thì dạng tập tin •EXE dùng để xây dựng các chương trình lớn có kích
thước lớn 64 KB.
Tập tin có một header ở đầu tập tin. Header này chứa các thong tin điều khiển về
tập tin để DOS để DOS có thể nạp nó vào bộ nhớ và thực hiện. Kích thước header
phụ thuộc vào số các lệnh trong chương trình cần định vị lại các địa chỉ đoạn khi
chương trình được nạp, nhưng nó luôn là bội số của 512 bytes.

b. Cách thực hiện một tập tin dạng • EXE:


Khi DOS thực hiện tập tin •EXE, DOS tạo ra vùng PSP (Program Segment Prefix)
gồm 256 bytes giống như nó tạo trong tập tin dạng •COM. Kế đó DOS kiểm tra
vùng header để xác định vị trí cuối của vùng header ở đâu, phần chương trình và
dữ liệu nằm ngay sau vùng header sẽ được DOS nạp vào bộ nhớ ngay sau PSP.

66
GV. Vương Quốc Dũng
Dựa vào thông tin header, DOS sẽ định vị lại một số lệnh có liên quan đến địa chỉ
đoạn trong chương trình.
Các thanh ghi đoạn DS và ES chỉ đến PSP. Thanh ghi CS và thanh ghi con trỏ lệnh
IP được xác định từ các thông tin trong vùng header để chỉ đến vị trí bắt đầu của
chương trình. Các thanh ghi SS và SP cũng được xác định từ các thông tin trong
header để chỉ đến ngăn xếp (stack).
Dạng tập tin • EXE có thể liên kết từ các phần tách rời nhau, mỗi phần tách riêng
trên các tập tin khác nhau. Mỗi phần có thể dùng một tên đoạn riêng, các chương
trình con có thể khai báo dưới dạng gần hoặc xa. Tuy nhiên các phần được liên kết
với nhau phải chứa duy nhất một đoạn với kiểu stack. Chương trình có duy nhất
một điểm bắt đầu được xác định bởi lệnh END trong phần chính.
c. Khung chương trình tập tin dạng • EXE
Từ khung tổng quát của chương trình hợp ngữ, ta có thể xây dựng một khung
tổng quát cho các chương trình hợp ngữ với kiểu kích thước bộ nhớ nhỏ.
Sau đây là khung cho chương trình hợp ngữ để rồi sau khi được dịch
(assembled) và kết nối (linked) trên máy IBM-PC, sẽ tạo ra một tệp chương
trình chạy được ngay (executable) với đuôi .exe
.MODEL small
.STACK 100
.DATA
; các định nghĩa cho biến và hằng để tại đây
.CODE
Tên_CTC Proc
MOV AX,@Data
MOV DS, AX ; Khởi tạo đoạn dữ liệu
MOV ES, AX ; Khởi tạo đoạn dữ liệu phụ - nếu cần
; các lệnh của thân CTC
CALL tên_CTc ; gọi CTc nếu có
; các lệnh của thân CTC
MOV AH, 4Ch ; Hàm 4Ch của
INT 21h ; ngắt 21h của DOS - để trở về DOS
Tên_CTC Endp
; các CTc nếu có
END tên_CTC
Khi một chương trình .exe được nạp vào bộ nhớ, DOS sẽ tạo ra 1 mảng gồm
256 bytes cho “đoạn mào đầu chương trình” (Program Segment Prefix - PSP)
dùng để chứa các thông tin liên quan đến chương trình và đặt nó ngay vào phía
trước phần bộ nhớ chứa mã lệnh của chương trình.
DOS cũng đưa các thông số liên quan đến chương trình vào các thanh ghi
DS và ES. Do vậy DS và ES không chứa giá trị địa chỉ của đoạn dữ liệu cho
chương trình. Để chương trình có thể chạy đúng, ta phải có các lệnh để khởi tạo
giá trị đầu cho thanh ghi DS (hoặc cả ES - nếu cần).
Ta phải dùng thanh ghi AX làm trung gian cho việc khởi đầu DS như trên là
do bộ VXL 8086/ 8088 vì lý do kỹ thuật không cho phép chuyển giá trị số (chế

67
GV. Vương Quốc Dũng
độ địa chỉ tức thì) vào các thanh ghi đoạn. Thanh ghi AX có thể được thay bằng
các thanh ghi đa năng khác trong các lệnh khởi tạo ở trên.

2.8.7. Khung của chương trình hợp ngữ dạng tập tin • COM:
Trên máy IBM-PC ngoài tệp chương trình (CT) với đuôi .exe, chúng còn có
khả năng hợp dịch chương trình hợp ngữ có kết cấu thích hợp ra một loại tệp
chương trình có khả năng chạy được kiểu khác với đuôi •COM.
Đây là tệp 1 CT ngắn gọn và đơn giản hơn nhiều so với tệp CT .exe.
Trong CT .com, các đoạn mã, đoạn dữ liệu và đoạn ngăn xếp được gộp lại
trong 1 đoạn duy nhất là đoạn mã. Nếu các ứng dụng mà dữ liệu và mã CT
không yêu cầu nhiều về không gian bộ nhớ thì ta có thể ghép luôn cả dữ liệu,
mã CT, ngăn xếp vào chung cùng một đoạn mã rồi tạo ra tệp .com.
Việc tạo ra tệp .com giúp ta tiết kiệm được thời gian và bộ nhớ khi chạy CT
và còn tiết kiệm được cả không gian nhớ khi phải lưu nó trên ổ đĩa.
Để có thể dịch được ra CT đuôi .com thì CT nguồn hợp ngữ phải được kết
cấu sao cho thích hợp với mục đích này.
a. Đặc điểm của tập tin • COM
Chỉ có duy nhất một đoạn. Đoạn chương trình (mã lệnh), đoạn dữ liệu, và đoạn
ngăn xếp (stack) đều dùng chung một đoạn.
Kích thước tối đa của tập tin là 64 KB trừ đi độ dài của PSP là 256 bytes và trừ đi
2 bytes dành cho đỉnh ban đầu của ngăn xếp.
Tập tin dạng •COM nạp vào bộ nhớ và thực hiện nhanh hơn tập tin dạng •EXE,
nhưng nó chỉ áp dụng cho các chương trình nhỏ, chỉ có thể gọi các chương trình
con dạng NEAR (dạng gần). Khi muốn xây dựng các chương trình lớn ta phải viết
dưới dạng •EXE.
Tập tin dạng •COM có thể liên kết từ các phần tách rời nhau, mỗi phần tách riêng
trên những tập tin khác nhau. Tất cả các phần này phải có cùng tên đoạn, cùng tên
lớp với phần chính và phải chứa điểm bắt đầu của chương trình và phải liên kết
trước.
b. Cách thực hiện tập tin dạng • COM
Khi DOS thực hiện tập tin • COM, DOS định vị bộ nhớ và tạo một vùng nhớ dài
256 bytes ở offset 0000h trong đoạn mã lệnh, vùng này gọi là PSP (Program
Segment Prefix). PSP chứa các thông tin cần thiết cho DOS. Sau đó tập tin . • COM
sẽ được nạp ngay sau PSP ở địa chỉ offset 100h và định vị đỉnh stack trước khi
DOS chuyển quyền điều khiển cho chương trình • COM.
Tất cả các thanh ghi đoạn CS, DS, ES, SS đều trỏ đến PSP. Thanh ghi cong trỏ
lệnh IP được gán giá trị 100h (địa chỉ offset của lệnh đầu tiên trong chương trình sẽ
được thực hiện), thanh ghi SP thường được gán giá trị FFFEh.
Sau đây là khung của 1 CT hợp ngữ để dịch được ra tệp CT đuôi .com.

c. Khung chương trình tập tin dạng • COM


.MODEL tiny
.CODE
68
GV. Vương Quốc Dũng
ORG 100h
Start: JMP continue
; các định nghĩa cho biến và hằng để tại đây
Continue:
Tên_CTC Proc
; các lệnh của thân CTC
CALL tên_CTc ; gọi CTc nếu có
; các lệnh của thân CTC
INT 20h ; Trở về DOS (cho gọn)
; Hoặc:
; MOV AH, 4Ch ; Hàm 4Ch của
; INT 21h ; ngắt 21h của DOS - để trở về DOS
Tên_CTC Endp
; các CTc nếu có
END Start
2.8.8. So sánh chương trình .COM với chương trình .EXE
Ta thấy:
Trong .Com không có khai báo đoạn ngăn xếp & đoạn DL
Trong .Com qui mô sử dụng bộ nhớ là kiểu TINY, .exe là SMALL
Trong CT .com ở ngay đầu đoạn mã là lệnh giả ORG (origin: điểm xuất phát) và
lệnh nhảy JMP.
Lệnh giả ORG 100h dùng để gán địa chỉ bắt đầu cho chương trình tại 100h trong
đoạn mã, tức là chừa lại vùng nhớ 256 byte (từ địa chỉ 0 đến 255) cho đoạn “mào
đầu chương trình” (PSP)
Lệnh JMP sau nhãn Start dùng để nhảy qua phần bộ nhớ dành cho việc định nghĩa
và khai báo dữ liệu. (về nguyên tắc ta có thể đặt khai báo dữ liệu ở đầu hoặc ở cuối
đoạn mã)
Đích của lệnh nhảy là phần đầu của chương trình chính.
Ngăn xếp của chương trình .COM:
Được xếp tại cuối đoạn mã, đỉnh của ngăn xếp lúc ban đầu là ô nhớ có địa
chỉ là FFFEH - từ cuối cùng trong đoạn. Ở đây người lập trình không phải
định nghĩa vùng ngăn xếp
* hạn chế của chương trình .COM:
Dung lượng cực đại của 1 đoạn là 64 KB, do đó ta phải luôn nắm chắc được rằng
chương trình ứng dụng phải có số lượng byte mã máy & DL cho chương trình
không lớn lắm (nếu không nó sẽ làm cho cả nhóm này nở ra về phải địa chỉ cao của
đoạn).
chương trình chỉ được phép sử dụng ngăn xếp 1 cách hạn chế (nếu không điều này
làm cho đỉnh của nó trong khi hoạt động dâng lên nhiều về phía địa chỉ thấp của
đoạn).
Tệp .COM được phân bổ trong bộ nhớ như sau:
offset

69
GV. Vương Quốc Dũng
0000H đoạn mào đầu chương trình
(PSP)
JMP CONTINUE
DL nằm tại đây
CONTINUE

offset = FFFEh
0100H
IP

chiều tiến của mã & DL



SP
chiều tiến của ngăn xếp

Tóm lại
chúng ta phải chắc chắn rằng không thể xảy ra hiện tượng chùm vào nhau
của các thông tin tại vùng ngăn xếp và thông tin tại vùng DL & mã lệnh.
Khi kết thúc chương trình .COM, để trở DOS ta dùng ngắt INT 20h của DOS để
làm cho chương trình ngắn gọn hơn mặc dù vẫn có thể dùng hàm 4CH của ngắt
INT 21h đã dùng trong tệp .EXE.
Để kết thúc toàn bộ chương trình ta dùng hướng dẫn chương trình dịch END đi
kèm theo nhãn START. ở đây nhãn START tương ứng với địa chỉ lệnh đầu tiên
của chương trình trong đoạn mã.

2.8.9. Các bước cơ bản khi lập trình trong giờ thực tập:
a. Các bước cơ bản khi lập trình trong giờ thực tập:
Bây giờ chúng ta đã có thể sẵn sàng xem xét các bước tạo lập và cho chạy 1
chương trình cụ thể trên máy. Có 4 bước cụ thể:
bước1: tạo lập file chương trình nguồn
Dùng 1 chương trình soạn thảo văn bản để tạo ra 1 file chương trình
nguồn. Ví dụ như NCedit, BK, notepad, Edit của DOS.
Yêu cầu phần mở rộng của tệp phải là .ASM vì đuôi .ASM được qui ước
dùng để định nghiã 1 file nguồn của hợp ngữ.
bước2: hợp dịch chương trình
Chúng ta sử dụng MASM (Microsoft Marco Assembler) để dịch file
nguồn sang file đối tượng ngôn ngữ máy *.obj.
Ví dụ đã có file nguồn VD1.ASM thì lệnh đơn giản nhất là:
C:\MASM VD1 ; ↵

70
GV. Vương Quốc Dũng
MASM hệ thống sẽ đưa ra thông tin bản quyền, MASM kiểm tra lỗi cú
pháp của file nguồn. Nếu tìm thấy 1 lỗi nào đó, nó sẽ hiển thị số dòng của
mỗi lỗi và 1 hướng dẫn ngắn gọn.
Nếu không có lỗi nào, MASM sẽ dịch mã hợp ngữ thành file đối tượng
ngôn ngữ máy VD1.OBJ
Dấu chấm phảy theo sau câu lệnh có nghĩa là chúng ta không muốn phát
sinh thêm các file khác nữa (chỉ có 1 file VD1.obj được sinh ra sau lệnh
trên).
Nếu bỏ dấu “;”đi thì ngoài file VD1.obj MASM sẽ in ra các tên có thể được
tạo thêm và đợi chúng ta đưa tên mới vào.
Các tên mặc định được viết trong dấu [ ] là:
Source listing [NUL.LST]: VD1 ↵
Cross reference [NUL.CRF]: VD1 ↵
Tên mặc định là NUL có nghĩa là sẽ không tạo tên file đó nếu ta không
đưa vào 1 tên. Do vậy ở đây ta trả lời (gõ vào) với tên VD1.
(Với TASM, gõ C:\TASM\TASM VD1 ↵ cho cả 2 loại khung CT)
Trong đó:
- Source listing [filename.LST] là một file văn bản mà các dòng được đánh
số hiển thị mã hợp ngữ bên cạnh mã máy tương ứng, đồng thời đưa ra các
thông tin khác về chương trình. Điều này đặc biệt có ích cho mục đích gỡ
rối bởi vì MASM báo lỗi đưa ra kèm theo chỉ số dòng.
Cross reference [filename.CRF] là một bảng liệt kê các tên xuất hiện trong
chương trình và số thứ tự các dòng mà nó có mặt. Nó cần thiết khi xác định
các biến và các nhãn trong 1 CT lớn.
bước3 hợp dịch chương trình:
Tệp *.opj được tạo lập ở bước 2 là 1 file ngôn ngữ máy nhưng nó không
thể thực hiện được bởi lẽ khuôn mẫu của nó chưa thích hợp với 1 file
chương trình, cụ thể:
Nó không biết được nơi mà chương trình sẽ được nạp vào trong bộ nhớ để thi hành
& địa chỉ mã máy có thể chưa được điền vào.
Một vài tên dùng trong chương trình có thể chưa được định nghĩa. Ví dụ: trong 1
chương trình lớn có thể cần phải tạo ra vài file & 1 thủ tục trong file này có thể
tham trỏ tới tên được định nghĩa trong file khác.
Chương trình LINK có nhiệm vụ liên kết các tệp đích (và có thể cả
các tập thư viện, thành 1 tệp chạy được duy nhất). Muốn vậy, nó phải
tham khảo được các tên dùng trong modul này nhưng lại được định
nghĩa trong modul khác. ta vẫn cần phải sử dụng chương trình LINK
thậm chí cả khi chỉ có 1 tệp đích. Trong trường hợp này chương trình
LINK đưa thêm vào modul .opj các yếu tố của 1 chương trình chạy
được.
Đầu vào chương trình LINK là 1 hay nhiều tệp đích & thư viện.
Đầu ra là 1 tệp chương trình chạy được & có thể có thêm 1 tệp “bản
đồ bộ nhớ khi nạp chương trình”.

71
GV. Vương Quốc Dũng
Để liên kết các chương trình ta gõ:
C:\> LINK VD1 hoặc C:\> LINK VD1; -> VD1.exe
(Với TASM: C:\>TLINK VD1 -> VD1.exe
và C:\>TLINK/t VD1 -> VD1.com cho khung CT .com).
C:\> exe2Bin VD1 ↵ -> VD.1 .com (Nếu CT khung .com)

Cú pháp tổng quát:


Với TASM:
1, C:\> TASM <tên file> ↵ VD1.obj
2, C:\> TLINK <tên file> ↵ VD1. exe
or C:\>TLINK/t <tên file> ra luôn VD1.COM (Với khung .com)
Với MASM:
1, C:\> MASM <tên file> ↵ VD.1.obj
2, C:\> LINK VD1 ↵ VD.1.exe
3, C:\> exe2Bin VD1 ↵ VD.1 .com (Nếu CT khung .com)
C:\>LINK [tuỳ chọn] D/sách*.opj tệp.exe tệp.Map D/sách tệp.LIB
(Xem tham khảo Tài liệu VXL)
bước4 : chạy 1 chương trình, ví dụ chay chương trình VD1.exe:
để chạy chương trình ta đánh
C:\>VD1 ↵ hoặc C:\>VD1.exe ↵
*chú ý: các lệnh trên giả định tất cả các tệp chương trình như MASM, LINK,
VD1... đều thuộc thư mục gốc ở ổ C.
b. Một số ví dụ
VD1.ASM: Chương trình đọc 1 ký tự bàn phím và hiển thị nó, và ở đầu dòng tiếp
theo cũng hiển thị nó.
TITLE VD1: SAMPNE Program
•MODE SMALL

•STACK 100h

•CODE

MAIN PROC
; Hiển thị lời nhắc là dấu ? ra màn hình
MOV ah, 2 ; Hàm hiển thị ký tự
MOV dl, ‘?’ ; Ký tự là “?”
INT 21h ; Hiển thị ký tự ra màn hình
; Vào 1 ký tự & hiền thị ký tự
MOV ah,1 ; Đọc ký tự từ bộ đệm bàn phím và gửi vào AL
; sau đó đưa ký tự tới thiết bị ra chuẩn (màn hình).

72
GV. Vương Quốc Dũng
INT 21h ; Gọi ngắt, thực hiện hàm 1.
MOV bl, al ; Cất ký tự trong BL
; Xuống dòng mới
MOV ah, 2 ; Hàm hiển thị ký tự
MOV dl, 0dh ; Về đầu dòng
MOV dl, 0ah ; xuống dòng
INT 21h ; Thực hiện xuống dòng
; Hiền thị ký tự
MOV dl, bl ; lấy ký tự
INT 21h ; và hiển thị nó
; Trở về DOS
MOV ah, 4ch ; Hàm thoát về DOS
INT 21h ; Thực hiện thoát về DOS
MAINT ENDP
END MAIN
Chú ý: Trong chương trình này do không dùng các biến nên bỏ qua
đoạn dữ liệu
VD2.ASM: Đọc 1 ký tự từ bàn phím & hiển thị nó ở đầu dòng tiếp theo
TITLE VD2 ; Sample Program
•Model Small

•Stack 100h

•Code

Main Proc
; Hiển thị lời nhắc
MOV ah, 2 ; Hàm hiển thị ký tự
MOV dl, ‘?’ ; ‘?’ - là lời nhắc
INT 21h ; Hiển thị ký tự
; Đọc 1 ký tự từ bàn phím : dùng hàm 0 ngắt 16H của BIOS
MOV ah, 0 ; Hàm đọc ký tự
INT 16h ; Đọc ký tự AH = mã Scan bàn phím
; AL = mã ASCH của ký tự
MOV bl, al ; Cất mã ASCII của ký tự vào BL
; Xuống dòng mới
MOV ah, 2 ; Hàm hiển thị ký tự
MOV dl, 0dh ; ký tự về đầu dòng
INT 21h ; Thực hiện về đầu dòng
MOV dl, 0ah ; ký tự xuống dòng
INT 21h ; Thực hiện xuống dòng
; Hiển thị ký tự vào từ bàn phím
MOV dl, bl ; Lấy ký tự lưu trong BL
INT 21h ; Hiển thị ký tự
; Trở về DOS
MOV ah, 4ch ; Hàm thoát về DOS
INT 21h ; Thoát về DOS

73
GV. Vương Quốc Dũng
MAIN ENDP
END MAIN
Ví dụ chương trình *. COM-
VD3.ASM: Chương trình đọc ký tự từ bàn phím, hiển thị nó & sau đó lại hiện
thị nó ở đầu dòng tiếp theo
TITLE VD1: SAMPLE PROGRAM
•Model Tiny

•Code

ORG 100h
START: JMP NHAN
; các định nghĩa cho các biến & hằng đăt tại đây
NHAN:
MAIN PROC
; Hiển thị lời nhắc
MOV ah, 2 ; Hàm hiển thị ký tự
MOV dl, ‘?’ ; là dấu ?
INT 21h ; Đưa ký tự ‘?’ ra màn hình
MOV ah, 1 ; Đọc ký tự vào từ bàn phím
; (AL chứa mã ASCII của ký tự đó)
INT 21h ; Hiển thị ký tự
MOV bl, al ; cất ký tự trong BL
; Xuống dòng mới
MOV ah, 2
MOV DL, 0dh
INT 21h
MOV dl, 0ah
INT 21h
MOV dl, bl ; lấy ký tự
INT 21h ; & hiển thị nó
MOV ah, 4ch
INT 21h
MAIN ENDP
END START
* Hợp dịch chương trình VD1 như sau
Với TASM:
1, C:\> TASM VD1 ↵ VD1.obj
2, C:\> TLINK VD1 ↵ VD1. exe
or C:\>TLINK/t VD1 ↵ ra luôn VD1.COM (Với CT khung .com)
Với MASM:
1, C:\> MASM VD1 ↵ VD.1.obj
2, C:\> LINK VD1 ↵ VD.1.exe
3, C:\> exe2Bin VD1 ↵ VD.1 .com (Nếu CT khung .com)

74
GV. Vương Quốc Dũng
2.9. Các phép định địa chỉ trong ASSEMBLER
2.9.1. Địa chỉ các phần tử trong mảng.
Mảng 2 chiều là một mảng 1 chiều mà mỗi phần tử trong nó lại là một mảng 1
chiều. Trước hết, chúng ta nghiên cứu cách xác định địa chỉ của các phần tử
trong mảng một chiều.
a. Địa chỉ các phần tử trong mảng 1 chiều
Giả sử có một một mảng 1 chiều A có n phần tử, số bytes một phần tử chiếm
chỗ là S và S phụ thuộc vào kiểu phần tử.
Kiểu phần tử là DB (Kiểu byte) thì S=1
Kiểu phần tử là DW (Kiểu word) thì S=2
Kiểu phần tử là DD (Kiểu Double word) thì S=4
Ta có công thức xác định địa chỉ của các phần tử trong mảng như sau:
Địa chỉ phần tử i = Địa chỉ cơ sở của mảng + (i-1)*S
Trong đó:
i = 1 ÷ n là chỉ số của các phần tử trong mảng (Số thứ tự của phần tử.
Địa chỉ cơ sở của mảng chính là địa chỉ của phần tử đầu tiên trong mảng (Địa chỉ
cơ sở của mảng = địa chỉ bắt đầu của mảng, hay còn gọi là địa chỉ biến mảng).
Ví dụ:
Giả sử mảng A1 DW 10, 9, 17, 258
Có địa chỉ cơ sở = địa chỉ của A1, và địa chỉ offset gán của A1 là 0200h, thì
địa chỉ các phần tử trong mảng được phân bổ như sau:

Thứ tự phần tử Địa chỉ offset Ký hiệu địa chỉ Giá trị phần tử
1 0200h A1 10
2 0202h A1 + 2h 9
3 0204h A1 + 4h 17
4 0206h A1 + 6h 258

b. Địa chỉ các phần tử trong mảng 2 chiều

b1. Cách lưu trữ mảng 2 chiều trong bộ nhớ


Vì bộ nhớ là mảng một chiều nên các phần tử của mảng 2 chiều phải được lưu
trữ liên tiếp nhau.
Có 2 phương pháp thường được sử dụng để lưu trữ mảng 2 chiều:
Phương pháp lưu trữ theo thứ tự hàng:
Các phần tử hàng 1 lần lượt được lưu trữ, tiếp theo đến các phần tử hàng
2, rồi đến các phần tử hàng 3,...
Phương pháp lưu trữ theo thứ tự hàng:
Các phần tử cột 1 lần lượt được lưu trữ, tiếp theo đến các phần tử cột 2,
rồi đến các phần tử cột 3,...
Ví dụ:
Mảng B có 3 hàng 4 cột có thể được lưu trữ như sau:

75
GV. Vương Quốc Dũng
+ Theo hàng: B DB 1 2 3 4
DB 5 6 7 8
DB 9 10 11 12
+ Theo cột: B DB 1 5 9
DB 2 6 10
DB 3 7 11
DB 4 8 12
Hầu hết các CT biên dịch ngôn ngữ bậc cao lưu trữ mảng 2 chiều theo thứ tự
hàng.
Trong hợp ngữ chúng ta có thể lưu trữ theo cả 2 cách
Nếu như các phần tử của hàng được xử lý liên tiếp nhau thì phải lưu trữ theo thứ tự
hàng vì các phần tử kề nhau trong một hàng sẽ chiếm các ô nhớ kế tiếp nhau.
Nếu như các phần tử của cột được xử lý liên tiếp nhau thì phải lưu trữ theo thứ tự
cột vì các phần tử kề nhau trong một cột sẽ chiếm các ô nhớ kế tiếp nhau.

b2. Xác định địa chỉ một phần tử trong mảng 2 chiều
Giả sử ta có một mảng 2 chiều lưu trữ trong bộ nhớ theo thứ tự hàng, có N
phần tử trong mỗi hàng và có M hàng, trong đó kích thước mỗi phần tử là S,
địa chỉ cơ sở của mảng là A (Địa chỉ của biến mảng là A). Để tìm địa chỉ của
phần tử A[i,j], ta xác định:
Vị trí bắt đầu của hàng i.
Vị trí của phần tử thứ j trong hàng.
Bước 1: Xác định địa chỉ bắt đầu hàng i
Hàng 1 bắt đầu từ địa chỉ A, do có N phần tử trong mỗi hàng và mỗi phần
tử có kích thước S bytes. Như ta đã biết
Địa chỉ phần tử cuối cùng của hàng 1 = A + (N-1)*S
Do đó địa chỉ phần tử đầu tiên hàng 2 = A + N*S
Tương tự, địa chỉ phần tử đầu tiên hàng 3 = A + 2*N*S
Một cách tổng quat ta có:
Hàng i sẽ bắt đầu tại địa chỉ: A + (i-1)*N*S
Bước 2: Xác định địa chỉ phần tử thứ i trong hàng i
Theo như mảng 1 chiều, phần tử thứ j được lưu trữ ở vị trí (j-1)*S kể từ vị
trí của phần tử đầu hàng.
Vậy mảng A kích thước M*N và mỗi phần tử chiếm S bytes, được lưu trữ
theo thứ tự hàng thì:
A[i,j] có địa chỉ = A + ((i -1)*N + (j -1))*S
Tương tự với cách lưu trữ theo cột:
A[i,j] có địa chỉ = A + ((j -1)*M + (i -1))*S

76
GV. Vương Quốc Dũng
2.9.2. Các chế độ địa chỉ trong ASSEMBLER
Bộ VXL 8086 có 4 chế độ địa chỉ, đó là:
- Chế độ địa chỉ thanh ghi
- Chế độ địa chỉ cơ sở Hữu ích khi thao tác với mảng 1 chiều
- Chế độ địa chỉ chỉ số
- Chế độ địa chỉ chỉ số cơ sở Hữu ích khi thao tác với mảng 2 chiều

Các chế độ này được sử dụng để đánh địa chỉ ô nhớ các toán hạng một cách
gián tiếp.
a. Chế độ địa chỉ thanh ghi (Chế độ địa chỉ gián tiếp thanh ghi)
Trong chế độ này, địa chỉ offset của toán hạng được chứa trong một thanh ghi.
Như vậy các thanh ghi đóng vai trò như một con trỏ trỏ đến các ô nhớ.
Khuôn dạng của toán hạng ở chế độ này là:
[Register]
Register có thể là BX, DI, SI hay BP
Với các thanh ghi BX, DI, SI địa chỉ đoạn của toán hạng được chứa trong thanh
ghi DS
- Với thanh ghi BP địa chỉ đoạn của toán hạng được chứa trong thanh ghi SS
Ví dụ 5.1:
SI chứa địa chỉ offset là 0100h và Word tại địa chỉ này có giá trị 1243h, khi
thi lệnh
MOV AX, [SI]
Thì CPU sẽ kiểm tra SI để suy ra địa chỉ của từ nhớ (word) chứa dữ liệu
là DS:0100h, sau đó nó chuyển nội dung của từ nhớ có địa chỉ trên vào
thanh ghi AX (Tức chuyển giá trị 1243h vào AX)
Điều đó khác hẳn lệnh
MOV AX, SI ; Chuyển giá trị của SI = 0100h vào AX
Ví dụ 5.2:
Viết các lệnh đưa vào AX tổng các phần tử của một mảng 10 phần tử kiểu
word được định nghĩa như sau:
W DW 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Giải:
Để tính tổng 10 phần tử của mảng ta dùng phương pháp cộng dồn bằng
cách thiết lập một con trỏ trỏ đến địa chỉ cơ sở của mảng, và cho nó dịch
chuyển đến hết lượt các phần tử của mảng đồng thời cộng phần tử được
tham trỏ tới vào tổng.
XOR AX, AX ; Khởi tạo tổng = 0
LEA SI, W ; SI trỏ đến mảng W
MOV CX, 10 ; Lặp 10 lần
ADDNOS: ADD AX, [SI] ; Cộng phần tử được SI trỏ tới vào AX
ADD SI, 2 ; SI trỏ tới phần tử tiếp theo

77
GV. Vương Quốc Dũng
LOOP ADDNOS; Lặp đến khi CX = 0

b. Chế độ địa chỉ cơ sở và chế độ địa chỉ chỉ số:


Trong các chế độ địa chỉ này, địa chỉ offset của các toán hạng nhận được bằng
cách cộng một số (được gọi là độ dịch) với nội dung của một thanh ghi. trong
đó độ dịch có thể là:
Địa chỉ offset của một biến
Một hằng số (âm hoặc dương)
Địa chỉ offset của một biến ± một hằng số
Ví dụ độ dịch có thể là: A - địa chỉ offfset của biến A
A+4 hoặc A-4 hoặc A-2
-2
Khuôn dạng của toán hạng:
Phải được viết tương đương với một trong các biểu thức sau:
[Thanh_ghi + độ_dịch]
[độ_dịch + Thanh_ghi]
[Thanh_ghi] + độ_dịch
độ_dịch + [Thanh_ghi]
độ_dịch[Thanh_ghi]
Trong đó
Các thanh ghi phải là BX, BP, SI hoặc DI.
Nếu dùng BX, SI hoặc DI thì DS chứa địa chỉ đoạn của toán hạng.
Nếu dùng BP thì SS chứa địa chỉ đoạn của toán hạng.
Chế độ địa chỉ được gọi là chế độ địa chỉ cơ sở (Based) nếu dùng thanh ghi BX
(Base Register) hay BP (Base Pointer)
Chế độ địa chỉ được gọi là chế độ địa chỉ chỉ số (Indexed) nếu dùng thanh ghi SI
(Source Indexed) hay DI (Destination Indexed)
Ví dụ 5.3:
W là mảng kiểu word, BX chứa giá trị 4, thì lệnh sau
MOV AX, [W+BX]
Sẽ chuyển nội dung phần tử có địa chỉ (W+4) vào AX, đây là nội dung
phần tử thứ 3 của mảng (Vì mỗi phần tử chiếm 2 bytes)
Lệnh trên còn có thể được viết dưới các dạng sau:
MOV AX, W[BX]
MOV AX, [BX+W]
MOV AX, W+[BX]
MOV AX, [BX]+W
Ví dụ 5.4: Làm lại ví dụ 5.2 bằng cách dùng chế độ địa chỉ cơ sở
XOR AX, AX ; Khởi tạo tổng = 0
XOR BX, BX ; Xoá BX = 0
MOV CX, 10 ; Lặp 10 lần
ADDNOS: ADD AX, W[BX] ; Cộng phần tử có địa chỉ [W+BX] vào AX

78
GV. Vương Quốc Dũng
ADD BX, 2 ; Trỏ tới phần tử tiếp theo
LOOP ADDNOS; Lặp đến khi CX = 0

c. Chế độ địa chỉ chỉ số cơ sở


Trong chế độ này, địa chỉ offset của một toán hạng là tổng của
1. Nội dung của thanh ghi cơ sở (BX
hay BP)
2. Nội dung của thanh ghi chỉ số (SI
hay DI)
3. (Có thể) Địa chỉ offset của một
biến kiểu bytes
4. (Có thể) Một hằng số (âm hoặc
dương).
- Nếu sử dụng BX thì DS chứa địa chỉ đoạn của toán hạng.
- Nếu sử dụng BP thì SS chứa địa chỉ đoạn của toán hạng.
Khuôn dạng của toán hạng:
Toàn hạng có thể được viết ở nhiều dạng khác nhau, sau đây là một số dạng
thường dùng
Biến_nhớ[thanh_ghi_cơ_sở][thanh_ghi_chỉ_số]
[thanh_ghi_cơ_sở + thanh_ghi_chỉ_số + biến_nhớ + hằng_số]
Biến_nhớ[thanh_ghi_cơ_sở + thanh_ghi_chỉ_số + hằng_số]
hằng_số[thanh_ghi_cơ_sở + thanh_ghi_chỉ_số + biến_nhớ]
Chú ý: thứ tự các phần tử trong dấu ngoặc là tùy ý

Ví dụ 5.5
W là một biến kiểu byte, BX chứa giá trị 2, SI chứa giá trị 5
Khi đó lệnh:
MOV AL, W[BX][SI]
Sẽ chuyển nội dung của ô nhớ có địa chỉ DS:(W+2+5) hay DS:(W+7)
vào AL.
Lệnh trên có thể được viết dưới dạng sau:
MOV AX, [W+BX+SI]
hoặc MOV AX, W[BX+SI]
Chế độ địa chỉ này đặc biệt hữu hiệu khi ta xử lý mảng 2 chiếu.
Ví dụ 5,6
Giả sử mảng A kiểu word, có kích thước 5*7, lưu trữ dữ liệu theo thứ tự
hàng. Hãy viết các lệnh sử dụng chế độ địa chỉ chỉ số cơ sở để:
Xoá hàng thứ 3
Xoá cột thứ 4.
Giải:

79
GV. Vương Quốc Dũng
Ta biết rằng hàng i sẽ bắt đầu tại địa chỉ
A+(i-1)*N*S ở đây S = 2 vì mảng kiểu word
Như vậy với mảng 5*7, hàng 3 bắt đầu tại địa chỉ:
A+(3-1)*7*2 = A + 28
Chung ta cũng biết rằng cột j bắt đầu tại địa chỉ là địa chỉ của phần tử thứ j
trong hàng thứ nhất, tức là địa chỉ
A+(j-1)*S
Như vậy cột 4 sẽ bắt đầu tại địa chỉ
A+(4-1)*2 = A + 6
Do A có 7 cột và được lưu trữ theo thứ tự hàng, mỗi phần tử chiếm 2 bytes,
nên để trỏ tới phần tử tiếp theo trong cột ta phải cộng thêm địa chỉ offset với
7*2 = 14
Vậy ta có thể vieet đoạn chương trình sau:
; Xoá hàng thứ 3
MOV BX, 28 ; BX chứa độ dịch đến đầu hàng thứ 3
XOR SI, SI ; SI chứa độ dịch đến phần tử thứ nhất
; trong hàng.
MOV CX, 7 ; Lặp 7 lần để xoá 7 phần tử trong hàng
CLEAR: MOV A[BX][SI], 0 ; Xoá giá trị một phần tử về 0
ADD SI, 2 ; SI chứa độ dịch đến phần tử tiếp theo
; trong hàng.
LOOP CLEAR ; Lặp 7 lần
; Xoá hàng thứ 4
MOV SI, 6 ; BX chứa độ dịch đến đầu cột thứ 4
XOR BX, BX ; SI chứa độ dịch đến phần tử thứ nhất
; trong cột.
MOV CX, 5 ; Lặp 5 lần để xoá 5 phần tử trong cột
CLEAR1: MOV A[BX][SI], 0 ; Xoá giá trị một phần tử về 0
ADD BX, 14 ; BX chứa độ dịch đến phần tử tiếp theo
; trong cột.
LOOP CLEAR1 ; Lặp 5 lần

CÂU HỎI ÔN TẬP VÀ BÀI TẬP

1. Tên nào dưới đây hợp lệ trong ngôn ngữ Assembly


TWO_WORD
?1
Two_word
.@
$143
LET’S GO
T=
2. Số nào trong các số dưới đây hợp lệ trong chương trình hợp ngữ? Nếu chúng
hợp lệ, hãy chỉ rõ chúng là số nhị phân, số thập phân, hay số thập lục phân.

80
GV. Vương Quốc Dũng
246
246h
101101
2A3h
FFF 7h
CDBAh
0Ah
Bh
111001b
3. Hãy nêu chỉ dẫn định nghĩa dữ liệu cho các biến và hằng sau đây nếu như chúng
hợp lệ
a. Biến A kiểu word được khởi tạo giá trị 76
b. Biến B kiểu word không được khởi tạo giá trị
c. Biến C kiểu byte được khởi tạo giá trị 5Ah
d. Biến D kiểu byte không được khởi tạo giá trị
e. Biến E kiểu word được khởi tạo giá trị 65536
f. Biến mảng Arr kiểu word được khởi tạo các giá trị 1, 2, 3, 4, 5
g. Hằng BEL có giá trị 07h
h. Hằng MSG có giá trị ‘This is a message’
4. Các biến Var1, Var 2, Var3 sau chiếm bao nhiêu byte trong vùng nhớ:
Var1 DB ?,?
Var2 DW 4 dup(?), 20
Var3 DB 10 dup(5 dup(?))
5. Giả thiết rằng các số liệu sau đây được nạp vào bộ nhớ bắt đầu từ địa chỉ offset
0000h
A DB 7
B DW 1ACBh
C DB ‘Hello’
a. Hãy cho biết địa chỉ offset của các biến A, B, C
b. Hãy cho biết nội dung của byte tại địa chỉ offset 0002h ở dạng Hex
c. Hãy cho biết nội dung của byte tại địa chỉ offset 0004h ở dạng Hex
d. Hãy cho biết địa chỉ offset của ký tự ‘o’ trong ‘Hello’
6. Trong đoạn dữ liệu chứa các dữ liệu sau:
•DATA
Arr1 DB 10, 34, 56, 89, 92, 47, 152, 200, 243
Arr2 DW 1234h, 2A57h, 32Ch, 136h, 0205h, 1009h
DW 1A76h, 94DCh, 4Ah, 678h, 0A7h, 1BCh
DW 0ACBDh, 2A76h, 7766h, 3324h, 10h, 54h
Arr3 DD 12345678h, 0FA123h, 10h, 73Ah
DD 0A1BCDEh, 0BCD1234h, 79h, 88h
DD 98DCBAh, 745ACh, 669097h, 123Ah
Và chúng ta có các câu lệnh sau:
MOV AX, @data
MOV DS, AX

81
GV. Vương Quốc Dũng
MOV BX, 2
MOV SI, 3
MOV DI, 1
Hãy xác định nội dung của các thanh ghiAL và DX trong các câu lệnh sau:
a. MOV AL, Arr1+3
b. MOV AL, Arr1[BX]
c. MOV AL, Arr1[BX+DI+3]
d. MOV AL, BYTE PTR Arr2
e. MOV AL, BYTE PTR Arr2+1
f. MOV DX, Arr2+2
g. MOV DX, Arr2[BX]
h. MOV DX, Arr2[BX+SI]
i. MOV DX, Arr2[BX+DI+3]
j. MOV DX, WORD PTR Arr3
k. MOV DX, WORD PTR Arr3[BX+SI]
l. MOV DX, WORD PTR Arr3[BX+DI+1]
m. MOV DX, WORD PTR Arr3[BX+SI+1]
7. Các câu lệnh sau đây đúng hay sai, nếu sai thì giải thích tại sao sai
a. K EQU 10
MOV K, 20
MOV K, AL
MOV AL, K
MOV AL,2*K+1
b. K DB 10
MOV K, 20
MOV K, 256
MOV AL, K
MOV AL,2*K+1
8. Hãy cho biết mỗi lệnh dưới đây là hợp lệ hay không hợp lệ, trong đó W1 và
W2 là các biến WORD; B1, B2 là các biến BYTE.
a. MOV DS, AX
b. MOV DS, 100h
c. MOV DS, ES
d. MOV W1, DS
e. XCHG W1, W2
f. SUB 5, B1
g. ADD B1, B2
h. ADD Al, 256
i. MOV W1, B1
9. Chỉ dùng các lệnh MOV, ADD, SUB, INC, DEC và NEG, hãy dịch các dòng
lệnh gán của ngôn ngữ bậc cao sau đây sang hợp ngữ, với các biến A, B, C
kiểu word.
a. A=A–B
b. A = - (A + 1)

82
GV. Vương Quốc Dũng
c. C=A+B
d. B=3*B+7
e. B=B–A–1
10. Hãy viết các lệnh (không phải các chương trình đầy đủ) thực hiện các công
việc sau đây:
a. Đọc một ký tự và hiển thị nó ở vị trí tiếp theo trên cùng một dòng.
b. Đọc một chữ hoa (bỏ qua việc kiểm tra lỗi) và hiển thị nó ở vị trí tiếp
theo trên cùng một dòng dưới dạng chữ thường.
11. Viết một chương trình thực hiện các công việc sau đây:
a. Hiển thị dấu hỏi chấm (?)
b. Nhập từ bàn phím 2 chữ số thập phân có tổng nhỏ hơn 10.
c. Hiển thị các số đó với tổng của chúng với dòng thông báo tương ứng.
Ví dụ: ?27
Tổng của 2 và 7 là 9.
12. Hãy viết một chương trình thực hiện các công việc sau đây:
a. Đưa ra thông báo cho người sử dụng.
b. Nhập 3 chữ cái đầu của họ, tên đệm, tên của một người.
c. Hiển thị chúng từ trên xuống trên lề trái.
Ví dụ:
Bạn hãy vào 3 chữ cái đầu: NTB
N
T
B
13. Hãy viết một chương trình nhập một chữ số Hex trong khoảng (A-F) rồi
hiển thị nó trên dòng tiếp theo ở dạng thập phân.
Ví dụ: Bạn nhập một chữ số Hex: C
Dạng thập phân của nó là: 12
14. Viết một chương trình hiển thị một bảng 10x10 diền đầy dấu sao.
Gợi ý: Khai báo một chuỗi gồm 10 dấu * rồi hiển thị nó 10 lần bằng hàm 9,
ngắt 21h của DOS.
15. Viết một chương trình thực hiện các công việc sau đây:
a. Hiển thị dấu hỏi chấm (?)
b. Nhập từ bàn phím 3 chữ cái.
c. Hiển thị chúng trong một bảng 11x11 được điền đầy các dấu sao.
d. Phát ra tiếng kêu bip của máy tính.
16. Hãy cho biết nội dung mới của toán hạng đích và trạng thái mới của các cờ
CF, SF, ZF, PF và OF sau khi thực hiện các lệnh sau (giả sử ban đầu giá trị
các cờ đều = 0).
a. ADD AX, BX ; Trước đó AX = 7FFFh, BX = 0001h
b. SUB AL, BL ; Trước đó BL = 0001h, BL = FFh
c. DEC AL ; Trước đó AL = 00h
d. NEG AL ; Trước đó AL = 7Fh
e. XCHG AX, BX ; Trước đó AX = 1ABCh, BX = 712Ah
f. ADD AL, BL ; Trước đó AL = 80h, BL = FFh

83
GV. Vương Quốc Dũng
g. SUB AX, BX ; Trước đó AX = 0000h, BX = 8000h
h. NEG AX ; Trước đó AX= 0001h
17. a. Giả sử AX, BX đều chứa số dương, lệnh ADD AX, BX được thực hiện,
hãy chứng minh rằng hiện tượng có nhớ vào MSB xảy ra, nhưng không
có nhớ ra từ MSB khi và chỉ khi có hiện tượng tràn có dấu.
b. Giả sử AX, BX đều chứa số âm, lệnh ADD AX, BX được thực hiện,
hãy chứng minh rằng hiện tượng có nhớ từ MSB xảy ra, nhưng không
có nhớ vào MSB khi và chỉ khi có hiện tượng tràn có dấu.
18. Giả sử lệnh ADD AX, BX được thực hiện. Trong các phần sau đây số hạng
thứ nhất được chứa trong AX, số hạng thứ 2 được trong BX, hãy cho biết kết
quả trong thanh ghi AX và có hiện tượng tràn (có dấu và không có dấu) xảy
ra không?
a. 512Ch + 4185h
b. FE12h + 1ACBh
c. E1E4h + DAB3h
d. 7132h + 7000h
e. 6389h + 1176h
18. Giả sử lệnh SUB AX, BX được thực hiện. Trong các phần sau đây số hạng
thứ nhất được chứa trong AX, số hạng thứ 2 được trong BX, hãy cho biết kết
quả trong thanh ghi AX và có hiện tượng tràn (có dấu và không có dấu) xảy
ra không?
a. 2143h – 1986h
b. 81Feh – 1986h
c. 19BCh – 81Feh
d. 0002h – FE0Fh
e. 8BCDh – 71ABh
19. Giả thiết
AX = 0500h
BX = 1000h
SI = 1500h
DI = 2000h
Offset 1000h chứa giá trị 0100h
Offset 1500h chứa giá trị 0150h
Offset 2000h chứa giá trị 0200h
Offset 2500h chứa giá trị 0250h
Offset 3000h chứa giá trị 0300h
Và Beta là một biến kiểu Word nằm ở địa chỉ 1000h.
Với mỗi câu lệnh sau đây, nếu hợp lệ, hãy cho biết địa chỉ offset của toán hạng
hay giá trị thanh ghi và kết quả được lưu trữ trong toán hạng đích.
a. MOV DL, SI
b. MOV DI, [DI]
c. ADD AX, [SI]
d. SUB BX, [DI]
e. LEA BX, Beta[BX]

84
GV. Vương Quốc Dũng
f. ADD [SI], [DI]
g. ADD BH, [BL]
h. ADD AH, [SI]
i. MOV AX, [BX+DI+Beta]
20. Các câu lệnh sau đúng hay sai, giải thích tại sao sai?
a. PUSH AL
b. MOV DX, ‘ABCD’
c. MOV [BX*2+SI], AX
d. MOV SS, AX
e. MOV [BX], 0
f. XOR AX, AX
i. XCHG DS, DX
21. Viết các lệnh thực hiện:
a. Đưa giá trị của đỉnh Stack vào AX, mà không làm thay đổi Stack.
b. Đưa 1 word ở dưới đỉnh Stack vào AX, mà không làm thay đổi Stack.
c. Đổi chỗ 2 word ở đỉnh Stack, có thể sử dụng AX và BX.
22. Biết rằng AX = 1234h, BX = 5678h, CX = 9ABCh, SP = 1000h.
Hãy cho biết nội dung các thanh ghi AX, BX, CX, SP sau khi kết thúc thực
hiện các lệnh sau:
PUSH AX
PUSH BX
XCHG AX, CX
POP CX
PUSH AX
POP BX
22. Viết các lệnh để thực hiện các công việc sau:
a. Giả sử AL chứa giá trị nhỏ hơn 10, đổi nó thành một chữ số thập phân.
b. Giả sử DL chứa mã ASCII của một chữ hoa, đổi nó thành chữ thường.

Ch¬ng 3. CÁC PHÉP TOÁN VỚI DỮ LIỆU


3.1. Nhóm lệnh chuyển dữ liệu
Nhóm lệnh chuyển dữ liệu có đặc điểm chung là;

85
GV. Vương Quốc Dũng
-Copy dữ liệu từ nguồn (source) sang đích (destination) (không xử lý, không
tính toán)
-không ảnh hưởng đến các cờ
3.1.1. Nhóm lệnh chuyển dữ liệu đa dụng:
1) Lệnh MOV
Cú pháp MOV đích , nguồn
Giải thích : đích → nguồn
Toán hạng đích và toán hạng nguồn có thể tìm được theo các chế độ địa
chỉ khác nhau, nhưng phải có cùng độ dài và không được phép đồng thời
là 2 ô nhớ hoặc 2 thanh ghi đoạn.
2) XCHG (exchange 2 operands) Trao đổi nội dung 2 toán hạng
Cú pháp : XCHG Đích, nguồn
Giải thích: Đích ↔ Nguồn
Sau lệnh XCHG toán hạng đích chứa nội dung cũ của toán hạng nguồn và
ngược lại.
Toán hạng đích và nguồn có thể tìm được theo các địa chỉ khác nhau, nhưng
phải chứa dữ liệu có cùng độ dài và không được phép đồng thời là 2 ô nhớ. Cả
2 toán hạng đều không được là phép là thanh ghi đoạn.
Ví dụ : XCHG Al, Ah ; trao đổi nội dung Al & Ah.
3) PUSH (push word on the stack).
Cất 1 từ vào ngăn xếp
Cú pháp : Push nguồn
Giải thích: SP←SP-2
Nguồn →SP
Trong đó toán hạng nguồn có thể tìm được theo các chế độ dịa chỉ khác
nhau : có thể là thanh ghi đa năng, thanh ghi đoạn hoặc là ô nhớ. Lệnh này
thường dùng với lệnh POP như là một cặp đối ngẫu để xử lý các dữ liệu và
trạng thái của chương trình chính (CTC) khi vào/ra chương trình con(CTc)
4) POP - (Pop word from top of stack)
Lấy 1 từ vào thanh ghi từ đỉnh của ngăn xếp.
Cú pháp : POP đích
Giải thích: Đích ← {SP}
SP ← SP +2
Trong đó các toán hạng đích có thể tìm được theo các chế độ địa chỉ khác
nhau : có thể là thanh ghi đa năng, ô nhớ, thanh ghi đoạn ( nhưng không được
phép là thanh ghi đoạn mã CS). Dữ liệu để tại ngăn xếp không thay đổi.

3.1.2. Nhóm lệnh chuyển địa chỉ:


1) LDS - Load Register and DS with words from memory
Cú pháp: LDS đích, nguồn

86
GV. Vương Quốc Dũng
Giải thích: đích ← nguồn, DS ← nguồn + 2
trong đó:
Đích : là các thanh ghi AX, BX, CX, DX, SP, BP, SI, DI
Nguồn: Là ô nhớ trong đoạn DS được chỉ rõ trong lệnh.
Lệnh này copy 1 word từ 2 ô nhớ vào trong thanh ghi (đích) xác định ở câu
lệnh và copy 1 word từ 2 ô nhớ tiếp theo vào thanh ghi DS.
LDS thì hữu ích cho SI và DS xác định vị trí bắt đầu của 1 xâu trước khi sử
dụng một trong những lệnh xử lý xâu
LDS không ảnh hưởng đến các cờ.
Ví dụ:
LDS SI, str1;
; nạp vào SI nội dung của ô nhớ: str1 & str1+1
; nạp vào DS nội dung của ô nhớ: str+2 & str1+3
; các ô nhớ này đều nằm trong đoạn dữ liệu DS và chứa địa chỉ của chuỗi
nguồn. Do vậy, DS:SI là địa chỉ đầu của chuỗi nguồn cần thao tác.
LDS BX, [4326]
; copy nội dung của ô nhớ có địa chỉ offset = 4326h trong đoạn dữ liệu
; vào thanh ghi BL
; copy nội dung của ô nhớ có địa chỉ offset = 4327h trong đoạn dữ liệu
; vào thanh ghi BH
; copy nội dung của 2 ô nhớ có địa chỉ offset = 4328h và 4329h trong
đoạn dữ liệu vào thanh ghi DS.
2) LEA - Load effective address (nạp địa chỉ hiệu dụng vào thanh ghi)
Cú pháp : Lea đích, nguồn
Giải thích: Đích ← Địa chỉ lệnh (offset) của nguồn hoặc
Đích ← địa chỉ hiệu dụng của nguồn
Ví dụ : LEA SI, S1 ; Nạp địa chỉ offset của xâu S1 trong đoạn dữ liệu

3) LES : Nạp vào thanh ghi và ES giá trị của words từ bộ nhớ
Cú pháp: LES đích, nguồn
Giải thích: đích ← nguồn, ES ← nguồn + 2
trong đó:
Đích : là các thanh ghi AX, BX, CX, DX, SP, BP, SI, DI
Nguồn: Là ô nhớ trong đoạn ES được chỉ rõ trong lệnh.
Lệnh này copy 1 word từ 2 ô nhớ vào trong thanh ghi (đích) xác định ở câu
lệnh và copy 1 word từ 2 ô nhớ tiếp theo vào thanh ghi ES.
LES thì hữu ích cho DI và ES xác định vị trí bắt đầu của 1 xâu trước khi sử
dụng một trong những lệnh xử lý xâu
LDS không ảnh hưởng đến các cờ.
Ví dụ:
LES DI, str2;
; nạp vào DI nội dung của ô nhớ: str2 & str2 + 1
; nạp vào ES nội dung của ô nhớ: str2 + 2 & str2 + 3

87
GV. Vương Quốc Dũng
; các ô nhớ này đều nằm trong đoạn dữ liệu ES và chứa địa chỉ của chuỗi
đích. Do vậy, ES:DI là địa chỉ đầu của chuỗi đích cần thao tác.
LES BX, [789A]
; copy nội dung của ô nhớ có địa chỉ offset = 789Ah trong đoạn dữ liệu
; vào thanh ghi BL
; copy nội dung của ô nhớ có địa chỉ offset = 789Bh trong đoạn dữ liệu
; vào thanh ghi BH
; copy nội dung của 2 ô nhớ có địa chỉ offset = 789Ch và 789Dh trong
đoạn dữ liệu vào thanh ghi ES.
MOV BX, 1234h
LES DI, [BX]
; copy nội dung của 2 ô nhớ có địa chỉ offset = 1234h và 1235h trong
đoạn dữ liệu vào thanh ghi DI
; copy nội dung của 2 ô nhớ có địa chỉ offset = 1236h và 1237h trong
đoạn dữ liệu vào thanh ghi ES.
3.1.3. Nhóm lệnh chuyển cờ hiệu.
1) CLC - clear the carry flag (xoá cờ nhớ)
Cú pháp : CLC
Giải thích : CF ← 0 ; Xoá cờ nhớ, không tác dụng đến các cờ khác
2) CLD - clear the direction Flag ( xoá cờ hướng)
Cú pháp: CLD
Giải thích: DF ← 0
Lệnh này định hướng thao tác theo chiều tiến cho các lệnh liên quan đến chuỗi.
Các thanh ghi liên quan là SI, DI sẽ tự dộng tăng lên khi làm việc xong với 1
phần tử của chuỗi. Không tác động đến các cờ khác.
3) CLI - clear the interrupt (xoá cờ cho phép ngắt)
Cú pháp: CLI
Giải thích: IF ← 0
Lệnh này xoá cờ cho phép ngắt. Các yêu cầu ngắt che được sẽ bị che (không
được tác động), không tác dụng đến các cờ khác.
4) CMC - Complement the carry flag (Đảo cờ nhớ).
Cú pháp : CMC
Giải thích CF ← CF
Lệnh này cập nhật CF, không có tác động đến cờ khác.
5) STC - Set the carry flag (lập cờ nhớ )
Cú pháp: STC
giải thích: CF ← 1 , không có tác động các cờ khác.
6) STD - Set the direction flag (lập cờ hướng)
Cú pháp: STD
Giải thích: DF ← 1

88
GV. Vương Quốc Dũng
Lệnh này định hướng thao tác cho các lệnh làm việc với chuỗi theo chiều lùi.
Các thanh ghi SI & DI liên quan sẽ được tự động giảm khi làm việc xong với 1
phần tử của chuỗi. Không tác động đến các cờ khác.
7) STI - Set the interrupt flag
Cú pháp : STI
Giải thích : IF ← 1
Lệnh này lập cờ cho phép ngắt để cho phép các yêu cầu ngắt tác động vào
chân INTR được CPU nhận biết, khi IF = 1 nếu có tín hiệu INTR = 1 thì CPU
sẽ bị ngắt. Nó sẽ tự động cất thanh ghi cờ và địa chỉ trở về vào ngăn xếp rồi
chuyển sang chạy chương trình con phục vụ ngắt (CTcPVN). Tại cuối
CTcPVN sẽ có lệnh trở về từ CTcPVN – lệnh IRET để CPU lấy lại từ ngăn xếp
giá trị của thanh ghi cờ và địa chỉ trở về.
Lệnh này không tác động đến các cờ khác.
8) LAHF (load AH from flag)
Chức năng: Chuyển phần thấp của thanh ghi cờ CF gồm các cờ SF, ZF, AF,
PF và CF tương ứng vào các bit 7, 6, 4, 2 và 0 của thanh ghi AH, các bít còn lại
của AH (5, 3, 1) không đổi.
9) SAHF (Store AH into flag)
Chức năng: Chuyển các bit 7, 6, 4, 2 và 0 của thanh ghi AH tương ứng vào
các cờ SF, ZF, AF, PF và CF của thanh ghi cờ CF, các cờ còn lại OF, DF, IF, TF
không bị ảnh hưởng.
10) PUSHF
Chức năng: PUSHF giảm thanh ghi SP đi 2 đơn vị và chuyển nội dung của
thanh ghi cờ CF vào đỉnh Stack
11) POPF
Chức năng: POPF chuyển nội dung của đỉnh Stack vào thanh ghi cờ CF và
tăng thanh ghi SP đi 2 đơn vị

3.1.4. Nhóm lệnh truyền dữ liệu qua cổng:


1) IN - input data from
Cú pháp : IN Acc, port
Giải thích : Acc ← {port}
Trong đó {port} là dữ liệu của cổng có địa chỉ là port. Lệnh này sẽ copy dữ liệu
từ một cổng tới thanh ghi AL/AX
Nếu một cổng 8 bit được đọc, dữ liệu được đưa tới thanh ghi AL
Nếu một cổng 16 bit được đọc, dữ liệu được đưa tới thanh ghi AX
Lệnh này có thể thực hiện ở 2 dạng : gán địa chỉ cổng hoặc gán biến cổng.
Ví dụ : IN AL, 0C8h ; chuyển 1 byte từ cổng 0C8h tới AL
IN AX, 34h ; chuyển 2 byte từ cổng 34h tới AX
A_TO EQU 4AH

89
GV. Vương Quốc Dũng
IN AX, E_TO ; chuyển 1 word từ cổng 4AH tới AX
2) Out - Out put a byte or word to a port (đưa dữ liệu ra cổng)
Cú pháp : OUT port, Acc
giải thích : Acc →{port}
lệnh OUT copy một byte từ AL hoặc 1 word từ AX tới 1 cổng xác định. Lệnh
này có thể thực hiện ở 2 dạng : gán địa chỉ cổng hoặc gán biến cổng.
Ví Dụ : OUT 3BH, AL ; chuyển nội dung của AL tới cổng 3Bh
OUT 2CH, AX ; chuyển nội dung của AX tới cổng 2Ch
Với kiểu sử dụng biến cổng trong lệnh OUT, địa chỉ cổng được nạp vào thanh
ghi DX trứơc khi thực hiện lệnh OUT. Sau đó nội dung của AL/AX sẽ được
chuyển tới cổng có địa chỉ được chứa trong DX.
MOV DX,0FFFH ; nạp địa chỉ cổng vào DX
OUT DX, AL ; copy nội dung của AL tới cổng 0FF8h
OUT DX, AX ; copy nội dung của AX tới cổng 0FF8h

3.2. Nhóm các lệnh số học


3.2.1. Nhóm lệnh xử lý phép cộng
1) AAA- ASCII Adjust after Addition
(chỉnh sau khi cộng 2 số ở dạng ASCII)
Cú pháp: AAA
Giải thích : dữ liệu truyền từ các thiết bị đầu cuối đến máy tính thường ở dưới
dạng mã ASCII. Khi đã truyền đi các số ở dạng mã ASCII rồi, đôi khi ta muốn
cộng luôn các số đó. Các bộ VXL của Intel cho phép làm điều này với điều
kiện phải chỉnh lại kết quả có trong AL bằng lệnh AAA để thu được kết quả là
số BCD không gói.
Lệnh này cập nhật : AF, CF
Không ảnh hưởng đến các cờ : OF, PF, SF, ZF
Lệnh này chỉ làm việc trên thanh ghi AL
Ví dụ: Ban đầu ta có
; AL = 00110101b = 53 = 35H, ASCII của số 5
; BL = 00111001b = 57 = 39H, ASCII của số 9
ADD AL, BL ; kết quả : AL = 01101110b = 6EH,
; không đúng cho số BCD
AAA ; bây giờ AL = 00000100, một số BCD gói đúng
; CF = 1 (nhớ 1) vậy kết quả là 14 theo số thập phân
Ghi chú:
Nếu muốn gửi kết quả tới thiết bị đấu cuối:
OR AL, 30H ; Nhận được 00110100b = 34H,
; mã ASCII của 4 , truyền 4 tới thiết bị hiện thị
; Đưa số 1 trong cờ nhớ (CF) vào 4 bit thấp của thanh
ghi AL do đó AL = 00000001b
OR AL, 30H ; Nhận được AL = 00110001b = 31H,
; mã ASCII của 1, truyền 1 tới thiết bị đầu cuối.

90
GV. Vương Quốc Dũng
2) ADD - Add (cộng hai toán hạng)
Đã nêu ở phần trước.
3) INC - Increment Destination register or memory
(tăng toán hạng đích lên 1)
Đã nêu ở phần trước
4) ADC - Add with carry (cộng có nhớ)
Cú pháp: ADC Đích , nguồn
Giải thích Đích ← Đích + nguồn + CF
Trong đó toán hạng đích hoặc nguồn có thể tìm được theo các chế độ địa chỉ
khách nhau, nhưng phải chứa dữ liệu cùng độ dài, không được phép đồng
thời là hai ô nhớ, toán hạng đích, nguồn không được phép là thanh ghi đoạn.
Cập nhật : AF, CF, OF, PF, SF, ZF.
Ví dụ:
ADC AL, 74H ; Al ← AL + 74H + CF
ADC CL, BL ; CL ← CL + BL + CF
ADC DL,[SI] ; DL ← DL + {DS:SI} + CF
ADC AL,Table[BX]; AL ← AL+{DS(table+BX)} + CF
3.2.2. Nhóm lệnh xử lý phép trừ
1) AAS - ASCII adjust after subtraction
(chỉnh sau khi trừ 2 số ở dạng ASCII)
Cú pháp: AAS
Lệnh này dùng để đổi một số hệ hai, là hiệu của hai số dạng mã ASCII có ở
AL sang số BCD không gói và chỉ thực hiện trên thanh ghi AL
Cập nhật : AF, CF
Không ảnh hưởng :OF, PF, SF, ZP
Ví dụ:
a. ; BL = 00110101B = 35H = ’5’
; AL = 00111001b = 39H = ’9’
SUB AL, BL ; Thu được AL = 00000100 =04H = BCD 04, CF=0
AAS ; Kết quả AL = 00000100 = 04H = BCD 04
; và CF = 0 - không cần đén mượn
b. ; AL = 00110101 = 35H = ’5’
; BL = 00111001 = 39H = ’9’
SUB AL,BL ; Kết quả AL = 11111100 = -4 trong số bù hai và CF=1
AAS ; Kết quả AL = 00000100 = 04 = BCD 04 và CF = 1
; phải mượn 1
2) SUB - Substract (trừ hai toán hạng)
Đã giới thiệu ở phần trước.
3) DEC - Decrement Destination Register or memory
Đã giới thiệu ở phần trước
4) SBB - Substract with borrow (trừ có mượn)
Cú pháp: SBB đích , nguồn
Giải thích: Đích ← Đích - Nguồn - CF

91
GV. Vương Quốc Dũng
Trong đó toán hạng đích nguồn có thể tìm theo các chế độ địa chỉ khác nhau
nhưng phải chứa cùng một loại dữ liệu và không được phép đồng thời là hai
ô nhớ và cũng không được là thanh ghi đoạn.
Cập nhật: AF, CF, OF, PF, SF, ZF (AF, PF chỉ liên quan đến địa chỉ của 8
bít thấp).
Ví dụ:
SBB AL,74H ; Al ← AL - 74h - CF
SBB AL,BL ; AL ← AL - BL - CF
SBB DL,[SI] ; DL ← DL - {DS:SI} - CF
SBB AL,Table[BX] ; AL ← AL - {[Table+BX]}- CF
3.2.3. Nhóm lệnh xử lý phép nhân
1) Các lệnh MUL và AMUL.
Trong phép nhân các số nhị phân, các số có dấu và không dấu phải được ‘đối
sử‘ khác nhau. Chẳng hạn chúng ta muốn nhân các số 8 bit 10000000 và
11111111 với nhau. Nếu coi chúng là các số không dấu thi
10000000 = +128
11111111 = +256
tích số bằng 0111111110000000 = +32640
nhưng khi xem chúng là số có dấu thì
10000000 = -128
11111111 = -1
tích số bằng 0000000010000000 = 128
Ta thấy kết quả nhân hai số không dấu và có dấu dẫn đến những kết quả khác
nhau, cho nên có hai lệnh nhân:
MUL (Multiply) : cho các số không dấu
IMUL(Integer multiply): cho các số có dấu
Các lệnh này làm việc với byte hoặc word
Cú pháp: MUL Nguồn
IMUL Nguốn
Lệnh này nhân 1 byte (hay 1 từ word) từ nguồn nào đó với 1 byte trong AL
hoặc 1 từ word trong AX. Nguồn có thể là một thanh ghi hay một ô nhớ có thể tìm
theo các chế độ địa chỉ khách nhau, song không được là một hằng số
Kết quả chứa trong AX khi các toán hạng là byte và chứa trong DXAX khi các
toán hạng là word, word có thứ tự cao nhất của kết quả thì đặt trong DX, còn word
có thứ tự thấp nhất (ít ý nghĩa nhất) của kết quả thì đặt trong AX
Khi nhân các số dương (bit MSB = 0) thì MUL và IMUL cho cùng một kết
quả
Tác động của MUL và IMUL đến các cờ trạng thái
- SF, ZF, AF, DF : không ảnh hưởng
- CF, OF:
Sau lệnh MUL:
- CF = OF = 0 nếu nửa cao của kết quả các bít = 0
- CF = OF = 1 nếu trong các trường hợp khác
Sau lệnh IMUL

92
GV. Vương Quốc Dũng
- CF = OF = 0 nếu nửa cao của kết quả là phần mở rộng dấu của nửa thấp
(tất cả các bit của nửa cao giống với bít dấu của nửa thấp)
- CF = OF = 1 trong các trường hợp khác
Trong cả hai lệnh trên, CF = OF = 1 có nghĩa là kết quả quá lớn để chứa trong
nửa thấp của toán hạng đích
Nếu muốn nhân 1 byte có dấu với 1 word có dấu, trước hết ta phải di
chuyển 1 byte vào byte thấp của word và điền đầy những byte cao bằng cách
copy bit dấu. Nếu di chuyển byte này vào AL, ta có thể điều chỉnh byte thành
word nhờ lệnh CBW của 8086. CBW mở rộng bit dấu từ AL vào tất cả các bít
của AH
Ví dụ 1 :
; 69*14
; AL = 01000101b = 69
; BL = 00001110b = 14
với :
MUL BL ; AX = 03C6H = 966 và OF = CF = 1
IMUL BL ; AX = 03C6H = +966 và OF = CF = 1
; MSB = 0 độ lớn của kết quả (+) → kết quả đúng.
ví dụ 2 :
; AL = 11100100
; BL = 00111011
với lệnh MUL
; AL = 228
; BL = 59
MUL BL ; AX = 0011010010001100 = 0348Ch =13452
; OF = CF = 1
với lệnh IMUL
; AL = - 28
; BH = 59
IMUL BL ; AX = 1111100110001100 = F98CH = - 1652
; SF = 1, CF = OF = 1
2) AAM - ASCII Adjust after Multiplication
(chỉnh hai số sau khi nhân 2 số BCD)
Trước khi ta có thể nhân 2 số ASCII, đầu tiên ta phải che 4 bit cao của mỗi số,
tức là ta được 2 số BCD không gói. Sau đó 2 số BCD không gói này được nhân
với nhau. Lệnh AAM dùng để đổi một số hệ hai là tích của hai số BCD không
gói sang thành hai số BCD không gói để trong AX.
Cập nhật : PF, SF, ZF
Không tác động: AF, CF, OF
Ví dụ :
Ta nhập 2 số 5 và 9 từ bàn phím, 2 số này ở dạng mã mã ASCII, trước khi
nhân hai số 9 va 5 ở dạng ASCII ta phải đổi chúng sang 2 số BCD không
gói, sau đó mới thực hiện phép nhân. Để có thể truyền tiếp kết quả tới thiết

93
GV. Vương Quốc Dũng
bị đầu cuối, ta phải đổi kết quả sang dạng BCD không gói bằng lệnh AAM
và sau đó đổi tiếp thành dạng mã ASCII để truyền tiếp.
; AL = 00110101b = ‘5’ → AL = 00000101b = 5
; BL = 00110101b = ‘9’ → BL = 00001001 = 9
Mul BL ; {AL nhân BL, kết quả trong AX = 002DH = 45}
AAM ; Thu được AX = 0405H, mã BCD không gói của 4 trong AH
; mã BCD không gói của 5 trong AL
OR AX, 3030H ; Thu được AX = 3435H, mã ASCII của 4 trong AH
; mã ASCII của 5 trong AL
; để truyền kết quả trở lại thiết bị đầu cuối.
3.2.4. Nhóm lệnh xử lý phép chia
1) Các lệnh DIV và IDIV
Mỗi khi thực hiện phép chia chúng ta nhận được hai kết quả là thương và số dư.
Cũng như đối với phép nhân cũng có những lệnh riêng biệt cho phép chia
không dấu và phếp chia có dấu.
DIV- Dùng cho phép chia không dấu
IDIV- Dùng cho phép chia có dấu
Cú pháp:
DIV số chia
IDIV số chia
Các lệnh này chia số 16 bit cho số 8 bit (32 bit cho số 16 bit) thương và số
dư có cùng kích thước với số chia
Số chia có thể là một thanh ghi hay một ô nhớ được tìm theo các chế độ địa
chỉ khác nhau, song không được phép là một hằng số. Số bị chia được chứa
trong AX thì số chia là 1 byte (8bit) hoặc trong DXAX thì số chia là một word
(16 bit) word có thứ tự cao nhất của số chia được đặt trong DX (word có ý
nghĩa nhất) còn word có thứ tự thấp nhất đặt trong AX (word ít có ý nghĩa
nhất).
Sau phép chia thương số để trong AL với số chia là 8 bit hoặc trong AX với số
chia là 16 bit, còn số dư để trong AH với số chia là 8 bit hoặc trong DX với số
chia là 16 bit. Số dư có cùng kích thước với số chia và dấu của số dư trùng với
dâú của dấu bị chia.
Các cờ AF, PF, ZF, CF, OF không bị ảnh hưởng
Chú ý : Sự tràn trong phép chia
Khi thương số quá lớn để chứa trong toán hạng đích xác định (AL hay AX)
điều này xảy ra khi số bị chia lớn hơn số chia rất nhiều hoặc số chia bằng 0 thì
8086 sẽ thực hiện ngắt INT 0 và hệ thống sẽ đưa ra thông báo “Divide overflow

Ví dụ:
a. ; DX = 0000h, AX = 0005h, BX = FFFEh
với lệnh DIV BX ; thương = AX = 0000h = 0
; Số dư = DX = 0005h = 5
với IDIV BX ; số bị chia DXAX = 00000005b = 5
; số chia = BX = FFFE = -2
94
GV. Vương Quốc Dũng
; thương = AX = FFFE = -2
; số dư = DX = 0001 = 1
b. ; DX = FFFFh, AX = FFFBh, BX = 0002h
với DIV BX ; Divide overflow
với IDIV BX ; Số bị chia DXAX = FFFFFFFBh = -5
; Số chia = BX = 0002 = 2
; thương = AX = FFFE =-2
; số dư = DX = FFFF = -1
trong lệnh DIV số bị chia DXAX = FFFFFFFBh = 4294967291, số chia
BX = 2, suy ra thương = 2147483645 số này quá lớn để chứa trong AX do
đó ngắt 0 và thông báo “divide over flow”
c. ; AX = 00FBH, BL = FFh
với DIV BL ; thương = AL = 00h =0
; số dư = AH = FBh = 251
với IDIV BL ; AX = 00FBh = 251
; BL = FFh = -1
Kết quả: Dividi over flow, ở đây 251/(-1) = - 251, số này quá lớn để
chứa trong AL do đó ngắt INT 0 được gọi.
Chú ý:
trong phép chia cần chú ý đến vấn đề mở rộng dấu của số bị chia như sau:
Với phép chia cho word:
Số bị chia được đặt trong DXAX ngay cả khi số bị chia chỉ có thể chứa vừa
vặn trong AX. Trong trường hợp này DX phải được chuẩn bị như sau:
+ Đối với lệnh DIV: DX phải được xoá
+ Đối với lệnh IDIV: DX phải được làm thành dấu mở rộng của AX.
Phép mở rộng được thực hiện bằng lệnh CWD (Convert Word to
Double Word)
Ví dụ:
1. Mov AX,-1250 ; AX chứa số bị chia
CWD ; mở rộng dấu sang DX
Mov BX,7 ; BX chứa số chia
IDIV BX ; AX chứa thương, DX chứa số dư
2. Mov AX, 32768 ; AX chứa số bị chia
Xor DX ; Xoá DX
Mov BX,Ofh ; BX chứa số chia
Div BX ; AX chứa thương, DX chứa số dư
Với phép chia cho byte :
Trong phép chia cho byte, số bị chia đặt trong AX. Trong trường hợp số bị
chia thực sự chỉ là 1 byte, AH cần được chuẩn bị như sau:
+ Đối với lệnh DIV: AH cần được xoá.
+ Đối với lệnh IDIV: AH cần được làm thành phần dấu mở rộng của
AL. Phép mở rộng này được thực hiện bằng lệnh CBW (Convert Byte to
Word )
Các lệnh CBW và CWD không làm ảnh hưởng tới cờ

95
GV. Vương Quốc Dũng
2) AAD - BCD to binary convert before division
(biến đổi số BCD sang số nhị phân trước phép chia)
Lệnh này biến đổi hai số BCD không gói trong AH và AL thành số nhị
phân tương đương trong AL, việc này phải thực hiện trước khi làm phép chia
hai số BCD không gói để trong AX cho một số BCD không gói khác. Sau phép
chia AL sẽ chứa số thương, AH chứa số dư, cả số thương và số dư đều là số
BCD không gói.
PF, SF, ZF được cập nhật
OF không ảnh hưởng
Ví dụ:
; AX = 0607H số BCD không gói biểu diễn số thập phân 67
; CH = 09H số BCD không gói biểu diễn số thập phân 9
; Điều chỉnh thành số nhị phân nhờ lệnh AAD
AAD ; kết quả: AX = 0043H = 67
Div CH ; chia AX cho số BCD không gói trong CH
; thương : AL = 07H = 7 là số BCD không gói
; số dư : AH = 04H = 4 là số BCD không gói
Tất cả các cờ sau phép chia không bị ảnh hưởng

3.3. Nhóm các lệnh logic


3.3.1. các lệnh AND, OR và XOR
Cú pháp: AND đích, nguồn
OR đích, nguồn
XOR đích, nguồn.
Kết quả thao tác được cất trong toán hạng đích, nó phải là một thanh ghi hay
một ô nhớ. Toán hạng nguồn có thể là hằng số, thanh ghi hay ô nhớ. Nhưng
không được phép đồng thời là 2 ô nhớ và không được là thanh ghi đoạn.
Các cờ
SF, ZF, PF phản ánh kết quả của lệnh
AF không ảnh hưởng
CF = OF = 0.
3.3.2. Lệnh NOT
Cú pháp: NOT đích.
Lệnh NOT lấy số bù 1 của toán hạng đích
Lệnh này không làm ảnh hưởng đến các cờ.
Ví dụ: NOT AX; đảo các bít trong thanh ghi AX
3.3.3. Lệnh TEST
Cú pháp: TEST đích, nguồn
Lệnh này thực hiện thao tác AND giữa toán hạng đích và toán hạng nguồn,
nhưng không làm thay đổi các toán hạng.
Mục đích của lệnh TEST là thiết lập các cờ, các cờ được tạo ra sẽ được
dùng làm điều kiện cho các lệnh nhảy có điều kiện.

96
GV. Vương Quốc Dũng
Toán hạng đích và gốc có thể tìm được theo các chế độ địa chỉ khác nhau,
nhưng phải chứa dữ liệu có cùng độ dài và không được phép đồng thời là 2 ô
nhớ, cũng không được là thanh ghi đoạn. Sau lệnh này kết quả không được lưu
giữ.
Xoá cờ: CF,OF
Cập nhật: PF, SF, ZF (PF chỉ liên quan đến 8 bít thấp)
Không xác định: AF
Ví dụ: TEST AL, 1 ; kiểm tra xem Al chứa số chẵn không?
JF BELOW ; nếu đúng, nhảy đến BELOW
; (nhảy đến nhãn BELOW nếu kết quả =0).

3.4. Nhóm các lệnh dịch chuyển và quay


3.4.1. Các lệnh dịch trái
1) Lệnh SHL (Shift Left ): dịch trái các bit của toán hạng đích:
Cú pháp: SHL đích, 1
Giải thích: 1 giá trị 0 sẽ được đưa vào vị trí bên phải nhất của toán hạng
đích, còn bít MSB của nó sẽ được đưa vào cờ CF và tất cả các bit của toán
hạng dịch sang trái một vị trí.
Nếu số đếm vị trí dịch khác 1 thì lệnh có dạng như sau
SHL đích, CL
Trong đó CL chứa n số đếm vị trí dịch. Trong trường hợp này n phép dịch
trái đơn được thực hiện. Giá trị của CL được giữ nguyên không thay đổi khi
thực hiện lệnh xong.
Toán hạng đích được tìm theo các chế độ địa chỉ khác nhau
Các cờ bị tác động:
SF, PF, ZF phản ánh kết quả.
CF chứa bít cuối cùng được dịch ra khỏi toán hạng.
OF =1 nếu kết quả bị thay đổi dấu trong phép dịch cuối cùng.
AF không bị ảnh hưởng.
Ví dụ: DH = 8AH , CL = 3
; DH = 10001010b, CL = 3
SHL DX,CL ; DH = 01010000b = 50h
Phép dịch trái số học đựơc áp dụng để thay cho phép nhân một số với 2 n. Trong
đó n là số lần dịch trái đơn
Ví dụ: DX = 8AH =138 = 0000000010001010b
DX*8 = 138*23 = 1104 = 210+26+24 =0000010001010000b = 0450h
2) Lệnh SAL (Shift Arithmetic Left)
Tạo ra cùng một mã máy như lệnh shift
Sự tràn: khi sử dụng lệnh dịch trái, để làm phép nhân, hiện tượng tràn có thể
xảy ra. Với lệnh dịch trái 1 bit CF và OF tương ứng chỉ ra một cách chính xác
sự tràn không dấu và có dấu. Tuy nhiên cờ tràn không đáng tin cậy trong lệnh
dịch trái nhiều bít vì lệnh dịch n bít thực ra là n lệnh dịch 1 bit và cờ CF, OF
chỉ phản ánh kết quả của sự dịch cuối cùng.
Ví dụ:

97
GV. Vương Quốc Dũng
; BL = 80H, CL = 2
SHL BL, CL ; cả 2 hiện tượng tràn có dấu và không dấu đều xảy ra
; xong CF = OF = 0
3.4.2. Các lệnh dịch phải
1) SHR
Cú pháp: SHR đích, 1 ; dịch phải 1 bít
SHR đích, CL ; dịch phải n bit, CL = n
Tương tự lệnh dịch trái, CL vẫn giữ nguyên không thay đổi khi thực hiện lệnh
xong.
Cứ mỗi một lần dịch, giá trị 0 được đưa vào MSB của toán hạng đích , còn
bít bên phải nhất của nó sẽ được vào cờ CF, tất cả các bít đều được dịch sang
phải một bít.
2) Lệnh SAR (Shift Arimethic Right) làm việc gần giống lệnh SHR, xong có
một điểm khác biệt là bít MSB của toán hạng đích giữ nguyên giá trị ban đầu của
nó.
Cú pháp: SAR đích, 1 ; dịch phải một lần
SAR đích, CL ; dịch phải n bít , CL = n
Tương tự lệnh dịch trái. Việc dịch phải sẽ chia đôi giá trị của toán hạng đích.
Kết quả của phép dịch phải là chia toàn hạng đích cho 2 n khi dịch n lần và làm
tròn.
phép chia không dấu và có dấu:
Trong phép chia bằng cách dịch phải, ta phải phân biệt hai trường hợp đối
với các số không dấu và có dấu
- Nếu đang làm việc với số không dấu ta sử dụng lệnh SHR
- Làm việc với số có dấu ta dùng lệnh SAR vì lệnh này giữ nguyên bít dấu
ví dụ:
1. Sử dụng phép dịch phải để thực hiện phép chia số không dấu
; chia 65143 cho 4, thương chứa trong AX
mov AX,65143 ; toán hạng đích = AX = 1111111001110111b
mov CL, 2 ; số lần dịch 2, tức số chia bằng 4
SHR AX, CL ; kết quả AX = 0111111100111011b = 32571.
2. Giả sử AL chứa (-15), hãy cho biết giá trị thập phân của AL sau khi thực
hiện lệnh SAR AL, 1
Số 15 = 00001111b
Số bù 1 (của 15) = 11110000b
Số bù 2 (của 15) = 11110001b = -15
Mov AL,-15 ; AL = 11110001b
SHR AL, 1 ; AL = 11111000b =-8
(giá trị tuyệt đối của số trong AL là 8)
Áp dụng các lệnh dịch trái và dịch phải cho phép nhân và chia một số cho 2 n
nhanh hơn rất nhiều so với dùng các lệnh MUL, IMUL, DIV
3.4.3. Các lệnh quay
1) Lệnh quay trái ROL (Rotate Left)

98
GV. Vương Quốc Dũng
cú pháp: ROL đích, 1 ; quay trái một bít
ROL đích, CL ; quay trái n bít , CL = n
Mỗi một lần quay các bít được dịch sang bên trái một bít và bít MSB được đưa
vào bít bên phải nhất (LSB), đồng thời được đưa vào cờ CF
Trong đó toán hạng đích được tìm theo các chế độ địa chỉ khác nhau
2) Lệnh quay phải ROR (Rotate Right)
Cú pháp: ROR đích, 1 ; quay phải 1 bít
ROR đích, CL ; quay phải n bít, CL = n
Giống lệnh ROL, chỉ khác là mỗi lần quay các bít được dịch phải 1 bít và bít bên
phải nhất (LSB) được đưa vào bít MSB, đồng thời cũng được đưa vào cờ CF.
Ví dụ:
Sử dụng lệnh ROR để đếm số bít 1 trong thanh ghi BX mà không làm thay
đổi nội dung của nó, kết quả chứa trong AX.
XOR AX, AX ; Xoá AX, khởi tạo giá trị đếm ban đầu
MOV CX, 16 ; Biến đếm vòng lặp.
TOP: ROR BX, 1 ; BX quay phải 1 bít, CF chứa bít bị đưa ra.
JNC NEXT ; tới NEXT nếu CF = 0 (nhảy nếu không nhớ)
INC AX ; CF=1, tăng AX lên 1 (tăng số đếm)
NEXT: LOOP TOP
3) RCL (Rotate carry left) - Lệnh quay trái qua cờ nhớ.
Cú pháp: RCL đích,1 ; quay trái 1 bít.
RCL đích, CL ; quay trái n bít, CL = n.
Lệnh này dịch các bít của toán hạng đích sang trái, bít MSB được đặt vào cờ
CF, giá trị cũ của CF được đưa vào bít phải nhất của toán hạng đích. Nói cách
khác RCL làm việc giống như ROL chỉ khác là cờ CF cũng là một phần của
vòng tròn tạo nên bởi các bít đang được quay.
4) RCR (Rotate carry right) - lệnh quay phải qua cờ nhớ.
Cú pháp: RCR đích, 1 ; quay phải 1 bít.
RCR đích, CL ; quay phải n bít, CL = n
Hoạt động giống như RCL nhưng các bít được quay sang phải.
Ví dụ:
Giả sử DH = 8AH, CF = 1, CL = 3 cho biết nội dung của DH và CF khi
thực hiện lệnh RCR DH, CL
Ta có: DH = 10001010

CF DH
Các giá trị ban đầu 1 10001010
Sau 1 lần dịch 0 11000101
Sau 2 lần dịch 1 01100010
Sau 3 lần dịch 0 10110001

Vậy sau khi thực hiện lệnh DH=10110001=B1h, CF=0.


* Tác động đến các cờ của lệnh quay

99
GV. Vương Quốc Dũng
SF, PF, ZF phản ánh kết quả
AF không xác định
CF = bít cuối cùng bị dịch ra khỏi toán hạng
OF = 1 nếu kết quả đổi dấu trong lần quay cuối cùng.

3.5. Nhóm các lệnh xử lí chuỗi


3.5.1. Lệnh MOVS/ MOVSB/ MOVSW (Move string byte/ string word)
Cú pháp MOVS chuỗi đích, chuỗi nguồn
Giải thích phần tử chuỗi đích ← phần tử chuõi nguồn
Lệnh này chuyển từng byte hay từng từ của chuỗi nguồn sang chuỗi đích, trong
đó :
+ DS : SI - là địa chỉ của phần tử trong chuỗi nguồn
+ ES : DI - là địa chỉ của phần tử trong chuỗi đích
+ Sau mỗi lần chuyển
Nếu cờ hướng DF = 0:
SI ← SI + 1 (Nếu là chuỗi byte).
DI ← DI + 1
hoặc SI ← SI + 2 (Nếu là chuỗi word).
DI ← DI + 2
Nếu cờ hướng DF = 1:
SI ← SI - 1
DI ← DI - 1
hoặc SI ← SI - 2
DI ← DI - 2
Có 2 cách để chỉ ra chuỗi là chuỗi byte hay chuỗi từ :
C1: Khai rõ tên chuỗi nguồn, chuõi đích là chuôi gì ở đầu chương trình
C2: Thêm vào lệnh MOVS đuôi B cho chuỗi byte, W cho chuỗi từ
Ví dụ 1 Ta đã định nghĩa 2 chuỗi như sau:
Data
Str1 db ‘hello’
Str2 db 5 dup (?)
Muốn chuyển 2 byte đầu tiên của str1 đến str2 ta có thể thực hiện những lệnh
sau :
Mov ax, @ data
Mov ds, ax ; Khởi tạo DS
Mov es, ax ; Khởi tạo ES
Lea si, str1 ; SI trỏ đến chuỗi nguồn
Lea di, str2 ; di trỏ đến chuỗi đích
CLD ; Xóa cờ hướng, xử lý xâu từ trái sang phải
Movsb ; chuyển byte đầu tiên, SI tăng 1, DI tăng 1
Movsb ; chuyển byte thứ hai

100
GV. Vương Quốc Dũng
MOVS là lệnh liên quan đến thanh ghi ES và cho phép thao tác với bộ nhớ.
Để chuyển cả chuỗi, lệnh MOSVB có thể dùng kèm với lệnh REP, xong
trước tiên phải khởi tạo CX với số n bằng số byte trong chuỗi nguồn (số byte
cần chuyển )
Cú pháp : REP MOVSB
Lệnh khởi tạo REP có tác dụng làm MOVSB được thực hiện n lần, sau mỗi
lệnh MOVSB, CX được giảm đi 1 cho đến khi nó = 0.
Ví dụ 2: Để chuyển 2 chuỗi trong ví trước ta có thể làm như sau:
CLD ; xoá cờ hướng, chuỗi được xử lý từ trái sang phải.
Lea SI, str1 ; SI trỏ đến chuỗi nguồn
Lea DI, str2 ; DI trỏ đến chuỗi đích
Mov CX,5 ; số kí tự cần chuyển = 5
REP MOVSB

Sau đây là các hình ảnhmô tả quá trình thực hiện các lệnh trên
Mô tả SI

Str1 ‘H’ ‘E’ ‘L’ ‘L’ ‘O’

offset 0 1 2 3 4
DI

Str2

offset 5 6 7 8 9

Sau lệnh MOVSB thứ nhất:


SI

Str1 ‘H’ ‘E’ ‘L’ ‘L’ ‘O’ SI đã tăng 1

offset 0 1 2 3 4
DI

Str2 ‘H’ DI đã tăng 1

offset 5 6 7 8 9

Sau lệnh MOVB thứ 2 :


SI

Str1 ‘H’ ‘E’ ‘L’ ‘L’ ‘O’ SI lại tăng thêm 1

101
GV. Vương Quốc Dũng
offset 0 1 2 3 4

DI

Str2 ‘H’ ‘E’ DI lại tăng 1

offset 5 6 7 8 9

Ví dụ 3 :
Viết các lệnh chép chuỗi str1 trong phần trước vào chuối str2 theo thứ tự
ngược lại.
Giải :
Ta cho SI trỏ đến cuối chuỗi str1 và giảm dần sau mỗi lần chuyển 1 ký tự.
Trong khi đó DI trỏ đến đầu str2 và tăng dần sau mỗi lần nhận 1 ký tự.
Lea SI, str1 +4 ; SI chuyển tới cuối chuỗi str1
Lea DI, str2 ; SI chuyển tới đầu chuỗi str2
STD ; thiết lập cờ hướng DF = 1
; xử lý chuỗi từ phải sang trái ⇒ SI↓, DI ↓
Mov cx,5 ; số lần chuyển DL giữa 2 chuỗi
Move: MOVSB ; chuyển từng Byte
Add DI, 2 ; cộng thêm 2 cho DI vì sau một lần chuyển
Loop move DI giảm 1 ⇒ cộng 2 thì DI tăng 1
Ví dụ 4 :
Cho mảng sau: Arr dw 10, 20, 40, 50, 60, ?
Viết lệnh để chèn 30 vào giữa 20 & 40 (giả thiết DE, ES đã được khởi tạo)
Giải : STD ; DF = 1, xử lý từ phải → trái
Lea SI, arr + 8h ; SI trỏ đến 60
Lea DI, arr + Ah ; DI trỏ đến phần tử cuối cùng của mảng
Mov CX,3 ; chuyển 3 phần tử
Rep Movsw ; chuyển 40, 50, 60
Mov word ptr[DI], 30 ; chèn 30 vào vị trí DI trỏ đến và
; chỉ rõ vị trí DI trỏ đến là 2 byte
Toán tử ptr định kiểu cho toán hạng đích .
Word ptr[DI], 30 ; Toán hạng đích được DI trỏ đến mang kiểu word
3.5.2. Lệnh STOS/ STOSB/ STOSW
Cú pháp : STOSB
Giải thích:
AL → phần tử hiện thời của chuỗi đích DF = 0 ⇒ DI + 1 → DI
DF = 1 ⇒ DI - 1 → DI
AX → phần tử hiện thời của chuỗi đích DF = 0 ⇒ DI + 2 → DI
DF = 1 ⇒ DI - 2 → DI

102
GV. Vương Quốc Dũng
Lệnh này có tác dụng chuyển nội dung của thanh ghi AL đến byte có địa chỉ
được định bởi ES:DI (AX → word có địa chỉ được định bởi ES:DI nhờ lệnh
STOSW).
Ví dụ:
Đoạn chương trình sau đưa 2 chữ cái ‘A’ vào xâu str1.

mov ax, @ data


Mov es, ax ; khởi tạo es
Lea di, str1 ; DI trỏ đến str1
CLD ; DF = 0 → DI và SI tăng dần
Mov Al, ‘A’ ; Al chứa ký tự ‘A’ cần lưu
Stosb ; Lưu chữ cái A
Stosb ; Lưu chữ cái A thứ 2
3.5.3. Lệnh LODS/ LODSB/ LODSW
Nạp vào AL/ AX 1 phần tử của chuỗi byte/ word.
Cú pháp: LODS chuỗi_nguồn
Giải thích:
Chuỗi byte: Al ← phần tử hiện thời của chuỗi được chỉ ra bởi DS:SI
SI ← SI + 1 nếu DF = 0 & chuỗi byte
SI ← SI - 1 nếu DF = 0 & chuỗi byte
Chuỗi từ : AX ← phần tử hiện thời của chuỗi được chỉ ra bởi DS:SI
SI ← SI + 2 nếu DF = 0
SI ← SI - 2 nếu DF = 1
Ví dụ :
str1 DB ‘ABC’
Đoạn lệnh sau nạp các byte thứ nhất & thứ 2 vào AL
mov AX, @ data
mov DS, AX ; khởi tạo DS
lea Si, str ; Si trỏ tới chuỗi str1
cld
Lodsd ; Nạp ký tự đầu tiên vào AL
Lodsb ; nạp ký tự thứ hai vào AL
3.5.4. Lệnh duyệt chuỗi: SCAS/ SCASB/ SCASW.
Cú pháp: SCAS chuỗi_đích
SCASB ; duyệt một chuỗi các byte
SCASW ; duyệt một chuỗi các word
Lệnh này được sử dụng để tìm 1 byte (tạm gọi là byte đích) chứa trong AL
hoặc 1 word (tạm gọi là word đích) chứa trong AX.
Phần tử byte đích hay word đích do ES:DI chỉ ra.
Lệnh SCAS lấy từng nội dung trong AL (hoặc AX) trừ đi từng Byte (hoặc
từng worrd) trong chuỗi đích và sử dụng kết quả để thiết lập các cờ. Kết quả

103
GV. Vương Quốc Dũng
không được lưu lại. Sau mỗi lần thực hiện phép trừ, DI tự động tăng hoặc giảm
tuỳ theo cờ hướng:
DI ← DI + 1: DF = 0, chuỗi byte
DI ← DI - 1 : DF = 1, chuỗi byte
DI ← DI + 2 : DF = 0, chuỗi word
DI ← DI - 2 : DF = 1, chuỗi word

Ví dụ:
Đoạn chương trình sau kiểm tra xem 2 byte đầu tiên của chuỗi str1 có chữ
‘B’ hay không?
STR1 DB ‘ABC’
MOV AX, @ data
MOV ES, AX ; khởi tạo ES
CLD ; Xoá cờ hướng, xử lý chuỗi từ trái -> Phải
LEA DI, STR1 ; DI trỏ đến đầu chuỗi STR1
MOV AL,’B’ ; byte đích
SCASB
SCASB ; duyệt 2 lần để tìm ký tự B (kiểu tra xem 2 byte
; đầu của chuỗi có ký tự ‘B’ hay không)
Lệnh này cập nhật các cờ AF, CF, OF, PF, SF, ZF
Khi tìm 1 byte trong chuỗi, để chuỗi sẽ được duyệt cho đến khi byte được
tìm thấy hoặc hết chuỗi. Nếu CX nhận giá trị ban đầu là số byte trong chuỗi,
lệnh SCAS kết hợp với REPNE để làm việc này.
REPNE SCAS ; lặp lại khi chưa bằng, chưa đến đích.
Sẽ lặp lại phép trừ nội dung của AL đi từng byte trong chuỗi được chỉ bởi
ED:DI, việc tìm kiếm thực hiện cho đến khi có kết quả = 0 (khi tìm thấy byte
đích) hoặc CX = 0 (khi duyệt hết chuỗi).
Chú ý: Lệnh REPNZ (Repeat while not Zero) tạo ra mã máy giống lệnh
REPNE (Repeat while not Equal)
Ví dụ:
CLD ; làm việc với chuỗi theo chiều tăng
MOV AL, 13 ; AL chứa mã ASCII của ký tự về đầu dòng(CR)
MOV CX, 80 ; quét 1 dòng 80 ký tự
LEA DI, STR1 ; DI chỉ vào đầu chuỗi STR1 để tại đoạn dữ liệu ES
REPNE SCASB ; duyệt chuỗi để tìm CR
3.5.5. CMPS/ CMPSB/ CMPSW (compane string bytes or string word)
(so sánh 2 chuỗi byte hay hai chuỗi từ)
Cú pháp: CMPS chuỗi đích, chuỗi nguồn
CMPSB ; so sánh 2 chuỗi byte
CMPSW ; so sánh 2 chuỗi word
Lệnh này so sánh từng phần tử (byte hay word) của 2 xâu có các phần tử cùng
loại. Lệnh chỉ tạo các cờ không lưu kết quả so sánh, sau khi so sánh các toán

104
GV. Vương Quốc Dũng
hạng không thay đổi. Trong lệnh này, ngầm định các thanh ghi với các chức
năng:
DS:SI là địa chỉ của phần tử so sánh trong chuỗi nguồn
ES:DI là địa chỉ của phần tử so sánh trong chuỗi đích
mỗi lần so sánh, thực hiện: phần tử nguồn - đích
Với chuỗi byte:
SI ← SI ± 1 tuỳ DF = 0/ DF = 1
DI ← DI ± 1 tuỳ DF = 0/ DF = 1
Với chuỗi word:
SI ← SI ± 2 tuỳ DF = 0/ DF = 1
DI ← DI ± 2 tuỳ DF = 0/ DF = 1.
Cập nhật các cờ: AF, CF, OF, PF, SF, ZF
Ví dụ:
a. strB1 DB ’ACD’ ;
StrB2 DB ’BCD’ ; để trong đoạn data
Mov AX, @data;
Mov DS, AX ; khởi tạo DS
Mov ES, AX ; và ES.
CLD ; xử lý xâu từ trái → phải
LEA SI, strB1 ; SI trỏ tới đầu strB1
` LEA DI, strB2 ; DI trỏ tới đầu strB2
CMPS strB2, strB1 ; so sánh byte đầu tiên
CMPS strB2, strB1 ; so sánh byte thứ 2
b. Mov DI, offset strB2
Mov SI, offset strB1
CLD
CMPSB
* Các lệnh khởi tạo REPE & REPZ
Việc so sánh chuỗi có thể được thực hiện bằng cách gắn thêm lệnh khởi
tạo REPE (Repeat while Equal) hay REPZ (Repeat while Zezo) vào các
lệnh CMPSB và CMPSW. CX nhận giá trị ban đầu là số byte hoặc số word
trong chuỗi ngắn hơn
cú pháp: REPE CMPSB ; so sánh chuỗi các byte khi còn bằng nhau
REPE CMPSW ; so sánh chuỗi các word khi còn bằng nhau
Lệnh này sẽ lặp lại việc thực hiện các lệnh CMPSB hay CMPSW rồi giảm
CX cho đến khi:
Hoặc: có 2 byte tương ứng ở 2 chuỗi khác nhau
Hoặc: CX = 0
Các cờ được thiết lập tùy theo kết quả phép so sánh cuối cùng.
Ví dụ: Mov CX,10 ; chiều dài của chuỗi ngắn
LEA SI, str1 ; SI trỏ tới chuỗi nguồn str1
LEA DI, str2 ; DI trỏ tới chuỗi đích str2
CLD
REPE CMPSB ; so sánh chuỗi các byte

105
GV. Vương Quốc Dũng
JL str1_1 ; nhảy nếu str1 < str2
JG str1_2 ; nhảy nếu str1>str2
Mov AX, 0 ; đặt 0 vào AX nếu str1 = str2
JMP EXIT ; thoát
Str1_1: ; trường hợp str1 đứng trước str2
Mov AX, 1 ; đặt 1 vào AX cho trường hợp này
JMP EXIT ; thoát
Str1_2: ; trường hợp str2 đứng trước str1
Mov AX, 2 ; đặt 2 vào AX cho trường hợp này
JMP EXIT ; thoát
EXIT:
đoạn chương trình trên giả định DS, ES đã được khởi tạo giá trị thích hợp. Nó
thực hiện so sánh 2 chuỗi str1 & str2
(có độ dài ngắn nhất là 10 : - AX = 0 nếu 2 chuỗi = nhau
- AX = 1 nếu chuỗi str1 < str2
- AX = 2 nếu chuỗi str1 > str2
3.5.6. Dạng tổng quát của các lệnh thao tác chuỗi như sau:

Lệnh Toán hạng đích 3.6. Toán Dạng byte Dạng word
hạng nguồn
Chuyển chuỗi ES:DI DS:SI MOVSB MOVSW
So sánh chuỗi ES:DI DS:SI CMPSB CMPSW
Lưu chuỗi ES:DI AL hay AX STOSB STOSW
Nạp chuỗi Al hay AX DS:SI LODSB LODSW
Duyệt chuỗi ES:DI AL hay AX SCASB SCASW

CÂU HỎI ÔN TẬP VÀ BÀI TẬP

1. Thực hiện các phép tính logic sau:


a. 10101111b AND 10001001b
b. 10110001b OR 01001001b
c. 01111100b XOR 11011010b
d. NOT 01011110b
2. Viết các lệnh logic để thực hiện các công việc sau:
a. Xóa các bits ở vị trí chẵn của AX, giữ nguyên các bits khác.
b. Thiết lập các bit LSB và MSB của BL, giữ nguyên các bits khác
c. Đảo MSB của BL, giữ nguyên các bits khác.
d. Thay nội dung của biến word1 bằng số bù 1 của nó.

3. Viết chương trình thực hiện các công việc sau:


a. Nhập một xâu ký tự từ bàn phím. Hiển thị xâu vừa nhập.
b. Đảo ngược xâu vừa nhập. Hiển thị xâu sau khi đảo ra màn hình.
Ví du:

106
GV. Vương Quốc Dũng
Nhap mot xau ky tu: 123456789
Xau sau khi dao: 987654321
4. Giả sử AL = 11001011b và CF = 1, cho biết nội dung mới của của AL khi mỗi
lệnh sau được thực hiện. Điều kiện đầu đúng cho tất cả các phần nhỏ:
a. SHL AL, 1
b. SHR AL, 1
c. MOV CL, 2
ROL AL, CL
d. MOV CL, 3
ROR AL, CL
e. MOV CL, 2
SAR AL, CL
f. RCL AL, 1
g. MOV CL, 3
RCR AL, CL
5. Viết các lệnh để thực hiện các công việc sau, giả sử không có hiện tượng tràn
xảy ra:
a. Nhân đôi một biến kiểu byte có giá trị = 0B5h
b. Nhân AL với 8
c. Chia 32142 cho 4, lưu kết quả trong AX
d. Chia -2145 cho 16, lưu kết quả trong BX.
6. Viết các lệnh để thực hiện các công việc sau:
a. AL < 10, đổi nó thành mã ASCII của chữ số thập phân.
b. DL = mã ASCII của một chữ cái hoa, đổi nó thành mã ASCII của một
chữ cái thường.
7. Viết các lệnh để thực hiện các công việc sau:
a. BL * 10, giả sử không có hiện tượng tràn
b. AL / 8, lưu số dư vào AH, giả sử AL < 0.
8. Viết chương trình thông báo cho người sử dụng nhập vào một ký tự, in ra trên
các dòng liên tiếp nhau mã ASCII của ký tự đó dưới dạng nhị phân, số các chữ
số 1 trong mã ASCII dưới dạng nhị phân vừa in ra như trên.
Ví dụ: Nhập vào một ký tự : A
Mã ASCII của ký tự A dưới dạng nhị phân là: 01000001
Số các bit 1 là 2
9. Viết một hay nhiều lệnh để thực hiện các công việc sau, giả sử không có hiện
tượng tràn xảy ra:
a. Nhân một biến kiểu byte với giá trị 0B5h
b. Nhân nội dung của Al với 8.
c. Chia 32142 cho 4 và lưu kết quả trong AX.
d. Chia -2145 cho 16 và lưu kết quả trong BX.

10. Viết các lệnh để thực hiện các công việc sau:
a. Nhân nội dung của BL với 10, giả sử không có hiện tượng tràn.
c. Giả sử AL chứa một số âm, hãy chia nó cho 8 và lưu số dư vào AH.

107
GV. Vương Quốc Dũng
11. Hãy cho biết nội dung của DX, AX nếu các lệnh sau là hợp lệ và được thực
hiện :
a. MUL BX ; với AX = 8, BX = 3
b. MUL BX ; với AX = 0FFh, BX = 1000h
c. IMUL CX ; với AX = 5, CX = 0FFFFh
d. IMUL 10h ; với AX = 0FEFh
e. DIV BX ; với DX = 0, AX = 7, BX = 2
f. DIV BX ; với DX = 0, AX = 0FFFEh, BX = 0010h
g. IDIV BX ; với DX = 0FFFEh, AX = 0FFFCh, BX = 3
h. DIV BL ; với AX = 0Dh, BX = 3
i. IDIV BL ; với AX = 0FFFBh, BX = 10
12. Cho biết nội dung của DX sau khi các lệnh CWD thực hiện nếu AX là:
a. 7E02h
b. 8ABCh
c. 1ABCh
13. Viết các lệnh hợp ngữ để thực hiện các lệnh sau trong ngôn ngữ bậc cao:
Giả thiết A, B, C là các biến kiểu Word, và tất cả các tích số đều chứa vừa
trong 16 bits. Sử dụng lệnh IMUL trong các phép tính, không cần giữ lại nội
dung các biến A, B, C
a. A := 5*A - 7
b. B := (A-B) * (B+10)
c. A := 6-9*A
14. Cho biết nội dung mới trong các thanh ghi AH và AL ở hệ hex khi thực hiện
các lệnh sau:
a. MUL BL ; AX = 1234h, BX = 5678h
b. IMUL BL ; BL = 85h, AX = 2
c. DIV BL ; BX = 1206h, AX = 97
d. IDIV BL ; BX = 1206h, AX = 9872h
15. Viết các câu lệnh hợp ngữ để mã hóa các mã lệnh giả sau:
a. AL = 7;
BX = 1234h
AL * BX
b. AL = -9
BX = 2
AL / BL
16. Cho biết nội dung các thanh ghi DX, AX, BX, BH, BL, DH, DL, AH, AL sau
khi thực hiện xong các lệnh câu a bài 15 ở trên.
17. Cho biết nội dung các thanh ghi AX, BX, BH, BL, AH, AL sau khi thực hiện
xong các lệnh câu b bài 15 ở trên.

108
GV. Vương Quốc Dũng

Ch¬ng 4. CÁC CẤU TRÚC RẼ NHÁNH CHƯƠNG TRÌNH


Đặc điểm của các cấu này là làm thay đổi trình tự thực hiện chương trình (rẽ
nhánh) tức lúc đó PC (Program Counter) được nạp 1 địa chỉ mới không có tính
tuyến tính.
4.1. Nhóm lệnh nhảy
4.1.1. Lệnh nhảy không điều kiện JMP
Cú pháp: JMP NHAN
giải thích:
lệnh JMP (jump) là lệnh nhảy không điều kiện. ở đây NHAN là tên nhãn mà
lệnh tiếp theo sẽ được thực hiện là lệnh bắt đầu tại địa chỉ ứng với nhãn NHAN
JMP có thể dùng để khắc phục khoảng giới hạn của lệnh nhảy có điều kiện. Ví
dụ: giả sử chúng ta muốn thực hiện vòng lặp

109
GV. Vương Quốc Dũng
Top:
; các lệnh khác trong thân vòng lặp
DEC CX ; giảm bộ đếm
JNZ Top ; lặp nếu CX>0
Mov AX, BX
Nhưng trong thân vòng lặp chứa quá nhiều lệnh đến mức nhãn Top nằm ngoài
khoảng giới hạn của lệnh JNZ (nhiều hơn 126 Byte trước JNZ Top). Chúng ta
có thể sửa lại:
Top:
; các lệnh khác trong thân vòng lặp
DEC CX ; giảm bộ đếm
JNZ bottom ; nhảy đến bottom nếu CX>0
JMP exit
Bottom: JMP Top
Exit: mov AX,BX
4.1.2. lệnh so sánh CMP
Các điều kiện nhảy cung cấp bởi lệnh CMP (Compare)
Cú pháp: CMP đích, nguồn
Giải thích: Lệnh này so sánh toán tử đích với toán tử nguồn, bằng cách lấy
toán tử đích trừ đi toán tử nguồn. Kết quả không được lưu lại nhưng các cờ bị
ảnh hưởng. Các toán hạng của lệnh CMP không thể cùng là các ô nhớ, toán
hạng đích không được phép là hằng số.
4.1.3. các lệnh nhảy có điều kiện.
Đặc điểm chung của các lệnh nhảy có điều kiện là đòi hỏi nhãn được chỉ ra
trong lệnh nhảy phải đứng trước lệnh nhảy không quá 126 byte hoặc đứng sau
không quá 127 byte. Song có cách để vượt qua giới hạn này như đã chỉ ra ở
lệnh JMP.
Để thực hiện một lệnh nhảy, CPU nhìn vào thanh ghi cờ. Thanh ghi cờ phản
ánh kết quả công việc gần nhất mà CPU thực hiện. Nếu điều kiện của lệnh nhảy
(được phát biểu như một tổ hợp các sự lập cờ trạng thái) thoả mãn, CPU điểu
chỉnh IP trỏ đến nhãn đích và như thế lệnh ở sau nhãn này sẽ được thi hành tiếp
theo. Nếu điều kiện nhảy không thoả mãn, IP không bị sửa đổi, điều này có
nghĩa lệnh trên dòng tiếp theo sẽ được thi hành.
Các lệnh nhảy có điều kiện có 3 loại đó là:
1) Các lệnh nhảy có dấu: Dùng khi kết quả trả về là các số có dấu.

Ký hiệu Chức năng Điều kiện


JG/JNLE Nhảy nếu lớn hơn ZF = 0 và SF = 0F
Nhảy nếu không ‘<’ hay ‘=’
JGE/JNL Nhảy nếu ‘>’ or ‘=’ SF = OF
Nhảy nếu không ‘<’
JL/JNGE Nhảy nếu ‘<’ SF <> 0F
110
GV. Vương Quốc Dũng
Nhảy nếu không ‘>’ or ‘=’
JLE/JNG Nhảy nếu ‘<’ or ‘=’ ZF = 1 or SF = 0F
Nhảy nếu không ‘>’

2) Các lệnh nhảy không dấu: Dùng với các số không dấu.

Ký hiệu Chức năng Điều kiện


JA/JNBE Nhảy nếu ‘>’ CF = 0 và ZF = 0
Nhảy nếu không ‘<’ hoặc ‘=’
JAE/JNB Nhảy nếu ‘>’ hoặc ‘=’ CF = 0
Nhảy nếu không ‘<’
JB/JNAE Nhảy nếu ‘<’ CF = 1
Nhảy nếu không ‘>’ hoặc ‘=’
JBE/JNA Nhảy nếu ‘<’ or ‘=’ CF = 1 or ZF = 1
Nhảy nếu không ‘>’
3) Các lệnh nhảy điều kiện đơn: Điều kiện phụ thuộc vào một cờ riêng
biệt.

Ký hiệu Chức năng Điều kiện nhảy


JE/JZ Nhảy nếu ‘=’ ZF = 1
Nhảy nếu ‘= 0’
JNE/JNZ Nhảy nếu không ‘=’ tức ‘<>’ ZF = 0
Nhảy nếu ‘<>0’
JC Nhảy nếu có nhớ CF = 1
JNC Nhảy nếu không nhớ CF = 0
JO Nhảy nếu tràn OF = 1
JNO Nhảy nếu không tràn OF = 0
JS Nhảy nếu dấu âm SF = 1
JNS Nhảy nếu dấu dương SF = 0
JP/JPE Nhảy nếu cờ chẵn PF = 1
JNP/JPO Nhảy nếu cờ lẻ PF = 0

Cú pháp chung của lệnh nhảy có điều kiện :


Lệnh nhảy nhãn
Một ví dụ về lệnh nhảy
Hiển thị ra màn hình 256 kí tự trong bảng mã ASCII. Cho nhận xét chung khi
chạy chương trình.
Lời giải:
1: Title VDM.ASM ; Hiển thị các kí tự IBM.
2: .Model Small
3: .Stack 100H.
4: .Code.

111
GV. Vương Quốc Dũng
5: MAIN PROC
6: Mov AH, 2 ; Hàm hiển thị kí tự của ngắt 21H của DOS.
7: Mov CX,256 ; Số kí tự được hiển thị
8: Mov DL,0 ; DL chứa mã ASCII của kí tự đầu tiên ‘NULL’
9: lặp: INT 21H ; Hiện thị một kí tự
10: INC DL ; Tăng mã ASCII
11 DEC CX ; Giảm CX (bộ đếm) đi 1
12: JNZ lặp ; Về lặp nếu CX <>0
13: ;trở về DOS.
14: Mov AH, 4CH
15: INT 21H
16 : MAIN ENDP
17: END MAIN
NHAN XET: Khi chạy chương trình tất cả các kí tự được hiển thị với các mã
ASCII của các kí tự điều khiển như lùi một kí tự, về đầu dòng xuống dong.....,
các chức năng điều khiển này sẽ được thực hiện thay vì hiển thị chúng.
4.2. Các cấu trúc lập trình bậc cao
Các lệnh nhảy có thể được dùng để thực hiện các công việc rẽ nhánh và lặp.
Tuy nhiên các lệnh nhảy quá sơ khai nên rất khó mã hóa một thuật toán (có hoặc
không có các dòng hướng dẫn) nhất là đối với những người mới lập trình.
Bởi vì đa số chúng ta đã có chút ít kinh nghiệm về các cấu trúc của ngôn ngữ
bậc cao như cấu trúc lựa chọn IF_THEN_ELSE hay các vòng lặp WHILE,
REPEAT_UNTIL. Chúng ta sẽ nghiên cứu các cấu trúc này thông qua cách giả
lập theo các toán tử giả của ngôn ngữ bậc cao, từ đó chuyển sang mã hóa bằng
hợp ngữ
4.2.1. Các cấu trúc rẽ nhánh
4.2.1.1. Cấu trúc IF_THEN
Cấu trúc này có thể được biểu diễn dưới dạng mã lệnh giả như sau:
IF điều_kiện THEN
Nhánh đúng
END_IF
Trong đó, điều_kiện là một biểu thức có thể đúng hoặc sai. Nếu nó đúng, nhánh
đúng được thực hiện. Ngược lại trong cấu trúc không thực hiện lệnh nào.
chương trình tiếp tục thực hiện với các lệnh tiếp theo sau cấu trúc.
Ví dụ: Thay số trong AX bằng giá trị tuyệt đối của nó
Thuật toán với mã lệnh giả như sau:
IF AX < 0 THEN
AX = -AX
END_IF
Ta chuyển sang mã hóa bằng hợp ngữ như sau:

112
GV. Vương Quốc Dũng
; If AX < 0
CMP AX, 0
JNL END_IF ; Nhảy nếu không nhỏ hơn
; Then
NEG AX
END_IF:
Điều kiện AX < 0 được kiểm tra bởi lệnh CMP AX, 0. Nếu AX không nhỏ hơn
0, lệnh JNL dùng để nhảy qua lệnh NEG AX để thực hiện các lệnh sau nhãn
END_IF (không thực hiện gì trong cấu trúc rẽ nhánh). Nếu điều kiện AX < 0
thỏa mãn, chương trình thực hiện lệnh NEG AX, sau đó thực hiện các lệnh tiếp
sau nhãn END_IF.
4.2.1.2. Cấu trúc IF_THEN_ELSE
Cấu trúc này có thể được biểu diễn dưới dạng mã lệnh giả như sau:
IF điều_kiện THEN
Nhánh đúng
ELSE
Nhánh sai
END_IF
Trong đó, điều_kiện là một biểu thức có thể đúng hoặc sai. Nếu nó đúng, nhánh
đúng được thực hiện. Ngược lại nếu điều kiện sai, nhánh sai được thực hiện.
Ví dụ: Giả sử AL và BL chứa các mã ký tự ASCII, hãy hiển thị có mã ASCII bé
hơn.
Thuật toán với mã lệnh giả như sau:
IF AX ≤ BL THEN
Hiển thị ký tự trong AL
ELSE
Hiển thị ký tự trong BL
END_IF
Ta chuyển sang mã hóa bằng hợp ngữ như sau:
MOV AH, 2
; If AX ≤ BL
CMP AL, BL
JNBE ELSE_ ; nhảy nếu không nhỏ hơn hoặc bằng
; Then
MOV DL, AL
INT 21h
JMP END_IF
ELSE_:

113
GV. Vương Quốc Dũng
MOV DL, BL
INT 21h
END_IF:
Để tối ưu, đoạn chương trình trên được viết lại như sau:
MOV AH, 2
; If AX ≤ BL
CMP AL, BL
JNBE ELSE_ ; nhảy nếu không nhỏ hơn hoặc bằng
; Then
MOV DL, AL
JMP DISPLAY
ELSE_:
MOV DL, BL
DISSPLAY:
INT 21h
END_IF:
Điều kiện AX ≤ BL được kiểm tra bởi lệnh CMP AL, BL. Nếu nó sai, chương
trình sẽ nhảy qua các lệnh
MOV DL, AL
JMP DISPLAY
đến thực hiện lệnh đứng sau nhãn ELSE_
Nếu điều kiện đúng, các lệnh
MOV DL, AL
JMP DISPLAY
được thực hiện và lệnh đứng sau nhãn ELSE bị bỏ qua.
Nhận xét:
Cấu trúc IF_THEN_ELSE bao giờ cũng gồm 2 loại lệnh nhảy, đó là loại lệnh
nhảy có điều kiện và loại lệnh nhảy không điều kiện JMP.
- Lệnh nhảy có điều kiện dùng để nhảy đến thực hiện nhóm các lệnh theo
điều kiện sai
- Lệnh nhảy JMP bao giờ cũng đặt ở cuối nhánh ứng với điều kiện đúng để
chương trình nhảy về cuỗi cấu trúc, bỏ qua không thực hiện nhóm các
lệnh ứng với điều kiện sai.
4.2.1.3. Cấu trúc CASE
CASE là cấu trúc nhiều lựa chọn (rẽ nhiều nhánh), nó kiểm tra các thanh ghi,
các biến hay các biểu thức với các giá trị riêng rẽ trong miền giá trị. Dạng tổng
quát của nó là:
CASE Phát_biểu

114
GV. Vương Quốc Dũng
Giá_trị_1: dòng_lệnh_1
Giá_trị_2: dòng_lệnh_2
…...
Giá_trị_n: dòng_lệnh_n
END_CASE
Ví dụ: Hãy cho BX nhận các giá trị sau:
BX = 0 nếu AX = 0
BX = 1 nếu AX > 0
BX = -1 nếu AX < 0
Ta có mã lệnh giả như sau:
CASE AX
< 0: BX = -1
= 0: BX = 0
> 0: BX = 1
END_CASE
Ta chuyển sang mã hóa bằng hợp ngữ như sau:
; Case AX
CMP AX, 0
JL am
JE khong
JG duong
am:
MOV BX, -1
JMP END_CASE
khong:
MOV BX, 0
JMP END_CASE
duong:
MOV BX, 1
END_CASE:
Nhận xét:
Cấu trúc CASE bao giờ cũng gồm 2 loại lệnh nhảy, đó là loại lệnh nhảy có điều
kiện và loại lệnh nhảy không điều kiện JMP.
- Các lệnh nhảy có điều kiện dùng để nhảy đến thực hiện nhóm các lệnh
theo điều kiện đúng tương ứng
- Lệnh nhảy JMP bao giờ cũng đặt ở cuối mỗi nhánh để chương trình nhảy
về cuỗi cấu trúc, bỏ qua không thực hiện nhóm các lệnh ứng với các điều
kiện khác đứng ngay sau nhánh đó.

115
GV. Vương Quốc Dũng
4.2.1.4. Các nhánh với điều kiện kép
Đôi khi điều kiện rẽ nhánh của IF_THEN hay CASE có dạng
điều_kiện_1 AND điều_kiện_2
Hoặc:
điều_kiện_1 OR điều_kiện_2
Ở đây điều_kiện_1 và điều_kiện_2 có thể đúng hoặc sai.
1) Các điều kiện AND
Điều kiện AND chỉ đúng khi các 2 điều kiện điều_kiện_1 và điều_kiện_2 đều
đúng. Ngược lại nếu một trong 2 điều kiện đó sai thì điều kiện AND cũng sẽ sai.
Ví dụ: Nhập một ký tự, nếu là chữ hoa thì hiển thị nó
Ta thấy trong bảng mã ASCII, một ký tự là chữ hoa nếu nó nằm trong khoảng từ
“A” đến “Z”. Hay nói cách khác là ký_tự ≤ “Z” và ký tự ≥ “A”
Vậy ta có mã lệnh giả như sau:
Nhập một ký tự
IF (‘A’ ≤ ký_tự) AND (ký_tự ≤ “Z”) THEN
Hiển thị ký tự
END_IF
Ta chuyển sang mã hóa bằng hợp ngữ như sau:
MOV AH, 1
INT 21h
; If (‘A’ ≤ký_tự) AND (ký_tự ≤ “Z”)
CMP AL, ‘A’
JNGE END_IF ; Nếu AL < ‘A’, nhảy đến END_IF
CMP AL, ‘Z’
JNLE END_IF ; Nếu AL > ‘Z’, nhảy đến END_IF
; Then hiển thị ký tự
MOV DL, AL
MOV AH, 2
INT 21H
END_IF:
2) Các điều kiện OR
Điều kiện OR đúng khi một trong 2 điều kiện: điều_kiện_1 hoặc điều_kiện_2
đúng. Ngược lại, điều kiện OR chỉ sai khi cả 2 điều kiện thành phần đều sai.
Ví dụ: Nhập một ký tự, nếu là ký tự ‘c’ hay ‘C’ thì hiển thị.
Ta có mã lệnh giả như sau:
Nhập một ký tự
IF (ký_tự = ‘c’) OR (ký_tự = “C”) THEN
Hiển thị ký tự

116
GV. Vương Quốc Dũng
END_IF
Ta chuyển sang mã hóa bằng hợp ngữ như sau:
MOV AH, 1
INT 21H
; If (ký_tự = ‘c’) OR (ký_tự = ‘C’)
CMP AL, ‘y’
JE THEN
CMP AL, ‘Y’
JE THEN
JMP END_IF
THEN:
MOV AH, 2
MOV DL, AL
INT 21H
END_IF:
4.2.2. Các cấu trúc lặp
4.2.2.1. Lặp biết trước số lần lặp LOOP
Nhảy đến một nhãn đã được chỉ rõ, nhãn này luôn đặt ở đầu vòng lặp, số lần lặp
chứa trong CX nếu CX<>0 và sau mỗi lần lặp CX tự động giảm đi 1, dừng lặp
khi CX = 0 và thực hiện lệnh tiếp sau lệnh LOOP.
Cú Pháp: LOOP Nhãn_gần
Giải thích: Lệnh này dùng để lặp lại một đoạn chương trình (gồm các lệnh nằm
trong khoảng từ nhãn đến hết lệnh Loop nhan_gan) cho đến khi số lần lặp CX =
0. Số lần lặp phải được khởi tạo trong CX trước khi vào vòng lặp. Nhãn
‘nhãn_gần’ phải ở trước lệnh lặp không quá 126 byte.
Ta có thể sử dụng lệnh loop để thực hiện vòng lặp FOR
Ví dụ: Hiển thị một dòng 60 dấu ‘*’
Mov CX, 60 ; Khởi tạo bộ đếm.
Mov AH, 2 ; Hàm hiển thị kí tự
Mov DL, ’*’ ; Kí tự cần hiển thị
Top: INT 21H ; Hiển thị một dấu’*’
LOOP Top ; Thực hiện lặp 60 lần
Lưu ý: Không được khởi tạo CX = 0 trước khi vào vòng lặp LOOP vì vòng lặp
FOR thực hiện bởi lệnh LOOP sẽ được thực hiện ít nhất một lần, khi CX = 0 trước
khi vào vòng lặp LOOP giảm CX đi 1 thì CX = 0FFFFH và lệnh LOOP sẽ được
thực hiện 0FFFFH = 65535 lần nữa, do đó trường hợp này gây lỗi lôgic.

4.2.2.2. Các lệnh LOOP có điều kiện

117
GV. Vương Quốc Dũng
Lệnh LOOP chỉ chấm dứt vòng lặp khi thanh ghi CX = 0, muốn chấm dứt vòng
lặp trước khi CX = 0, ta phải dùng các vòng lặp có điều kiện.
Lệnh LOOPE (Loop while equal)
Cú pháp:
LOOPE Nhãn_gần
Thi hành: CX = CX – 1
IF (ZF = 1) AND (CX <> 0) THEN
JMP Nhãn_gần
Cụ thể: Sau mỗi lần lặp, CX tự động giảm đi 1 và chuyển điều khiển về đầu
vòng lặp (về nhãn_gần) nếu cờ ZF = 1 và thanh ghi CX ≠ 0, ngược lại vòng lặp
kết thúc và thực hiện lệnh tiếp theo sau lệnh LOOPE.
Lệnh LOOPZ (Loop while zero)
Giống như lệnh LOOPE.
Lệnh LOOPNE (Loop while not equal)
Cú pháp:
LOOPNE Nhãn_gần
Thi hành: CX = CX – 1
IF (ZF = 0) AND (CX <> 0) THEN
JMP Nhãn_gần
Cụ thể: Sau mỗi lần lặp, CX tự động giảm đi 1 và chuyển điều khiển về đầu
vòng lặp (về nhãn_gần) nếu cờ ZF = 0 và thanh ghi CX ≠ 0, ngược lại vòng lặp
kết thúc và thực hiện lệnh tiếp theo sau lệnh LOOPE.
Lệnh LOOPNZ (Loop while not zero)
Giống như lệnh LOOPNE.
Ví dụ:
Cho vùng nhớ MEM có chiều dài 200 byte, trong đoạn dữ liệu DS, Tìm ký tự
“S” trongvùng nhớ này.
MOV CX, 200
MOV BX, OFFSET MEM-1
Cont: INC BX
CMP BYTE PTR [BX], ‘S’
LOOPNZ Cont
JZ Found
; Trả về thông báo không tìm thấy
Found:
; Trả về thông báo là tìm thấy
MEM DB ……

118
GV. Vương Quốc Dũng
4.2.2.3. Vòng lặp WHILE.
Đây là vòng lặp phụ thuộc vào một điều kiện. Dạng mã lệnh giả:
While điều_kiện do
Các dòng lệnh
End_While
Ta thấy: Điều kiện được kiểm tra ở đầu vòng lặp. Nếu nó đúng thì các dòng
lệnh sẽ được thi hành. Ngược lại nếu sai, chương trình tiếp tục thực hiện lệnh ở
sau vòng lặp.
Ví dụ: Viết lệnh để đếm số kí tự trong một dòng.
Giải: ý tưởng thuật toán
Khởi tạo bộ đếm bằng 0.
Đọc 1 kí tự
While kí tự <> kí tự về đầu dòng DO
đếm = đếm + 1
đọc một kí tự
End_While.
Vây ta có các lệnh sau.
Mov DX, 0 ; Khởi tạo bộ đếm, DX đếm số kí tự
Mov AH,1 ; Hàm đọc kí tự
INT 21H ; Đọc kí tự bộ đệm bàn phím
WHILE_:
Cmp AL, 0dh ; So sánh kí tự trong AL với kí tự về đầu dòng
JE END_While ; Nếu đúng (nhảy nếu =) về End_While
INT DX ; không phải CR, tăng bộ đếm
INT 21H ; Đọc tiếp một kí tự
JMP While_ ; Quay về đầu vòng lặp
End_While:
Nhận xét:
Cũng như các ngôn ngữ lập trình bậc cao, vòng lặp while … do là vòng lặp không
biết trước số lần lặp và có thể không thực hiện một lần lặp nào.
Trong lập trình hợp ngữ, vòng lặp while luôn có 2 loại lệnh nhảy, đó là
+ Loại lệnh nhảy có điều kiện để kiểm tra điều kiện bước vào vòng lặp, loại
này luôn được đặt ở đầu vòng lặp. Với vòng lặp có nhiều điều kiện thực hiện
kết hợp thì sẽ có nhiều lệnh nhảy có điều kiện.
+ Loại lệnh nhảy không điều kiện là lệnh nhảy JMP, luôn được đặt ở cuối
vòng lặp để chuyển điều khiển về đầu vòng lặp.
4.2.2.4. Nhóm lệnh vòng lặp REPEAT
Đây cũng là vòng lặp phụ thuộc vào một điều kiện, dạng mã lệnh giả của nó là:
REPEAT
Các dòng lệnh
UNTIL Điều kiện

119
GV. Vương Quốc Dũng
Trong vòng lặp này, các dòng lệnh được thi hành sau đó mới kiểm tra điều
kiện. Nếu điều kiện đúng vòng lặp kết thúc, nếu sai điều khiển rẽ nhánh đến
đầu vòng lặp.
Ví dụ:
Viết lệnh đọc vào các kí tự từ bàn phím, kết thúc khi gặp một kí tự trắng.
Giải:
REPEAT
đọc một kí tự
UNTIL kí tự trắng
Các lệnh là:
Mov AH,1 ; Hàm đọc kí tự
REPEAT_:
INT 21H ; Đọc kí tự trong AL
;UNTIL
Cmp AL,’ ‘ ; So sánh có đúng kí tự trắng không?
JNE Repeat_ ; Không đúng đọc tiếp
Ra:
Nhận xét:
Cũng như các ngôn ngữ lập trình bậc cao, vòng lặp Repeat … Until là vòng
lặp không biết trước số lần lặp và luôn thực hiện ít nhất một lần lặp.
Trong lập trình hợp ngữ, vòng lặp repeat chỉ có 1 loại lệnh nhảy, đó là
+ Loại lệnh nhảy có điều kiện để kiểm tra điều kiện kết thúc vòng lặp, loại
này luôn được đặt ở cuối vòng lặp để chuyển điều khiển về đầu vòng lặp.
Với vòng lặp có nhiều điều kiện thực hiện kết hợp thì sẽ có nhiều lệnh nhảy
có điều kiện.

Chú ý: So sánh While và Repeat


- Ưu điểm của vòng lặp While là: vòng lặp có thể bỏ qua khi điều kiện kết
thúc được khởi tạo với giá trị sai. Trong khi đó vòng lặp repeat luôn được thực
hiện ít nhất một lần.
- Tuy nhiên vòng lặp repeat có vẻ ngắn hơn đôi chút vì nó chỉ có một loại
lệnh nhảy có điều kiện ở cuối, trong khi vòng lặp while có những 2 loại lệnh
nhảy: các lệnh nhảy có điều kiện ở đầu và lệnh JMP ở cuối vòng lặp.
4.3. Lập trình với các cấu trúc bậc cao
Để có thể thấy được một chương trình có thể được phát triển từ các toán tử giả
bậc cao thành các lệnh hợp ngữ (Assembly) như thế nào, chúng ta xem xét vấn
đề sau:
Yêu cầu:
Thông báo cho người sử dụng nhập vào một dòng văn bản. Hiển thị trên dòng
tiếp theo chữ hoa có mã ASCII bé nhất và chữ hoa có mã ASCII lớn nhất của
chuỗi vừa nhập. Nếu không có chữ hoa nào được nhập thì hiển thị thông báo
‘Không có chữ hoa !’. Một ví dụ khi thực hiện chương trình:

120
GV. Vương Quốc Dũng
Bạn hãy nhập một dòng văn bản:
DONG CHAY THU CHƯƠNG TRÌNH
Chữ hoa có mã ASCII bé nhất là A, lớn nhất là U

Để giải quyết vấn đề này, chúng ta sẽ sử dụng phương pháp thiết kế chương
trình top_down (từ trên xuống) mà ta đã gặp trong lập trình ngôn ngữ bậc cao.
Trong phương pháp này, vấn đề nguyên thủy được chia thành một chuỗi các
vấn đề con mà việc thực hiện chúng đơn giản hơn nhiều so với các vấn đề ban
đầu. Mỗi vấn đề con lại có thể được chia nhỏ hơn nữa … Cứ tiếp tục như vậy
cho đến khi mỗi vấn đề con không thể chia nhỏ được nữa và có thể được mã
hóa trực tiếp. Việc sử dụng các chương trình con có thể phát triển phương pháp
này.
Sự phân chia đầu tiên:
1. Hiển thị thông báo ban đầu.
2. Nhập và xử lý dòng văn bản vừa nhập
3. Hiển thị kết quả
Vấn đề con thứ nhất: Hiển thị thông báo ban đầu
Vấn đề con này có thể mã hóa ngay lập tức
MOV AH, 9
LEA DX, message
INT 21h
Thông báo message có thể khai báo trong đoạn dữ liệu như sau:
Message DB ‘ Bạn hãy nhập một dòng văn bản’, 10, 13, ‘$’
Vấn đề con thứ hai: Nhập và xử lý dòng văn bản vừa nhập
Vấn đề nhỏ này thực hiện hầu hết các công việc chính của chương trình. Nó
thực hiện nhập từng ký tự của dòng văn bản từ bàn phím, trả về các chữ cái thỏa
mãn (nó cũng đưa ra thông báo nếu không có chữ hoa nào được nhập vào). Sau
đây là các bước thực hiện trong vẫn đề nhỏ này. Ta sử dụng 2 biến là ‘chữ hoa
đầu’ để lưu mã ASCII bé nhất và ‘chữ hoa cuối’ để lưu mã ASCII lớn nhất
Nhập 1 ký tự
WHILE (không phải ký tự xuống dòng) DO
IF (ký tự là chữ hoa) THEN
IF (ký tự < chữ hoa đầu) THEN
Chữ hoa đầu = ký tự
END_IF
IF (ký tự > chữ hoa cuối) THEN
Chữ hoa cuối = ký tự
END_IF
END_IF
Nhập 1 ký tự
END_WHILE
Dòng
IF (ký tự là chữ hoa) THEN

121
GV. Vương Quốc Dũng
Thực chất là : IF ((‘A’ <= ký tự) AND (ký tự <= ‘Z’) THEN
Vấn đề con thứ hai này có thể được mã hóa như sau:
MOV AH, 1
INT 21H ; Nhập vào một ký tự
WHILE_:
CMP AL, 13 ; So sánh ký tự nhập với ký tự xuống dòng
JE END_WHILE ; Nếu đúng, thoát ra
; IF (ký tự là chữ hoa)
CMP AL, ‘A’
JB END_IF
CMP AL, ‘Z’
JA END_IF
; THEN
; IF (ký tự < chữ hoa đầu)
CMP AL, FIRST ; So sánh ký tự nhập với chữ hoa đầu
JNB CHECK_LAST ; không thỏa mãn, kiểm tra tiếp
; THEN Chữ hoa đầu = ký tự
MOV FIRST, AL ; Đặt lại FIRST = ký tự
; END_IF
CHECK_LAST:
; IF (ký tự > chữ hoa cuối)
CMP AL, LAST
JNA END_IF
; THEN Chữ hoa cuối = ký tự
MOV LAST, AL
; END_IF
END_IF:
; Nhập một ký tự
INT 21h
JMP WHILE_ ; Quay về đầu vòng lặp WHILE
END_WHILE:
Các biến FIRST và LAST phải được khởi tạo trước khi vào vòng lặp, chúng có
thể được khai báo và khởi tạo trong đoạn dữ liệu như sau:
FIRST DB ‘[‘
LAST DB ‘@’
Các giá trị khởi tạo ‘[‘ và ‘@’được chọn vì ‘[‘ đứng sau ‘Z’ và ‘@’ đứng trước
‘A’. Như vậy khi chữ hoa được nhập đầu tiên thì sẽ làm thay đổi giá trị cả 2
biến.
Vấn đề con thứ ba: Hiển thị kết quả
IF (Không có chữ hoa) THEN
Hiển thị ‘Không có chữ hoa’
ELSE
Hiển thị chữ hoa đầu và chữ hoa cuối
END_IF

122
GV. Vương Quốc Dũng
Các thông báo được hiển thị có thể khai báo trong đoạn dữ liệu như sau:
TB1 DB ‘Khong co chu hoa !$’
TB2 DB ‘Chu hoa co ma ASCII nho nhat la ’
FIRST DB ‘[’
DB ‘Chu hoa co ma ASCII lon nhat la ‘
LAST DB ‘@’
Khi thông báo 2 được hiển thị bằng hàm 9, ngắt 21h của DOS, nội dung xâu sẽ
được hiển thị lần lượt từ ký tự đầu tiên cho đến khi gặp ký tự ‘$’, dấu hiệu kết
thúc xâu.
Ta nhận thấy nếu ở bước này, kiểm tra biến FIRST, nếu FIRST = ‘[‘ thì có
nghĩa là không có chữ hoa nào được nhập.
Vấn đề con thứ ba này có thể được mã hóa như sau:
MOV AH, 9 ; Hàm hiển thị chuỗi
; IF (Không có chữ hoa)
CMP FIRST, ‘[‘
JNE Kqua ; Không bằng, hiển thị TB2
; THEN Hiển thị thông báo TB1
LEA DX, TB1
JMP DISPLAY
; ELSE Hiển thị TB2
Kqua:
LEA DX, TB2
DISPLAY:
INT 21h ; Hiển thị thông báo.

Sau đây là chương trình đầy đủ:


TITLE Chu_hoa
.Model small
.Stack 100h
.Data
Message DB ‘ Bạn hãy nhập một dòng văn bản’, 10, 13, ‘$’
TB1 DB ‘Khong co chu hoa !$’
TB2 DB ‘Chu hoa co ma ASCII nho nhat la ’
FIRST DB ‘[’
DB ‘Chu hoa co ma ASCII lon nhat la ‘
LAST DB ‘@’
.Code
MAIN Proc
MOV AX, @data
MOV DS, AX
; Hien thi thong bao ban dau
MOV AH, 9
LEA DX, message
INT 21h

123
GV. Vương Quốc Dũng
; Nhap va xu ly dong van ban
MOV AH, 1
INT 21H ; Nhap vao mot ky tu
WHILE_:
CMP AL, 13 ; So sanh ky tu nhap voi ky tu xuong dong
JE END_WHILE ; Neu dung, thoat ra
; IF (ky tu la chu hoa)
CMP AL, ‘A’
JB END_IF
CMP AL, ‘Z’
JA END_IF
; THEN
; IF (ky tu < chu hoa đau)
CMP AL, FIRST ; So sanh ky tu nhap voi chu hoa dau
JNB CHECK_LAST ; không thoa man, kiem tra tiep
; THEN Chu hoa đau = ky tu
MOV FIRST, AL ; Đat lai FIRST = ky tu
; END_IF
CHECK_LAST:
; IF (ky tu > chu hoa cuoi)
CMP AL, LAST
JNA END_IF
; THEN Chu hoa cuoi = ky tu
MOV LAST, AL
; END_IF
END_IF:
; Nhap mot ky tu
INT 21h
JMP WHILE_ ; Quay ve dau vong lap WHILE
END_WHILE:
MOV AH, 9 ; Ham hien thi chuoi
; IF (Khong co chu hoa)
CMP FIRST, ‘[‘
JNE Kqua ; Khong bang, hien thi TB2
; THEN Hien thi thong bao TB1
LEA DX, TB1
JMP DISPLAY
; ELSE Hien thi TB2
Kqua:
LEA DX, TB2
DISPLAY:
INT 21h ; Hien thi thong bao.
; END_IF
; Tro ve DOS

124
GV. Vương Quốc Dũng
MOV AH, 4Ch
INT 21h
MAIN Endp
END MAIN

4.4. Kiểu cấu trúc và kiểu bản ghi

Assembler cho phép định nghĩa hai kiểu dữ liệu đặc biệt có cấu trúc gồm nhiều
thành phần: Kiểu cấu trúc (Structure) và kiểu bản ghi (Record).
4.4.1. Kiểu cấu trúc
Kiểu cấu trúc là kiểu dữ liệu được tạo từ nhiều thành phần nhỏ hơn. Kiểu cấu
trúc có thể dùng để định nghĩa biến có cấu trúc. Biến có cấu trúc được tạo bởi
các biến nhỏ hơn gọi là các trường (Field). Các trường có thể có các kích thước
khác nhau và có thể được truy xuất thông qua các tên biến trường.
1) Cách khai báo biến cấu trúc:
a) Cách mô tả kiểu dữ liệu cấu trúc:
Name STRUCT
field1
field2
.........
fieldn
Name ENDS
Trong đó:
Name: là tên của kiểu cấu trúc.
field1, field2, ....., fieldn: là các trường.
Ví dụ:
SinhVien Struct
ID dw ?
TenSv db 32 dup(‘’).
Diem db 10 dup (0)
SinhVien EndS
Trong ví dụ này: ta khai báo kiểu cấu trúc SinhVien, có các trường ID,
TenSV, Diem. Giả sử trường ID có địa chỉ offset là 0, thì các trường TenSV
có địa chỉ offset là 2, Diem có địa chỉ offset là 34.
Ta có thể khai báo các biến cấu trúc thông qua các kiểu cấu trúc đã được mô tả.
Ngoài ra còn có thể gán giá trị ban đầu cho các trường của biến cấu trúc.
b) Cách khai báo một biến kiểu cấu trúc:
[Tên_biến] Tên_kiểu_cấu_trúc <Các giá trị khởi tạo của các biến trường>
[Tên_biến] là tên biến kiểu cấu trúc.
Chúng ta không nhất thiết phải khởi tạo tất cả các biến trường cho biến cấu
trúc. Trường nào không khởi tạo thì Assembler sẽ tự động lấy giá trị định sẵn
đã được khởi tạo (mô tả) trong phần mô tả kiểu cấu trúc. Một biến trường có
thể không được khởi tạo trước (có giá trị NULL). Khi khởi tạo giá trị cho biến

125
GV. Vương Quốc Dũng
trường, các giá trị khởi tạo phải cùng kiểu với kiểu đã định nghĩa trong phần
mô tả kiểu cấu trúc. Giá trị khởi tạo của các biến trường cách nhau bởi dấu “,”.
Biến trường không khởi tạo giá trị thì không đưa giá trị vào xong vẫn phải xác
định chỗ của nó bằng dấu “,”.

Ví dụ:
Định nghĩa các biến cấu trúc S1, S2 có kiểu cấu trúc SinhVien đã mô tả ở
trên:
S1 SinhVien <> ; Các trường của S1 lấy giá trị định sẵn.
S2 SinhVien <35, “Nguyen Van An”>
; Khởi tạo 2 trường ID và TenSV, trường Diem lấy giá trị định sẵn.
Định nghĩa một mảng gồm 100 phần tử kiểu cấu trúc SinhVien với các giá trị
đã khởi tạo sẵn:
Sm SinhVien 100 Dup(<>)
Chú ý:
Trong khai báo kiểu cấu trúc, khi khai báo biến kiểu cấu trúc ta không thể
khởi tạo giá trị cho các trường kiểu mảng, xong vẫn có thể khởi tạo giá trị
cho trường kiểu chuỗi.
Ví dụ:
Stuff Struct
Bufer db 100 dup(?)
CRLF db 13, 10
Query db “FileName”
Mark db 20
Stuff EndS
Trong cấu trúc này, các trường Buffer và CRLF là các biến kiểu mảng, trong
các biến kiểu cấu trúc Stuff, ta không được khởi tạo lại giá trị cho chúng.
Các trường còn lại đều có thể khởi tạo lại được:
S1 stuff <,,”abcde”,10>
S2 stuff <,,”ABCDE”,>
- Các trường không khởi tạo được phải để trống hoàn toàn, kể cả dấu cách
trắng cũng không được đưa vào
- Khi khởi tạo giá trị với các trường kiểu chuỗi, giá trị khởi tạo không được
vượt quá chiều dài đã mô tả trong kiểu cấu trúc, nhưng có thể khởi tạo giá trị
chuỗi ngắn hơn.
2) Cách truy xuất biến cấu trúc:
Giống như các biến khác, biến cấu trúc có thể được truy xuất thông qua tên biến.
Các trường trong biến cấu trúc có thể truy xuất bằng toán tử dấu chấm ”.” và tên
của nó.
Cú pháp truy xuất: Tên_biến_cấu_trúc.Tên_trường
Ví dụ: Mô tả kiểu Date gồm 3 trường
Date Struct
Month db ?

126
GV. Vương Quốc Dũng
Day db ?
Nam dw ?
Date EndS
Ta khai báo các biến cấu trúc có kiểu Date
Yesterday Date <02, 04, 1988>
Today Date <02, 05, 1988>
Tomorrow Date <02, 06, 1988>
Ta truy xuất các trường một cách trực tiếp:
Mov al, Today.Day
Mov ah, Today.Month
Mov dx, Today.Year
Ta cũng có thể truy xuất các trường một cách gián tiếp:
Mov BX, offset Today
Mov al, [BX].Day
Mov ah, [BX].Month
Mov dx, [BX].Year

4.4.2. Kiểu bản ghi


Là một kiểu dữ liệu gồm một nhóm các bit gọi là các trường bit. Kiểu bản
ghi dùng để định nghĩa các biến bản ghi.
Biến bản ghi là một biến dạng byte hay dạng word gồm các trường bit, mỗi
trường có thể truy xuất thông qua tên của nó. Các trường bit trong một biến bản
ghi có thể có kích thước với số bit khác nhau.
1) Cách khai báo kiểu bản ghi:
Kiểu bản ghi được mô tả bằng chỉ dẫn RECORD, có cú pháp như sau:
Tên_kiểu_bản_ghi RECORD Field1 [, Field2,....]
Trong đó:
Field1, Field2,... là tên các trường bit của kiểu bản ghi, mô tả tên, kích
thước, giá trị khởi tạo của từng trường của kiểu bản ghi.
Cú pháp mô tả trường bit:
Tên_trường_bít: độ_rộng [= giá_trị]
Trong đó:
Độ_rộng: là số các bit của trường.
Giá_trị: là giá trị định sẵn của trường.
Nếu tổng số các bít của tất cả các trường lớn hơn 8 bit thì Assembler dùng 2
byte cho kiểu bản ghi này, ngược lại thì Assembler dùng 1 byte. Các bit
không dùng trong bản ghi được khởi tạo giá trị 0.
Trường cuối cùng là trường chứa bit thấp nhất (bit 0) của kiểu bản ghi,
trường bit đầu tiên là trường chứa bit cao nhất của kiểu bản ghi.
Ví dụ:
Mô tả bản ghi A gồm 4 trường bit như sau:
A RECORD R1: 3 = 0, R2: 1 = 1, R3: 1 = 0, R4: 3 = 3
Assembler sẽ dùng một byte có giá trị như sau:

127
GV. Vương Quốc Dũng

Tên trường R1 R2 R3 R4
Bit 7 6 5 4 3 2 1 0
Giá trị 0 0 0 1 0 0 1 1

Mô tả bản ghi B gồm 4 trường bit như sau:


B RECORD R1: 2 = 1, R2: 1 = 0, R3: 1 = 0, R4: 2 = 2
Assembler sẽ dùng một byte có giá trị như sau:
Tên trường R1 R2 R3 R4
Bit 7 6 5 4 3 2 1 0
Giá trị 0 0 0 1 0 0 1 0

Ta biết byte thuộc tính của một ký tự trên màn hình có dạng sau:

Bit 7 6 5 4 3 2 1 0
Blink Background Intense Foreground

Do đó, ta có thể mô tả một kiểu bản ghi Color để biểu diễn byte thuộc tính
này với giá trị chưa khởi tạo:
Color RECODR Blink: 1, Back: 3, Intense: 1, Fore: 3

Tên Blink Background Intense Foreground


trường
Bit 7 6 5 4 3 2 1 0
Giá trị - - - - - - - -

2) Khai báo biến bản ghi:

[Tên_biến] Tên_kiểu_bản_ghi <[giá trị field1][, giá trị field2]...>


Trong đó:
Tên_biến: là tên của biến bản ghi
<[giá trị field1][, giá trị field2]...>: Giá trị khởi tạo của các trường bit.
Ví dụ:
Định nghĩa các biến bản ghi C1, C2 có kiểu bản ghi Color đã mô tả ở trên:
C1 Color <1,0,1,4> ; Khởi tạo giá trị cho 4 trường bit.
C2 Color <> ; Các trường bit lấy giá trị định sẵn.
Định nghĩa một mảng gồm 16 biến bản ghi kiểu Color với các trị khởi tạo
sẵn:
ColorArray Color 16 dup(<>)

3) Cách truy xuất toán hạng bản ghi và biến bản ghi:
Một toán hạng bản ghi xác định một giá trị có kiểu bản ghi. Chúng ta cần phân
biệt toán hạng bản ghi với biến bản ghi, toán hạng bản ghi là hằng số, còn biến

128
GV. Vương Quốc Dũng
bản ghi là đối tượng mang giá trị lưu trong bộ nhớ. Biến bản ghi có thể được
truy xuất bằng tên biến.
Toán hạng bản ghi có thể dùng theo khuôn dạng sau:
Tên_kiểu_bản_ghi <[giá trị field1][, giá trị field2].... >
Trong đó:
<[giá trị field1][, giá trị field2]...>: Giá trị của các trường bit trong
toán hạng bản ghi, có thể là biểu thức hay tên biểu thị một hằng.
Ví dụ: Nạp toán hạng bản ghi có giá trị 32h vào thanh ghi AH
Mov ah, Color <0,3,0,2>
Nạp biến bản ghi có giá trị 32h vào thanh ghi AH
A1 Color <0,3,0,2>
Mov ah, A1
4.4.3. Các toán tử:

1) Toán tử MASK:
Khuôn dạng toán hạng:
MASK <[Tên_trường_bit]/[tên_kiểu_bản_ghi]>
Chức năng:
Toán tử này trả về một giá trị 8 bit hay 16 bit tùy thuộc vào kiểu bản ghi.
Các bít thuộc trường bít đã chỉ ra được nhận giá trị 1; các bit thuộc các
trường bit còn lại đều nhận giá trị 0.
Ví dụ:
Color RECORD Blink:1 = 0, Back:3 = 5, Intense:1 = 1, Fore:3 = 1
Cursor Color <>
Mov ah, Cursor ; ah = 01011001b
AND ah, MASK Cursor ; Toán hạng MASK Cursor = 11111111
; (Bật tất cả các bit của biến bản ghi Cursor)
; Lệnh này ⇔ And ah, 11111111b
; ah = 01011001b
OR ah, MASK Blink ; Toán hạng MASK Blink = 10000000
; (Bật các bit thuộc trường bit Blink)
; Lệnh này ⇔ OR ah, 10000000b
; ah = 11011001b
AND ah, Not MASK Back ; Toán hạng Not MASK Back = 01110000
; (Bật các bit thuộc trường bit Back)
; Lệnh này ⇔ AND ah, NOT 01110000b
; và ⇔ AND ah, 10001111b
; ah = 10001001b
XOR ah, MASK Intense ; Toán hạng MASK Intense = 00001000
; (Bật các bit thuộc trường bit Intense)
; Lệnh này ⇔ Xor ah, 00001000b
; ah = 10000001b

2) Toán tử WIDTH:

129
GV. Vương Quốc Dũng
Khuôn dạng toán hạng:
WIDTH <[Tên_trường_bit]/[tên_kiểu_bản_ghi]>
Chức năng:
Toán tử này trả về một giá trị bằng số các bit của một bản ghi hay một
trường bit tùy thuộc vào khai báo trong khuôn dạng toán hạng.
Ví dụ:
Color RECORD Blink:1, Back:3, Intense:1, Fore:3
Cursor Color <0,5,1,1>
WBlink EQU WIDTH Blink ; Hằng WBlink = 1
WBack EQU WIDTH Back ; Hằng WBack = 3
WIntense EQU WIDTH Intense ; Hằng WIntense = 1
WFore EQU WIDTH Fore ; Hằng WFore = 3
WColor EQU WIDTH Color ; Hằng WColor = 8

CÂU HỎI VÀ BÀI TẬP


1. Viết các lệnh hợp ngữ cho từng cấu trúc lựa chọn sau:
a. IF AX < 0 THEN
BX = -1
END_IF
b. IF AL < 0 THEN
AH = 0FFh
ELSE
AH = 0
END_IF
c. Giả thiết DL = mã ASCII của một ký tự:
IF ((DL >= ‘A’) AND (DL <= ‘Z’)) THEN
Hiển thị DL
END_IF
d. IF AX < BX THEN
IF BX < CX THEN
AX = 0
ELSE
BX = 0
END_IF
END_IF
e. IF ((AX < BX) OR (BX < CX)) THEN
DX = 0
ELSE
DX = 1
END_IF
f. IF AX < BX THEN
AX = 0
ELSE
IF (BX < CX) THEN

130
GV. Vương Quốc Dũng
BX = 0
ELSE
CX = 0
END_IF
END_IF
g. IF A2 = B2 Then
CF = 1
ELSE
CF = 0
2. Dùng cấu trúc CASE để mã hóa các công việc sau đây:
Nhập một ký tự từ bàn phím
Nếu là ‘A’, chuyển con trỏ về đầu dòng.
Nếu là ‘B’, chuyển con trỏ xuống dòng.
Nếu là ký tự khác, về DOS.
3. Viết các lệnh thực hiện các công việc sau:
a. AX = 1 + 4 + 7 + 10 + … + 148
b. BX = 100 + 95 + 90 + 85 + … + 5
4. Dùng lệnh LOOP thực hiện các công việc sau:
a. Tính tổng 50 phần tử đầu tiên của dãy số 1, 5, 9, 13,… đưa kết quả vào DX
b. Nhập một ký tự, hiển thị nó 80 lần ở dòng tiếp theo.
c. Nhập một mật khẩu 5 ký tự và viết đè lên nó bằng cách trở về đầu dòng
rồi hiển thị 5 chữ x. Không cần phải lưu giữ lại các ký tự này.
5. Thuật toán sau đây có thể sử dụng để chia 2 số dương bằng cách lặp lại phép trừ.
Khởi tạo thương số = 0
WHILE (số bị chia >= số chia) DO
Tăng thương số
Trừ bớt số chia đi một lượng là số chia
END_WHILE
Viết lệnh thực hiện chia AX cho BX rồi cất thương trong CX theo thuật toán
trên.
6. Thuật toán sau đây có thể sử dụng để nhân số dương M và N bằng cách lặp
lại phép cộng.
Khởi tạo tích số = 0
REPEAT
Cộng M vào tích.
Giảm N
UNTIL (N = 0)
Hãy viết lệnh thực hiện nhân AX với BX rồi cất tích trong CX theo thuật toán
trên. Có thể bỏ qua trường hợp tràn.
7. Ta có thể tạo lập một vòng lặp điều khiển bởi biến đếm mà nó còn tiếp tục thi
hành khi điều kiện được thoả mãn. Các lệnh
LOOPE Nhãn ; Lặp khi bằng
Và LOOPZ Nhãn ; Lặp khi bằng zero

131
GV. Vương Quốc Dũng
Sẽ làm giảm CX sau mỗi lần lặp. Sau đó nếu CX ≠ 0 và ZF = 1, điều khiển sẽ
chuyển đến lệnh sau nhãn đích. Nếu CX = 0 hoặc ZF = 0, lệnh ở sau vòng lặp
được thực hiện.
Tương tự như vậy các lệnh:
LOOPNE Nhãn ; Lặp khi không bằng
Và LOOPNZ Nhãn ; Lặp khi không bằng zero
Sẽ làm giảm CX sau mỗi lần lặp. Sau đó nếu CX ≠ 0 và ZF = 1, điều khiển sẽ
chuyển đến lệnh sau nhãn đích. Nếu CX = 0 hoặc ZF = 1, lệnh ở sau vòng lặp
được thực hiện.
a. Hãy sử dụng vòng lặp LOOPE viết các lệnh đọc các ký tự đến khi một
ký tự trắng được gõ vào hoặc đã nhập đủ 80 ký tự.
a. Hãy sử dụng vòng lặp LOOPNE viết các lệnh đọc các ký tự đến khi một
ký tự về đầu dòng được gõ vào hoặc đã nhập đủ 80 ký tự.
8. Dùng lệnh TEST để
a. Thiết lập cờ ZF nếu nội dung của AX = 0
b. Xoá cờ ZF nếu nội dung của AX là số lẻ.
c. Thiết lập SF nếu nội dung của nó chứa số âm
d. Thiết lập ZF nếu DX ≥ 0
c. Thiết lập PF nếu BL chứa một số chẵn các bit 1
9. Viết chương trình thông báo cho người sử dụng nhập vào một ký tự, in ra mã
ASCII của ký tự đó dưới dạng mã Hex ở dòng tiếp theo. Lặp lại cho đến khi
người sử dụng ấn Enter.
Ví dụ: Nhập vào một ký tự : Z
Mã ASCII của ký tự Z dưới dạng Hex là: 5A
Nhập vào một ký tự :
10. Viết chương trình thông báo cho người sử dụng nhập vào một số Hex nhỏ hơn
hay bằng 4 chữ số. Đưa ra số dưới dạng nhị phân ở dòng kế tiếp. Khi người sử
dụng đưa vào một ký tự không hợp lệ, thông báo để họ vào lại. Chương trình
chỉ nhận các chữ in hoa.
Ví dụ: Nhập vào một số Hex (0..FFFF): 1a
Chữ số Hex không hợp lệ, hãy vào lại: 1ABC
Giá trị số vừa nhập ở dạng nhi phân: 0001101010111100
Chú ý: Chương trình có thể bỏ qua các ký tự sau 4 ký tự đầu tiên.
11. Viết chương trình thông báo cho người sử dụng nhập vào một số nhị phân có
số chữ số ≤ 16. Đưa ra số dưới dạng Hex ở dòng kế tiếp. Khi người sử dụng
đưa vào một ký tự không hợp lệ, thông báo để họ vào lại. Kết thúc nhập khi ấn
enter
Ví dụ: Nhập vào một số nhị phân (nhiều nhất 16 chữ số): 111000A
Chữ số Hex không hợp lệ, hãy vào lại: 11100001
Giá trị số vừa nhập ở dạng Hex: E1
Chú ý: Chương trình có thể bỏ qua các ký tự sau 16 ký tự đầu tiên.
12. Viết chương trình thông báo cho người sử dụng nhập vào 2 số nhị phân, mỗi số
có 8 chữ số. Đưa ra số dưới dạng nhị phân ở dòng kế tiếp tổng của chúng. Mỗi

132
GV. Vương Quốc Dũng
khi người sử dụng đưa vào một ký tự không hợp lệ, sẽ có thông báo để họ vào
lại. Mỗi số được nhận khi người sử dụng ấn enter.
Ví dụ: Nhập vào một số nhị phân 8 chữ số thứ nhất: 101100001
Nhập vào một số nhị phân 8 chữ số thứ hai: 010010001
Tổng 2 số vừa nhập ở dạng nhị phân: 111110010
13. Viết chương trình thông báo cho người sử dụng nhập vào 2 số Hex không dấu
trong khoảng từ 0..FFFF. Đưa ra số dưới dạng Hex ở dòng kế tiếp tổng của
chúng. Mỗi khi người sử dụng đưa vào một ký tự không hợp lệ, sẽ có thông báo
để họ vào lại. Mỗi số được nhận khi người sử dụng ấn enter. Chương trình phải
có khả năng kiểm soát hiện tượng tràn không dấu.
Ví dụ: Nhập vào số Hex thứ nhất (0..FFFF): 21AB
Nhập vào số Hex thứ hai (0…FFFF): FE03
Tổng 2 số vừa nhập ở dạng Hex: 11FAE
13. Viết chương trình thông báo cho người sử dụng nhập vào một chuỗi các chữ số
thập phân, kết thúc khi ấn enter. Đưa ra màn hình ở dòng kế tiếp tổng của
chúng ở dạng Hex. Mỗi khi người sử dụng đưa vào một ký tự không hợp lệ, sẽ
có thông báo để họ vào lại.
Ví dụ: Nhập vào chuỗi các chữ số thập phân: 1299843
Tổng các chữ số vừa nhập ở dạng Hex: 0024
14. Viết chương trình con xác định loại máy tính đang sử dụng là loại máy gì. Biết
rằng tại địa chỉ logic F000:FFFE, nếu byte này có giá trị:
- F8: Máy PS/2 model 70 and 80
- FA: Máy PS/2 model
- FB: Máy PC/XT (later model), 101 keyboard.
- FC: Máy PC/AT
- FD: Máy PCJR
- FE: Máy PC/XT (Early model)
- FF: Máy PC classic
15. Viết chương trình nhập từ bàn phím một số chỉ thứ tự từ 1 đến 7 và in ra màn
hình tên ngày bằng chữ tương ứng (Monday, Tuesday, Wednesday, Thurday,
Friday, Saturday, Sunday – trong đó, 1 ứng với Sunday).
16. Viết chương trình nhập từ bàn phím một số chỉ thứ tự từ 1 đến 12 và in ra màn
hình tên tháng bằng chữ tương ứng.
17. Viết chương trình trò chơi bao, búa, kéo như sau:
Người thứ nhất nhập một trong các ký tự (O, K, B)
Người thứ hai nhập một trong các ký tự (O, K, B)
Với: O – Bao
K – Kéo
B – Búa
Biết rằng: O thắng B
B thắng K
K thắng O
18. Cho 2 số D1 và D2 không dấu, dạng double Word. Viết chương trình so sánh 2
số này và đưa kết quả so sánh ra màn hình.

133
GV. Vương Quốc Dũng
19. Viết chương trình nhập hai chuỗi ký tự từ bàn phím có chiều dài tối đa 80 ký
tự. So sánh 2 chuỗi này và in kết quả ra màn hình.
20. Viết chương trình hợp ngữ nhập từ bàn phím địa chỉ segment và offset của một
vùng nhớ bất kỳ. Hiển thị nội dung 256 byte vùng nhớ bắt đầu từ địa chỉ trên,
sau đó xuất ra màn hình theo dạng:
SEGMENT:OFFSET = XXXX:YYYY (ví dụ = 00000000:00)
00000000:00 33 C0 8E D0 BC 00 7C -8B F4 50 07 50 1F FB FC .3.....|..P.P..
00000010:BF 00 06 B9 00 01 F2 A5 -EA 1D 06 00 00 BE BE 07 ................
00000020:B3 04 80 3C 80 74 0E 80 -3C 00 75 1C 83 C6 10 FE ...<.t..<.u.....
00000030:CB 75 EF CD 18 8B 14 8B -4C 02 8B EE 83 C6 10 FE .u......L.......
00000040:CB 74 1A 80 3C 00 74 F4 -BE 8B 06 AC 3C 00 74 0B .t..<.t.....<.t.
00000050:56 BB 07 00 B4 0E CD 10 -5E EB F0 EB FE BF 05 00 V.......^.......
00000060:BB 00 7C B8 01 02 57 CD -13 5F 73 0C 33 C0 CD 13 ..|...W.._s.3...
00000070:4F 75 ED BE A3 06 EB D3 -BE C2 06 BF FE 7D 81 3D Ou...........}.=
00000080:55 AA 75 C7 8B F5 EA 00 -7C 00 00 49 6E 76 61 6C U.u.....|..Inval
00000090:69 64 20 70 61 72 74 69 -74 69 6F 6E 20 74 61 62 id partition tab
000000A0:6C 65 00 45 72 72 6F 72 -20 6C 6F 61 64 69 6E 67 le.Error loading
000000B0:20 6F 70 65 72 61 74 69 -6E 67 20 73 79 73 74 65 operating syste
000000C0:6D 00 4D 69 73 73 69 6E -67 20 6F 70 65 72 61 74 m.Missing operat
000000D0:69 6E 67 20 73 79 73 74 -65 6D 00 00 80 45 14 15 ing system...E..
000000E0:00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 ................
000000F0:00 00 00 00 00 00 00 00 -00 00 00 00 00 00 00 00 ................

Ch¬ng 5. CHƯƠNG TRÌNH CON


5.1. Chương trình con và khai báo
Một chương trình con gồm một tập các lệnh mà có thể gọi thực hiện nhiều lần
tại nhiều vị trí khác nhau trong chương trình chính. Chương trình con cho phép
người lập trình xây dựng chương trình một cách có cấu trúc, đặc biệt đối với
những chương trình lớn. Chương trình con có thể nhận các tham số (thường các
tham số là các thanh ghi) xử lý và xuất kết quả nếu cần. Chương trình con có
thể được gọi tại vị trí bất kỳ trong thân chương trình chính. Trong thân chương
trình con có thể có lời gọi các chương trình con khác và có thể gọi chính nó
(gọi là đệ qui).
Có 2 dạng chương trình con là chương trình con khai báo dạng NEAR và
chương trình FAR. Giống như chương trình chính, 1 chương trình con cũng
được định nghĩa dưới dạng 1 thủ tục nhờ các lệnh giả PROC, LABEL & ENDP
theo các mẫu sau:
Cách 1:
Tên_CTc PROC [NEAR | FAR]
; các lệnh của thân CTc
RET [constant]
Tên_CTC Endp

134
GV. Vương Quốc Dũng
Cách 2:
Nhãn:
; các lệnh của thân CTc
RETN [constant]
Nhãn Endp
Cách 3:
Nhãn LABEL FAR
; các lệnh của thân CTc
RETF [constant]
Nhãn Endp
chú ý trong mẫu chương trình nói trên, ngoài các lệnh giả có tính nghi thức bắt
buộc. Ta cần chú ý đến sự bố trí của lệnh gọi chương trình con (CTc) -(CALL)
trong chương trình chính (CTC) & lệnh RET, RETN, RETF (trở về CTC) trong
CTc.
5.1.1. Dạng NEAR
Sử dụng lệnh RET hoặc RETN ở cuối chương trình con dạng NEAR, chương
trình con dạng NEAR là chương trình con có khối mã lệnh của thân chương
trình nằm trong cùng đoạn mã lệnh với thân chương trình chính. Do vậy lệnh
RET hay RETN có chức năng lấy giá trị một từ nhớ (word) tại đỉnh hiện thời
của ngăn xếp đưa vào thanh ghi con trỏ lệnh IP, tức là nó có tác dụng kết thúc
chương trình con dạng NEAR và chuyển điều khiển về chương trình chính (lấy
địa chỉ offset của lệnh tiếp theo sẽ thực hiện mà đã được cất trước khi thực hiện
chương trình con). 2 Lệnh này cũng có thể thay lệnh nhảy JMP để nhảy đến
một địa chỉ được chỉ ra, ví dụ:
MOV AX, offset Begin
PUSH AX
RET
5.1.2. Dạng FAR
Sử dụng lệnh RET hoặc RETF ở cuối chương trình con dạng FAR chương trình
con dạng FAR là chương trình con có khối mã lệnh của thân chương trình
không nằm trong cùng đoạn mã lệnh với thân chương trình chính. Do vậy lệnh
RET hay RETF có chức năng lấy giá trị 2 từ nhớ (word) tại đỉnh hiện thời của
ngăn xếp đưa vào thanh ghi con trỏ lệnh IP và thanh ghi đoạn mã lệnh CS, tức
là nó có tác dụng kết thúc chương trình con dạng FAR và chuyển điều khiển về
chương trình chính (lấy địa chỉ offset và địa chỉ ssegment của lệnh tiếp theo sẽ
thực hiện mà đã được cất trước khi thực hiện chương trình con). 2 Lệnh này
cũng có thể thay lệnh nhảy JMP để nhảy đến một địa chỉ được chỉ ra, ví dụ:
MOV AX, 0F000h
PUSH AX
MOV AX, 0

135
GV. Vương Quốc Dũng
PUSH AX
RETF
5.1.3. Dạng RET (hoặc RETN | RET) constant
Trường hợp sau lệnh trở về có hằng số constant theo sau thì lệnh này cộng
thêm một giá trị là constant vào thanh ghi SP sau khi đã thực hiện xong lệnh
RET, RETN hoặc RETF. Với constant phải luôn là số chẵn vì lệnh PUSH, POP
chỉ làm việc với toán hạng 2 bytes.
Dạng này thường dùng để loại bỏ một số tham số của chương trình con ra khỏi
ngăn xếp (Stack) Đối với các ngôn ngữ cấp cao như C, PASCAL… thường các
tham số trong các chương trình con hay các hàm được được cất (PUSH) vào
ngăn xếp trước khi chương trình con được gọi. Do đó khi kết thúc chương trình
con ta phải loại bỏ các tham số hay các hàm đó ra khỏi ngăn xếp.
5.2. Cơ chế thực hiện
Với ngôn ngữ lập trình Assembly, để gọi một chương trình con, trong thân
chương trình chính sử dụng lệnh CALL với cú pháp như sau:
CALL LABEL
CALL Target (Register hay memory)
Lệnh CALL để gọi và thực hiện chương trình con, sau khi thực hiện xong,
quyền điều khiển trả về vị trí sau lệnh CALL vừa gọi.
Lệnh CALL có thể là NEAR hay FAR và ta có thể chia làm 2 dạng sau:
5.2.1. Dạng 1:
CALL LABEL
Chức năng: Gọi chương trình con tại vị trí được xác định bởi nhãn Label. Nhãn
Label có thể là NEAR hay FAR.
1) Dạng NEAR:
- Lệnh CALL chiếm 3 bytes trong bộ nhớ, nhãn LABEL phải nằm cùng đoạn
mã lệnh chứa lệnh CALL.
- Hoạt động của lệnh CALL NEAR:
+ Trước tiên lệnh CALL cất (Push) địa chỉ offset của lệnh kế tiếp sau lệnh
CALL vào ngăn xếp (Stack).
+ Sau đó nạp địa chỉ offset của nhãn LABEL vào thanh ghi con trỏ lệnh IP
và chuyển điều khiển đến chương trình con (lúc này địa chỉ của nó được xác
định bởi cặp thanh ghi CS:IP).
Ví dụ:
1100:03E0 MOV DX, offset Str
1100:03E3 CALL PrintStr
1100:03E6 Next: int 20h
1100:03E8 Str DB ‘Khoa cong nghe thong tin’

136
GV. Vương Quốc Dũng
………..
1100:0600 PrintStr Proc Near
MOV AH,9
INT 21h
RET
PrintStr ENDP
+ Giải thích: Khi CPU thực hiện lệnh CALL PrintStr , nó cất địa chỉ offset
của nhãn NEXT (03E6h) vào Stack và nạp địa chỉ offset của nhãn PrintStr
(0600h) vào thanh ghi IP. Cuối cùng chuyển điều khiển đến địa chỉ mới là
0600h để thực hiện chương trình con PrintStr.
Sau khi thực hiện xong chương trình con PrintStr , CPU sẽ lấy lại (POP) địa
chỉ offset của nhãn NEXT (03E6h) ra khỏi Stack đưa vào thanh ghi IP và trả
quyền điều khiển về chương trình chính tại địa chỉ offset 03E6h
2) Dạng FAR:
- Lệnh CALL chiếm 5 bytes trong bộ nhớ, nhãn LABEL có thể nằm trong
đoạn mã lệnh khác với đoạn mã lệnh chứa lệnh CALL.
- Hoạt động của lệnh CALL FAR:
+ Trước tiên lệnh CALL cất (Push) địa chỉ segment (trong thanh ghi CS) của
lệnh kế tiếp sau lệnh CALL vào ngăn xếp (Stack).
+ Sau đó cất địa chỉ offset của lệnh kế tiếp sau lệnh CALL vào ngăn xếp.
+ Sau đó nạp địa chỉ segment và offset của nhãn LABEL vào thanh ghi CS
và thanh ghi IP (cặp địa chỉ đầy đủ của lệnh là CS:IP) rồi chuyển điều khiển
đến chương trình con (lúc này địa chỉ của nó được xác định bởi cặp thanh
ghi CS:IP).
Ví dụ:
1100:03E0 MOV DX, offset Str
1100:03E3 CALL FAR PTR PrintStr
1100:03E6 Next: int 20h
1100:03E8 Str DB ‘Khoa cong nghe thong tin’
………..
4100:0600 PrintStr Proc FAR
MOV AH,9
INT 21h
RET
PrintStr ENDP

137
GV. Vương Quốc Dũng
+ Giải thích: Khi CPU thực hiện lệnh CALL FAR PTR PrintStr, nó cất địa
chỉ đoạn (1100h) và địa chỉ offset (03E6h) của nhãn NEXT vào Stack rồi
nạp địa chỉ đoạn (4100h) và địa chỉ offset(0600h) của nhãn PrintStr vào cặp
thanh ghi CS:IP. Cuối cùng chuyển điều khiển đến địa chỉ mới là
4100:0600h để thực hiện chương trình con PrintStr.
Sau khi thực hiện xong chương trình con PrintStr (khi CPU thực hiện lệnh
RET), CPU sẽ lấy lại (POP) địa chỉ offset của nhãn NEXT (03E6h) ra khỏi
Stack đưa vào thanh ghi IP và địa chỉ đoạn của nhãn NEXT (1100h) ra khỏi
Stack đưa vào thanh ghi CS, rồi trả quyền điều khiển về chương trình chính
tại địa chỉ 1100:03E6
5.2.2. Dạng 2:
CALL Target
Chức năng: Gọi chương trình con tại vị trí được xác định bởi nhãn Target.
Target có thể là toán hạng thanh ghi hay bộ nhớ.
1) Target là toán hạng thanh ghi:
Ví dụ:
CALL BX
+ Giải thích: Lệnh này cất địa chỉ offset của lệnh kế tiếp sau lệnh CALL vào
Stack, rồi chuyển nội dung thanh ghi BX cho thanh ghi IP, và chương trình
con có địa chỉ xác định bởi cặp thanh ghi CS:IP.
2) Target là toán hạng bộ nhớ:
- Target có thể là dạng Word (ứng với dạng NEAR) hoặc doubleword (ứng
với dạng FAR).
Ví dụ:
CALL WORD PTR [BX]
+ Giải thích: Lệnh này cất giá trị các thanh ghi IP vào Stack rồi chuyển nội
dung toán hạng bộ nhớ [BX] cho IP (IP chứa nội dung của từ nhớ có địa chỉ
offset = nội dung BX) và chương trình con có địa chỉ xác định bởi CS:IP
CALL DWORD PTR [BX]
+ Giải thích: Lệnh này lần lượt cất giá trị các thanh ghi CS, IP vào Stack rồi
chuyển nội dung toán hạng bộ nhớ [BX] cho IP (IP chứa nội dung của từ
nhớ có địa chỉ offset = nội dung BX) và nội dung của toán hạng bộ nhớ [BX
+ 2] cho CS (CS chứa nội dung của từ nhớ có địa chỉ offset = BX+2). Và
chương trình con có địa chỉ xác định bởi cặp thanh ghi CS:IP.
Tóm lại: Lệnh CALL NEAR chỉ thay đổi nội dung thanh ghi con trỏ lệnh IP, còn
lệnh CALL FAR thay đổi nội dung của cả thanh ghi đoạn CS lẫn thanh ghi con trỏ
lệnh IP.

138
GV. Vương Quốc Dũng
1100:03E6 Next: int 20h
1100:03E8 Str DB ‘Khoa cong nghe thong tin’
………..
4100:0600 PrintStr Proc FAR
MOV AH,9
INT 21h
RET
PrintStr ENDP
+ Giải thích: Khi CPU thực hiện lệnh CALL FAR PTR PrintStr, nó cất địa
chỉ đoạn (1100h) và địa chỉ offset (03E6h) của nhãn NEXT vào Stack rồi
nạp địa chỉ đoạn (4100h) và địa chỉ offset(0600h) của nhãn PrintStr vào cặp
thanh ghi CS:IP. Cuối cùng chuyển điều khiển đến địa chỉ mới là
4100:0600h để thực hiện chương trình con PrintStr.
Sau khi thực hiện xong chương trình con PrintStr (khi CPU thực hiện lệnh
RET), CPU sẽ lấy lại (POP) địa chỉ offset của nhãn NEXT (03E6h) ra khỏi
Stack đưa vào thanh ghi IP và địa chỉ đoạn của nhãn NEXT (1100h) ra khỏi
Stack đưa vào thanh ghi CS, rồi trả quyền điều khiển về chương trình chính
tại địa chỉ 1100:03E6
5.3. Lập trình đa đơn thể
5.3.1. Chương trình đa đơn thể
Ở các phần trước, chúng ta chỉ nói về việc viết chương trình với các lệnh nằm
cùng trong một tập tin nguồn. Nhưng cách viết như vậy chỉ tiện lợi cho chương
trình nhỏ. Với các chương trình lớn, người ta thường viết riêng thành nhiều tập
tin, mỗi tập tin đó gọi là một đơn thể (module), sau đó liên kết chúng lại với
nhau tạo thành một chương trình lớn gọi là chương trình đa đơn thể. Thuận lợi
cơ bản của các chương trình đa đơn thể là ta chỉ cần hiệu chỉnh các đơn thể thay
vì phải hiệu chỉnh toàn bộ chương trình. Với 3 chỉ dẫn PUBLIC, EXTRN,
GLOBAL là ta có thể tạo ra một chương trình đa đơn thể, ví dụ sau đây là một
chương trình gồm 2 đơn thể.
Trước hết là chương trình chính, đơn thể MAIN.ASM
; Main.ASM
DOSSEG
•MODEL SMALL
•STACK 200h
•DATA
Str1 DB ‘Hello$’
Str2 DB ‘ World ’,10, 13,’$’
GLOBAL str3:Byte:50
Str3 DB 50 DUP(?)
•CODE

139
GV. Vương Quốc Dũng
EXTRN noi_xau:PROC
Begin:
MOV AX, @data
MOV DS, AX
MOV AX, offset Str1
MOV BX, offset Str2
Call noi_xau ; gọi chương trình con ghép 2 xâu
MOV AH, 9
MOV DX, offset Str3
INT 21h
MOV AH, 4Ch
INT 21h
END Begin
Tiếp theo là chương trình con độc lập, đơn thể thứ hai SUB.ASM
; Sub.ASM
DOSSEG
•MODEL SMALL
•DATA
GLOBAL str3:Byte
•CODE
PUBLIC noi_xau
noi_xau PROC
CLD
MOV DI, seg Str3
MOV ES, DI
MOV DI, offset Str3 ; ES:DI = địa chỉ str3, str3 là xâu đích
MOV SI, AX ; DS:AX = địa chỉ xâu Str1  Str1 xâu nguồn
Copy_str1:
LODSB
CMP AL, ‘$’
JE Cbi_Copy_str2 ; Là ký tự kết thúc xâu, thôi không copy
STOSB
JMP Copy_str1
Cbi_Copy_str2:
MOV SI, BX ; DS:BX = địa chỉ xâu Str2  Str2 xâu nguồn
Copy_str2:
LODSB
STOSB
CMP AL, ‘$’
JNE Copy_str2 ; Chưa phải là ký tự kết thúc xâu, thực hiện
; copy tiếp
RET
Noi_xau ENDP
END

140
GV. Vương Quốc Dũng
Sau khi có 2 đơn thể trên ta dịch từng đơn thể một:
C:\>\TASM\TASM Main ↵
C:\>\TASM\TASM Sub ↵
Cuối cùng liên kết để tạo ra file Main.EXE như sau:
C:\>\TASM\Tlink /x Main+Sub ↵
Bây giờ chúng ta hãy tìm hiểu các chỉ dẫn tạo ra các chương trình đa đơn thể.
5.3.2. Các chỉ dẫn cho chương trình đa đơn thể
1) Chỉ dẫn PUBLIC
Cú pháp:
PUBLIC name1[,name2…]
Chức năng: Cho phép các name được dùng ở các đơn thể khác, các name có
thể là tên các thủ tục, các biến, các tên gán trị (hằng có tên).
Ví dụ:
•DATA
PUBLIC MemVar, Array1, Const1
MemVar DW 10
Const1 EQU 100
Array1 DB Const1 dup(0)
•CODE
PUBLIC NearProc, FarProc
NearProc Proc Near
……
NearProc Endp
FarProc Proc Far
……
FarProc Endp
……
END
Chú ý: name dùng với PUBLIC phải là hằng giá trị 1 hoặc 2 bytes. Ví dụ các
hằng sau không được dùng với PUBLIC:
Long_value EQU 10001h
Text_Symbol EQU ‘hello’
2) Chỉ dẫn EXTRN
Cú pháp:
EXTRN definition1[, definition2…]
Chức năng: Khai báo các tên (nhãn) được định nghĩa ở các đơn thể khác.
Mỗi definition có dạng sau:
Name:Type[:count]
Với: name là tên đã được định nghĩa ở các đơn thể khác
Type phải theo đúng kiểu dữ liệu của name đã khai báo ở đơn thể
khác. Có thể là:
NEAR, FAR hay PROC. PROC là NEAR hay FAR tuỳ theo chỉ dẫn MODEL
Byte là biến kiểu Byte
Word là biến kiểu Word

141
GV. Vương Quốc Dũng
Dword là biễn kiểu DoubleWord
Fword là biến kiểu 6 bytes
Qword là biến kiểu 8 bytes
Tbyte là biến kiểu 10 byté hay tên cấu trúc.
ABS là trị tuyệt đối được khai báo bằng EQU hoặc = trong đơn thể gốc.
Count là số phần tử được định nghĩa. Nếu không ghi thì ngầm định
là 1.
Ví dụ:
•DATA
EXTRN MemVar:Word, Array1:Byte, Const1:ABS
•CODE
EXTRN NearProc:Near, FarProc:Far
……
MOV AX, MemVar
MOV BX, offset Array1
MOV CX, Const1
…….
CALL NearProc
……
CALL FarProc
……

3) Chỉ dẫn GLOBAL


Cú pháp:
GLOBAL name1:type[, name2:type]
Chức năng: Bao gồm cả 2 chức năng của PUBLIC và EXTRN. Đây là chỉ
dẫn của riêng Turbo Assembler (TASM). Với chỉ dẫn này, ta không cần
dùng PUBLIC hay EXTRN. Việc tồn tại của PUBLIC và EXTRN trong
TASM chỉ là để tương thích với các trình hợp dịch khác. Khi khai báo tên
(nhãn) với GLOBAL và định nghĩa nó thì tương đương với PUBLIC, còn
khai báo mà không định nghĩa thì có nghĩa là nó được dùng với EXTRN.
Ví dụ:
•DATA
GLOBAL FinalCount:Word, PromptString:byte
…….
FinalCount DW ?
…….
•CODE
GLOBAL DoReport:Near, TallyUp:Far
TallyUp Proc Far
…….
CALL DoReport
…….

142
GV. Vương Quốc Dũng
Ta thấy ở ví dụ trên biến FinalCount kiểu Word và thủ tục TallyUp được khai
báo là PUBLIC, trong khi đó biến PromptString kiểu byte và thủ tục DoReport
được khai báo là EXTRN.

Sau đây là một vídụ với chương trình có sử dụng chỉ dẫn PUBLIC
; Hiển thị số Hex trong DL
; WriteHex.ASM
•Model Tiny
•Code
ORG 100h
Begin:
MOV DL,3Fh
CALL Write_Hex
INT 20h
; Chương trình con đổi giá trị trong thanh ghi DL ra số Hex và đưa giá trị ra màn hình
; Input: giá trị trong DL
PUBLIC Write_Hex
Write_Hex Proc
PUSH CX
PUSH DX
MOV DH, DL ; lưu lại DL
; Lấy nible cao trong DL (4 bits cao)
MOV CX, 4
SHR DL, CL
CALL Write_Hex_Digit ; Hiển thị số Hex thứ nhất
MOV DL, DH ; Lấy lại giá trị cũ của DL
AND DL, 15 ; Xóa 4 bits cao trong DL
CALL Write_Hex_Digit ; Hiển thị số Hex thứ hai
POP DX
POP CX
RET
Write_Hex Endp
; Chương trình con đổi 4 bits thấp trong DL thành số hệ16 và hiển thị ra màn hình
; Input: DL = 4 bits thấp chứa số cần hiển thị theo dạng Hex
PUBLIC Write_Hex
Write_Hex_Digit Proc
PUSH DX
CMP DL, 10
JAE Hex_Letter
ADD DL, 48
JMP Write_Digit
Hex_Letter:
ADD DL, 55
Write_Digit:

143
GV. Vương Quốc Dũng
CALL Write_Char
POP DX
RET
Write_Hex_Digit Endp
; Chương trình hiển thị một ký tự ra màn hình qua hàm 2 ngắt 21h của DOS
; Input: DL = mã ASCII của ký tự hiển thị, AH = 2
PUBLIC Write_Char
Write_Char Proc
PUSH AX
MOV AH, 2
INT 21h
POP AX
RET
Write_Char Endp
END Begin
Ví dụ sau đây cho thấy cách dùng EXTRN.
Từ chương trình WriteHex.ASM, ta có thể xây dựng chương trình hiển thị 16
byte trong vùng nhớ (Buffer) bằng 2 đơn thể: đơn thể thứ nhất gồm các chương
trình con Write_Hex, Write_Hex_Digit, Write_Char với tên là PROCS.ASM,
đơn thể thứ 2 là DISPMEM.ASM có sử dụng các chương trình con trong
PROCS.ASM. Để có được chương trình khả chạy DISPMEM.COM, ta dùng
TASM để dịch DISPMEM và PROCS sau đó liên kết chúng lại bằng lệnh
Tlink /t.
Đơn thể thứ nhất, chương trình PROCS.ASM
; Procs.ASM
•Model Tiny
•Code

; Chương trình con đổi giá trị trong thanh ghi DL ra số Hex và đưa giá trị ra màn hình
; Input: giá trị trong DL
PUBLIC Write_Hex
Write_Hex Proc
PUSH CX
PUSH DX
MOV DH, DL ; lưu lại DL
; Lấy nible cao trong DL (4 bits cao)
MOV CX, 4
SHR DL, CL
CALL Write_Hex_Digit ; Hiển thị số Hex thứ nhất
MOV DL, DH ; Lấy lại giá trị cũ của DL
AND DL, 15 ; Xóa 4 bits cao trong DL
CALL Write_Hex_Digit ; Hiển thị số Hex thứ hai

144
GV. Vương Quốc Dũng
POP DX
POP CX
RET
Write_Hex Endp
; Chương trình con đổi 4 bits thấp trong DL thành số hệ16 và hiển thị ra màn hình
; Input: DL = 4 bits thấp chứa số cần hiển thị theo dạng Hex
PUBLIC Write_Hex
Write_Hex_Digit Proc
PUSH DX
CMP DL, 10
JAE Hex_Letter
ADD DL, 48
JMP Write_Digit
Hex_Letter:
ADD DL, 55
Write_Digit:
CALL Write_Char
POP DX
RET
Write_Hex_Digit Endp
; Chương trình hiển thị một ký tự ra màn hình qua hàm 2 ngắt 21h của DOS
; Input: DL = mã ASCII của ký tự hiển thị, AH = 2
PUBLIC Write_Char
Write_Char Proc
PUSH AX
MOV AH, 2
INT 21h
POP AX
RET
Write_Char Endp
END
Đơn thể thứ hai, chương trình DISPMEM.ASM
; DISPMEM.ASM
; Hiển thị 16 bytes trong vùng nhớ ở dạng Hex trên cùng một dòng, giá trị ;
mỗi bytes cách nhau một dấu cách trắng
; DISPMEM.ASM
•Model Tiny
•Code

ORG 100h
EXTRN Write_Hex:Near

145
GV. Vương Quốc Dũng
EXTRN Write_Char:Near
Begin:
XOR BX, BX
MOV CX, 16 ; Lặp 16 lần cho hiển thị giá trị của 16 bytes
Hex_Loop:
MOV DL, Buffer[BX] ; Lấy 1 byte trong Buffer đưa vào DL
CALL Write_Hex ; Hiển thị giá trị trong DL ở hệ Hex
MOV DL, 32
CALL Write_Char ; Hiện ký tự trắng trong DL
INC BX ; Chuẩn bị lấy byte tiếp theo trong Buffer
LOOP Hex_Loop
INT 20h
; Dữ liệu thử (Khai báo dữ liệu trong Buffer)
Buffer DB 10h, 11h, 12h, 13h, 14h, 15h, 16h, 17h
DB 18h, 19h, 1Ah, 1Bh, 1Ch, 1Dh, 1Eh, 1Fh
END Begin
5.3.3. Gộp tập tin

Trong thực tế, có một hoặc nhiều đoạn chương trình có thể dùng chung cho
nhiều đơn thể khác nhau, thay vì đưa ngay vào các đơn thể đó, ta có thể để
riêng, khi cần thì gộp vào. Như vậy ta tiết kiệm được nhiều về bộ nhớ lưu trữ,
và công sức vì không phải lặp lại cùng một đoạn chương trình ở nhiều nơi. Chỉ
dẫn INCLUDE sẽ giúp ta làm việc này. Khi gặp chỉ dẫn INCLUDE, Assembler
sẽ tìm tập tin tương ứng trên đĩa và dịch cho hết tập tin này, rồi quay trở lại
chương trình có chỉ dẫn INCLUDE dịch tiếp các lệnh tiếp theo sau chỉ dẫn
INCLUDE. Chỉ dẫn INCLUDE có cú pháp khai báo sau:
INCLUDE filename
Filename là tên tập tin trên đĩa, có thể có thêm cả đường dẫn. Nếu không
ghi phần mở rộng của tên tập tin thì phần mở rộng ngầm định là •ASM, nếu
không chỉ ra đường dẫn thì Assembler sẽ tìm tập tin trong thư mục hiện thời,
nếu không có trong thư mục hiện thời Assembler tiếp tục tìm trong thư mục
theo đường dẫn ghi kèm trong lệnh hợp dịch. Ví dụ sau cho đường dẫn là
C:\INCLUDE.
C:\>TASM\TASM -iC:\INCLUDE Hello ↵
Ví dụ về gộp tập tin:
; Main.ASM
…….
•Code

146
GV. Vương Quốc Dũng
……
MOV AX, 1
INCLUDE SUB.ASM
PUSH AX
……

; SUB.ASM
MOV BX, 5
ADD AX, BX
……

Khi hợp dịch sẽ được chương trình tương đương:


; Main.ASM
…….
•Code
……
MOV AX, 1
MOV BX, 5
ADD AX, BX
PUSH AX
……
CÂU HỎI VÀ BÀI TẬP:
1. Giả sử đoạn ngăn xếp được khai báo như sau:
.Stack 100h
a. SP bằng bao nhiêu khi bắt đầu chương trình (dạng số Hex)
b. Ngăn xếp chứa được nhiều nhất bao nhiêu từ.
2. Biết AX = 1234h, BX = 5678h, CX = 9ABCh và SP = 1000h. Hãy cho biết nội
dung các thanh ghi AX, BX, CX và SP sau khi thực hiện các lệnh dưới đây:
PUSH AX
PUSH BX
XCHG AX, CX
POP CX
PUSH AX
POP BX
3. Khi ngăn xếp điền đầy vùng nhớ dành cho nó thì SP = 0. Nếu có một từ nữa lại
được cất vào ngăn xếp thì SP sẽ như thế nào?, điều gì sẽ xảy ra với chương
trình?
4. Giả thiết chương trình chứa các dòng lệnh sau:
CALL PROC1
MOV AX, BX
Biết rằng lệnh MOV nằm ở địa chỉ 08FD:0203 và PROC1 là thủ tục NEAR bắt
đầu tại địa chỉ 08FD:0300, SP = 010Ah. Cho biết nội dung của IP và SP sau
mỗi lệnh. Giá trị đỉnh ngăn xếp là bao nhiêu?

147
GV. Vương Quốc Dũng
5. Giả thiết SP = 0200h và giá trị đỉnh ngăn xếp là 012Ah, cho biết nội dung của
IP và SP:
a. Sau khi thi hành lệnh RET (của một thủ tục NEAR)
b. Sau khi thi hành lệnh RET 4 (của một thủ tục NEAR)
6. Viết các lệnh thực hiện:
a. Đưa giá trị của đỉnh ngăn xếp vào AX, không làm thay đổi ngăn xếp
b. Đưa từ nằm sau đỉnh ngăn xếp vào AX, không làm thay đổi ngăn xếp
c. Đổi chỗ 2 từ ở đỉnh ngăn xếp. Có thể dùng thanh ghi AX, BX.
7. Ngăn xếp phải được thủ tục trả về chương trình gọi giống hệt như khi nó nhận
được . Tuy nhiên cũng tốt nếu có các thủ tục làm thay đổi ngăn xếp. Ví dụ
chúng ta muốn viết một thủ tục NEAR có tên SAVE_REGS để cất các thanh
ghi BX, CX, DX, SI, DI, BP, DS và ES vào ngăn xếp. Sau khi cất các thanh
ghi, ngăn xếp như sau:
Nội dung ES
Nội dung DS
……
Nội dung CX
Nội dung BX
địa chỉ trở về chương trình gọi chương trình SAVE_REGS
Thật không may là bây giờ SAVE_REGS không thể trả điều khiển cho chương
trình gọi nó bởi địa chỉ trở về không nằm ở đỉnh ngăn xếp.
a. Hãy viết lại thủ tục SAVE_REGS khắc phục khó khăn nêu trên.
b. Viết thủ tục RESTORE_REGS phục hồi các thanh ghi được cất bởi
SAVE_REGS
8. Viết chương trình để từ đó nhập vào các dòng văn bản trong đó các từ được
ngăn cách nhau bởi ký tự trắng, kết thúc bằng ký tự trở về đầu dòng và hiểnthị
chúng theo cùng một thứ tự, nhưng các chữ trong mỗi từ được đảo ngược. Ví
dụ “This is a text” thành “sihT si a txet”.
9. Một biểu thức đại số có chứa các dấu ngoặc (như (); []; {}) được coi là hợplệ
nếu thoả mãn:
- Số các dấu ngoặc trái và ngoặc phải của mỗi loại bằng nhau
- Các dấu ngoặc đóng mở tương ứng phải cùng loại
Ví dụ:
(a + [b – {c * (d-e)}] + f) là hợp lệ, nhưng
(a + [b – {c * (d-e)]} + f) là không hợp lệ
Công việc kiểm tra có thể thực hiện nhờ ngăn xếp. Biểu thức được quét từ
trái qua phải. Nếu gặp dấu ngoặc trái, ta cất nó vào ngăn xếp. Nếu gặp dấu
ngoặc phải, ta thực hiện lấy ra từ ngăn xếp (nếu ngăn xếp rỗng tức là có quá
nhiều dấu ngoặc phải) và so sánh, nếu cùng loại ta quét tiếp, còn nếu khác loại
có nghĩa là biểu thức được điền các dấu ngoặc không tương ứng. Đến cuối biểu
thức nếu ngăn xếp rỗng, biểu thức hợp lệ, nếu ngăn xếp còn dữ liệu tức là có
nhiều dấu ngoặc trái hơn dấu ngoặc phải.
Bạn hãy viết một chương trình để người sử dụng gõ vào một biểu thức đại
số có chứa các dấu ngoặc (tròn, vuông, nhọn) và kết thúc bằng ký tự về đầu

148
GV. Vương Quốc Dũng
dòng. Khi gõ vào biểu thức, chương trình kiểm tra từng ký tự. Nếu vào thời
điểm nào đó vào các dấu ngoặc không hợp lệ (Quá nhiều dấu ngoặc phải hoặc
cặp dấu ngoặc tương ứng không cùng loại), chương trình sẽ báo cho người sử
dụng bắt đầu lại. Khi ký tự về đầu dòng được gõ vào, nếu biểu thức hợp lệ,
chương trình sẽ hiện thông báo “biểu thức hợp lệ”, nếu ngược lại, chương trình
hiển thị thông báo “quá nhiều ngoặc trái”. Trong cả 2 trường hợp chương trình
đều hỏi xem người sử dụng có muốn tiếp tục hay không, Nếu trả lời “Y”,
chương trình sẽ được thực hiện lại.
Chương trình của bạn không cần lưu lại chuỗi nhập vào, chỉ kiểm tra biểu
thức có được điền các cặp dẫu ngoặc hợp lệ hay không.
Một ví dụ khi chạy chương trình:
Bạn nhập một biểu thức đại số:
(a+b)) quá nhiều dấu ngoặc phải, bạn vào lại!
Bạn nhập một biểu thức đại số:
[a+(b-c)*d]
Biểu thức hợp lệ
Nhấn phím ‘Y’ nếu bạn muốn tiếp tục: Y
Bạn nhập một biểu thức đại số:
(a+b*(c-d)-e] vào sai kiểu, bạn vào lại!
Bạn nhập một biểu thức đại số:
((a+[b-{c*(d-e)}]+f) có quá nhiều ngoặc trái, bạn vào lại!
Bạn nhập một biểu thức đại số:
Tôi đã vào đủ!
Biểu thức hợp lệ
Nhấn phím ‘Y’ nếu bạn muốn tiếp tục: N
10. Phương pháp sau đây có thể dùng để tạo ra các số ngẫu nhiên trong khoảng từ 1
đến 32767:
Bắt đầu bằng một số bắt kỳ trong miền giới hạn.
Dịch trái một bit
Thay bit 0 bằng kết quả của phép XOR bit 14 và bit 15
Xoá bit 15
Viết các thủ tục sau đây:
a. Thủ tục READ để người sử dụng vào một số nhị phân và chứa nó trong AX.
b. Thủ tục RANDOM nhận một số vào AX và trả về một số ngẫu nhiên trong
AX
c. Viết thủ tục WRITE hiển thị AX dưới dạng nhị phân.
Cuối cùng bạn viết một chương trình hiển thị một dấu ?, gọi READ để nhập
một số nhị phân sau đó gọi RANDOM và WRITE để tính toán và hiển thị 100
số ngẫu nhiên. Các số được hiển thị thành các dòng 4 số và 2 số trong cùng một
dòng ngăn cách nhau bằng 4 ký tự trắng.
11. Một chuỗi ký tự được kết thúc bằng ký tự có mã ASCII là 0, chiều dài của
chuỗi là tổng số ký tự tính từ ký tự đầu tiên đến ký tự đứng trước ký tự có mã
ASCII là 0.

149
GV. Vương Quốc Dũng
a. Viết chương trình con tính chiều dài của một chuỗi. Chương trình con này
nhận tham số là cặp thanh ghi DS:SI, là địa chỉ segment:offset của chuỗi ký
tự, kết quả chiều dài của chuỗi được lưu vào thanh ghi AX.
b. Viết chương trình con xuất nội dung của chuỗi ra màn hình. Chương
trình con này nhận tham số là cặp thanh ghi DS:SI là địa chỉ segment:offset
của chuỗi ký tự.
c. Viết chương trình con tìm ký tự có mã ASCII lớn nhất và nhỏ nhất của
chuỗi. Chương trình con này nhận tham số là cặp thanh ghi DS:SI là địa chỉ
segment:offset của chuỗi ký tự. Kết quả phần tử lớn nhất và nhỏ nhất được
lưu vào thanh ghi AX và BX.
d. Viết chương trình con nối 2 chuỗi ký tự thành một chuỗi ký tự duy nhất, kết
thúc bằng ký tự có mã ASCII bằng 0. Chương trình con này nhận tham số là
cặp thanh ghi DS:SI là địa chỉ segment:offset của chuỗi ký tự thứ nhất, nhận
tham số là cặp thanh ghi DS:DI là địa chỉ segment:offset của chuỗi ký tự
thứ hai, chuỗi ký tự kết quả có địa chỉ trong cặp thanh ghi DS:BX.
e. Viết chương trình con tìm vị trí xuất hiện đầu tiên của một chuỗi ký tự con
trong một chuỗi ký tự. Chương trình con này nhận tham số là cặp thanh ghi
DS:SI là địa chỉ segment:offset của chuỗi ký tự lớn, nhận tham số là cặp
thanh ghi DS:DI là địa chỉ segment:offset của chuỗi ký tự con, thanh ghi
CX chứa chiều dài của chuỗi ký tự con. Kết quả vị trí xuất hiện được lưu
vào thanh ghi AX (giá trị 0 xem như không tìm thấy chuỗi ký tự con trong
chuỗi ký tự lớn).
f. Viết chương trình con tìm vị trí xuất hiện cuối cùng của một chuỗi ký tự con
trong một chuỗi ký tự. Chương trình con này nhận tham số là cặp thanh ghi
DS:SI là địa chỉ segment:offset của chuỗi ký tự lớn, nhận tham số là cặp
thanh ghi DS:DI là địa chỉ segment:offset của chuỗi ký tự con, thanh ghi
CX chứa chiều dài của chuỗi ký tự con. Kết quả vị trí xuất hiện được lưu
vào thanh ghi AX (giá trị 0 xem như không tìm thấy chuỗi ký tự con trong
chuỗi ký tự lớn).
g. Viết chương trình con tìm và thay thế trong một chuỗi ký tự lớn tất cả các
chuỗi ký tự con (chuỗi con thứ nhất) thành một chuỗi ký tự con khác (chuỗi
con thứ 2). Chương trình con này nhận tham số là cặp thanh ghi DS:BX là
địa chỉ segment:offset của chuỗi ký tự lớn, nhận tham số là cặp thanh ghi
DS:SI là địa chỉ segment:offset của chuỗi ký tự con thứ nhất, nhận tham số
là cặp thanh ghi DS:DI là địa chỉ segment:offset của chuỗi ký tự con thứ
hai. Chiều dài 2 ký tự con là bằng nhau và được lưu vào trong thanh ghi
CX.
h. Viết chương trình con sắp xếp một chuỗi ký tự theo chiều tăng dần của mã
ASCII. Chương trình con này nhận tham số là cặp thanh ghi DS:SI là địa
chỉ segment:offset của chuỗi ký tự

150
GV. Vương Quốc Dũng
Ch¬ng 6. MACRO
6.1. Chương trình con và Macro
Trong Assembler có một dạng cấu trúc chương trình khá giống với các thủ tục
(chương trình con) - đó là MACRO.
Macro là một khối văn bản có tên - tên đó gọi là tên Maccro, khi gặp tên đó
trong quá trình biên dịch, chương trình dịch sẽ chèn khối văn bản vào chương
trình thay cho tên MACRO. Khối văn bản nói trên có thể là: bao gồm các lệnh,
các toán tử giả, lời bình hay sự tham chiếu đến các Macro khác.

Giống như thủ tục - tên 1 MACRO đại diện cho 1 nhóm lệnh, mỗi khi cần
thực hiện các lệnh đó, chúng ta chỉ việc đưa vào chương trình tên Macro đó, mà
không cần lệnh CALL (Tên thủ tục phải đứng sau lệnh CALL). Trong một số
trường hợp rất khó lựa chọn cấu trúc nào tốt hơn trong tình huống đã cho. Sau
đây là một số nhận xét:
Về thời gian biên dịch:
Trong chương trình khi gặp lời gọi Maccro, trình biên dịch sao chép nội dung
của Maccro vào trong chương trình tại vị trí có lời gọi Macro thay thế cho tên
Macro, quá trình này gọi là mở rộng Macro. Do vậy một chương trình chứa các
Macro thường cần nhiều thời gian biên dịch hơn là một chương trình tương tự
chứa các thủ tục. Điều này đặc biệt đúng trong trường hợp chương trình tham
chiếu đến một thư viện các Macro.
Về thời gian thực hiện
Mã lệnh được tạo bởi việc mở rộng Macro nói chung thực hiện nhanh hơn một
lời gọi thủ tục có mã lệnh tương tự, vì với thủ tục CPU phải thực hiện lệnh
CALL và lệnh RET (Tức phải cất giữ địa chỉ trở về chương trình chứa lời gọi
thủ tục, chuyển điều khiển cho thủ tục, chuyển dữ liệu vào thủ tục và cuối cùng
là trở về chương trình gọi thủ tục từ thủ tục).
Về kích thước chương trình
Một chương trình với các Macro nói chung có kích thước lớn hơn một chương
trình với các thủ tục, bởi vì mỗi lần tham chiếu đến Macro, toàn bộ khối mã
lệnh thuộc nội dung của đều được chép lại vào trong thân chương trình. Trong
khi đó nội dung của thủ tục được mã hóa đúng một lần duy nhất trong chương
trình.
Về các mặt khác
Macro đặc biệt phù hợp cho các công việc nhỏ, xuất hiện thường xuyên, bởi
việc sử dụng tự do các Macro đó tạo ra một chương trình nguồn giống như
chương trình viết bằng ngôn ngữ bậc cao. Tuy nhiên các công việc lớn thì
thường tốt nhất là nên quản lý bằng các thủ tục vì các Macro lớn sẽ tạo ra một
số lượng mã lệnh quá lớn nếu chúng được tham chiếu thường xuyên.
Tóm lại Macro có các ưu nhược điểm như sau:
Ưu:
Có thể thay đổi các tham số truyền cho Macro một cách dễ dàng.
Chương trình dùng Macro thực hiện nhanh hơn chương trình tương ứng dùng
chương trình con vì CPU không cần thực hiện lệnh CALL và RET.

151
GV. Vương Quốc Dũng
Các Macro có thể tập hợp thành một thư viện các Macro.
Macro có thể lồng nhau, có thể định nghĩa lại, và có thể đệ qui.
Nhược:
Macro làm cho chương trình mã máy dài hơn so với chương trình dùng thủ tục do
đó chương trình sử dụng Macro tốn nhiều bộ nhớ hơn.
Trong chương trình khi gặp lời gọi thủ tục, điều khiển được chuyển cho thủ tục
để sau đó trả lại cho chương trình khi đã thực hiện xong các lệnh của thủ tục
được gọi.
Maccro đặc biệt có ích khi thực hiện các công việc 1 cách thường xuyên, chúng
ta cũng có thể dùng các Maccro để tạo ra các lệnh mới khắc phục hạn chế của
lệnh có sẵn chẳng hạn lệnh
MOV đich, nguồn
Hai toán hạng đích và nguồn không thể cùng là 2 ô nhớ, ta có thể viết 1
Macro khắc phục hạn chế trên.
Macro sử dụng các biến giả như là các tham số hình thức
6.2. Khai báo (định nghĩa) và tham chiếu đến Maccro
6.2.1. Khai báo Maccro:
Cú pháp:
Tên_Macro MACRO d1, d2,...,dn
diễn tả nội dung Maccro
ENDM
Trong đó:
Tên_Macro do người sử dụng đặt
MACRO & ENDM là các toán tử giả chỉ ra sự bắt đầu và kết thúc của việc khai
báo MACRO
d1, d2,..., dn: danh sách tuỳ chọn các biến giả sử dụng trong MACCRO
Ví dụ:
Định nghĩa MACRO chuyển 1 word vào 1 WORD
MOVW MACRO word1, word2
PUSH word2
POP word1
ENDM
Trong đó:
MOVW là tên Macro
word1, word2 là các biến giả
Chú ý: Khai báo một Macro có thể đặt bất kỳ vì trí nào trong chương trình miễn
là nó phải đứng trước mọi tham chiếu đến Macro đó.
6.2.2. Tham chiếu đến Maccro
1) Cú pháp tham chiếu:
Để sử dụng MACRO trong chương trình, ta tham chiếu nó theo cú pháp sau:
Tên_Macro a1, a2, ..., an
trong đó a1, a2, ..., an là danh sách các biến thực

152
GV. Vương Quốc Dũng
2) Các Macro tham chiếu đến Macro khác
Một Macro có khả năng tham chiếu đến một Macro khác.
Ví dụ, chúng ta có 2 Macro thực hiện việc cất giữ và lấy lại giá trị cho các
thanh ghi:
Save_Regs MACRO r1, r2, r3
PUSH r1
PUSH r2
PUSH r3
ENDM
Restore_Regs MACRO s1, s2, s3
POP s1
POP s2
POP s3
ENDM
Hai Macro là Save_Regs và Restore_Regs sẽ được tham chiếu bởi một Macro
là COPY để copy chuỗi như sau:
Copy MACRO nguon, dich, do_dai
Save_Regs CX, SI, DI
LEA SI, nguon
LEA DI, dich
CLD
MOV CX, do_dai
REP MOVSB
Restore_Regs DI, SI, CX
ENDM
Khi trình biên dịch gặp lời gọi Macro
COPY str1, str2, 25
Nó sẽ sao chép đoạn mã sau vào chương trình:
PUSH CX
PUSH SI
PUSH DI
LEA SI, str1
LEA DI, str2
CLD
MOV CX, 25
REP MOVSB
POP DI
POP SI
POP CX
Chú ý: Một Macro có thể tham chiếu đến chính nó, những Macro như vậy gọi
là các Macro đệ qui.
3) Thư viện các Macro

153
GV. Vương Quốc Dũng
Các Macromaf chương trình tham chiếu đến có thể chứa trong một file riêng
biệt. Nhờ đó có thể tạo ra một thư viện các Macro có ích. Ví dụ, giả sử tên của
file là Macros và được đặt trong đĩa A thì khi trình biên dịch gặp dẫn hướng:
INCLUDE A:\Macros
trong chương trình, nó sẽ sao toàn bộ các định nghĩa Macro từ file Macros vào
chương trình tại vị trí của thông báo INCLUDE. Thông báo INCLUDE có thể
xuất hiện ở bất kỳ vị trí nào trong chương trình miễn là nó đứng trước mọi
tham chiếu đến các Macro trong file thư viện đó.
6.3. Mở rộng Macro
Khi trình biên dịch gặp tên MACRO nó sẽ “mở rộng MACRO” có nghĩa là: nó
sẽ sao toàn bộ các lệnh của MACRO vào trong chương trình tại lời gọi
MACRO (giống hệt như user đã gõ vào các lệnh đó). Trong khi sao chép các
lệnh, chương trình dịch sẽ thay thế các biến giả d i bằng các biến thực tương
ứng ai và tạo nên mã máy cho mọi lệnh (giống như truyền tham số cho chương
trình con có tham số ở PASCAL)
chú ý:
Việc khai báo MACRO phải được thực hiện trước khi có thể tham chiếu đến nó
(gọi nó) trong chương trình. để đảm bảo trình tự này, các MACRO thường đặt ở
đầu chương trình.
Ta có thể tạo thư viện các MACRO để sử dụng cho mọi chương trình.
Ví dụ:
Xây dựng chương trình có MACRO với tên MOVW như đã nói và tham
chiếu 2 lần đến nó.
TITLE VIDU: MACRO Demo
.model small
MOVW MACRO word1, word2
PUSH word2
POP word1
ENDM
.stack 100h
.data
A DW 4, 8
B DW 3
.code
CTC proc
mov ax, @ data
movw A, dx ; gọi macro movw lần 1
movw A+2, B ; gọi macro movw lần 2
mov ah, 4ch
int 21h
CTC ENDP
END CTC
Khi biên dịch: MOVW A, dx sẽ được thay bằng 2 lệnh sau:

154
GV. Vương Quốc Dũng
PUSH dx
POP A
Movw A+2, B được thay bằng 2 lệnh sau
PUSH B
POP A+2
Chương trình trên được viết lại như sau (Không dùng MACRO):
TITLE VIDU: MACRO Demo
.model small
.stack 100h
.data
A DW 4, 8
B DW 3
.code
CTC proc
mov ax, @ data
PUSH dx
POP A
PUSH B
POP A+2
mov ah, 4ch
int 21h
CTC ENDP
END CTC
6.4. Các dẫn hướng (chỉ dẫn) dùng trong MACRO
6.4.1. Chỉ dẫn LOCAL
Cú pháp:
LOCAL Tên_chỉ_dẫn_1[, Tên_chỉ_dẫn_2] [, Tên_chỉ_dẫn_3]...
Chức năng:
Chỉ dẫn này được dùng trong một MACRO để định nghĩa các ký hiệu mà
các ký hiệu này chỉ có thể đưọc dùng trong MACRO đó.
Ví dụ 1: Wait MACRO Count
PUSH CX
Mov CX, count
Next: Add ax, ax
Loop Next
POP CX
ENDM
Nhận xét:
Macro Wait chỉ có thể được gọi tối đa 1 lần duy nhất vì như ta đã biết một
nhãn chỉ có thể được định nghĩa một lần duy nhất trong chương trình. Để
Macro Wait có thể gọi được nhiều lần, ta phải dùng chỉ dẫn LOCAL. Chỉ
dẫn LOCAL được dùng trong phần thân MACRO để báo cho Assembler

155
GV. Vương Quốc Dũng
biết các nhãn được khai báo bởi chỉ dẫn LOCAL để mỗi lần gọi Macro các
nhãn này sẽ được đổi thành một tên nhãn mới - tên nhãn mới có dạng:
??Number
Number: là một số Hex có giá trị trong khoảng 0000h ÷ FFFFh. Do đó
không nên định nghĩa các tên nhãn, tên thủ tục... không nên định nghĩa có
dạng ??Number vì có thể sinh ra các ký hiệu trùng nhau.
Chú ý: Chỉ dẫn này phải được đặt ở đầu Macro ngay sau phần khai báo tên.
Để Macro Wait có thể gọi được nhiều lần trong chương trình, ta viết lại định
nghĩa Macro Wait với chỉ dẫn LOCAL như sau:
Wait MACRO Count
LOCAL Next
PUSH CX
Mov CX, count
Next: Add ax, ax
Loop Next
POP CX
ENDM
Macro Wait với chỉ dẫn LOCAL như trên, khi được gọi trong chương trình lần
đầu tiên, ví dụ:
Wait 1000
Assembler sẽ chép lại nội dung của Macro Wait như sau:
PUSH CX
Mov CX, 1000
??0000: Add ax, ax
Loop ??0000
POP CX
Ta thấy nhãn Next đã được đổi tên thành “??0000”
Macro Wait với chỉ dẫn LOCAL như trên, khi được gọi trong chương trình lần
đầu thứ hai, ví dụ:
Wait 200
Assembler sẽ chép lại nội dung của Macro Wait như sau:
PUSH CX
Mov CX, 200
??0001: Add ax, ax
Loop ??0001
POP CX
Ta thấy nhãn Next đã được đổi tên thành “??0001...
Ví dụ 2: Macro tính luỹ thừa của một số
Power MACRO Number, Exp
LOCAL Lap, Ra
Xor DX, DX
Mov CX, Exp
Mov AX, 1

156
GV. Vương Quốc Dũng
JCXZ Ra ; Nhảy tới Ra nếu CX = 0
Mov BX, Number
Lap: Mul BX
Loop Lap
Ra:
ENDM
Để tính 2 mũ 8 ta gọi Macro Power lần 1 như sau:
Power 2, 8 ; Kết quả lưu trong DXAX. Nhãn “Lap” đổi
; thành “??0000”, nhãn “Ra” đổi thành “??0001”
Nếu gọi Macro Power lần 2, để tính 5 mũ 4, ta viết như sau:
Power 5, 4 ; Kết quả lưu trong DXAX. Nhãn “Lap” đổi
; thành “??0002”, nhãn “Ra” đổi thành “??0003”
Ví dụ 3: Viết Macro tìm giá trị Min trong 2 số
Min MACRO First, Second, Result
LOCAL Ketqua
Mov Result, First
Cmp Result, Second
JL Ketqua
Mov Result, Second
Ketqua:
ENDM
Để tìm min của 2 số lưu trong BX, CX và kết quả lưu trong AX, ta gọi
MACRO Min như sau: Min BX, CX, AX
Ví dụ 3: Viết MACRO in một xâu ký tự ra màn hình
InXau MACRO Chuoi
LOCAL str, in
JMP in
str DB Chuoi, “$”
in: Push AX
Push DX
Mov ah, 9
Lea DX, str
Int 21h
Pop DX
PoP AX
ENDM
Gọi Macro InXau 2 lần ta viết như sau:
InXau <”Hello !”, 0Dh, 0Ah>
InXau “Chao cac ban !”
6.4.2. Chỉ dẫn EXITM (Exit Marco):
Cú pháp:
EXITM
Chức năng: Dùng để thoát khỏi một Macro hoặc một khối lặp Repeat.

157
GV. Vương Quốc Dũng
Khi assembler gặp tên MACRO, nó xử lý tất cả các câu lệnh trong MACRO và
tiếp tục với câu lệnh kế tiếp sau lệnh gọi MACRO. Tuy nhiên ta có thể dùng chỉ
dẫn EXITM để thoát khỏi MACRO khi thực hiện hết các lệnh của MACRO.
Lệnh EXITM được dùng với các chỉ dẫn có điều kiện (Conditional directive).
Khi sử dụng các MACRO lồng nhau hay một khối lặp Repeat lồng nhau mà ta sử
dụng EXITM thì chỉ thoát khỏi Macro hay vòng lặp Repeat chứa chỉ dẫn EXITM đó
6.4.3. Chỉ dẫn điều kiện
1) Các lệnh kiểm tra tham số của MACRO:
1a) Chỉ dẫn IFB (IF Blank):
Chức năng:
Khi gọi Macro, nếu ta truyền tham số cho Macro ít hơn số các tham số mà
Macro đòi hỏi thì assembler tự định các tham số truyền thiếu (các tham số để
trống) có giá trị truyền là 0. Những tham số truyền thiếu đó gọi là các tham số
trống Blank.
Chỉ dẫn IFB dùng để kiểm tra tham số mà ta truyền có trống hay không.
Cú pháp:
IFB <Tên_tham_số>
Các câu lệnh ASM nếu Tên_tham_số phải truyền trong diện Blank.
[ELSE
Các câu lệnh ASM nếu Tên_tham_số phải truyền được truyền đủ]
ENDIF
Ví dụ: Viết Macro in ra màn hình 1 ký tự có thuộc tính hay màu tại một
vị trí xác định.
InKyTu Macro dong, cot, KyTuIn, ThuocTinh, ManHinh
PUSP SI
PUSP ES
IFB <ManHinh>
Mov SI, 0B000h
ELSE
Mov SI, ManHinh
ENDIF
Mov ES, SI
Mov SI, ((dong-1)*80*2 + (Cot-1)*2)
Mov AL, KyTu
IFB <ThuocTinh>
Mov AH,7
ELSE
Mov ThuocTinh
ENDIF
Mov ES:[SI], AX
POP ES
POP SI
ENDM

158
GV. Vương Quốc Dũng
Để in ký tự “A” nhấp nháy ra màn hình màu tại dòng 10, cột 20, ta gọi
Macro như sau:
InKyTu 10, 20, “A”, 8Fh, 0B800h
Để in ký tự “B” có thuộc tính Normal ra màn hình màu tại dòng 11, cột 26,
ta gọi Macro như sau:
InKyTu 11, 26, “B”,, 0B800h
Để in ký tự “C” có thuộc tính Normal ra màn hình mono tại dòng 16, cột
31, ta gọi Macro như sau:
InKyTu 16, 31, “C”
1b) Chỉ dẫn IFNB (IF No Blank):
Chức năng:
Ngược lại với IFB
Cú pháp:
IFNB <Tên_tham_số>
Các câu lệnh ASM nếu Tên_tham_số phải truyền được truyền đủ
(NOT BLANK).
[ELSE
Các câu lệnh ASM nếu Tên_tham_số phải truyền trong diện Blank].
ENDIF
Ví dụ:
Viết Macro in ra màn hình 1 xâu ký tự và xuống dòng, nếu trường hợp
không có tham số thì chỉ xuống dòng.
InChuoi Macro Chuoi
LOCAL Xau, InXau
JMP InXau
Xau: IFNB <Chuoi>
DB &Chuoi
ELSE
DB 0Dh, 0Ah, “$”
ENDIF
InXau: PUSP AX
PUSH DX
Mov AH, 9
Lea DX, Xau
Int 21h
POP DX
POP AX
ENDM
2) Các lệnh so sánh 2 tham số của MACRO:
2a) Chỉ dẫn IFIDN[I] (IF Identical):
Cú Pháp:
IFIDN[I] <Tên_tham_số_1>, < Tên_tham_số_2>

159
GV. Vương Quốc Dũng
Các câu lệnh ASM nếu giá trị của Tên_tham_số_1 bằng giá trị của
Tên_tham_số_2
[ELSE
Các câu lệnh ASM nếu giá trị của Tên_tham_số_1 khác giá trị của
Tên_tham_số_2]
ENDIF
Chức năng:
Chỉ dẫn dùng để so sánh xem 2 tham số có bằng nhau hay không.
Nếu dùng tuỳ chọn [I]: Phép so sánh không phân biệt chữ hoa, chữ
thường.
2b) Chỉ dẫn IFDIF[I]:
Cú Pháp:
IFDIF[I] <Tên_tham_số_1>, < Tên_tham_số_2>
Các câu lệnh ASM nếu giá trị của Tên_tham_số_1 khác giá trị của
Tên_tham_số_2
[ELSE
Các câu lệnh ASM nếu giá trị của Tên_tham_số_1 bằng giá trị của
Tên_tham_số_2]
ENDIF
Chức năng:
Chỉ dẫn dùng để so sánh xem 2 tham số có khác nhau hay không.
Nếu dùng tuỳ chọn [I]: Phép so sánh không phân biệt chữ hoa, chữ
thường.
3) Chỉ dẫn Repeat
Khái niệm Repeat Block:
Repeat Block là một dạng đặc biệt của Macro. Repeat Block là một
vòng lặp với số lần lặp biết trước. Giống như Macro, ta có thể truyền một
dãy các tham số cho một Repeat Block và cũng có thể dùng các chỉ dẫn của
Macro trong Repeat Block. Repeat Block được kết thúc bằng lệnh giả
ENDM. Các lệnh được lặp nằm trong khoảng giữa tên chỉ dẫn và lệnh giả
ENDM kết thúc Repeat Block.
Repeat Block khác Macro ở chỗ là nó không có tên, do đó nó không thể
gọi trong chương trình như Macro.
3a) Chỉ dẫn REPT:
Cú pháp:
REPT Biểu_thức
Các lệnh của ASM
ENDM
Trong đó:
Biểu_thức: Là biểu thức số không dấu 16 bit
Chức năng:
Chỉ dẫn này định nghĩa một Repeat Block mà số lần lặp được cho trong
Biểu_thức.

160
GV. Vương Quốc Dũng

Ví dụ:
Viết Macro tạo một mảng kiểu bytegồm n phần tử có giá trị tăng dần
từ X đến (n + X)
TaoMang MACRO TenMang, n, X
TenMang LABLE Byte
Mov Giatri, X
REPT n
DB Giatri
ADD Giatri,1
ENDM
ENDM
Để tạo một mảng R1 gồm 255 phần tử kiểu byte có giá trị từ 1 đến 255 ta gọi
Macro như sau:
TaoMang R1, 255, 1
3b) Chỉ dẫn IRP:
Cú pháp:
IRP Tên_Tham_số, <Danh_sách_các_đối_số>
Các câu lệnh
ENDM
Trong đó:
Tên_Tham_số: Là một tham số mà nó lần lượt được nhận giá trị của
các đối số trong < Danh_sách_các_đối_số >
Danh_sách_các_đối_số là: một dãy các đối số có dạng
<đối_số_1[,đối_số_2]....> và phải đặt trong
cặp ngoặc nhọn “<>”
Mỗi đối số có thể là 1 ký tự, 1 xâu ký tự hoặc một số.
Chức năng:
IRP định nghĩa một Repeat Block mà số lần lặp bằng số các đối số trong
<danh_sách_các_đối_số>, mỗi lần lặp tham số lần lượt nhận giá trị của một
đối số trong <Danh_sách_các_đối_số>.
Ví dụ:
Định nghĩa mảng R kiểu Word gồm 15 phần tử là luỹ thừa bậc 2 của 6 số
nguyên tố đầu tiên
R LABLE Word
PUSP AX
PUSP BX
IRP Giatri, <1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43>
DW Giatri
Xor AH, AH
Mov AL, Giatri
Mov BL, Giatri
Mul BL
Mov Giatri, AX

161
GV. Vương Quốc Dũng
ENDM
POP BX
POP AX
3c) Chỉ dẫn IRC:
Cú pháp:
IRC Tên_Tham_số, Xâu_ký_tự
Các câu lệnh
ENDM
Trong đó:
Tên_Tham_số: Là một tham số mà nó lần lượt được nhận giá trị của
từng ký tự trong xâu_ký_tự
Chức năng:
IRP định nghĩa một Repeat Block mà số lần lặp bằng độ dài của xâu_ký_tự,
mỗi lần lặp tham số lần lượt nhận giá trị của một ký tự trong <Xâu_ký_tự >.
Ví dụ:
Định nghĩa mảng R2 kiểu byte gồm 10*11 phần tử mà các phần tử trong
hàng có cùng một giá trị, các phần tử trong cột có giá trị khác nhau.
R2 LABLE Byte
IRC Giatri, abcdefghijk
DB 10 dup(Giatri)
ENDM

6.5. Các toán tử dùng trong Macro


6.5.1. Toán tử <>
Cú pháp:
<Text>
Chức năng:
Dùng để báo cho assembler biết văn bản giữa 2 dấu < > được truyền như
một tham số duy nhất cho Macro ngay cả khi nó chứa dấu “,”, dấu cách
trắng hoặc TAB.
Ví dụ: Macro định nghĩa một mảng gồm các phần tử kiểu byte
ARR MACRO X
IRP Y, <X>
DB Y
ENDM
ENDM
Để tạo một mảng gồm 7 phần tử ta gọi Macro như sau:
ARR <2, 3, 4, 5, 6, 7, 8>
6.5.2. Toán tử &
Cú pháp:
&Tên_tham_số

162
GV. Vương Quốc Dũng
Tên_tham_số&
&Tên_tham_số&

Chức năng:
Dùng để một tham số bằng giá trị của đối số tương ứng. Toán tử & thường
được dùng khi tham số đứng ngay sau hay trước các ký tự khác, hoặc khi tham
số xuất hiện trong một chuỗi.
&Tên_tham_số: Dùng khi tham số đứng ngay sau các ký tự khác
Tên_tham_số&: Dùng khi tham số đứng trước các ký tự khác
&Tên_tham_số&: Dùng khi tham số đứng giữa các ký tự khác
Ví dụ: Ta viết lại Macro xuất một ký tự ra màn hình như sau:
InKyTu MACRO KyTu
PUSP AX
PUSP DX
Mov ah, 2
Mov dl, ‘&KyTu’
Int 21h
POP DX
POP AX
ENDM
Để in ký tự “A”, “B” ta gọi Macro như sau:
InKyTu A
InKyTu B
6.5.3. Toán tử !
Cú pháp:
! Character
Chức năng:
Toán tử này dùng với các ký tự đặc biệt như dấu “,”,dấu “&”,dấu “<”,dấu
“!”..... để báo cho assembler biết Character là ký tự thật sự mà không phải là
một toán tử hay ký tự điều khiển.
Chú ý:
!! tương đương với <!>
!& tương đương với <&>
Ví dụ: Khai báo Macro
ErrMess MACRO Num, Str
ErrMess&Num DB ‘Error#&Num: &Str’
ENDM
Gọi MACRO
ErrMess 103, <Expression !> 255> ; Macro này tạo ra
; chuỗi: Error#10: Expression > 255
6.5.4. Toán tử %
Cú pháp:

163
GV. Vương Quốc Dũng
%Symbol
Chức năng:
Toán tử 5% dùng để chuyển một ký hiệu thánh giá trị tương ứng. Nó báo
cho Assembler biết Symbol theo sau toán tử 5% là một biểu thức. Assemler
tính toán giá trị của biểu thức và thay thế Symbol bằng giá trị của biểu thức
đó.
6.5.5. Toán tử quan hệ
Toán tử quan hệ so sánh giá trị của 2 biểu thức và trả về giá trị logic:
+ True (1) nếu điều kiện của toán tử đúng (thỏa mãn)
+ False (0) nếu điều kiện của toán tử sai (không thỏa mãn)
Chú ý:
Các biểu thức phải là hằng 16 bit. Các chỉ dẫn quan hệ thường phải dùng
chung với các chỉ dẫn điều kiện sau:

Toán tử ý nghĩa
EQ =
NE ≠
LT <
LE ≤
GT >
GE ≥

Ví dụ 1: Khai báo Macro nhạp một chuỗi ký tự từ bàn phím


NhapXau Macro xau, dodai
LOCAL nhan
jmp nhan
IFNB <dodai>
IF dodai GT 255
Mov dodai, 255
ENDIF
ELSE
Mov dodai, 255
ENDIF
xau DB dodai
DB ?
DB len +1 dup(0)
nhan: Mov ah, 0ah
Mov dx, offset xau
int 21h
ENDM
Gọi Macro nhập một chuỗi S1 tối đa 80 ký tự
NhapXau S1, 80
Gọi Macro nhập một chuỗi S2 tối đa 255 ký tự

164
GV. Vương Quốc Dũng
NhapXau S2
Gọi Macro nhập một chuỗi S3 tối đa 255 ký tự
NhapXau S3, 300

CÂU HỎI VÀ BÀI TẬP


1. Viết chương trình nhập từ bàn phím một số chỉ thứ tự từ 1 đến 7 và in ra màn
hình tên ngày bằng chữ tương ứng (Monday, Tuesday, Wednesday, Thurday,
Friday, Saturday, Sunday – trong đó, 1 ứng với Sunday) – sử dụng MACRO để
xuất các xâu ký tự là tên ngày ra màn hình.
2. Viết chương trình nhập từ bàn phím một số chỉ thứ tự từ 1 đến 12 và in ra màn
hình tên tháng bằng chữ tương ứng- sử dụng MACRO để xuất các xâu ký tự là
tên tháng ra màn hình.
3. Viết chương trình nhập vào một chuỗi ký tự, sau đó cắt bỏ các ký tự trắng bên
trái, bên phải, các ký tự trắng liên tiếp ở giữa thành một ký tự trắng duy nhất.
Và in kết quả ra màn hình (chuẩn hóa xâu ký tự).
Ví dụ:
Nhap vao mot chuoi: Tran Lan Huong
Chuoi sau chuan hoa: Tran Lan Huong
4. Cho 2 số có dấu dạng double word D1, D2. Viết chương trình so sánh 2 số này
và kết quả so sánh ra màn hình.
5. Viết chương trình nhập từ bàn phím họ và tên của một người, hãy chuyển đổi
các ký tự đầu của một từ (họ, tên, tên đệm) thành các chữ hoa tương ứng, các
ký tự còn lại thành chữ thường. In kết quả ra màn hình.
Ví dụ:
Nhap ten day du cua mot nguoi: trAn VAN cuong
Ten chuan hoa: Tran Van Cuong
6. Cho một từ điển gồm các từ khóa Assembler như sau:
MOV, PUSH, POP, XCHG, PUSHF, POPF, IN, OUT, JMP, CALL, CMP, JZ,
JA, JB, JNA, JNB, LOOP, LOOPZ, LOOPNZ,…
Viết chương trình nhập từ bàn phím một chuỗi ký tự (coa chiều dài tối đa là 6
ký tự), in ra màn hình chuỗi đó có nằm trong từ điển khóa hay không.
7. Viết chương trình nhập từ bàn phím một chuỗi ký tự gồm các chữ in hoa, sau
đó in ra màn hình chuỗi ký tự lớn nhất các chữ in hoa liên tục theo thứ tự
AN_PHA_B (trong số các ký tự vừa nhập).
Ví dụ:
Nhap vao mot chuoi chu hoa: VI DU DE KIEM TRA CHUONG TRINH
Chuoi lon nhat cac ky tu lien tuc: CDE
8. Viết chương trình hiển thị số không dấu trong thanh ghi AX dưới dạng:
a. Thập phân
b. Nhị phân
c. Thập lục phân
9. Viết chương trình hiển thị số không dấu trong cặp thanh ghi AXDX dưới dạng:
a. Thập phân
b. Nhị phân

165
GV. Vương Quốc Dũng
c. Thập lục phân
10. Viết chương trình nhập từ bàn phím một số nguyên không dấu kiểu word dưới
dạng thập phân. Hãy in số này ra màn hình dưới dạng hex tương ứng. Nếu nhập
sai hoặc có lỗi thì đưa ra thông báo lỗi
Ví dụ:
- Trường hợp nhập đúng:
Nhap vao mot so: 90
Dang thap luc phan: 5Ah ;
- Trường hợp nhập sai:
Nhap vao mot so: 23C1
Số nhập vào sai hay số quá lớn.
11. Viết chương trình nhập từ bàn phím một số nguyên có dấu kiểu word dưới dạng
thập phân. Hãy in số này ra màn hình dưới dạng thập lục phân tương ứng.
12. Viết chương trình nhập từ bàn phím một số nguyên không dấu kiểu word dưới
dạng thập phân. Hãy in số này ra màn hình dưới dạng nhị phân tương ứng.
Ví dụ:
Nhap vao mot so: 97
Dang nhi phan: 01100001b
13. Viết chương trình nhập từ bàn phím một số nguyên có dấu kiểu word dưới dạng
thập phân. Hãy in số này ra màn hình dưới dạng nhị phân tương ứng.
14. Viết chương trình nhập từ bàn phím một số nhị phân không quá 16 chữ số 0 và
1. Hãy in số này ra màn hình dưới dạng số:
- Thập phân không dấu tương ứng.
- Thập phân có dấu tương ứng.
15. Viết chương trình nhập từ bàn phím một xâu không quá 10 ký tự, xâu chỉ gồm
chữ cái, chữ số, dấu cách. Hiển thị xâu vừa nhập ra màn hình.
16. Viết chương trình nhập một số hex gồm 4 chữ số từ bàn phím, hãy in số này ra
màn hình dưới dạng số:
- Thập phân không dấu tương ứng.
- Thập phân có dấu tương ứng.
17. Viết chương trình nhập một số hex gồm 8 chữ số từ bàn phím, hãy in số này ra
màn hình dưới dạng số:
- Thập phân không dấu tương ứng.
- Thập phân có dấu tương ứng.
18. Viết chương trình nhập từ bàn phím một số nguyên không dấu kiểu double
word dưới dạng thập phân. Hãy in số này ra màn hình dưới dạng thập lục phân
tương ứng.
19. Viết chương trình nhập từ bàn phím một số nguyên có dấu kiểu double word
dưới dạng thập phân. Hãy in số này ra màn hình dưới dạng thập lục phân tương
ứng.
20. Viết chương trình nhập từ bàn phím một số nguyên không dấu kiểu double
word dưới dạng thập phân. Hãy in số này ra màn hình dưới dạng thập lục phân
tương ứng.

166
GV. Vương Quốc Dũng
21. Viết chương trình nhập từ bàn phím một số nguyên có dấu kiểu double word
dưới dạng thập phân. Hãy in số này ra màn hình dưới dạng nhị phân tương ứng.
22. Viết chương trình nhập từ bàn phím một số nguyên không dấu kiểu double
word dưới dạng thập phân. Hãy in số này ra màn hình dưới dạng nhị phân
tương ứng.
23. Viết chương trình nhập từ bàn phím 2 số nguyên thập phân không dấu kiểu
word. Cộng 2 số nguyên này, in kết quả ra màn hình.
Yêu cầu xử lý lỗi khi nhập và khi tổng vượt quá giới hạn một word.
24. Viết chương trình nhập từ bàn phím 2 số nguyên thập phân không dấu kiểu
word. Tìm USCLN, in kết quả ra màn hình.
Yêu cầu xử lý lỗi khi vượt quá giới hạn một word.
25. Viết chương trình nhập từ bàn phím 2 số nguyên thập phân không dấu kiểu
doubel word. Tìm USCLN, in kết quả ra màn hình.
Yêu cầu xử lý lỗi khi vượt quá giới hạn một double word.
26. Viết chương trình nhập từ bàn phím 2 số nguyên thập phân có dấu kiểu word.
Cộng 2 số nguyên này, in kết quả ra màn hình.
Yêu cầu xử lý lỗi khi nhập và khi tổng vượt quá giới hạn.
27. Viết chương trình nhập từ bàn phím 2 số nguyên thập phân có dấu kiểu word.
Tìm USCLN, in kết quả ra màn hình.
Yêu cầu xử lý lỗi khi vượt quá giới hạn.
28. Viết chương trình nhập từ bàn phím 2 số nguyên thập phân có dấu kiểu doubel
word. Tìm USCLN, in kết quả ra màn hình.
Yêu cầu xử lý lỗi khi vượt quá giới hạn.
29. Viết chương trình nhập từ bàn phím 2 số nguyên thập phân không dấu kiểu
doubel word. Tìm số lớn nhất, in kết quả ra màn hình.
30. Viết chương trình nhập từ bàn phím 2 số nguyên thập phân không dấu kiểu
word. Tìm số lớn nhất, in kết quả ra màn hình.
31. Viết chương trình nhập số nguyên A không dấu dạng double word và số
nguyên B không dấu dạng word, lấy A chia cho B, in kết quả phần thương và
phần dư ra màn hình.
32. Viết chương trình nhập từ bàn phím một câu tiếng Anh, hãy đếm các nguyên
âm A, E, I, O, U và in kết quả ra màn hình.
Ví dụ:
Nhap mot cau tieng Anh: I Love You
Số nguyên âm A: 0
Số nguyên âm E: 1
Số nguyên âm I: 1
Số nguyên âm O: 2
Số nguyên âm U: 1
33. Viết chương trình nhập từ bàn phím một dãy các số nguyên thập phân có dấu
kiểu word cho khi gặp số có giá trị 0 thì kết thúc, hãy tính tổng các số đã nhập
vào và in kết quả ra màn hình.

167
GV. Vương Quốc Dũng
34. Viết chương trình nhập từ bàn phím một dãy các số nguyên thập phân có dấu
kiểu double word cho khi gặp số có giá trị 0 thì kết thúc, hãy tính tổng các số
đã nhập vào và in kết quả ra màn hình.
35. Viết chương trình nhập từ bàn phím một số nguyên thập phân n không dấu kiểu
byte, hãy tính tổng n số tự nhiên đầu tiên và in kết quả ra màn hình.
36. Viết chương trình nhập một dãy ký tự từ bàn phím, nhập cho đến khi gặp ký tự
^Z (Ctrl + Z, có mã ASCII là 1Ah). Đếm tổng số các ký tự chữ hoa, tổng số các
ký tự chữ thường, tổng số các ký tự còn lại. In kết quả ra màn hình.
37. Viết chương trình nhập từ bàn phím một số nguyên thập phân n không dấu kiểu
byte (0 ≤ n ≤ 5), hãy tính n! và in kết quả ra màn hình.
38. Viết mộtct con bật bit thứ i của một số không dấu, chương trình này nhận 2
tham số:
- Thanh ghi AX lưu số không dấu 16 bit
- Thanh ghi CL lưu giá trị của i.
39. Viết mộtct con tắt bit thứ i của một số không dấu, chương trình này nhận 2
tham số:
- Thanh ghi AX lưu số không dấu 16 bit
- Thanh ghi CL lưu giá trị của i.

168
GV. Vương Quốc Dũng

MỤC LỤC

Ch¬ng 1.MÔ HÌNH LẬP TRÌNH CỦA 8086...............................................................2


1.1.Giới thiệu chung về máy tính và sơ đồ cấu trúc của IBM-PC......................2
1.1.1.Giới thiệu chung về máy tính:...................................................................2
1.1.2.Biểu diễn thông tin.....................................................................................2
1.1.2.1.Các số nhị phân (số hệ hai).................................................................2
1.1.2.2.Biểu diển dữ liệu ...............................................................................3
Hệ đếm......................................................................................................3
Chuyển đổi giữa các hệ đếm..................................................................4
1.1.2.3.Các phép toán số học với số hệ hai ...................................................6
Phép cộng...................................................................................................6
Phép trừ .....................................................................................................7
Số bù 2........................................................................................................7
Phép nhân...................................................................................................8
Phép chia.....................................................................................................9
1.1.2.4.Biểu diễn số trong máy tính ............................................................11
Biểu diễn số nguyên................................................................................12
Biểu diễn số thực ...................................................................................12
Biểu diễn ký tự........................................................................................14
1.1.2.5.Mã hai byte.........................................................................................17
1.1.3.Sơ đồ khối cấu trúc của IBM-PC............................................................18
1.2. Tổ chức các thanh ghi của vi xử lý 8086.......................................................21
1.2.1.Các thanh ghi đoạn:..................................................................................21
1.2.2.Các thanh ghi con trỏ và chỉ số:...............................................................21
1.2.3.Các thanh ghi đa năng:.............................................................................23
1.2.4.Thanh ghi cờ (Flag Register - FR):...........................................................23
1.3.Cơ chế quản lý bộ nhớ....................................................................................25
1.3.1.Tổng quan về hệ thống nhớ:....................................................................25
1.3.1.1.Các đặc trưng của hệ thống nhớ:....................................................25
1.3.1.2.Mô hình phân cấp hệ thống nhớ:.....................................................25
1.3.2.Bộ nhớ bán dẫn:.......................................................................................26

169
GV. Vương Quốc Dũng
1.3.2.1.ROM - Read Only Memory: ..............................................................26
1.3.2.2.RAM - Random Access Memory:......................................................26
1.3.2.3.Bộ nhớ Cache:....................................................................................27
1.3.2.4.Bộ nhớ ngoài: ...................................................................................27
1.3.3.Cơ chế quản lý bộ nhớ của 8086............................................................28
1.4.Cơ chế thực hiện chương trình......................................................................30
1.4.1.Thực hiện chương trình:..........................................................................30
1.4.2.Xử lý ngắt.................................................................................................31
1.4.2.1.Ngắt (Interrupt):.................................................................................31
1.4.2.2.Đáp ứng của CPU khi có yêu cầu ngắt............................................31
1.4.2.3.Xử lý ưu tiên ngắt.............................................................................33
1.4.3.Chuyển nhượng Bus:................................................................................34
CÂU HỎI VÀ BÀI TẬP.................................................................................................34
Ch¬ng 2.TỔNG QUAN VỀ HỢP NGỮ......................................................................36
2.1.Giới thiệu hợp ngữ..........................................................................................36
2.2.Cú pháp dòng lệnh trong chương trình hợp ngữ:..........................................37
2.2.1.Dữ liệu chương trình...............................................................................38
2.2.2.Các biến.....................................................................................................39
2.2.3.Các hằng có tên.........................................................................................41
2.3.Các chỉ dẫn dùng trong Assembler..................................................................41
2.3.1.Các chỉ dẫn đoạn:.....................................................................................41
2.4.Các toán tử dùng trong Assembler..................................................................54
2.4.1.Các toán tử số học....................................................................................54
2.4.2.Các toán tử logic.......................................................................................54
2.4.3.Các toán tử quan hệ..................................................................................55
2.4.4.Các toán tử cung cấp thông tin về biển và nhãn....................................55
2.4.5.Toán tử chỉ số []........................................................................................55
2.4.6.Toán tử :....................................................................................................56
2.4.7.Toán tử $....................................................................................................56
2.4.8.Toán tử TYPE............................................................................................56
2.4.9.Toán tử LENGTH......................................................................................57
2.4.10.Toán tử SIZE...........................................................................................57
2.4.11.Các toán tử thuộc tính ...........................................................................57
2.5.Giới thiệu một số lệnh cơ bản.......................................................................57
2.6.Giới thiệu qua về ngắt 21h của DOS.............................................................62
2.7.Giới thiệu qua về ngắt 10h của BIOS.............................................................63
2.8.Cấu trúc chương trình nguồn hợp ngữ..........................................................64
2.8.1.Khai báo mô hình bộ nhớ.........................................................................64
2.8.2.Khai báo ngăn xếp....................................................................................64
2.8.3. Khai báo đoạn dữ liệu:...........................................................................65
2.8.4.Khai báo mã...............................................................................................65
2.8.5.Khung của chương trình hợp ngữ tổng quát..........................................66
2.8.6.Khung của chương trình hợp ngữ dạng tập tin .exe:.............................66
2.8.7.Khung của chương trình hợp ngữ dạng tập tin •COM:........................68
2.8.8.So sánh chương trình .COM với chương trình .EXE.............................69
2.8.9.Các bước cơ bản khi lập trình trong giờ thực tập:................................70

170
GV. Vương Quốc Dũng
2.9.Các phép định địa chỉ trong ASSEMBLER.....................................................75
2.9.1.Địa chỉ các phần tử trong mảng...............................................................75
2.9.2.Các chế độ địa chỉ trong ASSEMBLER..................................................77
CÂU HỎI ÔN TẬP VÀ BÀI TẬP ...................................................................................80
Ch¬ng 3.CÁC PHÉP TOÁN VỚI DỮ LIỆU................................................................85
3.1.Nhóm lệnh chuyển dữ liệu ............................................................................85
3.1.1.Nhóm lệnh chuyển dữ liệu đa dụng:......................................................86
3.1.2.Nhóm lệnh chuyển địa chỉ:......................................................................86
3.1.3.Nhóm lệnh chuyển cờ hiệu......................................................................88
3.1.4.Nhóm lệnh truyền dữ liệu qua cổng:......................................................89
3.2.Nhóm các lệnh số học.....................................................................................90
3.2.1.Nhóm lệnh xử lý phép cộng.....................................................................90
3.2.2.Nhóm lệnh xử lý phép trừ........................................................................91
3.2.3. Nhóm lệnh xử lý phép nhân....................................................................92
3.2.4.Nhóm lệnh xử lý phép chia......................................................................94
3.3.Nhóm các lệnh logic........................................................................................96
3.3.1.các lệnh AND, OR và XOR.....................................................................96
3.3.2.Lệnh NOT..................................................................................................96
3.3.3.Lệnh TEST.................................................................................................96
3.4.Nhóm các lệnh dịch chuyển và quay..............................................................97
3.4.1.Các lệnh dịch trái......................................................................................97
3.4.2.Các lệnh dịch phải....................................................................................98
3.4.3.Các lệnh quay ...........................................................................................98
3.5.Nhóm các lệnh xử lí chuỗi...........................................................................100
3.5.1.Lệnh MOVS/ MOVSB/ MOVSW (Move string byte/ string word).......100
3.5.2.Lệnh STOS/ STOSB/ STOSW..................................................................102
3.5.3.Lệnh LODS/ LODSB/ LODSW...............................................................103
3.5.4.Lệnh duyệt chuỗi: SCAS/ SCASB/ SCASW..........................................103
3.5.5.CMPS/ CMPSB/ CMPSW (compane string bytes or string word)...........104
3.5.6.Dạng tổng quát của các lệnh thao tác chuỗi như sau:.........................106
3.6.Toán hạng nguồn...........................................................................................106
CÂU HỎI ÔN TẬP VÀ BÀI TẬP..................................................................................106
Ch¬ng 4.CÁC CẤU TRÚC RẼ NHÁNH CHƯƠNG TRÌNH .....................................109
4.1.Nhóm lệnh nhảy............................................................................................109
4.1.1.Lệnh nhảy không điều kiện JMP..........................................................109
4.1.2.lệnh so sánh CMP....................................................................................110
4.1.3.các lệnh nhảy có điều kiện...................................................................110
4.2.Các cấu trúc lập trình bậc cao......................................................................112
4.2.1.Các cấu trúc rẽ nhánh.............................................................................112
4.2.1.1.Cấu trúc IF_THEN...........................................................................112
4.2.1.2.Cấu trúc IF_THEN_ELSE................................................................113
4.2.1.3.Cấu trúc CASE.................................................................................114
4.2.1.4.Các nhánh với điều kiện kép..........................................................116
4.2.2.Các cấu trúc lặp......................................................................................117

171
GV. Vương Quốc Dũng
4.2.2.1.Lặp biết trước số lần lặp LOOP....................................................117
4.2.2.2.Các lệnh LOOP có điều kiện ........................................................117
Lệnh LOOPE (Loop while equal)............................................................118
Lệnh LOOPZ (Loop while zero)..............................................................118
Lệnh LOOPNE (Loop while not equal)...................................................118
Lệnh LOOPNZ (Loop while not zero).....................................................118
4.2.2.3.Vòng lặp WHILE.............................................................................119
4.2.2.4.Nhóm lệnh vòng lặp REPEAT........................................................119
4.3.Lập trình với các cấu trúc bậc cao...............................................................120
4.4.Kiểu cấu trúc và kiểu bản ghi......................................................................125
4.4.1.Kiểu cấu trúc..........................................................................................125
4.4.2.Kiểu bản ghi...........................................................................................127
4.4.3.Các toán tử:.............................................................................................129
CÂU HỎI VÀ BÀI TẬP...............................................................................................130
Ch¬ng 5.CHƯƠNG TRÌNH CON...........................................................................134
5.1.Chương trình con và khai báo......................................................................134
5.1.1.Dạng NEAR............................................................................................135
5.1.2.Dạng FAR...............................................................................................135
5.1.3.Dạng RET (hoặc RETN | RET) constant ..............................................136
5.2.Cơ chế thực hiện ..........................................................................................136
5.2.1.Dạng 1:....................................................................................................136
5.2.2.Dạng 2:....................................................................................................138
5.3.Lập trình đa đơn thể....................................................................................139
5.3.1.Chương trình đa đơn thể......................................................................139
5.3.2.Các chỉ dẫn cho chương trình đa đơn thể...........................................141
5.3.3.Gộp tập tin..............................................................................................146
CÂU HỎI VÀ BÀI TẬP:..............................................................................................147
Ch¬ng 6.MACRO......................................................................................................151
6.1.Chương trình con và Macro..........................................................................151
6.2.Khai báo (định nghĩa) và tham chiếu đến Maccro......................................152
6.2.1.Khai báo Maccro:....................................................................................152
6.2.2.Tham chiếu đến Maccro........................................................................152
6.3.Mở rộng Macro..............................................................................................154
6.4.Các dẫn hướng (chỉ dẫn) dùng trong MACRO............................................155
6.4.1.Chỉ dẫn LOCAL......................................................................................155
6.4.2.Chỉ dẫn EXITM (Exit Marco):................................................................157
6.4.3.Chỉ dẫn điều kiện..................................................................................158
6.5.Các toán tử dùng trong Macro.......................................................................162
6.5.1.Toán tử <>...............................................................................................162
6.5.2.Toán tử &................................................................................................162
6.5.3.Toán tử !..................................................................................................163
6.5.4.Toán tử %................................................................................................163
6.5.5.Toán tử quan hệ......................................................................................164
CÂU HỎI VÀ BÀI TẬP...............................................................................................165

172

You might also like