Professional Documents
Culture Documents
1
CHƯƠNG IV: CÁC LỆNH LOGIC, DỊCH VÀ QUAY .................................................. 40
4.1 Các lệnh logic........................................................................................................... 40
4.1.1 Các phép toán logic ........................................................................................... 40
4.1.2 Các lệnh logic trong hợp ngữ ............................................................................ 41
4.1.3 Ứng dụng các lệnh logic.................................................................................... 42
4.2 Các lệnh dịch............................................................................................................ 43
4.2.1 Các phép toán dịch ............................................................................................ 43
4.2.2 Các lệnh dịch trong hợp ngữ ............................................................................. 43
4.3 Các lệnh quay ........................................................................................................... 45
4.3.1 Lệnh quay trái (ROL - Rotate Left)................................................................... 45
4.3.2 Lệnh quay phải (ROR - Rotate Right)............................................................... 45
4.3.3 Lệnh quay trái qua cờ CF (RCL – Rotate Carry Left) ...................................... 46
4.3.4 Lệnh quay phải qua cờ CF (RCR – Rotate Carry Right) .................................. 46
BÀI TẬP CHƯƠNG IV................................................................................................. 48
CHƯƠNG V: CÁC LỆNH NHÂN VÀ CHIA .................................................................. 49
5.1 Các lệnh nhân ........................................................................................................... 49
5.1.1 Lệnh MUL (Multiply) ....................................................................................... 49
5.1.2 Lệnh IMUL (Integer Multiply).......................................................................... 50
5.2 Các lệnh chia (Divide) ............................................................................................. 50
5.2.1 Cú pháp lệnh...................................................................................................... 50
5.2.2 Hiện tượng tràn trong phép chia........................................................................ 51
BÀI TẬP CHƯƠNG V .................................................................................................. 53
CHƯƠNG VI: NGĂN XẾP VÀ THỦ TỤC...................................................................... 54
6.1 Các thành phần của chương trình............................................................................. 54
6.2 Cách sử dụng ngăn xếp ............................................................................................ 54
6.2.1 Cất dữ liệu vào ngăn xếp ................................................................................... 54
6.2.2 Lấy dữ liệu khỏi ngăn xếp ................................................................................. 55
6.2.3 Ứng dụng của ngăn xếp..................................................................................... 55
6.2.4 Cách thức làm việc của ngăn xếp ...................................................................... 57
6.3 Thủ tục...................................................................................................................... 60
6.3.1 Cấu trúc thủ tục ................................................................................................. 60
6.3.2 Sử dụng thủ tục.................................................................................................. 60
6.3.3 Quan hệ giữa thủ tục và ngăn xếp ..................................................................... 62
BÀI TẬP CHƯƠNG VI................................................................................................. 64
CHƯƠNG VII: MẢNG VÀ CÁC LỆNH THAO TÁC CHUỖI....................................... 65
7.1 Mảng một chiều (chuỗi) ........................................................................................... 65
7.1.1 Khai báo mảng................................................................................................... 65
7.1.2 Các phần tử của mảng một chiều ...................................................................... 66
7.1.3 Các chế độ địa chỉ.............................................................................................. 67
2
7.2 Các lệnh thao tác với chuỗi ...................................................................................... 69
7.2.1 Lệnh chuyển chuỗi (Moving a String) .............................................................. 69
7.2.2 Lệnh chuyển dữ liệu từ thanh ghi vào chuỗi (Store a String) ........................... 73
7.2.3 Lệnh chuyển dữ liệu từ chuỗi vào thanh ghi (Load a String)............................ 75
BÀI TẬP CHƯƠNG VII ............................................................................................... 77
CHƯƠNG VIII: SỬ DỤNG NGẮT TRONG HỢP NGỮ ................................................ 78
8.1 Khái niệm ngắt ......................................................................................................... 78
8.2 Phân loại ngắt ........................................................................................................... 79
8.2.1 Ngắt mềm .......................................................................................................... 79
8.2.2 Ngắt cứng .......................................................................................................... 79
8.3 Một số ngắt thông dụng............................................................................................ 79
8.3.1 Ngắt 17h – Vào/ra máy in ................................................................................. 80
8.3.2 Ngắt 21h ............................................................................................................ 81
8.4 Bảng vector ngắt....................................................................................................... 85
BÀI TẬP CHƯƠNG VIII .............................................................................................. 87
CHƯƠNG IX: MỘT SỐ VẤN ĐỀ MỞ RỘNG ................................................................ 88
9.1 Số nguyên 32 bit....................................................................................................... 88
9.1.1 Các phép toán Cộng và Trừ............................................................................... 88
9.1.2 Các phép Logic.................................................................................................. 89
9.1.3 Các phép toán Dịch ........................................................................................... 90
9.2 Số thực dấu phảy động ............................................................................................. 91
9.2.1 Biểu diễn số thực dưới dạng nhị phân............................................................... 91
9.2.2 Biểu diễn số thực dấu phảy động ...................................................................... 92
9.3 Bộ vi xử lý 80286..................................................................................................... 93
9.3.1 Các đặc trưng..................................................................................................... 93
9.3.2 Một số lệnh mở rộng của 80286........................................................................ 94
9.4 Các bộ vi xử lý 80386 và 80486 .............................................................................. 95
BÀI TẬP CHƯƠNG IX................................................................................................. 97
PHỤ LỤC 1: CHƯƠNG TRÌNH DEBUG ........................................................................ 98
1. Giới thiệu chương trình .............................................................................................. 98
2. Sử dụng chương trình................................................................................................. 98
3. Các lệnh của DEBUG ................................................................................................ 98
4. Dùng Debug để kiểm soát chương trình .................................................................. 101
PHỤ LỤC 2: BẢNG MÃ ASCII ..................................................................................... 108
TÀI LIỆU THAM KHẢO ............................................................................................... 109
3
CÁC TỪ VIẾT TẮT
4
CHƯƠNG I: CÁC KHÁI NIỆM CƠ BẢN
1.1 Mở đầu
Hợp ngữ (Assembly language) là một ngôn ngữ lập trình cấp thấp, mục đích nhằm giao
tiếp trực tiếp với phần cứng của máy tính.
Máy tính chỉ có khả năng hiểu được các tín hiệu 0, 1 dưới dạng điện hoặc từ, gọi là tín
hiệu nhị phân (ngôn ngữ nhị phân còn được gọi là ngôn ngữ máy). Các lệnh của hợp ngữ
thực chất là dạng kí hiệu của ngôn ngữ máy: hợp ngữ sử dụng các kí hiệu bằng tiếng Anh
để biểu diễn các lệnh ngôn ngữ máy cho dễ nhớ hơn. Ví dụ:
Ta thấy các lệnh máy tính bằng các dãy bít 0, 1 rất dài và khó nhớ, còn lệnh hợp ngữ
thì ngắn gọn hơn nhiều. Khi chạy một chương trình hợp ngữ thì phải dịch nó sang ngôn
ngữ máy.
Học hợp ngữ, không chỉ để học một ngôn ngữ lập trình có tốc độ rất nhanh (hợp ngữ là
ngôn ngữ lập trình có tốc độ nhanh nhất) mà còn để nắm bắt được bản chất bên trong của
máy tính, biết cách tác động trực tiếp vào phần lõi của máy tính.
Trong hợp ngữ, người ta kí hiệu 1 số thập phân bằng chữ D hoặc d ở cuối (viết tắt của
Decimal), cũng có thể không cần viết các chữ đó.
Ví dụ:
(1998)10 được kí hiệu là: 1998D, 1998d, hoặc đơn giản là 1998
5
Hệ nhị phân sử dụng 2 kí hiệu (0,1) để biểu diễn thông tin. Các số trong hệ nhị phân
được biểu diễn dưới dạng tổng các luỹ thừa cơ số 2.
Ví dụ:
Số 1101 trong hệ nhị phân có thể biểu diễn như sau:
(1101)2 = 1×23 + 1×22 + 0×21 + 1×20
= (13)10
Trong hợp ngữ, người ta kí hiệu 1 số nhị phân bằng chữ B hoặc b ở cuối (viết tắt của
Binary).
Ví dụ:
(1101)2 được kí hiệu là: 1101B, hoặc 1101b
Trong hợp ngữ, người ta kí hiệu 1 số thập lục phân bằng chữ H hoặc h ở cuối (viết tắt của
Hexa Decimal).
Ví dụ:
(2B)16 được kí hiệu là: 2BH, hoặc 2Bh
Chú ý: Kí hiệu một số thập lục phân trong chương trình hợp ngữ phải luôn bắt đầu bằng
số. Ví dụ số (FA)16 được kí hiệu là 0FAh (chứ không kí hiệu là FAh).
Hệ thập lục phân (gọi tắt là hệ hex) là hệ đếm được sử dụng nhiều nhất trong hợp ngữ,
do nó có thể biểu diễn những dãy bít nhị phân dài bằng những kí hiệu ngắn gọn, dễ nhớ
hơn.
6
13 2
dư 1 6 2
dư 0 3 2
dư 1 1 2
dư 1 0
Viết các số dư ngược từ dưới lên ta thu được số nhị phân 1101b.
7
Ví dụ 1:
Chuyển đổi 2Ah sang hệ nhị phân.
Tra bảng ta thấy: 2h = 0010b, Ah = 1010b
Vậy 2Ah = 00101010b
Ví dụ 2:
Chuyển đổi 10110110b sang hệ hex.
Đầu tiên ta chia dãy bít nhị phân thành từng nhóm 4 bít, thu được 2 nhóm sau: 0110 và
1011.
Tra bảng ta thấy: 0110b = 6h, 1011b = Bh
Vậy 10110110b = B6h
8
b) Số nguyên có dấu:
Đối với số nguyên có dấu thì khi biểu diễn dưới dạng nhị phân ta phải dành ra 1 bít để
xác định dấu. Đó là bít đầu tiên của dãy (bít nặng nhất - Msb).
Msb = 0: Dấu Dương
Msb = 1: Dấu Âm
Như vậy, nếu chiều dài dãy bít là 8 thì bít đầu tiên để xác định dấu, 7 bít còn lại xác định
giá trị số nguyên?
Ví dụ:
Số +13 được biểu diễn bởi dãy bít 0000 1101.
Vậy số -13 được biểu diễn như thế nào, có phải là dãy bít 1000 1101 hay không?
Nguyên tắc để biểu diễn số âm trong máy tính: phải thoả mãn điều kiện sau
Số Âm (nhị phân) + Số Dương (nhị phân) = 0
Giả sử số -13 được biểu diễn bởi dãy bít 1000 1101, ta đem nó cộng với dãy bít biểu diễn
số +13 để kiểm tra:
0000 1101
+ 1000 1101
1001 1010 ≠ 0
Ta thấy tổng thu được khác 0, như vậy đây không phải là dãy bít cần tìm
Quy tắc tìm số đối: Cho 1 số nguyên A. Giả sử đã biết dãy bít biểu diễn A, khi đó muốn
tìm dãy bít biểu diễn số -A ta làm như sau:
Bước 1: Tìm số bù 1 của A bằng cách đảo tất cả các bít.
Ví dụ: A = 0000 1101
Khi đó số bù 1 của A là 1111 0010
Bước 2: Tìm số bù 2 (bằng cách lấy số bù 1 cộng với 1)
1111 0010
+ 1
1111 0011
Số bù 2 tìm được (1111 0011) chính là dãy bít biểu diễn số -A.
9
Bài toán ngược:
Cho một dãy bít nhị phân sau đây (16 bit):
1110 0111 0001 1000b
Hãy xác định xem nó biểu diễn số nguyên nào?
Giải:
Gọi số nguyên đó là N.
Có 2 trường hợp xảy ra:
• Nếu đây là số nguyên không dấu:
N = 1×215 + 1×214 + 1×213 + 1×210 + 1× 29 + 1×28 + 1×24 + 1×23
= 32768 + 16384 +8192 + 1024 + 512 + 256 + 16 + 8
= 59160
• Nếu đây là số nguyên có dấu:
Vì Msb = 1 nên N là số âm. Để đơn giản ta sẽ xác định –N (số dương) trước, từ đó suy ra
N.
Tìm –N băng cách tìm số bù 2 của N
Bước 1: đảo bít
Số bù 1 = 0001 1000 1110 0111
Bước 2: đem cộng với 1
0001 1000 1110 0111
+ 1
0001 1000 1110 1000
Ta tìm được –N = 0001 1000 1110 1000b
= 1×212 + 1×211 + 1× 27 + 1×26 + 1×25 + 1×23
= 4096 + 2048 + 128 + 64 + 32 + 8
= 6376
Vậy N = -6376
10
Ví dụ:
Hiện nay có rất nhiều bảng mã khác nhau được sử dụng, phổ biến nhất là mã Unicode,
với độ dài dãy bít có thể thay đổi khá mềm dẻo, và nó vẫn giữ được sự tương thích với mã
ASCII truyền thống.
Nhập Xử lý Xuất
Lưu trữ
Trong đó:
• Các khối Nhập/Xuất dữ liệu: bao gồm các thiết bị như bàn phím, chuột, màn
hình...
• Khối lưu trữ: bao gồm các ổ đĩa cứng, đĩa mềm...
• Khối xử lý: đây là phần quan trọng nhất của một máy tính, ta sẽ tập trung xem xét
nó.
11
Một hệ thống xử lý của máy tính bao gồm 3 bộ phận chính:
+ Bộ vi xử lý – CPU (Central Processing Unit): Là bộ não của máy tính, nó xử lý các
thông tin và điều khiển mọi hoạt động của máy tính.
+ Bộ nhớ trong: Là bộ nhớ có khả năng liên lạc trực tiếp với bộ vi xử lý,là nơi lưu trữ dữ
liệu phục vụ cho quá trình xử lý.
+ Các mạch vào ra: Để điều khiển việc giao tiếp với thiết bị ngoại vi.
Ngoài ra còn có hệ thống các dây dẫn, cáp nối để liên kết giữa 3 bộ phận trên (hệ thống
Bus)
Bus
1.3.2 Bộ vi xử lý - CPU
Đây là trung tâm xử lý của máy tính. Mỗi bộ vi xử lý được trang bị một hệ thống các
lệnh cơ bản bằng ngôn ngữ nhị phân (do nhà sản xuất quy định). Người lập trình có thể sử
dụng các lệnh này “ra lệnh” cho máy tính thực hiện điều mình muốn.
a) Các thành phần chính của bộ vi xử lý:
• ALU (Arithmetic & Logic Unit): Khối số học và logic. Đây là nơi thực hiện các
phép tính số học (cộng, trừ, nhân, chia...) và các phép logic (Not, And, Or...).
• Các thanh ghi: Cung cấp khả năng nhớ bên trong CPU. Mỗi thanh ghi có khả năng
chứa được một dãy các bít dữ liệu (độ dài còn phụ thuộc vao từng loại CPU).
• Hệ thống nối ghép bên trong CPU (Bus nội bộ): Cho phép liên lạc giữa các bộ
phận bên trong CPU.
b) Họ vi xử lý Intel x86
Có nhiều hãng khác nhau sản xuất bộ vi xử lý cho máy tính. Ta sẽ tập trung nghiên
cứu các bộ vi xử lý thuộc dòng x86 do hãng Intel chế tạo.
Bộ vi xử lý đầu tiên thuộc dòng này là 8086. Nó được sản xuất năm 1978, là bộ vi xử
lý 16 bit đầu tiên của Intel. 8088 ra đời sau 8086 một chút, về cơ bản nó cũng có cấu tạo
giống như 8086, nhưng có giá thành rẻ hơn vì chỉ có bus dữ liệu 8 bít, và tốc độ cũng thấp
hơn. Tiếp sau đó là sự ra đời của các bộ vi xử lý 80186, 80286, 80386, 80486, 80586
(Pentium), PII, PIII, P4...Càng về sau thì các bộ vi xử lý lại càng trở nên mạnh mẽ hơn
với độ dài các thanh ghi dài hơn, tốc độ đồng hồ cao hơn, bề rộng bus lớn hơn...
c) Bộ vi xử lý 8086
12
Ta chọn 8086 để nghiên cứu vì nó có cấu trúc đơn giản, dễ tìm hiểu. Và điều quan
trọng là hầu hết các lệnh của nó đều được các bộ vi xử lý sau này kế thừa, nên các chương
trình viết cho 8086 vẫn có thể áp dụng trên các bộ vi xử lý hiện đại hơn. Ta sẽ xem xét 14
thanh ghi cơ bản của 8086, mỗi thanh ghi có chiều dài 16 bit.
• Nhóm các thanh ghi dữ liệu (Thanh ghi công dụng chung):
Các thanh ghi này được sử dụng khi thực hiện thao tác với dữ liệu. Có 4 thanh ghi 16
bit được đặt tên là AX, BX, CX, DX. Mỗi thanh ghi lại được chia làm 2 thanh ghi 8 bit
với các tên tương ứng như trên hình vẽ:
AX AH AL
BX BH BL
CX CH CL
DX DH DL
CS
DS
SS
ES
13
SI
DI
SP
BP
IP
(Các khái niệm về địa chỉ segment và offset sẽ nói kỹ hơn ở phần sau)
1111...1111111 FFFFFh
… ... … …
0000...0000011 Địa chỉ dạng hex: 00003h
0000...0000010 00002h
0000...0000001 00001h
0000...0000000 1 ô nhớ 00000h 1 ô nhớ
(20 bít)
14
Các thanh ghi của bộ vi xử lý 8086 chỉ dài 16 bít, chúng không thể chứa được địa chỉ
dài 20 bít. Do đó người ta phải sử dụng một phương pháp đánh địa chỉ khác, gọi là địa chỉ
logic.
10A20h
+ 34B4h
13ED4h
15
F0000h ROM BIOS
E0000h
⎫
D0000h ⎬ Để dành
⎭
C0000h
B0000h ⎫
⎬ Bộ nhớ hiển thị
A0000h ⎭
90000h ⎫
⎪
... ⎪⎪
⎬ Bộ nhớ cơ sở: 10 đoạn (640 KB)
20000h ⎪
10000h ⎪
⎪⎭
00000h 64 KB
Trong đó, 10 đoạn đầu tiên (Bộ nhớ cơ sở - Base memory) dùng để nạp và chạy các
ứng dụng của hệ điều hành DOS. Hai đoạn địa chỉ tiếp theo dành cho RAM trên vỉ mạch
màn hình. Đoạn cuối cùng dành cho ROM BIOS.
BIOS (Base Input – Output System) là một phần mềm điều khiển các thao tác vào ra cơ
bản của máy tính. Nó được nhà sản xuất cài sẵn trong ROM và tự động thi hành khi bật
máy tính.
16
BÀI TẬP CHƯƠNG I
17
CHƯƠNG II: CÁC LỆNH CƠ BẢN CỦA HỢP NGỮ
2.1.1 Ví dụ
Để có cái nhìn tổng quát về một chương trình hợp ngữ, ta xét ví dụ sau đây:
TITLE VI DU 1
.MODEL SMALL
.STACK 100H
.DATA
A DB 4
B DB 6
C DB ?
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
MOV AL, A
ADD AL, B
MOV C, AL
18
Trong hợp ngữ có nhiều nội dung cần phải khái báo như kiểu bộ nhớ, ngăn xếp, biến,
hằng...
• Khai báo kiểu bộ nhớ
Kiểu bộ nhớ được viết sau từ .MODEL. Kiểu bộ nhớ sẽ quy định kích thước của đoạn
mã và dữ liệu trong chương trình.
Trong chương trình ở phần 2.1.1, kiểu bộ nhớ là SMALL, nghĩa là kiểu bộ nhớ nhỏ, mã
lệnh sẽ nằm trong 1 đoạn nhớ, dữ liệu nằm trong 1 đoạn nhớ. Ngoài kiểu SMALL còn có
nhiều kiểu bộ nhớ khác:
MEDIUM Mã lệnh chiếm nhiều hơn 1 đoạn
Dữ liệu trong 1 đoạn
COMPACT Mã lệnh trong 1 đoạn
Dữ liệu chiếm nhiều hơn 1 đoạn
LARGE Mã lệnh chiếm nhiều hơn 1 đoạn
Dữ liệu chiếm nhiều hơn 1 đoạn
Không có mảng nào lớn hơn 64 KB
HUGE Mã lệnh chiếm nhiều hơn 1 đoạn
Dữ liệu chiếm nhiều hơn 1 đoạn
Các mảng có thể lớn hơn 64 KB
19
+ Có thể chứa chữ cái, chữ số và các kí tự đặc biệt (? . @ _ $ %).
+ Không được bắt đầu bằng số.
+ Nếu dùng dấu chấm thì nó phải đứng đầu tiên.
+ Tên không được chứa dấu cách.
Nói chung, trong hợp ngữ không phân biệt chữ hoa và chữ thường.
c) Phần mã lệnh
Phần này bao gồm các thủ tục được viết sau từ .CODE. Trong số các thủ tục này phải
chọn một thủ tục làm chương trình chính, tên của thủ tục đó được viết sau từ END ở cuối
chương trình. Tên chương trình chính thường đặt là MAIN, cũng có thể chọn một tên
khác.
• Cấu trúc một thủ tục:
<Tên thủ tục> PROC
Lệnh 1
Lệnh 2
Lệnh 3
...
<Tên thủ tục> ENDP
20
Ngoài ra có thể viết thêm các lời chú thích để làm rõ ý nghĩa câu lệnh. Lời chú thích
được bắt đầu bằng dấu chấm phảy (;).
Ví du:
MOV DS, AX ;Chuyển nội dung của thanh ghi AX vào thanh ghi DS
MOV là tên lệnh.
DS và AX là các toán hạng.
• Lưu ý: Các lệnh được trình bày trong tài liệu này hầu hết thuộc tập lệnh của bộ vi xử
lý 8086, trừ một số trường hợp sẽ có chú thích riêng.
Chú ý:
• Không được chuyển trực tiếp nội dung của hai thanh ghi đoạn cho nhau.
Ví dụ: MOV CS, DS
Lệnh trên bị sai do cả CS và DS đều là thanh ghi đoạn. Muốn thực hiện được điều này
thì phải sử dụng một biến hay một thanh ghi khác làm trung gian:
Ví dụ: MOV AX, DS ; dùng AX làm trung gian
MOV CS, AX
• Không được chuyển trực tiếp nội dung của hai biến cho nhau.
Ví dụ: MOV A, B Lệnh này không thực hiện được
• Không được chuyển trực tiếp một hằng số vào một thanh ghi đoạn.
Ví dụ: MOV DS, 2000h
Nói chung, muốn khắc phục các trường hợp nêu trên trên thì ta đều phải sử dụng trung
gian.
21
b) Lệnh XCHG (Exchange)
Lệnh này dùng để hoán đổi dữ liệu dữ liệu giữa hai toán hạng.
Cú pháp lệnh:
XCHG <Toán hạng 1>, <Toán hạng 2>
Các toán hạng có thể là thanh ghi công dụng chung, hoặc một thanh ghi công dụng chung
và một ô nhớ.
Ví dụ 1:
XCHG AX, BX ;hoán đổi nội dung của AX và BX
22
Lệnh này có tác dụng đổi dấu toán hạng Đích.
Ví dụ:
NEG AL
Vào: AH = 2
DL = Mã ASCII của kí tự cần hiển thị
Ra: AL chứa mã ASCII của kí tự hiển thị
23
MOV AH, 2
MOV DL, ‘M’
INT 21h
Sau đây là chương trình đầy đủ:
TITLE HIEN KI TU
.MODEL SMALL
.STACK 100H
.CODE
MAIN PROC
MOV AH, 2 ;Chức năng số 2
MOV DL, ’M’ ;Kí tự cần hiển thị
INT 21h ;Gọi ngắt
MAIN ENDP
END MAIN
Ta có thể sử dụng mã ASCII của kí tự hoặc viết trực tiếp kí tự giữa 2 dấu nháy đơn như
trong chương trình trên (nói chung, các kí tự và chuỗi kí tự trong hợp ngữ phải có dấu
nháy đơn ở 2 đầu).
Ngoài chức năng số 2, trong chương trình trên còn sử dụng chức năng số 4Ch của ngắt
21h. Chức năng này có tác dụng kết thúc chương trình và trả lại quyền điều khiển cho hệ
điều hành DOS.
Vào: AH = 9
DX = Địa chỉ offset của vùng nhớ chứa chuỗi kí
tự
Ví dụ:
Hiện ra màn hình dòng chữ: KHOA CONG NGHE THONG TIN
Giải:
TITLE HIEN CHUOI KT
.MODEL SMALL
.STACK 100H
24
.DATA
ChuoiKT DB ’KHOA CONG NGHE THONG TIN$’
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
MAIN ENDP
END MAIN
Vào: AH = 1
Ra: AL chứa mã ASCII của kí tự
25
INT 21h
Khi gặp các lệnh trên, chương trình sẽ dừng lại chờ ta gõ một kí tự từ bàn phím, mã
ASCII của kí tự đó sẽ được cất trong thanh ghi AL.
Ví dụ:
Nhập một kí tự thường từ bàn phím, đổi nó thành kí tự in hoa rồi hiện ra màn hình.
Giải:
TITLE DOI KI TU
.MODEL SMALL
.STACK 100H
.CODE
MAIN PROC
MOV AH, 1 ;Chức năng số 1: Nhập một kí tự
INT 21h
MAIN ENDP
END MAIN
Giải thích: Mã ASCII của kí tự thường lớn hơn kí tự in hoa tương ứng là 20h. Muốn
chuyển từ kí tự thường thành in hoa thì chỉ việc lấy mã ASCII của nó trừ đi 20h.
26
Ví dụ: tên file là Baitap.asm
• Bước 2:
Dịch file ASM thành file OBJ bằng công cụ MASM.EXE. Gõ lệnh như sau:
MASM Baitap; (Enter)
(file MASM.EXE và file Baitap.asm nên để cùng một thư mục)
Nếu dịch thành công (chương trình không có lỗi) thì ta sẽ thu được file Baitap.obj.
Nếu chương trình bị lỗi thì phải sửa, sau đó tiến hành dịch lại.
• Bước 3:
Sử dụng công cụ LINK.EXE để liên kết các file OBJ thu được ở bước 2 thành file thi
hành được (EXE). Vì trong ví dụ này chỉ có 1 file OBJ nên cách gõ lệnh như sau:
LINK Baitap; (Enter)
(file LINK.EXE cũng ở cùng thư mục nói trên)
Ta sẽ thu được file Baitap.exe. Để chạy file này chỉ việc gõ lệnh:
Baitap (Enter)
27
BÀI TẬP CHƯƠNG II
28
CHƯƠNG III: CÁC LỆNH NHẢY
Ta thấy bộ vi xử lý 8086 mới sử dụng 9 bít của thanh ghi cờ, sau đây là tên và chức
năng của các cờ:
Bit Tên cờ Kí hiệu
0 Cờ nhớ (Carry Flag) CF
2 Cờ chẵn lẻ (Parity Flag) PF
4 Cờ nhớ phụ (Auxiliary Flag) AF
6 Cờ Zero (Zero Flag) ZF
7 Cờ dấu (Sign Flag) SF
11 Cờ tràn (OverFlow Flag) OF
8 Cờ bẫy (Trap Flag) TF
9 Cờ ngắt (Interrupt Flag) IF
10 Cờ định hướng (Direction Flag) DF
Các cờ chia làm hai nhóm khác nhau: Nhóm cờ trạng thái (gồm 6 cờ: CF, PF, AF, ZF,
SF, OF) và nhóm cờ điều khiển (gồm 3 cờ: TF, IF, DF). Trong chương này ta sẽ tập trung
tìm hiểu các cờ trạng thái, còn các cờ điều khiển sẽ trình bày sau.
Mỗi khi môt lệnh trong chương trình được thực hiện thì trạng thái của bộ vi xử lý lại
thay đổi, sự thay đổi này được phản ánh trong các cờ trạng thái. Để hiểu rõ hơn vấn đề
này ta sẽ xem xét một vài cờ trạng thái hay dùng nhất.
3.1.1 Cờ nhớ CF
Ví dụ: xét các lệnh sau đây:
MOV AX, 0FFFFh
ADD AX, 1
Trước khi thực hiện lệnh ADD thì AX = FFFFh = 1111 1111 1111 1111b = 65535
Sau khi thực hiện phép cộng với 1 thì AX bằng bao nhiêu?
1111 1111 1111 1111b
+ 1
1 0000 0000 0000 0000b
29
Thanh ghi AX dài 16 bit nên sau lệnh ADD thì AX = 0! Phép cộng đã không còn chính
xác do kết quả vượt quá phạm vi chứa của AX (gọi là hiện tượng tràn khi cộng số không
dấu). Khi đó cờ CF được thiết lập bằng 1.
Như vậy, cờ CF sẽ được thiết lập khi thực hiện phép cộng có nhớ ở bít Msb hoặc khi
thực hiện phép trừ có vay ở bít Msb.
3.1.2 Cờ Zero ZF
Ví dụ: xét các lệnh sau đây:
MOV CX, 2Ah
SUB CX, 2Ah
Sau khi thực hiện lệnh SUB thì CX = 0, cờ ZF được thiết lập bằng 1.
Như vậy, cờ ZF sẽ được thiết lập khi kết quả của lệnh vừa thực hiện bằng 0.
3.1.3 Cờ tràn OF
Ví dụ: xét các lệnh sau đây:
MOV AX, 7FFFh
ADD AX, 7FFFh
Trước khi thực hiện lệnh ADD thì AX = 7FFFh = 0111 1111 1111 1111b = 32767
Sau khi thực hiện phép cộng thì AX bằng bao nhiêu?
0111 1111 1111 1111b
+ 0111 1111 1111 1111b
1111 1111 1111 1110b
Sau lệnh ADD thì AX = FFFEh. Nếu coi đây là số không dấu thì AX = 65534, không
có hiện tượng tràn, cờ CF = 0. Nhưng nếu coi đây là số có dấu thì AX = -2 (32767 +
32767 = -2!), phép cộng đã không còn chính xác do kết quả vượt quá phạm vi chứa của
AX (gọi là hiện tượng tràn khi cộng số có dấu). Khi đó cờ OF được thiết lập bằng 1.
Như vậy, cờ OF sẽ được thiết lập khi xuất hiện hiện tượng tràn trong phép tính với số
có dấu.
3.2.1 Ví dụ
Xét chương trình hợp ngữ sau:
TITLE Lenh nhay
.MODEL SMALL
.STACK 100H
.CODE
30
MAIN PROC
NHAPLAI:
MOV AH, 1 ;Chức năng số 1: Nhập 1 kí tự
INT 21h
MAIN ENDP
END MAIN
Giải thích:
Chương trình trên sẽ nhập một kí tự từ bàn phím, kiểm tra xem đó có phải là kí tự
khoảng trống ‘ ’ hay không, nếu đúng thì tiến hành nhập lại. Quá trình đó được thực hiện
nhờ lệnh so sánh CMP và lệnh nhảy JZ.
b) Lệnh nhảy JZ
Lệnh JZ là lệnh nhảy khi cờ ZF = 1 (Jump if Zero).
Cú pháp lệnh:
JZ <Nhãn>
Trong chương trình trên, lệnh JZ sẽ kiểm tra cờ ZF, nếu ZF = 1 thì sẽ nhảy tới nhãn
NHAPLAI, nghĩa là thực hiện lại các lệnh nhập dữ liệu.
31
Lệnh Chức năng Điều kiện nhảy
JC Nhảy nếu có nhớ (Jump if Carry) CF = 1
JNC Nhảy nếu không nhớ (Jump if Not Carry) CF = 0
JO Nhảy nếu tràn có dấu (Jump if OverFlow) OF = 1
JNO Nhảy nếu không tràn (Jump if Not OverFlow) OF = 0
JS Nhảy nếu dấu âm (Jump if Sign) SF = 1
JNS Nhảy nếu dấu dương (Jump if Not Sign) SF = 0
... ... ...
Việc kiểm tra trạng thái của các cờ khi sử dụng lệnh nhảy gây rất nhiều khó khăn cho
người lập trình (do có quá nhiều lệnh nhảy, khó nhớ, không hợp với tư duy thông
thường...). Để khắc phục điều này, người ta thường sử dụng các lệnh nhảy kèm với lệnh
CMP theo quy tắc sau:
CMP <Đích>, <Nguồn>
Điều kiện nhảy Lệnh nhảy không dấu Lệnh nhảy có dấu
Đích > Nguồn JA/ JNBE JG/ JNLE
Đích < Nguồn JB/ JNAE JL/ JNGE
Đích = Nguồn JE/ JZ JE/ JZ
Đích ≥ Nguồn JAE/ JNB JGE/ JNL
Đích ≤ Nguồn JBE/ JNA JLE/ JNG
Đích ≠ Nguồn JNE/ JNZ JNE/ JNZ
Một số từ viết tắt:
A: Above (lớn hơn) = G: Greater than
B: Below (nhỏ hơn) = L: Less than
E: Equal (bằng)
N: Not (không)
Ví dụ:
JNA: Jump if Not Above = JBE: Jump if Below - Equal
Giải thích:
Trước mỗi lệnh nhảy cần dùng một lệnh CMP để tạo điều kiện nhảy. Người lập trình sẽ
căn cứ vào quan hệ giữa <Đích> và <Nguồn> để lựa chọn lệnh nhảy thích hợp.
Ví dụ:
CMP AL, 5Ah
JA KetThuc ;Nếu AL > 5Ah thì nhảy tới nhãn KetThuc
32
Nếu Đích > Nguồn: Ta có thể sử dụng lệnh nhảy JA hoặc JNBE (trong trường hợp
Đích và Nguồn là số không dấu). Hai lệnh này có tác dụng giống hệt nhau. Nếu coi đích
và nguồn là các số có dấu thì phải sử dụng lệnh JG hoặc JNLE.
KetThuc:
MOV AH, 4Ch
INT 21h
Đoạn lệnh trên chỉ thực hiện được khi khoảng cách giữa lệnh JA và vị trí đặt nhãn
KetThuc không quá 127 byte. Tuy nhiên, nếu khoảng cách đó vượt quá giới hạn cho phép
thì ta có thể khắc phục bằng phương pháp “nhảy hai bước” nhờ lệnh JMP (đầu tiên nhảy
tới một “Nhãn trung gian”, sau đó mới nhảy tới nhãn KetThuc):
NhanTrungGian:
33
JMP KetThuc
TiepTuc:
... ;Các lệnh khác
KetThuc:
MOV AH, 4Ch
INT 21h
<Nhãn>
Ví dụ:
Nhập một kí tự từ bàn phím, nếu là kí tự in thường thì đổi sang in hoa. Hiện kí tự ra
màn hình.
Thuật toán như sau:
• Nhập một kí tự KT
• IF (KT ≤ ‘z’) AND (KT ≥ ‘a’) THEN Đổi KT sang in hoa
• Hiện KT ra màn hình
Giải:
TITLE DOI KI TU
.MODEL SMALL
.STACK 100H
.CODE
MAIN PROC
MOV AH, 1 ;Nhập một kí tự
INT 21h
34
JA HienChu ;Nếu AL > ’z’ thì hiện kí tự ra màn hình
CMP AL, ’a’
JB HienChu ;Nếu AL < ’a’ thì hiện kí tự ra màn hình
HienChu:
MOV AH, 2 ;Chức năng số 2: Hiện kí tự
MOV DL, AL
INT 21h
MAIN ENDP
END MAIN
<Lệnh nhảy>
Ví dụ:
Nhập một kí tự số từ bàn phím (‘0’, ‘1’, ... , ‘9’), đổi nó sang số thập phân tương ứng.
Thuật toán như sau:
REPEAT
Nhập một kí tự KT
UNTIL (KT ≥ ‘0’) AND (KT ≤ ‘9’)
Đổi KT sang số thập phân
Giải:
TITLE VI DU LAP
.MODEL SMALL
.STACK 100H
.CODE
MAIN PROC
NhapLai:
35
MOV AH, 1 ;Nhập một kí tự
INT 21h
MAIN ENDP
END MAIN
Giải thích:
Kí tự ‘0’ có mã ASCII bằng 30h
Kí tự ‘1’ có mã ASCII bằng 31h
...
Kí tự ‘9’ có mã ASCII bằng 39h
Để đổi kí tự số sang số thập phân tương ứng, ta lấy mã ASCII của nó đem trừ cho 30h.
Giải:
TITLE VI DU LAP FOR
.MODEL SMALL
.STACK 100H
.DATA
I DB 0 ;Khởi tạo giá trị biến I bằng 0
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
36
Lap:
MOV DL, I
ADD DL,30h ;Đổi số nguyên sang kí tự số tương ứng
MOV AH, 2 ;Chức năng số 2: Hiện kí tự
INT 21h
MAIN ENDP
END MAIN
37
ADD DL,30h ;Đổi số nguyên sang kí tự số tương ứng
MOV AH, 2 ;Chức năng số 2: Hiện kí tự
INT 21h
INC I ;Tăng biến I lên 1
LOOP Lap
MAIN ENDP
END MAIN
38
BÀI TẬP CHƯƠNG III
1. Cờ CF bằng bao nhiêu khi thi hành các lệnh sau đây:
MOV BX,2
ADD BX,0FFFEh
2. Cờ CF bằng bao nhiêu khi thi hành các lệnh sau đây:
MOV CX,0FFFAh
ADD CX,5
3. Cờ ZF bằng bao nhiêu khi thi hành các lệnh sau đây:
MOV CX,100
CMP CX,100
4. Lệnh nhảy JAE được sử dụng trong điều kiện nào?
5. Lệnh nhảy JO được sử dụng trong điều kiện nào?
39
CHƯƠNG IV: CÁC LỆNH LOGIC, DỊCH VÀ QUAY
40
A NOT A
0 1
1 0
Ví dụ:
Cho M = 16h, hãy tính NOT M = ?
Giải:
M = 0001 0110b (16h)
NOT M = 1110 1001b = E9h
41
NOT BX
Ví dụ 2:
Hãy xoá bit LSB trong thanh ghi BH.
AND BH, 0FEh
Ví dụ 3:
Nhập một kí tự số từ bàn phím (‘0’, ‘1’, ... , ‘9’), đổi nó sang số thập phân tương ứng.
Giải:
Ta sẽ sử dụng các lệnh logic để chuyển đổi kí tự sang số.
TITLE VI DU 3
.MODEL SMALL
.STACK 100H
.CODE
MAIN PROC
NhapLai:
MOV AH, 1 ;Nhập một kí tự
INT 21h
42
MAIN ENDP
END MAIN
43
Ví dụ 2:
MOV CL, 3
SHL AX, CL ;Dịch các bít của thanh ghi AX sang trái 3 lần
• Ứng dụng của lệnh dịch trái:
Một trong số các ứng dụng của lệnh dịch trái là thực hiện phép nhân với 2. Xét ví dụ
sau đây:
Giả sử AH = 0001 1010b = 1×24 + 1×23 + 1×21 = 26
Sau khi dịch trái 1 lần thì AH = 0011 0100b = 1×25 + 1×24 + 1×22
= 2×(1×24 + 1×23 + 1×21 ) = 52
Như vậy, phép dịch trái 1 lần tương đương phép nhân toán hạng đích với 2.
Tổng quát: Phép dịch trái N lần tương đương phép nhân toán hạng đích với 2N.
Chú ý: Kết luận trên chỉ đúng khi không có hiện tượng tràn xảy ra.
Ví dụ:
Giả sử AH = 1000 0001b = 129
Sau khi dịch trái 1 lần thì AH = 0000 0010b = 2: Không phải là phép nhân với 2.
Bít Msb của AH được chuyển vào cờ CF: cờ CF = 1 báo hiệu hiện tượng tràn xảy ra, kết
quả không còn đúng nữa.
44
Như vậy, phép dịch phải 1 lần tương đương phép chia toán hạng đích cho 2.
Tổng quát: Phép dịch phải N lần tương đương phép chia toán hạng đích cho 2N.
Chú ý: Kết luận trên chỉ đúng nếu không làm thay đổi bít dấu khi dịch phải.
Ví dụ:
Giả sử AL = 1000 0001b = -127
Sau khi dịch phải 1 lần thì AL = 0100 0000b = 64: Không phải là phép chia cho 2.
Do đó, đối với các số có dấu ta không được sử dụng lệnh SHR mà phải sử dụng lệnh
SAR. Lệnh SAR sẽ giữ nguyên bít dấu khi dịch phải.
Ví dụ:
Ban đầu AL = 1000 0001b = -127
Sau lệnh SAR AL, 1 thì AL = 1100 0000b = -64 (bít dấu vẫn được giữ nguyên).
CF
Cú pháp lệnh:
- Dạng 1: ROL <Đích>, 1 ;Quay trái một lần
- Dạng 2: ROL <Đích>, CL ;Quay trái nhiều lần, CL chứa số lần quay
<Đích>: là một thanh ghi hay một ô nhớ
CF
45
Cú pháp lệnh:
- Dạng 1: ROR <Đích>, 1 ;Quay phải một lần
- Dạng 2: ROR <Đích>, CL ;Quay phải nhiều lần, CL chứa số lần quay
<Đích>: là một thanh ghi hay một ô nhớ
CF
Cú pháp lệnh:
- Dạng 1: RCL <Đích>, 1 ;Quay một lần
- Dạng 2: RCL <Đích>, CL ;Quay nhiều lần, CL chứa số lần quay
<Đích>: là một thanh ghi hay một ô nhớ
CF
Cú pháp lệnh:
- Dạng 1: RCR <Đích>, 1 ;Quay một lần
- Dạng 2: RCR <Đích>, CL ;Quay nhiều lần, CL chứa số lần quay
<Đích>: là một thanh ghi hay một ô nhớ
46
Ví dụ: Đếm số bít 1 trong thanh ghi AX
Giải:
Ta sẽ thực hiện lệnh quay AX 16 lần (quay trái hay quay phải đều được). Mỗi lần quay
thì một bít sẽ được đưa vào cờ CF, nếu bít đó bằng 1 thì tăng BX lên 1 (BX chứa giá trị
đếm được). Sau 16 lần quay thì thanh ghi AX sẽ trở lại giá trị ban đầu.
Lap:
ROL AX, 1 ;Quay trái AX 1 lần
JNC TiepTuc ;Nếu CF = 0 (gặp bít 0) thì nhảy
INC BX ;Nếu gặp bít 1 thì tăng BX
TiepTuc:
LOOP Lap
47
BÀI TẬP CHƯƠNG IV
1. Câu lệnh sau đây có tác động gì tới toán hạng đích:
XOR BX, 8000h
2. Câu lệnh sau đây có tác động gì tới toán hạng đích:
AND DX, 7FFFh
3. Câu lệnh sau đây có tác động gì tới toán hạng đích:
OR BX, 8000h
4. Nội dung thanh ghi BX bằng bao nhiêu sau khi thực hiện các lệnh sau đây:
MOV BX,12
MOV CL,2
SHL BX,CL
5. Nội dung thanh ghi AX bằng bao nhiêu sau khi thực hiện các lệnh sau đây:
MOV AX,24
MOV CL,3
SHR AX,CL
48
CHƯƠNG V: CÁC LỆNH NHÂN VÀ CHIA
Ví dụ:
Hãy thực hiện phép nhân hai số: 51 và 5
Giải:
Cách 1:
MOV AL, 51
MOV BL, 5
MUL BL
Kết quả: Tích = AX = 255 = 00FFh (16 bit)
Cách 2:
MOV AX, 51
MOV BX, 5
MUL BX
Kết quả: Tích = DX:AX = 255 = 0000 00FFh (32 bit)
Nhận xét:
Cả hai cách trên đều cho cùng một kết quả: Tích = 255 (8 bit: 1111 1111b). Tuy nhiên
cách 1 vẫn phải dùng một thanh ghi 16 bít để chứa kết quả này. Cách 2 quá lãng phí tài
nguyên vì nó phải dùng tới 2 thanh ghi 16 bít để chứa một giá trị dài 8 bít!
Vấn đề đặt ra là phải xác định được độ dài của kết quả phép nhân nhằm tránh sự lãng
phí tài nguyên trong các thao tác tiếp theo. Việc này được thực hiện bằng cách kiểm tra
các cờ CF và OF:
• Trường hợp 1: <Thừa số 1> dài 8 bít:
+ Nếu sau phép nhân 2 cờ CF/OF = 0: Tích được chứa trong AL (8 bít)
49
+ Nếu sau phép nhân 2 cờ CF/OF = 1: Tích được chứa trong AX (16 bít)
• Trường hợp 2: <Thừa số 1> dài 16 bít:
+ Nếu sau phép nhân 2 cờ CF/OF = 0: Tích được chứa trong AX (16 bít)
+ Nếu sau phép nhân 2 cờ CF/OF = 1: Tích được chứa trong DX:AX (32 bít)
+ Nếu <Số chia> có kích thước 1 byte thì Số bị chia sẽ được chứa trong AX (2 byte).
Kết quả: Thương số chứa trong AL, Số dư chứa trong AH.
50
+ Nếu <Số chia> có kích thước 2 byte thì Số bị chia sẽ được chứa trong DX:AX (4 byte).
Kết quả: Thương số chứa trong AX, Số dư chứa trong DX.
Ví dụ 1:
Hãy thực hiện phép chia 65 cho 2.
Giải:
MOV AX, 65
MOV BL, 2
DIV BL
Kết quả: Thương = AL = 32
Số dư = AH = 1
Ví dụ 2:
Hãy thực hiện phép chia -128 cho 4.
Giải:
MOV AX, -128
MOV BL, 4
IDIV BL
Kết quả: Thương = AL = -32
Số dư = AH = 0
Ví dụ 3:
Hãy thực hiện phép chia -1024 cho 256.
Giải:
Vì số chia bằng 256 (là một số 16 bit) nên số bị chia phải chứa trong DX:AX, tức là
DX:AX = -1024.
Nhưng -1024 có thể chứa trọn vẹn trong thanh ghi AX (16 bit), muốn chuyển nó thành
số 32 bit cần sử dụng lệnh CWD (Convert Word to Double Word). Lệnh này sẽ chuyển
dữ lệu có dấu dạng Word trong AX thành dữ liệu có dấu dài 2 Word trong DX:AX.
MOV AX, -1024
CWD
MOV BX, 256
IDIV BX
Kết quả: Thương = AX = -4
Số dư = DX = 0
51
Giải:
MOV AX, 512
MOV BL, 2
DIV BL
Kết quả phép chia bằng 256, không thể chứa trong thanh ghi AL: Hiện tượng tràn xảy
ra. Khi đó hệ thống sẽ đưa ra thông báo: “Divide OverFlow”.
52
BÀI TẬP CHƯƠNG V
1. Nội dung thanh ghi AL bằng bao nhiêu sau khi thực hiện các lệnh sau đây:
MOV AL,64
MOV BL,2h
MUL BL
2. Nội dung cặp thanh ghi DX:AX bằng bao nhiêu sau khi thực hiện các lệnh sau đây:
MOV AX,512
MOV CX,2
MUL CX
3. Nội dung thanh ghi AX bằng bao nhiêu sau khi thực hiện các lệnh sau đây:
MOV AX,513
MOV DX,0
MOV BX,5
DIV BX
4. Nội dung thanh ghi DX bằng bao nhiêu sau khi thực hiện các lệnh sau đây:
MOV AX,513
MOV DX,0
MOV BX,5
DIV BX
5. Nội dung thanh ghi AH bằng bao nhiêu sau khi thực hiện các lệnh sau đây:
MOV AX,1
MOV BX,2
DIV BL
53
CHƯƠNG VI: NGĂN XẾP VÀ THỦ TỤC
Một chương trình bao gồm ba phần cơ bản: Mã lệnh, Dữ liệu, Ngăn xếp. Khi chương
trình được nạp vào bộ nhớ thì ba phần trên được nạp vào các đoạn nhớ khác nhau:
+ Đoạn nhớ chứa phần mã lệnh được gọi là Đoạn mã (Code Segment), địa chỉ của nó
được lưu giữ trong thanh ghi đoạn CS.
+ Đoạn nhớ chứa phần dữ liệu được gọi là Đoạn dữ liệu (Data Segment), địa chỉ của nó
được lưu giữ trong thanh ghi đoạn DS.
+ Đoạn nhớ chứa phần ngăn xếp được gọi là Đoạn ngăn xếp (Stack Segment), địa chỉ của
nó được lưu giữ trong thanh ghi đoạn SS.
Nội dung của chương này chủ yếu nghiên cứu về đoạn ngăn xếp.
Ví dụ 1:
PUSH AX
Lệnh trên cất nội dung thanh ghi AX vào ngăn xếp.
54
Ví dụ 2:
PUSH A
Lệnh trên cất nội dung biến A vào ngăn xếp (A phải là biến kiểu Word).
Ví dụ 1:
POP AX
Lệnh trên lấy dữ liệu từ ngăn xếp đặt vào thanh ghi AX.
Ví dụ 2:
POP A
Lệnh trên lấy dữ liệu từ ngăn xếp đặt vào biến A (A phải là biến kiểu Word).
55
PUSH AX ;Cất kí tự vào ngăn xếp
MAIN ENDP
END MAIN
Giải thích:
Kí tự nhập vào được cất ở thanh ghi AL. Để đưa con trỏ xuống đầu dòng tiếp theo thì
phải hiển thị hai kí tự có mã ASCII là 0Dh (CR: về đầu dòng) và 0Ah (LF: xuống dòng).
Quá trình hiển thị hai kí tự này sẽ làm thanh ghi AL bị thay đổi (xem lại chức năng số 2
của ngắt 21h). Do đó cần phải lưu kí tự ban đầu vào ngăn xếp trước khi xuống dòng, khi
nào muốn hiển thị kí tự này thì lại lấy nó ra từ ngăn xếp.
c) Ví dụ 3:
Viết lệnh thực hiện các công việc sau:
+ Lưu nội dung thanh ghi cờ vào AX.
+ Xoá thanh ghi cờ.
Giải:
Ta không thể tác động tới thanh ghi cờ bằng các lệnh thông thường đã học như MOV,
ADD, SUB, AND, OR…Bộ vi xử lý 8086 cung cấp hai lệnh sau để thao tác với thanh ghi
cờ (cả hai lệnh đều liên quan tới ngăn xếp):
PUSHF ;Cất nội dung thanh ghi cờ vào ngăn xếp
POPF ;Lấy dữ liệu từ ngăn xếp đặt vào thanh ghi cờ
Sử dụng hai lệnh này ta có thể giải quyết yêu cầu đặt ra ở trên:
PUSHF
POP AX ;chuyển nội dung thanh ghi cờ từ ngăn xếp vào AX
XOR BX, BX ;xóa BX (BX = 0)
PUSH BX ;Đặt giá trị 0 vào ngăn xếp
56
POPF ;Chuyển giá trị 0 từ ngăn xếp vào thanh ghi cờ (xoá các cờ)
…
0000h Kết thúc stack
…
00FAh
00FCh
00FEh
offset = 0100h ← SP = 0100h
…
57
Khi ngăn xếp chưa có dữ liệu thì SP trỏ tới ô nhớ có địa chỉ cao nhất trong ngăn xếp.
Sau mỗi lệnh Push thì SP sẽ giảm đi 2 để trỏ tới ô tiếp theo của ngăn xếp, dữ liệu sẽ được
cất vào ô nhớ do SP trỏ tới.
Ví dụ:
• Cất ba thanh ghi AX, BX, CX vào ngăn xếp:
PUSH AX
PUSH BX
PUSH CX
00FAh
00FCh
00FEh AX ← SP = 00FEh
0100h Rỗng
…
Do giảm SP đi 2 rồi mới cất thanh ghi AX vào ngăn xếp nên sẽ tạo ra một ô rỗng ở địa
chỉ cao nhất.
- Sau lệnh PUSH BX:
…
00FAh
00FCh BX ← SP = 00FEh
00FEh AX
0100h Rỗng
…
00FAh CX ← SP = 00FEh
00FCh BX
00FEh AX
0100h Rỗng
…
• Muốn lấy nội dung của ba thanh ghi ra khỏi ngăn xếp thì phải tiến hành theo trình tự
ngược lại:
58
POP CX
POP BX
POP AX
00FAh -
00FCh BX ← SP = 00FEh
00FEh AX
0100h Rỗng
…
Dữ liệu tại ô nhớ do SP trỏ tới (offset = 00FAh) sẽ được nạp vào thanh ghi CX, sau đó
SP tăng lên 2 để trỏ tới ô cao hơn. Ô nhớ chứa CX đã được giải phóng nên ta không cần
quan tâm tới nội dung bên trong nó nữa.
- Sau lệnh POP BX:
…
00FAh -
00FCh BX
00FEh AX ← SP = 00FEh
0100h Rỗng
…
Dữ liệu tại ô nhớ có offset = 00FCh được nạp vào thanh ghi BX.
- Sau lệnh POP AX:
…
00FAh -
00FCh BX
00FEh AX
0100h Rỗng ← SP = 00FEh
…
Dữ liệu tại ô nhớ có offset = 00FEh được nạp vào thanh ghi AX.
59
6.3 Thủ tục
Ví dụ:
Viết một thủ tục đưa con trỏ màn hình xuống đầu dòng tiếp theo.
Giải:
Để đưa con trỏ xuống đầu dòng tiếp theo cần hiển thị các kí tự CR (0Dh) và LF (0Ah).
Ta đặt tên thủ tục này là Writeln.
Writeln PROC
MOV AH, 2 ;Chức năng số 2 của ngắt 21h để hiện kí tự
MOV DL, 0Dh
INT 21h
MOV DL, 0Ah
INT 21h
RET
Writeln ENDP
60
<Khai báo dữ liệu>
.CODE
<Chương trình chính> PROC
Lệnh 1
Lệnh 2
Lệnh 3
...
CALL <Tên thủ tục> ;Gọi thủ tục
…
<Chương trình chính> ENDP
• Ví dụ:
Viết chương trình nhập một kí tự từ bàn phím rồi hiện nó ở đầu dòng tiếp theo (có sử
dụng thủ tục).
Giải:
TITLE Vi du
.MODEL SMALL
.STACK 100H
.CODE
MAIN PROC
MOV AH, 1 ;Nhập một kí tự
INT 21h
61
INT 21h
Writeln PROC ;Thủ tục đưa con trỏ về đầu dòng tiếp theo
MOV AH, 2
MOV DL, 0Dh
INT 21h
MOV DL, 0Ah
INT 21h
RET
Writeln ENDP
END MAIN
Khi sử dụng lệnh Call thì các công việc sau đây được thực hiện:
+ Cất địa chỉ của lệnh đứng sau lệnh Call (trong chương trình chính) vào ngăn xếp.
+ Nạp địa chỉ lệnh đầu tiên của thủ tục vào cặp thanh ghi CS:IP (tức là thi hành lệnh này).
Lần lượt các lệnh trong thủ tục sẽ được thi hành cho tới khi gặp lệnh RET. Lệnh RET
sẽ lấy địa chỉ lệnh từ ngăn xếp (do lệnh Call cất trước đó) rồi nạp vào các thanh ghi
CS:IP. Như vậy quyền điều khiển đã được trả về chương trình chính (xem sơ đồ bên
dưới).
62
…
…
Thủ tục …
RET
…
63
BÀI TẬP CHƯƠNG VI
1. Lệnh PUSH được dùng để cất 1 byte dữ liệu vào ngăn xếp?
2. Thanh ghi ES dùng để chứa địa chỉ đoạn ngăn xếp?
3. Lệnh PUSHF được dùng để làm gì?
4. Khai báo ngăn xếp như sau sẽ cho phép sử dụng tối đa bao nhiêu lệnh PUSH:
. Stack 100h
5. Các lệnh của thủ tục nằm trong đoạn bộ nhớ tiếp theo sau đoạn mã?
64
CHƯƠNG VII: MẢNG VÀ CÁC LỆNH THAO TÁC CHUỖI
‘K’
‘H’
‘O’
‘A’
‘’
‘C’
…
Cách khai báo như trên tương đương với cách khai báo sau đây:
.DATA
ChuoiKT DB ‘K’, ‘H’, ‘O’, ‘A’, ‘CONG’, ‘NGHE’, ‘THONG TIN$’
Các khai báo đó được gọi là khai báo liệt kê, tức là sẽ tạo ra trong bộ nhớ một mảng có
số lượng phần tử xác định, đồng thời khởi tạo luôn giá trị cho từng phần tử. Dưới đây sẽ
đề cập tới các phương pháp khai báo tổng quát.
65
Cách 2: <Tên mảng> DB <Số phần tử của mảng> DUP (Giá trị khởi tạo)
Ví dụ 1:
A DB 50 DUP (0)
Khai báo trên tạo ra mảng A có 50 phần tử, giá trị ban đầu của các phần tử bằng 0.
Ví dụ 2:
B DB 100 DUP (?)
Khai báo trên tạo ra mảng B có 100 phần tử, không khởi tạo giá trị ban đầu cho các
phần tử.
Ví dụ 2:
N DW 1, 6, 20, 10, 15
Các phần tử của mảng có thể kí hiệu như sau (chú ý: kích thước của mỗi phần tử trong
mảng này là 2 byte):
66
Kí hiệu Giá trị
Phần tử 1 N 1
Phần tử 2 N+2 6
Phần tử 3 N+4 20
Phần tử 4 N+6 10
Phần tử 5 N+8 15
Ví dụ 3:
Cho mảng A gồm 12 phần tử, các phần tử có kiểu là Byte. Hãy đổi chỗ phần tử đầu tiên
và phần tử cuối cùng của mảng cho nhau.
Giải:
Phần tử đầu tiên là: A
Phần tử cuối cùng là: A + 11
MOV AL, A
MOV BL, A + 11
MOV A, BL
MOV A+11, AL
Ví dụ:
Cho mảng sau:
A DB 10, 12, 3, 4, 9, 5, 7, 6
Hãy tính tổng các phần tử của mảng (cất tổng vào AL).
67
Giải:
Ta sẽ sử dụng thanh ghi SI lần lượt trỏ tới từng phần tử của mảng để thực hiện phép
tính tổng.
XOR AL, AL ;Xoá AL để chuẩn bị chứa tổng
LEA SI, A ;SI chứa địa chỉ offset phần tử đầu tiên của mảng
MOV CX, 8 ;Số lần lặp (mảng có 8 phần tử)
Lap:
ADD AL, [SI] ;Cộng phần tử của mảng vào AL
INC SI ;SI trỏ tới phần tử tiếp theo
LOOP Lap
Cách viết như trên được gọi là Chế độ địa chỉ gián tiếp thanh ghi.
Ví dụ 2:
Ta sẽ viết lại đoạn chương trình ở phần a bằng một cách khác.
Cho mảng sau:
A DB 10, 12, 3, 4, 9, 5, 7, 6
Hãy tính tổng các phần tử của mảng (cất tổng vào AL).
Giải:
XOR AL, AL ;Xoá AL để chuẩn bị chứa tổng
XOR BX, BX ;<Khoảng cách> = 0: phần tử đầu tiên của mảng
MOV CX, 8 ;Số lần lặp (mảng có 8 phần tử)
Lap:
ADD AL, A[BX] ;Cộng phần tử của mảng vào AL
INC BX ;tăng <Khoảng cách> để trỏ tới phần tử tiếp theo
LOOP Lap
68
Ngoài cách kí hiệu A[BX] còn có thể sử dụng các kí hiệu khác tương đương như [A +
BX], [BX + A], A + [BX], [BX] + A.
Nếu sử dụng các thanh ghi BX (Base Register) hay BP (Base Pointer) trong cách viết
trên thì gọi là Chế độ địa chỉ cơ sở, còn nếu sử dụng SI (Source Index) hay DI
(Destination Index) thì gọi là Chế độ địa chỉ chỉ số.
69
Chuoi1 DB ‘Khoa CNTT$’
Chuoi2 DB 10 DUP (?)
Hãy sao chép nội dung của Chuoi1 sang Chuoi2.
Giải:
Để thực hiện yêu cầu trên ta sẽ lần lượt sao chép từng byte của Chuoi1 sang Chuoi2.
‘K’ ← DS:SI
Chuoi1 ‘h’
10 byte ‘o’
‘a’
‘’
‘C’
‘N’
‘T’
‘T’
‘$’
? ← ES:DI
Chuoi2 ?
10 byte ...
Muốn sao chép byte đầu tiên (kí tự ‘K’) thì DS:SI phải chứa địa chỉ đầu của Chuoi1,
ES:DI phải chứa địa chỉ đầu của Chuoi2. Điều này được thực hiện bởi các lệnh sau:
LEA SI, Chuoi1 ;SI chứa offset của Chuoi1
LEA DI, Chuoi2 ;DI chứa offset của Chuoi2
MOVSB ;Chuyển 1 byte
Mỗi chuỗi có độ dài 10 byte nên phải lặp lại quá trình trên 10 lần thì mới sao chép
xong.
• Lưu ý:
+ Mỗi khi sao chép xong 1 byte thì phải tăng SI và DI lên 1 để nó trỏ tới ô nhớ tiếp theo.
Sau mỗi lệnh MOVSB thì SI và DI sẽ được tự động tăng lên 1 nếu cờ DF = 0 (SI và DI sẽ
tự động giảm đi 1 nếu cờ DF = 1). Như vậy vấn đề là phải xoá được cờ DF trước khi thi
hành lệnh MOVSB. Điều này được thực hiện nhờ lệnh CLD (Clear Direction Flag):
LEA SI, Chuoi1 ;SI chứa offset của Chuoi1
LEA DI, Chuoi2 ;DI chứa offset của Chuoi2
CLD ;Xoá cờ định hướng: DF = 0
MOVSB ;Chuyển 1 byte
70
+ Ngược lại với lệnh CLD là lệnh STD (Set Direction Flag), lệnh này sẽ thiết lập cờ
DF=1. Ta có thể sử dụng lệnh STD để chuyển các byte dữ liệu theo chiều ngược lại.
TITLE Vi du Chuoi
.MODEL SMALL
.STACK 100H
.DATA
Chuoi1 DB ’Khoa CNTT$’
Chuoi2 DB 10 DUP (?)
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
MOV ES, AX ;DS và ES chứa segment của đoạn dữ liệu
MAIN ENDP
END MAIN
Ví dụ 2:
Làm lại ví dụ 1 bằng cách dùng lệnh MOVSW.
Giải:
71
Do lệnh MOVSW mỗi lần sao chép được 2 byte nên chỉ phải thực hiện lặp 5 lần, các
lệnh cụ thể như sau:
MOV CX, 5 ;Số lần lặp
LEA SI, Chuoi1 ;SI chứa offset của Chuoi1
LEA DI, Chuoi2 ;DI chứa offset của Chuoi2
CLD ;Xoá cờ định hướng: DF = 0
Lap:
MOVSW ;Thực hiện lặp 5 lần
LOOP Lap
Cú pháp lệnh:
Dạng 1: REP MOVSB
Lệnh trên sao chép nhiều byte dữ liệu từ ô nhớ có địa chỉ DS:SI sang ô nhớ có địa chỉ
ES:DI, số byte cần chuyển chứa trong thanh ghi CX.
Dạng 2: REP MOVSW
Lệnh trên sao chép nhiều word dữ liệu từ ô nhớ có địa chỉ DS:SI sang ô nhớ có địa chỉ
ES:DI, số word cần chuyển chứa trong thanh ghi CX.
Ví dụ:
Để thực hiện việc sao chép nội dung của Chuoi1 sang Chuoi2 trong ví dụ trước, ta có
thể viết lại các lệnh như sau:
hoặc:
MOV CX, 5 ;Số word cần chuyển
LEA SI, Chuoi1 ;SI chứa offset của Chuoi1
LEA DI, Chuoi2 ;DI chứa offset của Chuoi2
CLD ;Xoá cờ định hướng: DF = 0
REP MOVSW
72
7.2.2 Lệnh chuyển dữ liệu từ thanh ghi vào chuỗi (Store a String)
Lệnh này còn được gọi là lệnh lưu chuỗi.
Cú pháp lệnh:
Dạng 1: STOSB
Lệnh trên chuyển nội dung của thanh ghi AL (1 byte) tới ô nhớ có địa chỉ ES:DI.
Dạng 2: STOSW
Lệnh trên chuyển nội dung của thanh ghi AX (2 byte) tới ô nhớ có địa chỉ ES:DI.
Ví dụ 1:
Xét chuỗi sau đây:
.DATA
ChuoiKT DB 6 DUP (?)
Hãy nhập một kí tự từ bàn phím rồi đặt kí tự đó vào phần tử đầu tiên của chuỗi.
Giải:
Ta sẽ sử dụng chức năng số 1 của ngắt 21h để nhập kí tự, thanh ghi AL sẽ chứa mã
ASCII của kí tự đó. Muốn chuyển kí tự từ AL vào phần tử đầu tiên của chuỗi bằng lệnh
STOSB thì ES:DI phải chứa địa chỉ đầu của ChuoiKT, điều đó được thực hiện nhờ lệnh
sau:
LEA DI, ChuoiKT ;DI chứa offset của ChuoiKT
TITLE Vi du Chuoi
.MODEL SMALL
.STACK 100H
.DATA
ChuoiKT DB 6 DUP (?)
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
MOV ES, AX ;DS và ES chứa segment của đoạn dữ liệu
73
STOSB ;Chuyển kí tự từ AL vào đầu chuỗi
MAIN ENDP
END MAIN
Ví dụ 2:
Nhập một chuỗi 10 kí tự từ bàn phím.
Giải:
Để nhập 10 kí tự và cất nó vào một chuỗi trong bộ nhớ ta vẫn sử dụng phương pháp
như ví dụ 1. Có một số điểm khác biệt như sau:
+ Phải có một vòng lặp với số lần lặp bằng 10.
+ Sau mỗi lệnh lưu chuỗi (STOSB) thì DI phải được tăng lên 1 để trỏ tới ô nhớ tiếp theo.
Điều này được thực hiện nhờ lệnh xoá cờ định hướng (CLD).
TITLE Vi du 2
.MODEL SMALL
.STACK 100H
.DATA
ChuoiKT DB 10 DUP (?)
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
MOV ES, AX ;DS và ES chứa segment của đoạn dữ liệu
Lap:
MOV AH, 1 ;Chức năng nhập kí tự của ngắt 21h
INT 21h
74
LOOP Lap
MAIN ENDP
END MAIN
7.2.3 Lệnh chuyển dữ liệu từ chuỗi vào thanh ghi (Load a String)
Lệnh này còn được gọi là lệnh nạp chuỗi.
Cú pháp lệnh:
Dạng 1: LODSB
Lệnh trên chuyển 1 byte dữ liệu từ ô nhớ có địa chỉ DS:SI vào thanh ghi AL.
Dạng 2: LODSW
Lệnh trên chuyển 1 word dữ liệu từ ô nhớ có địa chỉ DS:SI vào thanh ghi AX.
Ví dụ:
Xét chuỗi sau đây:
.DATA
ChuoiKT DB ‘Viet Nam’
Hãy hiển thị chuỗi ra màn hình.
Giải:
Vì chuỗi không kết thúc bằng dấu ‘$’ nên không thể hiện chuỗi bằng chức năng số 9
của ngắt 21h. Ta sẽ cho hiện lần lượt các kí tự của chuỗi bằng chức năng số 2 của ngắt
21h (các tham số: AH = 2, DL = Mã ASCII của kí tự cần hiển thị). Chuỗi có 8 kí tự nên
cần 8 lần lặp.
Đầu tiên cần chuyển từng kí tự từ chuỗi vào thanh ghi AL bằng lệnh LODSB, sau đó
chuyển từ AL sang DL, rồi gọi chức năng số 2 của ngắt 21h.
TITLE Vi du
.MODEL SMALL
.STACK 100H
.DATA
ChuoiKT DB ’Viet Nam’
.CODE
MAIN PROC
75
MOV AX, @DATA
MOV DS, AX ;DS chứa segment của đoạn dữ liệu
Lap:
LODSB ;Chuyển kí tự từ chuỗi vào AL
;SI được tự động tăng lên 1 (để
;trỏ tới kí tự tiếp theo)
MOV DL, AL ;Chuyển kí tự vào DL
MOV AH, 2 ;Hiển thị kí tự
INT 21h
LOOP Lap
MAIN ENDP
END MAIN
76
BÀI TẬP CHƯƠNG VII
77
CHƯƠNG VIII: SỬ DỤNG NGẮT TRONG HỢP NGỮ
Chương trình xử lý ngắt cần được kết thúc bằng lệnh IRET để sau khi thực hiện xong
có thể quay trở về thực hiện tiếp chương trình bị ngắt trước đó.
Có nhiều loại ngắt khác nhau, để phân biệt các ngắt cần dựa vào số hiệu của chúng. Bộ
vi xử lý 8086 có thể quản lý 256 ngắt, được đánh số lần lượt từ 0, 1, 2,..., FFh. Dưới đây
là bảng danh sách các ngắt:
Giải thích:
78
Chương trình xử lý ngắt có thể là một bộ phận của BIOS hay của DOS, cũng có thể do
người sử dụng tự viết. Ta cần phân biệt rõ hai khái niệm: “Ngắt” và “Chương trình xử lý
ngắt”. Không phải số hiệu ngắt nào cũng có có chương trình xử lý ngắt tương ứng.
Khi một ngắt có số hiệu từ 0 – 1Fh xuất hiện thì chúng sẽ được xử lý bởi các chương
trình viết sẵn nằm trong ROM BIOS (chủ yếu là giải quyết các yêu cầu vào/ ra cơ bản).
Còn nếu ngắt có số hiệu từ 20h – 3Fh thì sẽ do hệ điều hành DOS xử lý.
79
8.3.1 Ngắt 17h – Vào/ra máy in
Đây là ngắt do BIOS quản lý, nó có ba chức năng:
a) Chức năng số 0: Đưa một kí tự ra máy in
Các tham số: AH = 0
AL = Mã ASCII của kí tự cần in
DX = Số hiệu máy in
Ví dụ:
Gửi kí tự ‘A’ ra máy in.
Giải:
MOV AH, 0
MOV AL, ‘A’ ;Kí tự cần in
MOV DX, 0 ;Máy in số 0
INT 17h
MOV AH, 0
MOV AL, 0Ah ;in tiếp kí tự xuống dòng
INT 17h
Các trạng thái của máy in được liệt kê trong bảng sau:
80
8.3.2 Ngắt 21h
Đây là ngắt hay dùng nhất của DOS, nó có rất nhiều chức năng. Ở các chương trước ta
đã sử dụng bốn chức năng của ngắt này (chức năng số 1, 2, 9 và 4Ch). Trong phần này ta
sẽ tìm hiểu thêm một số chức năng khác.
MAIN ENDP
END MAIN
81
Cách sử dụng tương tự như chức năng 39h ở trên.
MAIN ENDP
END MAIN
Ví dụ:
Viết chương trình hiện ra màn hình giờ hiện tại của hệ thống.
82
Giải:
TITLE Hien thoi gian
.MODEL SMALL
.STACK 100H
.DATA
Time_Buf DB ’00:00:00$’
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
83
MOV AL, DH ;AX chứa Giây
MOV AH, 0
MOV DL, 10 ;Chia AX cho 10
DIV DL ;AL chứa số hàng chục của Giây
;AH chứa số hàng đơn vị của Giây
OR AX, 3030h ;Đổi sang mã ASCII
MOV Time_Buf+6, AL
MOV Time_Buf+7, AH
MAIN ENDP
END MAIN
Giải thích:
• Chương trình trên sẽ hiện ra màn hình giờ của dồng hồ hệ thống dưới dạng
HH:MM:SS (Giờ:Phút:Giây). Các giá trị thời gian được đổi sang dạng kí tự rồi cất
vào chuỗi Time_Buf. Chuỗi này được kết thúc bởi ‘$’ để có thể hiển thị bằng chức
năng số 9 của ngắt 21h.
• Sau khi gọi ngắt, các giá trị giờ, phút và giây được chứa trong các thanh ghi CH,
CL, DH. Chúng là các số thập phân có 2 chữ số, muốn hiển thị chúng cần tách
riêng chữ số hàng chục và hàng đơn vị ra, sau đó chuyển đổi sang mã ASCII tương
ứng.
Ví dụ:
Giả sử AX đang chứa số thập phân 46.
Để tách riêng hàng chục và hàng đơn vị, ta đem chia AX cho 10. Như vậy Thương = 4
= AL và Số dư = 6 = AH.
Để đổi một số sang mã ASCII ta đem số đó cộng với 30h (xem lại chương III):
Chữ số hàng chục: AL + 30h = 34h
Chữ số hàng đơn vị: AH + 30h = 36h
Một cách khác để đổi sang mã ASCII là thực hiện phép toán OR với 30h:
84
4 OR 30h = 34h
6 OR 30h = 36h
Vậy số thập phân 46 ứng với 2 kí tự có mã ASCII là 34h và 36h.
...
003FFh
...
00002h
00001h
00000h
Mỗi địa chỉ bao gồm 4 byte (2 byte thấp cho offset 2 byte cao cho segment). Có tất cả
256 ngắt, do đó kích thước của bảng = 256 x 4 byte = 1024 byte.
Nếu người sử dụng muốn viết các chương trình xử lý ngắt của riêng mình (không muốn
dùng các chương trình có sẵn của DOS hay BIOS) thì cần tác động tới bảng vector ngắt
này. Các bước thực hiện như sau:
Bước 1: Viết chương trình xử lý ngắt mới.
Bước 2: Nạp chương trình xử lý ngắt mới vào bộ nhớ.
Bước 3: Truy nhập vào bảng vector ngắt để thay thế địa chỉ của chương trình xử lý ngắt
cũ bằng địa chỉ của chương trình tự viết.
Người sử dụng có thể tự lập trình truy nhập vào bảng vector để tìm kiếm địa chỉ của
các ngắt (cứ mỗi ngắt ứng với 4 byte), hoặc đơn giản hơn là sử dụng các chức năng có sẵn
của ngắt 21h:
• Chức năng số 35h: Xác định địa chỉ của chương trình xử lý ngắt
Vào: AH = 35h
AL = Số hiệu ngắt
Ra: ES:BX chứa địa chỉ của chương trình xử lý
ngắt
85
Ví dụ:
Hãy xác định địa chỉ của chương trình xử lý ngắt 13h.
Giải:
MOV AH, 35h
MOV AL, 13h
INT 21h
• Chức năng số 25h: Thay thế địa chỉ của chương trình xử lý ngắt
Vào: AH = 25h
AL = Số hiệu ngắt
DS:DX = Địa chỉ của chương trình xử lý ngắt
mới
Ví dụ:
Hãy thay thế chương trình xử lý ngắt 13h bằng một thủ tục do người dùng tự viết.
Giải:
Giả sử thủ tục do người dùng tự viết có tên là NewProc.
MOV AH, 25h
MOV AL, 13h
MOV BX, SEG NewProc
MOV DS, BX ;DS chứa segment của thủ tục tự viết
MOV DX, OFFSET NewProc ;DX chứa offset của thủ tục tự viết
INT 21h
Ở đây ta sử dụng các toán tử giả SEG và OFFSET để lấy địa chỉ của thủ tục NewProc.
86
BÀI TẬP CHƯƠNG VIII
1. Các thủ tục ngắt của BIOS được cất trong bộ nhớ RAM?
2. Khi máy tính đang hoạt động, các thủ tục ngắt của DOS được cất trong bộ nhớ
ROM?
3. Bảng vector ngắt là nơi chứa các thủ tục xử lí ngắt?
4. Địa chỉ mỗi ngắt sẽ chiếm bao nhiêu byte trong bảng vector ngắt?
5. Kích thước bảng vector ngắt bằng bao nhiêu?
87
CHƯƠNG IX: MỘT SỐ VẤN ĐỀ MỞ RỘNG
Giải:
Đầu tiên phải cộng 16 bít thấp, sau đó mới cộng 16 bít cao:
MOV AX, B
MOV BX, B+2
ADD A, AX ;Cộng 16 bít thấp của số thứ hai vào A
ADC A+2, BX ;Cộng 16 bít cao của số thứ hai và cờ CF vào A+2
88
Giải thích:
Ví dụ trên có sử dụng một lệnh mới (Add with Carry):
ADC <Đích>, <Nguồn>
<Đích>: là một thanh ghi hay một ô nhớ
<Nguồn>: là một thanh ghi, một ô nhớ, hoặc một hằng số
<Đích>,<Nguồn> không đồng thời là hai ô nhớ.
Lệnh ADC sẽ thực hiện phép cộng: Đích + Nguồn + CF, kết quả chứa trong Đích.
Như vậy, nếu phép cộng 16 bít thấp của hai số có nhớ (CF = 1) thì phép cộng 16 bít cao
(bằng lệnh ADC) sẽ cộng cả bít nhớ này vào tổng.
b) Phép trừ
Ví dụ:
Thực hiện phép trừ giữa hai số nguyên 32 bit: A+2:A và B+2:B
Giải:
Đầu tiên phải trừ 16 bít thấp, sau đó mới trừ 16 bít cao:
MOV AX, B
MOV BX, B+2
SUB A, AX ;Trừ 16 bít thấp
SBB A+2, BX ;Trừ A+2 cho BX và CF
Giải thích:
Ví dụ trên có sử dụng một lệnh mới (Subtract with Borrow):
SBB <Đích>, <Nguồn>
<Đích>: là một thanh ghi hay một ô nhớ
<Nguồn>: là một thanh ghi, một ô nhớ, hoặc một hằng số
<Đích>,<Nguồn> không đồng thời là hai ô nhớ.
Lệnh SBB sẽ thực hiện phép trừ: Đích - Nguồn - CF, kết quả chứa trong Đích.
Như vậy, nếu phép trừ 16 bít thấp của hai số có vay (CF = 1) thì phép trừ 16 bít cao
(bằng lệnh SBB) sẽ trừ thêm cả bít đã vay này.
89
Việc thực hiện phép phủ định (đảo bít) đối với số 32 bít rất đơn giản vì không cần thêm
lệnh nào để hiệu chỉnh kết quả:
NOT A ;Đảo 16 bít thấp
NOT A+2 ;Đảo 16 bít cao
Nói chung, các phép logic (And, Or, Not, Xor) vẫn có thể áp dụng bình thường trên
nửa thấp và nửa cao của số 32 bít.
A+2 CF A
b) Dịch phải
Ví dụ:
Thực hiện phép dịch phải số nguyên 32 bit sau: A+2:A
Giải:
Trình tự dịch ngược lại so với ví dụ phần a:
+ Đầu tiên cần dịch phải 16 bít cao
+ Sau đó quay phải 16 bít thấp (quay qua cờ CF)
SHR A+2, 1
RCR A, 1
A+2 CF A
90
9.2 Số thực dấu phảy động
Kết quả:
Viết lần lượt các phần nguyên thu được ở trên vào sau dấu phảy của số nhị phân:
(0,75)10 = (0,11)2
Ví dụ 2:
Chuyển số 4,9 sang dạng nhị phân.
Giải:
Số trên có phần nguyên = 4 và phần thập phân = 0,9. Các bước tiến hành như sau:
• Chuyển phần nguyên sang dạng nhị phân:
(4)10 = (100)2
• Chuyển phần thập phân sang dạng nhị phân (giống như ví dụ 1):
Bước 1: Lấy phần thập phân nhân với 2:
0,9 x 2 = 1,8 → Phần nguyên = 1, phần thập phân = 0,8
Bước 2: 0,8 x 2 = 1,6 → Phần nguyên = 1, phần thập phân = 0,6
Bước 3: 0,6 x 2 = 1,2 → Phần nguyên = 1, phần thập phân = 0,2
Bước 4: 0,2 x 2 = 0,4 → Phần nguyên = 0, phần thập phân = 0,4
Bước 5: 0,4 x 2 = 0,8 → Phần nguyên = 0, phần thập phân = 0,8
Bước 6: 0,8 x 2 = 1,6 → Phần nguyên = 1, phần thập phân = 0,6
...
Quá trình này sẽ không bao giờ kết thúc vì phần thập phân không thể bằng 0. Do đó ta
chỉ có thể thu được một số nhị phân xấp xỉ với số thập phân đã cho.
91
9.2.2 Biểu diễn số thực dấu phảy động
Ví dụ 1:
Xét số thực dạng nhị phân sau: M = (10,1)2 (bằng 2,5 trong hệ thập phân)
Số M cũng có thể được viết là: M = 10,1 x 20 = 1,01 x 21
= <Phần định trị> x 2<Phần mũ>
Như vậy ta có thể thay đổi vị trí của dấu phảy mà giá trị của số thực vẫn giữ nguyên
bằng cách điều chỉnh phần mũ và phần định trị.
• Phần định trị (Mantissa):
Để thống nhất trong cách biểu diễn, phần định trị của các số khác 0 thường phải thoả
mãn điều kiện sau:
1 ≤ Mantissa < 2
Phần định trị thoả mãn điều kiện trên được gọi là phần định trị đã được chuẩn hoá.
• Phần mũ (Exponent):
Trên thực tế phần mũ có thể là số âm hoặc số dương. Việc biểu diễn số mũ âm là phức
tạp nên người ta thường cộng thêm một giá trị (gọi là Bias) vào phần mũ để nó luôn là
số dương.
Bias được lựa chọn theo quy tắc sau:
Nếu độ dài dãy bít biểu diễn phần mũ là N thì Bias = 2N – 1 - 1
(Nếu N = 8 thì Bias = 27 – 1 = 127)
Ví dụ 2:
Xét số thực dạng nhị phân sau: M = 0,0001b
Hãy xác định phần định trị và phần mũ của M sau khi chuẩn hoá.
Giải:
M = 0,0001b = 1,0 x 2-4
Mantissa = 1,0
Giả sử dùng 8 bít để biểu diễn phần mũ: Bias = 127
→ Exponent = -4 + 127 =123
31 30 23 22 0
S Exponent Mantissa
92
Ví dụ 1:
Chuyển số 4,9 sang dạng ShortReal.
Giải:
(4,9)10 = (100,11100 1100 1100...)2
Chuẩn hoá:
4,9 = 1,0011100 1100 1100...x 22
+ S = 0 (dấu dương)
+ Mantissa = 0011100 1100 1100... (23 bít)
+ Exponent = 2 + 127 = 129 = 1000 0001b
→ Số thực dạng ShortReal:
31 30 23 22 0
0 1 0 0 0 0 0 01 0 0 1 1 10 0 1 1 0 0 1100 1100 1100
Ví dụ 2:
Chuyển số - 4,9 sang dạng ShortReal.
Giải:
Cách làm tương tự ví dụ 1, chỉ khác ở bít dấu: S = 1 (dấu âm)
→ Số thực dạng ShortReal:
31 30 23 22 0
1 1 0 00 0 001 0 0 1 1 1 0 0 1 100 1100 1100 1100
Trên đây là các ví dụ về biểu diễn số thực bằng 32 bít nhị phân trong máy tính. Có
nhiều kiểu số thực khác nhau ứng với các độ dài dãy bít khác nhau, dãy bít càng dài thì
phạm vi biểu diễn càng lớn.
9.3 Bộ vi xử lý 80286
93
80286 là bộ vi xử lý 16 bít. Nó có các thanh ghi dài 16 bít tương tự 8086, nhưng sử
dụng bus địa chỉ lớn hơn (24 bít), do đó nó có thể địa chỉ hoá được 16 MB bộ nhớ vật lý
(224 Byte). Tuy nhiên, trong chế độ thực, do phải giữ tính tương thích với 8086 nên nó
cũng chỉ quản lý được khoảng 1MB bộ nhớ. Muốn sử dụng toàn bộ không gian nhớ 16
MB thì phải chuyển sang chế độ bảo vệ.
• Cất các thanh ghi thường dùng vào ngăn xếp bằng lệnh:
PUSHA ;Push All Register
Các thanh ghi sẽ được cất vào ngăn xếp theo trình tự sau: AX, CX, DX, BX, SP,
BP, SI, DI.
Ngược lại, muốn khôi phục giá trị của các thanh ghi trên thì dùng lệnh:
POPA ;Pop All Register
Các thanh ghi sẽ được lấy ra khỏi ngăn xếp theo trình tự :DI, SI, BP, SP, BX, DX, CX,
AX.
b) Lệnh nhân
80386 cung cấp ba dạng lệnh nhân mới:
Dạng 1: IMUL reg16, immed
Dạng 2: IMUL reg16, reg16, immed
Dạng 3: IMUL reg16, mem16, immed
Trong đó:
+ reg16: là một thanh ghi 16 bít
+ immed: là một hằng số
+ mem16: là một biến kiểu Word
Toán hạng 1 (reg16) sẽ là nơi chứa kết quả phép nhân. Nếu tích số vượt quá phạm vi
của reg16 thì các cờ CF và OF = 1.
Ví dụ:
IMUL BX, 15 ; Nhân BX với 15, kết quả chứa trong BX
IMUL AX, BX, 15 ; Nhân BX với 15, kết quả chứa trong AX
94
IMUL AX, A, 15 ; Nhân biến A với 15, kết quả chứa trong AX
; (biến A có kiểu Word)
Bộ vi xử lý 80286 cho phép viết trực tiếp số lần dịch (hay quay) trong lệnh, không cần
dùng tới CL.
Ví dụ:
SHL AX, 3 ;Dịch các bít của thanh ghi AX sang trái 3 lần
Chú ý: Nếu trong chương trình có sử dụng các lệnh mở rộng của 80286 thì ở đầu
chương trình phải có khai báo .286
TITLE VI DU
.MODEL SMALL
.286 ;Khai báo sử dụng lệnh mở rộng
.STACK 100H
.CODE
...
95
EAX
AX
AH AL
Với cách tổ chức các thanh ghi như trên, tất cả các chương trình viết cho 8086 đều có
thể chạy trên 80386.
Bộ vi xử lý 80386 sử dụng bus địa chỉ 32 bit, do đó nó có thể địa chỉ hoá được 4 GB bộ
nhớ vật lý (232 Byte). Tuy nhiên, trong chế độ thực nó cũng chỉ sử dụng được khoảng
1MB (để tương thích với 8086).
Chú ý: Muốn lập trình với 80386 và sử dụng các thanh ghi 32 bit thì ở đầu chương
trình phải có khai báo .386
TITLE VI DU
.MODEL SMALL
.386 ;Khai báo sử dụng các lệnh của 80386
.STACK 100H
.CODE
...
96
BÀI TẬP CHƯƠNG IX
1. Một dãy nhị phân dài 32 bit có thể biểu diễn số nguyên lớn nhất bằng bao nhiêu?
2. 80286 là bộ vi xử lí bao nhiêu bit?
3. Trong chế độ thực, bộ vi xử lí 80286 có thể sử dụng được không gian bộ nhớ có
kích thước tối đa bằng bao nhiêu?
4. Chuyển số thực sau sang dạng nhị phân: 9,25
5. Cho số thực 32 bit dưới dạng Hex như sau:
7E00 12B4h
Hãy xác định phần mũ của nó.
97
PHỤ LỤC 1: CHƯƠNG TRÌNH DEBUG
Một cửa sổ màu đen sẽ hiện ra, khi đó ta có thể gõ các lệnh tại dấu nhắc trên cửa sổ này
để thao tác với Debug.
98
Màn hình sẽ hiện ra danh sách các lệnh, các tham số của từng lệnh... Dưới đây là một
số lệnh thường dùng (các tham số tuỳ chọn được viết trong cặp ngoặc vuông. Tất cả các
hằng số được biểu diễn dưới dạng hex):
99
gõ phím ENTER để kết thúc
G[=start] [addr1 addr2... Thực hiện bắt đầu từ start đến các điểm
addrN] dừng
tại addr1 addr2... addrN.
Ví dụ:
G Thực hiện lệnh từ CS: IP cho đến cuối
chương trình
G=100 Thực hiện từ CS:0100h cho đến cuối
chương trình
G 100 200 300 Thực hiện từ CS: IP cho đến khi gặp điểm
dừng đầu tiên trong số CS:0100h, CS:0200h
hay CS: 0300h
G=100 150 Thực hiện bắt đầu từ CS:0100h cho đến CS:
0150h
L address [drive start_sector Nạp các sector của đĩa hoặc nạp file vào bộ
end_sectox] nhớ. Tên file được xác định bởi lệnh N, còn
ổ đĩa được xác định bằng các chữ số 0 cho ổ
A, 1 cho ổ B...
Ví dụ:
L DS: 100 0 C 1B Nạp các sector từ Ch cho đến 1Bh trong ổ
đĩa A vào địa chỉ DS:0100h
L 8FD0:0 1 2A 3B Nạp các sector từ 2A đến 3B trong ổ đĩa B
vào địa chỉ 8FD0h.
L DS:100 Nạp file vào địa chỉ DS:0100h (xem lệnh N)
N filename Đặt tên hiện thời cho các file dùng trong
lệnh L và W.
Ví dụ:
N myfile Đặt tên file để đọc ghi là myfile.
Q Thoát khỏi DEBUG và trở về DOS
R [register] Hiển thị/thay đổi nội dung thanh ghi
Ví dụ: Hiển thị nội dung thanh ghi và các cờ
R
R AX Hiển thị nội dung thanh ghi AX và thay đổi
nếu muốn
T [=start] [vlue] Duyệt "value" lệnh bắt đầu từ start
Ví dụ:
T Duyệt lệnh tại CS : IP
T = 100 Duyệt lệnh tại CS: 100h
T = 100 5 Duyệt 5 lệnh bắt đầu tại CS:0100h
100
T 4 Duyệt 4 lệnh bắt đầu từ CS:IP
U [start [end]] [range] Dịch ngược dữ liệu thành dạng lệnh
Ví dụ:
U100 Dịch ngược khoảng 32 bte từ CS:0100h
U CS : 100 110 Dịch ngược dữ liệu từ CS: 100h đến
CS:0110h
U 200 L 20 Dịch ngược 20 lệnh bắt đầu từ CS:0200h
U Dịch ngược khoảng 32 byte bắt đầu từ CS:
last trong đó last là byte cuối cùng đã được
dịch ngược.
W [start] Viết BX:CX byte vào file (xem lệnh N)
Ví dụ:
W 100 Viết BX:CX byte vào file từ vị trí
CS:0100h.
101
Sau khi dịch sang file EXE ta sẽ thi hành chương trình trong môi trường DEBUG bằng
cách gõ lệnh sau ở cửa sổ Run:
DEBUG C:\ASM\Vidu1.exe
(giả sử file Vidu1.exe được cất trong thư mục ASM trên ổ đĩa C)
Cửa sổ DEBUG xuất hiện với dấu nhắc lệnh "-". Để xem các thanh ghi chúng ta gõ
lệnh: R.
Màn hình trình bày nội dung của các thanh ghi dưới dạng hex. Tại dòng thứ 3 chứa địa
chỉ segment:offset của lệnh đầu tiên trong chương trình kèm theo nó là lệnh ở dạng mã
máy và dạng hợp ngữ. Các cặp chữ ở cuối dòng thứ 2 là trạng thái hiện thời của các cờ
(bao gồm cả cờ trạng thái và cờ điều khiển). Dưới đây là các ký hiệu được sử dụng trong
DEBUG để hiển thị trạng thái của các cờ:
102
Để đổi nội dung của một thanh ghi - ví dụ DX với 1ABCh chúng ta gõ lệnh: R DX
DEBUG đáp lại bằng việc hiển thị nội dung hiện thời của DX, sau dó nó hiển thị dấu 2
chấm và đợi chúng ta đánh vào nội dung mới của DX. Chúng ta đánh vào 1ABC và đánh
phím ENTER (DEBUG ngầm định rằng tất cả các số người sử dụng đánh vào đều biểu
diễn dưới dạng hex nên ở đây không cần phải đánh thêm chữ "h"). Để giữ nguyên nội
dung của DX chúng ta chỉ việc đánh ENTER ngay sau dấu hai chấm. Để kiểm chứng lại
việc thay đổi chúng ta có thể hiển thị lại một lần nữa nội dung của các thanh ghi.
Ta dùng lệnh T để lần lượt thực hiện các lệnh tiếp theo trong chương trình:
Nếu bây giờ chúng ta lại đánh T lần nữa thì DEBUG sẽ tiến hành từng lệnh trong
thường trình phục vụ ngắt 21h mà đó là điều chúng ta không mong muốn.
103
Từ lần hiển thị thanh ghi cuối cùng chúng ta thấy rằng INT 21h là một lệnh 2 byte. Do giá
trị hiện nay của IP là 000Bh, lệnh tiếp theo nằm tại địa chỉ 000Dh, và chúng ta có thể tạo
điểm ngắt tại đó:
Hàm 09 của ngắt 21h hiển thị "HELLO!" và việc thực hiện dừng lại ở điểm gẫy 000Dh.
Để kết thúc việc thực hiện chương trình chúng ta chỉ việc đánh 'G':
-G
Program terminate normally
Dòng nhắc này thông báo cho ta biết rằng chương trình đã hoàn thành. Chương trình phải
được nạp để có thể chạy lại do đó chúng ta hãy thoát khỏi DEBUG.
-Q
Để thử lệch U hãy quay lại DEBUG và dùng nó để liệt kê chương trình của chúng ta:
(Gõ DEBUG C:\ASM\Vidu1.exe trong cửa sổ Run)
-U
104
DEBUG đã dịch ngược khoảng 32 bytes; nó đã dịch nội dung của các byte này như là
các lệnh. Chương trình kết thúc tại 000Fh và phần còn lại là các ký tự đi kèm theo mã
assemby do chương trình dịch DEBUG tạo ra. Để hiển thị chương trình đánh:
- U 0000 000F ( hoặc viết gọn là U 0 F)
Trong phần liệt kê chưa biên dịch DEBUG thay thế những tên bằng các địa chỉ
segment và offset được gán cho những tên đó. Chẳng hạn thay vì lệnh MOV AX,
@DATA chúng ta có MOV AX, 13B4, lệnh LEA DX, MSG trở thành LEA DX, [0002]
vì 0002h chính là offset gán cho MSG trong đoạn dữ liệu DATA.
Để biểu diễn lệnh D chúng ta hãy liệt kê nội dung vùng nhớ có chứa lời chào
"HELLO!". Trước tiên chúng ta thực hiện 2 lệnh khởi tạo DS:
-G 5
105
Bây giờ chúng ta hãy hiển thị nội dung của bộ nhớ bắt đầu từ DS:0000
-D 0
DEBUG hiển thị 128 byte bộ nhớ. Nội dung của mỗi ô nhớ được biểu diễn bằng 2 chữ
số hex. Chẳng hạn nội dung hiện thời của ô nhớ 0002h là 48h. Dọc theo hàng thứ nhất
chúng ta thấy nội dung của các byte từ 0 đến 7, dấu gạch ngang rồi đến nội dung của các
byte từ 8 đến 0Fh. Nội dung của các byte từ 10 đến 1Fh được trình bày ở dòng thứ 2 và
cứ như vậy. Phần bên phải của hiển thị là nội dung của ô nhớ được dịch thành các ký tự
(các ký tự không in ra được thì đánh dấu bằng một chấm).
Để hiển thị chỉ riêng lời chào "HELLO!" chúng ta đánh:
-D 0002 0008
(xem hình bên dưới)
Trước khi tiếp tục chúng ta hãy chú ý đến một đặc trưng của nội dung bộ nhớ. Chúng ta
thường viết nội dung của một word theo trình từ byte cao rồi đến byte thấp. Thế nhưng
DEBUG lại hiển thị nội dung của ô nhớ theo trình tự byte thấp rồi đến byte cao, chẳng
hạn word tại địa chỉ DS:0002 có nội dung là 4548h nhưng DEBUG hiển thị nó thành 48
45. Điều này có thể gây ra nhầm lẫn khi chúng dịch nội dung của ô nhớ như các từ.
Bây giờ hãy đổi thông báo "HELLO!" thành "GOODBYE!" bằng lệnh E:
-E 0002 'GOODBYE!$'
Để kiểm chứng lại sự thay đổi chúng ta hãy hiển thị nội dung bộ nhớ:
-D 0000 000F
Dùng lệnh G để chạy thử chương trình xem kết quả có thay đổi gì không:
-G
106
Như vậy kết quả của chương trình đã bị thay đổi do có sự tác động trực tiếp vào nội
dung của đoạn dữ liệu DATA.
107
PHỤ LỤC 2: BẢNG MÃ ASCII
Mã ASCII (American Standard Code for Information Interchange) dùng 8 bít để biểu
diễn 1 kí tự. Tuy nhiên trên thực tế chỉ dùng 7 bít cuối (còn bít Msb luôn bằng 0). Do đó
số lượng kí tự trong bảng là 27=128.
Dec Hex Char Dec Hex Char Dec Hex Char Dec Hex Char
0 00 NUL 32 20 SP 64 40 @ 96 60 `
1 01 SOH 33 21 ! 65 41 A 97 61 a
2 02 STX 34 22 " 66 42 B 98 62 b
3 03 ETX 35 23 # 67 43 C 99 63 c
4 04 EOT 36 24 $ 68 44 D 100 64 d
5 05 ENQ 37 25 % 69 45 E 101 65 e
6 06 ACK 38 26 & 70 46 F 102 66 f
7 07 BEL 39 27 ' 71 47 G 103 67 g
8 08 BS 40 28 ( 72 48 H 104 68 h
9 09 HT 41 29 ) 73 49 I 105 69 i
10 0A LF 42 2A * 74 4A J 106 6A j
11 0B VT 43 2B + 75 4B K 107 6B k
12 0C FF 44 2C , 76 4C L 108 6C l
13 0D CR 45 2D - 77 4D M 109 6D m
14 0E SO 46 2E . 78 4E N 110 6E n
15 0F SI 47 2F / 79 4F O 111 6F o
16 10 DLE 48 30 0 80 50 P 112 70 p
17 11 DC1 49 31 1 81 51 Q 113 71 q
18 12 DC2 50 32 2 82 52 R 114 72 r
19 13 DC3 51 33 3 83 53 S 115 73 s
20 14 DC4 52 34 4 84 54 T 116 74 t
21 15 NAK 53 35 5 85 55 U 117 75 u
22 16 SYN 54 36 6 86 56 V 118 76 v
23 17 ETB 55 37 7 87 57 W 119 77 w
24 18 CAN 56 38 8 88 58 X 120 78 x
25 19 EM 57 39 9 89 59 Y 121 79 y
26 1A SUB 58 3A : 90 5A Z 122 7A z
27 1B ESC 59 3B ; 91 5B [ 123 7B {
28 1C FS 60 3C < 92 5C \ 124 7C |
29 1D GS 61 3D = 93 5D ] 125 7D }
30 1E RS 62 3E > 94 5E ^ 126 7E ~
31 1F US 63 3F ? 95 5F _ 127 7F DEL
Khi sử dụng nốt bít còn lại để biểu diễn kí tự (Msb = 1) ta sẽ có thêm 128 kí tự nữa, gọi
là bẳng mã ASCII mở rộng (không trình bày ở đây).
108
TÀI LIỆU THAM KHẢO
[1] Ytha Yu & Charles Marut - Ngôn ngữ lập trình Assembly và máy vi tính IBM-PC -
Nhà xuất bản Giáo dục 1996.
[2] Văn Thế Minh - Kỹ thuật vi xử lí – Nhà xuất bản Giáo dục 1997.
[3] Micheal Tischer - Cẩm nang lập trình hệ thống – Nhà xuất bản giáo dục 1996.
109
THÔNG TIN TÁC GIẢ
110