Tóm tắt lý thuyết lập trình Assembly
Tóm tắt lý thuyết lập trình Assembly
Preview text:
lOMoAR cPSD| 36066900
BƯỚC ĐẦU VỚI LẬP TRÌNH ASSEMBLY TRÊN VI XỬ LÝ 8088/8086
1. Giái thiệu về hợp ngữ:
Hợp ngữ (Assembly) là một ngôn ngữ lập trình cấp thấp, nó thực chất là dạng gợi nhớ (Mnemonic),
hay dạng kí hiệu, của ngôn ngữ máy.
Như đã biết, lệnh ngôn ngữ máy là một dãy các con số 0, 1 nên rất khó đọc và khó lập trình, vì thế
các nhà thiết kế vi xử lý đã đưa ra tập lệnh hợp ngữ gần với ngôn ngữ tự nhiên h¢n nên dễ đọc và dễ
lập trình h¢n. Tuy vậy, các lệnh hợp ngữ vẫn giao tiếp với phần cứng máy tính một cách rất chặt chẽ,
nhờ đó việc tiếp cận với lập trình hợp ngữ giúp chúng ta hiểu rõ h¢n về kiến trúc và tổ chức hoạt động của máy tính.
Ngoài ra nó còn giúp chúng ta thấy rõ h¢n mối quan hệ giữa các thành phần chức năng bên trong
máy tính và hệ điề hành. Có thể nói ngược lại là, việc tìm hiểu và lập trình trên hợp ngữ giúp chúng ta
hiểu rõ h¢n về kiến trúc máy tính, tổ chức hoạt động bên trong máy tính và hệ điều hành.
Trong giới hạn của tài liệu này chúng ta chỉ tìm hiểu tập lệnh hợp ngữ của các vi xử lý họ Intel
8088/8086, để lập trình chạy trên các máy IBM-PC: Sử dụng họ vi xử lý này và hoạt động trong sự
phối hợp với hệ điều hành MS_DOS.
Một trong những đặc điểm của hợp ngữ là chư¢ng trình viết trên nó có kích thước nhỏ h¢n và tốc độ
nạp/thực hiện chư¢ng trình nhanh h¢n so với viết (chư¢ng trình cùng chức năng) trên các ngôn ngữ lập trình bậc cao.
Ngoài ra, hầu hết các ngôn ngữ lập trình bậc cao hiện nay đều cho phép viết (ngữ trong nó. Điều này giúp người lập trình khai thác tối đa thế mạnh của các ngôn ngữ lập trình,
hợp ngữ rất mạnh trong các thao tác can thiệp sâu vào các thành phần bên trong hệ thống, trong khi
đó ngôn ngữ bậc cao mạnh trong các thao tác xử lý dữ liệu và thiết kế giao diện. Như vậy sẽ là rất
thuận lợi nếu sử dụng ngôn ngữ bậc cao để viết chư¢ng trình xử lý thông tin hệ thống, khi đó nhiệm
vụ truy xuất hệ thống (thanh ghi, bộ nhớ, cổng vào/ra, thiết bị,...) để lấy dữ liệu sẽ được giao cho các
đoạn mã lệnh hợp ngữ được nhúng trong chư¢ng trình này.
Hợp ngữ hỗ trợ 2 chế độ tư¢ng tác hệ thống: (1) Nhập trực tiếp từng lệnh/đoạn lệnh vào bộ nhớ rồi
cho phép thực hiện ngay trên bộ nhớ mà không cần qua bước biên dịch chư¢ng trình. Chư¢ng trình
gỡ rối Debug (đi kèm hệ điều hành MS_DOS: Debug.exe) là một trong những chư¢ng trình hỗ trợ
chế độ này cho hợp ngữ 16 bít; (2) Viết chư¢ng trình hợp ngữ, rồi sau đó sử dụng các chư¢ng trình
biên dịch để dịch nó sang chư¢ng trình thực thi (dạng EXE hoặc COM) và cho thực hiện chư¢ng trình này.
Hiện nay có hai loại trình biên dịch được sử dụng để biên dịch chư¢ng trình hợp ngữ (từ tập lệnh
hợp ngữ của các vi xử lý họ Intel) sang chư¢ng trình thực thi: Trình biên dịch hợp ngữ 16 bít, MASM
(Macro Assembler), được sử dụng để dịch thành các chư¢ng trình chạy trên nền hệ điều hành 16 bít
MS_DOS; Trình biên dịch hợp ngữ 32 bít, MASM32 (Macro Assembler 32 bít), được sử dụng để dịch
thành các chư¢ng trình chạy trên nền hệ điều hành 32 bít MS_Windows. Trong thực tế, để chuyển
một chư¢ng trình hợp ngữ sang dạng chư¢ng trình thực thi EXE 16 bít hoặc COM 16 bít thì cần phải
có sự hỗ trợ của chư¢ng trình tiện ích của hệ điều hành MS_DOS: Link (Link.exe) và EXE2Bin (EXE2Bin.com). lOMoAR cPSD| 36066900
Chư¢ng trình hợp ngữ 16 bít sử dụng hệ thống các ngắt mềm (Interrupt) của BIOS và DOS như là
thư viện lập trình của nó, trong khi đó chư¢ng trình hợp ngữ 32 bít sử dụng tập hàm API làm thư viện lập trình của nó.
2. Biến – Hằng trong chương trình hợp ngữ: Biến và hằng
Biến và hằng (hằng có tên) trong chư¢ng trình hợp ngữ có tính chất, mục đích sử dụng, kiểu dữ liệu,
quy tắc đặt tên, quy tắc gán giá trị,... tư¢ng tự như biến và hằng trong các ngôn ngữ lập trình bậc cao
khác. Biến trong chư¢ng trình hợp ngữ chỉ có các kiểu dữ liệu là: Byte, Word, Doubleword,... và hằng
trong chư¢ng trình hợp ngữ có thể là số, kí tự hoặc một xâu kí tự.
Khi viết chư¢ng trình hợp ngữ chúng ta cần quan tâm đến địa chỉ của biến trong bộ nhớ. Một biến
được khai báo trong chư¢ng trình sẽ được hệ thống gán cho một địa chỉ trong bộ nhớ (khi chư¢ng
trình được nạp vào bộ nhớ để hoạt động). Cụ thể: mỗi biến trong chư¢ng trình sẽ được định vị tại
một địa chỉ xác định trong bộ nhớ, và các biến được khai báo liên tiếp nhau trong chư¢ng trình (từ
trên xuống dưới) sẽ được định vị tại các địa chỉ liên tiếp nhau trong bộ nhớ (từ offset thấp đến offset
cao). Nhờ đó, nếu chư¢ng trình xác định được địa chỉ của một biến nào đó thì nó dễ dàng có được
địa chỉ và nội dung của các biến khác trong chư¢ng trình.
Khác với biến, hằng trong chư¢ng trình hợp ngữ không được cấp phát bộ nhớ để lưu trữ, tức là, n¢i
nào trong chư¢ng trình chứa trên hằng thì sẽ được trình biên dịch thay bằng giá trị của nó một cách trực tiếp.
Hợp ngữ cung cấp các toán tử giả để định nghĩa/khai báo dữ liệu: DB (định nghĩa byte), DW (định
nghĩa word), DD (định nghĩa doubleword),. ... Và toán tử EQU để khai báo hằng. Biến có thể được
khai báo ở đầu hoặc ở cuối chư¢ng trình. Trong khi đó, hằng có thể khai báo ở bất kỳ n¢i đâu trong
chư¢ng trình, khi đó ta có thể sử dụng toán tử dấu <== để gán giá trị cho hằng.
Khai báo biến – hằng: Cú pháp khai báo: • a: DB • b: DW • c: DD • d: EQU
Trường hợp a được sử dụng để khai báo biến kiểu byte, trường hợp b được sử dụng để khai
báo biến kiểu word, trường hợp c được sử dụng để khai báo biến kiểu doubleword, trường hợp d
được sử dụng để khai báo hằng. có thể một hoặc nhiều giá trị, nó có thể là một số, một
kí tự hoặc một xâu kí tự, và cũng có thể là một dấu hỏi chấm (<?=). có thể là một số, một
kí tự hay một xâu kí tự. Ví dā 1: • Spt DB 0 • KiTu DB 8a9 • TieuDe DB 8Tin hoc9 • SoNguyen DW ? lOMoAR cPSD| 36066900 • DaySo DD 1020, 1345, 2389, 5763
Trong ví dụ trên, hai biến Spt và Kitu đều là biến kiểu byte, kích thước 1byte. Biến TieuDe cũng là
biến kiểu byte nhưng gồm 7 byte ô nhớ liên tiếp (kích thước 7 byte), mỗi byte chứa 1 kí tự ASCII.
Biến SoNguyen là biến kiểu word, chưa được gán giá trị khởi tạo. Biến DaySo là biến kiểu
doubleword, gồm 4 phần tử có giá trị lần lượt (từ thấp đến cao) là: 1020, 1345, 2389, 5763. Ví dụ 2: • LF EQU 0Ah • TB EQU 8Cong nghe Thong tin9 • TieuDe DB TB
Khai báo trên cho thấy, có thể khởi tạo giá trị ban đầu cho biến thông qua một hằng đã được định nghĩa trước.
Ví dụ 3: TenKhoa DB 8Cong nghe Thong tin9, 0Ah, 0Dh, 8$9
Khai báo biến TenKhoa cho thấy, có thể khai báo một biến mà trong đó bao gồm cả số, kí tự và xâu
kí tự, đây là biến kiểu byte, gồm 22 byte. Ví dụ 4: SoPT DW 2345h
Biến SoPT ở trên là một biến word, trong trường hợp này byte thấp của nó nhận giá trị 45h, byte cao
nhận giá trị 23h, nhưng byte thấp định vị tại địa chỉ SoPT, byte cao định vị tại địa chỉ SoPT + 1.
Trong hợp ngữ, một dãy các byte hay word liên tiếp nhau trong bộ nhớ có thể xem là một mảng
(mảng byte hay mảng word). Biến DaySo trong ví dụ 1 ở trên có thể được xem là một mảng word
gồm 4 phần tử. Giá trị của các phần tử trong mảng có thể được xác định thông qua tên biến và chỉ
số tư¢ng ứng (địa chỉ). Cụ thể:
DaySo[0] = 1020; DaySo[2] = 1345; DaySo[4] = 2389; DaySo[6] = 5763.
Hợp ngữ cho phép sử dụng toán tử DUP để khai báo một biến dạng mảng mà trong đó gồm nhiều
phần tử có cùng giá trị khởi tạo. Dạng sử dụng toán tử DUP là m Dup (n): gồm m phần tử có cùng
giá trị khởi tạo là n.
Ví dụ 5: MangSN DW 23, 45, 50 Dup (0), 12
Như vậy, biến MangSN được xem là một mảng word gồm 53 phần tử, hai phần tử đầu tiên nhận giá
trị lần lượt là 23 và 45, 50 phần tử tiếp theo nhận cùng giá trị 0 và phần tử cuối cùng nhận giá trị 12.
Trong ví dụ 1 ở trên: Các biến được khai báo ở đây sẽ được định vị tại các địa chỉ liên tiếp nhau
trong bộ nhớ. Nếu biến Spt được định vị tại địa chỉ offset 100 trong đoạn nhớ dữ liệu thì các biến tiếp
theo sẽ được định vị tại các offset sau đó. Cụ thể: Biến KiTu bắt đầu tại offset 101, biến TieuDe bắt
đầu tại offset 102, biến SoNguyen định vị tại offset 109, biến DaySo bắt đầu tại offset 111 (xem hình sau):
100 101 102 103 104 105 106 107 108 109 111 113 115 117 0 a T i n _ H o c 1020 1345 2389 5763
(dòng trên là địa chỉ offset của biến, dòng dưới là các ô nhớ chứa giá trị của các phần tử trong biến) lOMoAR cPSD| 36066900
Điều cần quan tâm ở đây là, có thể truy xuất đến giá trị của một phần tử trong biến này thông qua tên
của một biến khác. Ví dụ: Spt[0] = 0, TieuDe[0] = 8T9, TieuDe[1] = 89i, DaySo[0] = 1020, DaySo[6]
= 5763,... nhưng cũng có thể
Spt[2] = KiTu[1] = 8T9, KiTu[5] = 8h9, DaySo[-5] = 8h9, TieuDe[11] = 1345,... lOMoAR cPSD| 36066900
Cấu trúc của một chương trình Assembly
Hầu hết các hệ điều hành máy tính hiện nay, đặc biệt là các hệ điều hành của Microsoft, đều hỗ trợ hai dạng cấu
trúc tập tin thực thi có thể hoạt động trên nó, đó là tập tin cấu trúc dạng COM và tập tin cấu trúc dạng EXE. Có
nhiều điểm khác nhau giữa hai cấu trúc chư¢ng trình này, nhưng điểm khác biệt lớn nhất là: Các chư¢ng trình
cấu trúc dạng EXE gồm 3 đoạn: Mã lệnh (Code), dữ liệu (Data) và Ngăn xếp (Stack). Khi hoạt động, 3 đoạn này
sẽ được nạp vào 3 đoạn (Segment) bộ nhớ tách biệt trên bộ nhớ;
Các chư¢ng trình dạng COM thì ngược lại, nó chỉ có 1 đoạn mã lệnh, trong đó chứa cả mã lệnh và ngăn
xếp. Vì thế, khi được nạp vào bộ nhớ để hoạt động nó chỉ được cấp phát một đoạn bộ nhớ. Rõ ràng kích thước
của một chư¢ng trình dạng COM không thể vượt quá giới hạn của một đoạn bộ nhớ (với Intel 8088/80286 và
MSDOS, 1 Segment bộ nhớ = 64KB).
Trong khi đó một chư¢ng trình dạng EXE có thể lớn h¢n 3 Segment bộ nhớ. Do đó, khi thiết kế các
chư¢ng trình lớn, với chức năng phức tạp, trong đó có liên kết giữa các modun chư¢ng trình khác nhau thì ta
phải thiết kế theo cấu trúc chư¢ng trình dạng EXE.
Hợp ngữ hỗ trợ thiết kế cả hai dạng cấu trúc chư¢ng trình EXE và COM, mỗi dạng phù hợp
với một nhóm trình biên dịch nào đó. Muốn biên dịch một chư¢ng trình hợp ngữ sang dạng EXE thì
ngoài việc nó phải được viết theo cấu trúc dạng EXE ta còn cần phải sử dụng một trình biên dịch phù
hợp. Điều này cũng tư¢ng tự với việc muốn có một chư¢ng trình thực thi dạng COM.
Văn bản của một chư¢ng trình hợp ngữ dạng EXE cũng cho thấy rõ nó gồm 3 đoạn: Code,
Data và Stack. Tư¢ng tự, văn bản của chư¢ng trình hợp ngữ dạng COM cho thấy nó chỉ có 1 đoạn:
Code, cả Data và Stack (không tường minh) đều nằm ở đây.
Một chư¢ng trình hợp ngữ gồm hai thành phần chính: phần lệnh hợp ngữ và phần chỉ dẫn
biên dịch. Chỉ có các lệnh là được biên dịch thành ngôn ngữ máy. Phần hướng dẫn biên dịch không
được dịch sang ngôn ngữ máy, nó chỉ có tác dụng với các trình biên dịch. Thông thường mỗi chư¢ng
trình biên dịch có một nhóm hướng dẫn biên dịch phù hợp với nó, những với các hướng dẫn biên
dịch c¢ bản và đ¢n giản thì nó phù hợp với hầu hết các trình biên dịch hợp ngữ hiện nay. Trong tài
liệu này chúng tôi sử dụng các hướng dẫn biên dịch phù hợp với trình biên dịch Microsoft Macro Assembler (MASM).
Cấu trúc chư¢ng trình được giới thiệu sau đây sử dụng các hướng dẫn biên dịch định nghĩa
đoạn đ¢n giản (.Model, .Code, .Stack, .Data) phù hợp với MASM, TASM (Turbo Macro Assembler),
A86. Việc sử dụng định nghĩa đoạn đ¢n giản sẽ làm cho văn bản chư¢ng trình sáng sủa và dễ đọc
h¢n. Với các định nghĩa đoạn đ¢n giản ta cũng có thể xây dựng được các chư¢ng trình từ đ¢n giản đến phức tạp.
Cấu trúc chương trình dạng COM: .Model ‹Chế độ bộ nhớ> .Code ORG 100h ‹Nhãn chính>: JMP ‹Thủ tục chính> lOMoAR cPSD| 36066900
‹Khai báo dữ liệu đặt tại đây> ‹Thủ tục chính> PROC
‹Các lệnh của chương trình đặt tại đây> ‹Thủ tục chính> Endp
‹Các thủ tục khác đặt tại đây>
End ‹Nhãn chính>
Trong cấu trúc chư¢ng trình trên các từ khóa Model, Code, ORG, Proc, Endp, End là các hướng dẫn
biên dịch. là nhãn của lệnh Jmp.
Cấu trúc này cho thấy rõ, một chư¢ng trình hợp ngữ dạng COM chỉ có 1 đoạn, đó chính là đoạn
Code (đoạn mã lệnh), trong này bao gồm cả phần khai báo dữ liệu. Các khai báo dữ liệu trong
chư¢ng trình dạng COM có thể đặt ở đầu hoặc ở cuối chư¢ng trình, nhưng với việc sử dụng định
nghĩa đoạn đ¢n giản các khai báo dữ liệu phải đặt ở đầu chư¢ng trình.
Chỉ dẫn ORG 100h và lệnh JMP sẽ được đề cập trá lại á các phần sau đây của tài liệu này.
Cấu trúc chương trình dạng EXE: .Model ‹Chế độ bộ nhớ> .Stack 100h .Data
‹Khai báo dữ liệu đặt tại đây> .Code ‹Thủ tục chính> PROC
‹Các lệnh của chương trình đặt tại đây> ‹Thủ tục chính> Endp END
Trong cấu trúc chư¢ng trình trên các từ khóa Model, Code, Data, Stack, Proc, Endp, End là các hướng dẫn biên dịch.
Cấu trúc này cho thấy rõ, một chư¢ng trình hợp ngữ dạng gồm 3 đoạn: đoạn Code, chứa toàn bộ mã
lệnh của chư¢ng trình. Đoạn Data, chứa phần khai báo dữ liệu của chư¢ng trình. Đoạn Stack, n¢i
chứa stack (ngăn xếp) của chư¢ng trình khi chư¢ng trình được nạp vào bộ nhớ để hoạt động. lOMoAR cPSD| 36066900
Chỉ dẫn .Stackđặt ở đầu chư¢ng trình với mục đích khai báo kích thước của Stack dùng cho chư¢ng
trình sau này. Kích thước thường được chọn là 100h (256) byte.
Chỉ dẫn .Model được đặt ở đầu cả cấu trúc chư¢ng trình dạng COM và EXE với mục đích khai báo
chế độ bộ nhớ mà chư¢ng trình sử dụng.
Ví dā: Sau đây là hai chư¢ng trình hợp ngữ đ¢n giản, dạng COM và dạng EXE, cùng thực hiện
nhiệm vụ in ra màn hình 3 dòng văn bản như sau : Nguyen Kim Le Tuan Nguyen Le Tram Thanh Nguyen Le Tram Uyen
Hai chương trình dưới đây chỉ có tác dụng minh họa cho việc sử dụng các hướng dẫn biên dịch định
nghĩa đoạn đơn giản và giúp các bạn thấy được những điểm giống nhau, khác nhau giữa hai dạng
cấu trúc chương trình dạng COM và EXE, vì vậy, á đây các bạn chưa cần quan tâm đến ý nghĩa của
các lệnh và các hàm/ngắt trong nó. Phần lệnh hợp ngữ và các hàm/ngắt sẽ được trình bày ngay sau đây.
Chương trình viết theo cấu trúc dạng COM: .Model Small .Code ORG 100h Start: Jmp Main MyChildren DB
8Nguyen Kim Le Tuan9,0Ah,0Dh DB
8Nguyen Le Tram Thanh9,0Ah,0Dh DB
8Nguyen Le Tram Uyen9,9$9 Main PROC
;------- in ra mot xau voi ham 09/21h ------- Mov Ah, 09h Lea Dx, MyChildren Int 21h
;------- ket thuc chuong trinh ------- Int 20h Main Endp End Start lOMoAR cPSD| 36066900
Chư¢ng trình này chọn chế độ bộ nhớ Small. Tên thủ tục chính là Main (tên thủ tục chính là tùy ý).
Nhãn chính của chư¢ng trình là Start (tên thủ tục chính là tùy ý), đó chính là nhãn của lệnh Jmp.
Phần khai báo dữ liệu chỉ khai báo một biến, đó là MyChildren.
Chư¢ng trình này gọi hàm 4Ch của ngắt 21h để kết thúc chư¢ng trình. Có thể gọi ngắt 20h để kết
thúc các chư¢ng trình dạng COM.
Chương trình viết theo cấu trúc dạng EXE: .Model Small .Stack 100h .Data MyChildren DB
8Nguyen Kim Le Tuan9,0Ah,0Dh DB
8Nguyen Le Tram Thanh9,0Ah,0Dh DB
8Nguyen Le Tram Uyen9,9$9 .Code Main PROC
;------- khởi tạo DS ------- Mov Ax, @Data Mov DS, Ax
;------- in ra mot xau voi ham 09/21h ------- Mov Ah, 09h Lea Dx, MyChildren Int 21h
;------- ket thuc chuong trinh ------- Mov Ah, 4Ch Int 21h Main Endp END Main
Chư¢ng trình này chọn chế độ bộ nhớ Small. Khai báo kích thước Stack là 100h byte. Phần khai báo
dữ liệu được đặt trong đoạn Data, ở đây chỉ khai báo một biến, đó là MyChildren. Tên thủ tục chính là
Main (tên thủ tục chính là tùy ý).
Thao tác đầu tiên của chư¢ng trình là trỏ thanh ghi đoạn DS về đầu đoạn Data, hay còn gọi là khởi tạo thanh ghi đoạn DS: Mov Ax, @Data Mov DS, Ax lOMoAR cPSD| 36066900
thao tác này được xem như là bắt buộc đối với cấu trúc chư¢ng trình dạng EXE sử dụng định nghĩa
đoạn đ¢n giản. Các chư¢ng trình viết theo cấu trúc dạng EXE phải gọi hàm 4Ch của ngắt 21h để kết thúc.
Có thể thấy, cấu trúc chư¢ng trình dạng COM và cấu trúc chư¢ng trình dạng EXE chỉ khác phần
hướng dẫn biên dịch, phần khai báo biến và phần lệnh thao tác chính hoàn toàn giống nhau. Hai
chư¢ng trình đ¢n giản ở trên hoàn toàn giống nhau ở biến là MyChildren và các lệnh gọi hàm 09h
của ngắt 21h để in ra màn hình một xâu kí tự (xâu này chính là giá trị khởi tạo của biến MyChildren).
Chú ý 1: Trình biên dịch hợp ngữ (Macro Assembler) cho phép các chư¢ng trình được dịch bởi nóc
họn sử dụng một trong các chế độ bộ nhớ sau:
- Small: Đoạn mã lệnh (Code) và đoạn dữ liệu (Data) của chư¢ng trình đều chỉ có thể chứa trong
một đoạn (segment) bộ nhớ. Tức là, kích thước của chư¢ng trình chỉ có thể tối đa là hai đoạn bộ
nhớ. Tuy vậy chế độ bộ nhớ này đủ dùng cho hầu hết các chư¢ng trình hợp ngữ.
- Medium: Đoạn Code của chư¢ng trình có thể chiếm nhiều h¢n một đoạn bộ nhớ. Trong khi đó,
đoạn Data chỉ có thể chiếm 1 đoạn bộ nhớ.
- Compact: Đoạn Data của chư¢ng trình có thể chiếm nhiều h¢n một đoạn bộ nhớ. Trong khi đó,
đoạn Code chỉ có thể chiếm 1 đoạn bộ nhớ.
- Large: Đoạn Code và đoan Data của chư¢ng trình đều có thể chiếm nhiều h¢n một đoạn bộ nhớ.
Nhưng trong trường hợp này không thể định nghĩa một mảng dữ liệu có kích thước lớn h¢n 64 Kbyte.
- Huge: Tư¢ng tự như Large, nhưng trong trường hợp này có thể định nghĩa một mảng dữ liệu có
kích thước lớn h¢n 64 Kbyte.
Chế độ bộ nhớ Small là đ¢n giản nhất, được hầu hết các chư¢ng trình lựa chọn.
Chú ý 2: Với các chư¢ng trình hợp ngữ sử dụng định nghĩa đoạn đ¢n giản: Khi được nạp vào bộ
nhớ để hoạt động thì các thanh ghi đoạn sẽ tự động trỏ về các đoạn chư¢ng trình tư¢ng ứng. Cụ thể:
Thanh ghi đoạn CS chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Code của chư¢ng trình.
Thanh ghi đoạn DS (và có thể cả ES) chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Data của
chư¢ng trình. Thanh ghi đoạn SS chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Stack của chư¢ng trình.
Tuy nhiên, trong thực tế, khi nạp chư¢ng trình EXE vào bộ nhớ DOS luôn dành ra 256 byte đầu tiên
của vùng nhớ, mà DOS cấp phát cho chư¢ng trình, để chứa PSP (Program Segment Prefix) của
chư¢ng trình. PSP chứa các thông tin cần thiết mà trình biên dịch chuyển đến cho DOS để hỗ trợ
DOS trong việc thực hiện chư¢ng trình này, đặc biệt, chư¢ng trình cũng có thể truy xuất vùng nhớ
PSP. Do đó, DOS phải đưa địa chỉ segment của vùng nhớ chứa PSP vào cả DS và ES trước khi
chư¢ng trình được thực hiện. Tức là, ngay khi chư¢ng trình được nạp vào bộ nhớ DS không phải
chứa địa chỉ segment của đoạn Data của chư¢ng trình mà chứa địa chỉ segment của PSP.
Vì vậy, để trỏ DS về lại đoạn Data chư¢ng trình chúng ta phải đặt ngay hai lệnh sau đây ở đầu
chư¢ng trình viết theo cấu trúc EXE: Mov Ax, @Data Mov DS, Ax lOMoAR cPSD| 36066900
Với việc khởi tạo thanh ghi đoạn DS ở trên, địa chỉ segment của tất cả các biến khai báo trong đoạn
Data đều được chứa trong thanh ghi DS, do đó, trong các thao tác xử lý biến sau này chư¢ng trình
không cần quan tâm đến địa chỉ segment của nó nữa.
Chú ý 3: Hợp ngữ còn cho phép các chư¢ng trình sử dụng các hướng dẫn biên dịch định nghĩa đoạn
toàn phần, các định nghĩa này phù hợp với hầu hết các trình biên dịch hợp ngữ hiện nay. Định nghĩa
đoạn toàn phần giúp cho việc viết chư¢ng trình hợp ngữ trở nên mềm dẻo và linh hoạt h¢n, nó giúp
người lập trình có thể điều khiển thứ tự các đoạn chư¢ng trình, kết hợp các đoạn chư¢ng trình, liên
kết các đoạn chư¢ng trình trong bộ nhớ,... , ngay trong khi lập trình.
Chi tiết về cách sử dụng và mục đích sử dụng của các hướng dẫn biên dịch nói chung và các định
nghĩa đoạn toàn phần nói riêng dễ dàng tìm thấy trong rất nhiều tài liệu về lập trình hợp ngữ [1], [2]. à
đây chúng tôi chỉ giới thiệu sơ lược về nó thông qua ví dụ dưới đây.
Ví dā: Sau đây là một chư¢ng trình dạng EXE sử dụng các hướng dẫn biên dịch định nghĩa đoạn
toàn phần (phù hợp với Macro Assembler): S_Seg Segment Stack DB 100h DUP (?) S_Seg Ends D_Seg Segmet MyChildren DB
8Nguyen Kim Le Tuan9,0Ah,0Dh DB
8Nguyen Le Tram Thanh9,0Ah,0Dh DB
8Nguyen Le Tram Uyen9,9$9 D_Seg Ends C_Seg Segment ASSUME CS:C_Seg, SS:S_Seg, DS:D_Seg Main PROC
;------- khởi tạo DS ------- Mov Ax, D_Seg Mov DS, Ax Mov Ah, 09h Lea
Dx, MyChildren ; địa chỉ offset của biến MyChildren Int 21h Mov Ah, 4Ch Int 21h Main Endp C_Seg Ends lOMoAR cPSD| 36066900 END Main
Điều dễ nhận thấy đầu tiên là phần khai báo biến và phần lệnh chính trong chư¢ng trình này hoàn
toàn giống như trong chư¢ng trình sử dụng định nghĩa đoạn đ¢n giản (hai chư¢ng trình ví dụ ở trên).
Chư¢ng trình này sử dụng hướng dẫn biên dịch định nghĩa đoạn toàn phần Segment ... Ends để
định nghĩa 3 đoạn chư¢ng trình với tên lần lượt là: S_Seg (đoạn stack), D_Seg (đoạn Data), C_Seg
(đoạn Code). Tên của các đoạn được định nghĩa ở đây là tùy ý.
Hướng dẫn biên dịch Assume được sử dụng để báo cho trình biên dịch biết chư¢ng trình muốn
chứa địa chỉ segment của các đoạn chư¢ng trình trong các thanh ghi đoạn nào (trỏ thanh ghi đoạn về
đoạn chư¢ng trình). Cụ thể ở đây là: Thanh ghi đoạn CS chứa địa chỉ segment của đoạn Code
(CS:C_Seg). Thanh ghi đoạn SS chứa địa chỉ segment của đoạn Stack (SS:S_Seg). Thanh ghi đoạn
DS chứa địa chỉ segment của đoạn Data (DS:C_Seg). Tuy nhiên, trong thực tế Assume
DS:D_Seg không tự động nạp địa chỉ segment của D_Seg vào DS, do đó chư¢ng trình phải nạp trực tiếp bằng các lệnh: Mov Ax, D_Seg Mov DS, Ax
Nên nhớ, hướng dẫn biên dịch Segment ... Ends chỉ có tác dụng định nghĩa đoạn, nó không thể báo
cho trình biên dịch biết đoạn được định nghĩa thuộc loại đoạn chư¢ng trình nào (Code, Data, Stack,
Extra). Chỉ có định nghĩa Segment Stack ... Ends là báo cho trình biên dịch biết đoạn được định
nghĩa là đoạn Stack, nhờ đó, khi chư¢ng trình được nạp vào bộ nhớ thanh ghi đoạn SS sẽ được trỏ về đoạn này lOMoAR cPSD| 36066900
Phần 1: MỘT SỐ LỆNH ASSEMBLY CƠ SỞ Cú pháp LỆNH:
Một lệnh hợp ngữ đầy đủ gồm bốn thành phần sau đây: [Nhãn lệnh:] [Các toán hạng]
[;Lời giải thích] Trong đó:
- [Nhãn lệnh:]: Là một dãy các kí tự đứng trước câu lệnh (kết thúc bởi dấu hai chấm (:)), nó được
chỉ định thay thế cho địa chỉ của câu lệnh trong các đoạn lệnh lặp, rẽ nhánh,... Do đó, nó chỉ được sử dụng khi cần.
Trong một chư¢ng trình hợp ngữ không thể có hai nhãn lệnh trùng tên, tên của các nhãn cũng không
thể trùng với tên của các thủ tục trong chư¢ng trình.
- : Là một trong các lệnh thuộc tập lệnh hợp ngữ (lệnh gợi nhớ: Mnemonic) của vi xử lý
trên máy tính thực hiện lệnh này.
Lệnh hợp ngữ không phân biệt chữ hoa hay chữ thường. Trong chư¢ng trình hợp ngữ mỗi dòng chỉ
có thể chứa một lệnh và mỗi lệnh phải được đặt trên một dòng.
- [Các toán hạng]: Là đối tượng mà lệnh tác động vào. Một lệnh hợp ngữ của Intel 8088/8086 có
thể không có toán hạng, có một toán hạng, hoặc có hai toán hạng. Nếu có hai toán hạng thì toán
hạng đứng trước gọi là [Toán hạng đích], toán hạng đứng sau gọi là [Toán hạng nguồn]. [Toán hạng
đích] không thể là một hằng số.
Một số lệnh hợp ngữ của các Intel 80286/80386/... có thể có đến 3 toán hạng, trong trường hợp này
cũng chỉ có một [Toán hạng đích].
- [;Lời giải thích]: Chỉ có tác dụng với người viết và người đọc chư¢ng trình, nó không có ý nghĩa
với trình biên dịch, tức là, không được dịch sang mã máy. Lời giải thích thường được sử dụng để làm
rõ ý nghĩa của câu lệnh (khi người viết thấy cần). Lời giải thích phải nằm sau dấu chấm phảy (;).
Ví dā 1: Xét lệnh sau đây:
Lenh_VD: Mov AX,BX
; đặt giá trị thanh ghi BX vào thanh ghi AX Trong đó:
• Lenh_VD: Trong trường hợp này dãy kí tự Lenh_VD được sử dụng làm nhãn lệnh cho lệnh Mov.
• Mov: Là tên lệnh.
• AX và BX: Là các toán hạng (đích và nguồn). Trong trường hợp này toán hạng là các thanh ghi đa năng 16 bít.
• <đặt giá trị thanh ghi BX vào thanh ghi AX=: Là lời giải thích cho lệnh này. Trong thực tế lời giải thích
thường là tiếng Việt không dấu.
Ví dā 2: Xem các lệnh sau đây: - NOP
; đây là lệnh không có toán hạng lOMoAR cPSD| 36066900 - Mov Ax, Bl
; lệnh này có hai toán hạng, [Toán hạng đích] là thanh
; ghi 16 bít, [Toán hạng nguồn] là thanh ghi 8 bít - Add Cl, Spt
; lệnh này có hai toán hạng, [Toán hạng đích] là thanh
; ghi 8 bít, [Toán hạng nguồn] là một biến byte - Mov Ax, [SI]
; lệnh này có hai toán hạng, [Toán hạng đích] là thanh
; ghi 16 bít, [Toán hạng nguồn] là một ô nhớ - Sub Dl, 8a9 – 8A9
; lệnh này có hai toán hạng, [Toán hạng đích] là thanh
; ghi 8 bít, [Toán hạng nguồn] là một hằng số - IMul Ax, Bx, 20
; lệnh này có ba toán hạng, [Toán hạng đích] là thanh
; ghi 16 bit (Ax), [Toán hạng nguồn] là thanh ghi 16 bít
; (Bx) và một hằng số (20)
Lệnh Imul ở trên là một lệnh nhân mới của vi xử lý Intel 80286. Lệnh này thực hiện như sau: lấy nội
dung/giá trị hai [Toán hạng nguồn] nhân với nhau, kết quả chứa ở [Toán hạng đích] (trong lệnh trên
là: Bx*20, tích kết quả chứa ở thanh ghi Ax (chỉ lấy 16 bít thấp của tích để đưa vào Ax)).
1. Lßnh Mov (Move): Cú pháp lßnh: Mov
[Toán hạng đích], [Toán hạng nguồn] Trong đó:
- [Toán hạng đích]: Có thể là thanh ghi (8 bít hay 16 bít), ô nhớ (chính xác h¢n là địa chỉ của một ô
nhớ) hay một biến nào đó. [Toán hạng đích] không thể là hằng số.
- [Toán hạng nguồn]: Có thể là hằng số, biến, thanh ghi, ô nhớ (chính xác h¢n là địa chỉ của một ô nhớ) nào đó.
Tác dāng: Lấy nội dung (giá trị) của [Toán hạng nguồn] đặt vào [Toán hạng đích]. Nội dung của
[Toán hạng nguồn] không bị thay đổi. Ví dā 1: - Mov Ax, 5
; Ax ß 5: đặt giá trị 5 vào thành ghi Ax - Mov Ax, 5*2
; Ax ß 5*2: đặt giá trị 10 vào thành ghi Ax - Mov
Bx, (80*(Dong - 1) + (Cot - 1))*2 ; Dong, Cot là các biến - Mov Dl, 8A9
; Dl = 41h: đặt mã ASCII của 8A9 vào thanh ghi Dl - Mov Cx, Var1
; Cx = Var1: đặt giá trị của biến Var1 vào thanh ghi Cx - Mov Ax, Bx
; Ax = Bx: đặt giá trị của thanh ghi Bx vào Ax - Mov Ax, Dl
; Ax = Dl: đặt giá trị của Dl (8 bít) vào Ax (16 bít) lOMoAR cPSD| 36066900 - Mov Bl, Dx
; Bl = Dx: không hợp lệ, vì: Dx (16 bít) mà Bl (8 bít) - Mov Dl, 300
; Dl = 300: không hợp lệ, vì 300 vượt giới hạn 1 byte
Ví dā 2: Giả sử DI = 100; Ô nhớ tại địa chỉ offset 100 trong đoạn nhớ Data (được chỉ bởi DS) chứa kí tự B. Thì : - Mov Ax, DI
; (1) đặt giá trị thanh ghi DI vào thanh ghi Ax: Ax = 100 - Mov Ax, [DI] ; (2) Ax =
; chứ địa chỉ offset của ô nhớ)>. Tức là, đặt nội dung của
; ô nhớ được chỉ bởi DI vào thanh ghi Ax: Ax = 41h
Hãy phân biệt sự khác nhau giữa hai lệnh trên: Lệnh (1) sử dụng chế độ địa chỉ thanh ghi. Lệnh (2)
sử dụng chế độ địa chỉ gián tiếp thanh ghi.
Nhớ lại rằng: Trong chế độ địa chỉ gián tiếp thanh ghi, các thanh ghi chỉ có thể là BX, DI, SI (địa chỉ
đoạn chứa trong DS) hay BP (địa chỉ đoạn chứa trong SS). Như vậy lệnh (2) tư¢ng đư¢ng với lệnh (3) nhưng khác lệnh (4): - Mov Ax, DS:[DI] ; (3) - Mov Ax, ES:[DI] ; (4) Ví dā 3: - Mov Ax, [SI]
; đặt nội dung ô nhớ được chỉ bởi SI vào thanh ghi Ax - Mov [DI], Bx
; đặt giá trị của thanh ghi bx vào ô nhớ được chỉ bởi DI - Mov [DI], [SI]
; [DI] ß [SI] : lệnh không hợp lệ, vì: không thể chuyển
; nội dung của ô nhớ vào một ô nhớ một cách trực tiếp - Mov Var1, Ax
; Var1 ß Ax : đặt giá trị t/ghi Ax vào biến word Var1 Chú ý: -
Lệnh Mov không làm ảnh hưởng đến các cờ. - Mov DS:[DI], ES:[SI]
; lệnh không hợp lệ, vì: không thể chuyển dữ liệu
; trực tiếp giữa hai toán hạng bộ nhớ với nhau - Mov DS, ES
; DS ß ES: lệnh không hợp lệ, - Mov ES, 0100
; lệnh không hợp lệ, vì: không thể chuyển
; trực tiếp một hằng số vào thanh ghi đoạn.
Để chuyển giá trị của hai thanh ghi đoạn hay nội dung của hai ô nhớ ta có thể mượn một thanh ghi đa năng làm trung gian: - Mov Ax, ES
; hai lệnh này chuyển nội dung của thanh ghi đoạn ES Mov DS, Ax
; vào thanh ghi đoạn DS thông qua thanh ghi Ax lOMoAR cPSD| 36066900
Theo cách thông thường, để hoán đổi giá trị của hai thanh ghi đoạn hay nội dung của hai ô nhớ
người ta thường sử dụng hai thanh ghi đa năng làm trung gian: - Mov Ax, [DI]
; lưu tạm nội dung ô nhớ được chỉ bởi DI và Ax Mov Bx, [SI]
; lưu tạm nội dung ô nhớ được chỉ bởi SI và Bx Mov [DI], Bx
; chuyển giá trị của t/ghi Bx và ô nhớ được chỉ bởi DI Mov [SI], Ax
; chuyển giá trị của t/ghi Ax và ô nhớ được chỉ bởi SI
Bốn lệnh trên có tác dụng hoán đổi nội dung của hai ô nhớ trong đoạn Data (DS) được chỉ bởi DI và
SI (DI và SI chứa địa chỉ Offset của các ô nhớ). -
Không thể dùng thanh ghi đoạn CS làm [Toán hạng đích] trong lệnh Mov.
2. Các lßnh Inc – Dec – Add và Sub Cú pháp lßnh: • Inc
[Toán hạng đích] • Add
[Toán hạng đích],[Toán hạng nguồn] • Dec
[Toán hạng đích] • Sub
[Toán hạng đích],[Toán hạng nguồn]
Trong đó: [Toán hạng đích], [Toán hạng nguồn]: tư¢ng tự lệnh Mov. Tác dāng:
• Lệnh Inc (Increment): làm tăng giá trị của [Toán hạng đích] lên 1 đ¢n vị.
• Lệnh Dec (Decrement): làm giảm giá trị của [Toán hạng đích] xuống 1 đ¢n vị.
• Lệnh Add (Addition): lấy giá trị/nội dung của [Toán hạng nguồn] cộng vào giá trị/nội dung của [Toán hạng
đích], kết quả này đặt vào lại [Toán hạng đích].
• Lệnh Sub (Subtract): lấy giá trị/nội dung của [Toán hạng đich] trừ đi giá trị/nội dung của [Toán hạng nguồn],
kết quả này đặt vào lại [Toán hạng đích]. Ví dā 1: Mov Ax, 121
; đặt giá trị 121 vào thanh ghi Ax Mov Bx, 223
; đặt giá trị 232 vào thanh ghi Bx Inc Ax
; Ax = Ax + 1: tăng Ax lên 1 đ¢n vị (Ax = 122) Dec Bx
; Bx = Bx + 1: giảm Bx xuống 1 đ¢n vị (Bx = 222) Sub Ax, Bx ; Ax = Ax – Bx : Ax = -100 lOMoAR cPSD| 36066900 Add Ax, 120 ; Ax = Ax + 120 : Ax = 20 Mov Cx, Ax ; Cx= Ax : Cx = 20
Dãy lệnh trên, đặt giá trị cuối cùng của thanh ghi Ax vào thanh ghi Cx (Cx = 20). Ví dā 2: - Inc Spt
; Spt = Spt + 1; tăng giá trị biến Spt lên 1 đ¢n vị - Inc DS:[SI]
; tăng ndung ô nhớ được chỉ bởi DS:SI lên 1 đ¢n vị - Add Ax, Var1
; Ax = Ax + Var1; cộng giá trị biến Var1 vào Ax - Add Var2, Dx
; Var2 = Var2 + Dx. Biến Var2 là biến dạng word - Add Dx, [SI]
; cộng thêm nội dung ô nhớ được chỉ bởi SI vào Dx - Add [DI], [SI]
; [DI] = [DI] + [SI] : lệnh không hợp lệ, vì: không thể
; cộng trực tiếp nội dung hai ô nhớ với nhau.
; Yêu cầu của lệnh trên có thể được viết lại như sau: Mov Ax, [SI]
; lưu tạm nội dung ô nhớ được chỉ bởi SI và Ax Mov Bx, [DI]
; lưu tạm nội dung ô nhớ được chỉ bởi DI và Bx Add Bx, Ax
; cộng Ax và Bx, kết quả chứa ở Bx Mov [DI], Bx
; đặt kết quả phép cộng vào lại ô nhớ được chỉ bởi DI
Ví dā 3: Cộng thêm giá trị của thanh ghi Ax vào nội dung của ô nhớ tại địa chỉ offset 0100 trong đoạn DS: Mov DI, 0100
; trỏ DI về ô nhớ offset 0100 Mov Bx, DS:[DI]
; lưu tạm ndung ô nhớ DS:DI vào thanh ghi Bx Add Bx, Ax ; cộng thêm Ax vào Bx Mov DS:[DI], Bx
; đặt kết quả vào lại ô nhớ DS:DI (DS:0100)
Trong trường hợp này ta có thể sử dụng lệnh Add DS:[DI],Ax.
Ví dā 4: Giả sử tại ô nhớ 0B800:0100 trong bộ nhớ có chứa một word dữ liệu. Hãy tăng nội dung của
ô nhớ này lên một đ¢n vị. Mov Ax, 0B800h
; mượn thanh ghi Ax làm trung gian để chuyển Mov ES, Ax
; địa chỉ đoạn của ô nhớ cần truy xuất vào ES Mov DI, 01
; đặt địa chỉ offset của ô nhớ cần truy xuất vào DI
; --------------------------- ; (gọi ngắn gọn: trỏ ES:DI về ô nhớ cần truy xuất) Mov Dx, ES:[DI]
; chuyển tạm nội dung ô nhớ cần tăng vào Dx Inc Dx
; tăng giá trị thanh ghi Dx lên 1 đơn vị Mov ES:[DI], Dx
; đặt giá trị Dx đã tăng vào lại ô nhớ cần tăng lOMoAR cPSD| 36066900
Ví dā 5: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một byte dữ liệu. Hãy chuyển nội dung
của ô nhớ này vào thành ghi AL. Mov Ax, 0A00h
; (1); Các lệnh (1), (2), (3) trỏ cặp thanh Mov ES, Ax
; (2); ghi ES:DI về ô nhớ có địa chỉ 0A00:0100 Mov DI, 0100h
; (3); trong đó 0A00 là địa chỉ Segment và ;-------------------------
; 0100 là địa chỉ Offset. Lệnh (4) chuyển nội Mov Al, ES:[DI]
; (4); dung ô nhớ được chỉ bởi ES:DI vào Al.
Ví dā 6: Giả sử tại địa chỉ 0100:0100 trong bộ nhớ có chứa 2 word dữ liệu liên tiếp (hai ô nhớ liên
tiếp). Hãy tính tổng nội dung hai word nhớ này, rồi lấy kết quả ghi vào ô nhớ tại địa chỉ 0100:0120. Mov Ax, 0100 Mov ES, Ax
; trỏ cặp thanh ghi ES:SI về đầu vùng nhớ Mov SI, 0100 ; cần truy xuất. Mov DI,0120
; trỏ cặp thanh ghi ES:DI về ô nhớ chứa kết quả ;-------------------------
; các ô nhớ này ở trong cùng một Segment Mov Ax, ES:[SI]
; lưu tạm nội dung ô nhớ đầu tiên vào Ax Add Ax, ES:[SI+2]
; cộng nội dung ô nhớ kế tiếp vào Ax Mov ES:[DI], Ax
; ghi kết quả vào ô nhớ 0100:0120 Lệnh Add
Ax, ES:[SI+2] ở trên sử dụng chế độ định địa chỉ bộ nhớ gián tiếp, cụ thể là định địa
chỉ chỉ mục (sử dụng thanh ghi chỉ mục SI).
Qua 3 ví dụ 4, 5, 6 ta có thể rút ra nguyên tắc c¢ bản khi truy xuất dữ liệu/nội dung của một ô nhớ là:
Sử dụng một cặp thanh ghi thích hợp (DS:DI, DS:SI, ES:DI, ES:SI,...) để chứa địa chỉ logic (gồm cả
Segment và Offset) của ô nhớ cần truy xuất. Thao tác này thường gọi là trỏ về ô nhớ cần truy xuất.
Sau đó sử dụng cặp thanh ghi này để ghi/đọc nội dung của ô nhớ đã được trỏ tới.
Ngoài ra, khi truy xuất ô nhớ cần phải xác định dữ liệu/nội dung tại đó là một Byte hay một Word và
nếu là truy xuất đọc thì kết quả sẽ được lưu vào đâu (thanh ghi hay ô nhớ). Chú ý 1:
• Không thể cộng trực tiếp hai thanh ghi đoạn. Trong trường hợp này phải sử dụng các thanh ghi đa năng làm trung gian.
• Lệnh Add thực hiện phép cộng không nhớ. Để thực hiện phép cộng có nhớ (cộng thêm giá trị của cờ nhớ
(CF) hiện tại vào kết quả) phải sử dụng lệnh ADC (ADD with Carry) [2 - 171]. Tư¢ng tự với lệnh Sub và SBB [2 - 180].
• Để thực hiện phép cộng trên các số/giá trị BCD (Binary Coded Decimal) ta phải sử dụng các lệnh
cộng AAA (Ascii Adjust for Addition) và DAA (Decimal Adjust for Addition) để điều chỉnh (adjust) kết quả
cuối cùng [2 - 172]. Tư¢ng tự, với phép trừ trên các số BCD phải sử dụng lệnh AAS và DAS [2 - 183]. Chú ý 2: lOMoAR cPSD| 36066900
• Các thanh ghi của vi xử lý Intel 8086/8088 đều là 16 bít, nên để chứa một đại lượng dữ liệu 32 bít nó phải
dùng 2 thanh ghi, thường là các thanh ghi đa năng (thanh ghi tích lũy): Ax, Bx, Cx, Dx. Cặp thanh ghi Dx:Ax
thường được sử dụng nhất, khi đó Ax chứa 16 bít thấp, Dx chứa 16 bít cao của đại lượng 32 bít.
• Để cộng/trừ trên các số 32 bít ta không thể sử dụng Add/Sub theo cách thông thường, mà phải thực hiện
như sau: Cộng/Trừ 16 bít thấp, sau đó Cộng/Trừ 16 bít cao. Nếu phép Cộng/Trừ trên 16 bít thấp xuất hiện
bít nhớ/bít mượn thì phải tiến hành điều chỉnh kết quả, nếu không kết quả sẻ sai. Sử dụng các phép kiểm
tra cờ để biết phép Cộng/Trừ có xuất hiện bít nhớ/bít mượn hay không [1 - 477]. 3. Lßnh LOOP Cú pháp: Loop
<Nhãn đích>
Trong đó: là một nhãn lệnh và nó phải đứng trước lệnh lặp Loop không quá 126 byte.
Tác dāng: Khi gặp lệnh này chư¢ng trình sẽ lặp lại việc thực hiện các lệnh sau đủ n
lần, với n được đặt trước trong thanh ghi CX. Sau mỗi lần lặp CX tự động giảm 1 đ¢n vị (Cx = Cx - 1)
và lệnh lặp sẽ dừng khi Cx = 0.
Lệnh Loop thường được sử dụng để cài đặt các đoạn chư¢ng trình lặp với số lần lặp xác định, được
cho trước trong thanh ghi Cx (tư¢ng tự các vòng lặp For trong các ngôn ngữ lập trình bậc cao).
Ví dā 1: Xem đoạn lệnh sau đây: Mov Ax, 6 Mov Cx, 4 ; lặp lại 4 lần Lap: Add Ax, 10 ; cộng thêm 10 vào Ax Loop Lap
; lặp lại việc cộng 10 vào Ax đủ 4 lần
Kết thúc đoạn lệnh trên: Ax = 46 (cụ thể: LÁn 1: Ax = 6 + 10; LÁn 2: Ax = 16 + 10; LÁn 3: Ax = 26 +
10; LÁn 4: Ax = 36 + 10 = 46).
Ví dā 2: Xem đoạn lệnh sau đây: Mov Ax, 6 Mov Bx, 3 Mov Cx, 4 ; lặp lại 4 lần Lap_TT: Add Ax, Bx
; cộng thêm giá trị thanh ghi Bx vào thanh ghi Ax Inc Bx ; tăng Bx lên 1 đ¢n vị Loop Lap_TT
; lặp lại các lệnh sau nhãn lệnh Lap_TT đủ 4 lần
;-----------------------------
; sau lệnh lặp này Ax = 24, Bx = 7 Mov Dx, Ax ; Dx ß Ax Mov Cx, Bx
; Cx = 7, sau Loop Cx = 0, được thay bằng Bx = 7 lOMoAR cPSD| 36066900
Kết thúc đoạn lệnh trên: Ax = 24 (LÁn 1: Ax = 6 + 3;LÁn 2: Ax = 9 + 4; LÁn 3: Ax = 13 + 5; LÁn 4: Ax = 18 + 6) và Dx = Ax = 24. Khi gặp lệnh Loop
Lap_TT chư¢ng trình sẽ quay lại (nếu Cx <> 0) thực hiện lệnh Add Ax,
Bx (Lap_TT là nhãn của lệnh này), tất nhiên khi đó nó cũng phải thực hiện lại lệnh Inc Bx. Dó đó,
có thể nói lệnh Loop này thực hiện vòng lặp cho cả hai lệnh Add và Inc. Đó cũng chính là lý do mà
người ta thường viết nhãn của các lệnh phải được lặp theo kiểu như trên (nhãn lệnh và lệnh không cùng trên một dòng).
Ví dā 3: Xem đoạn lệnh sau đây: Mov Dx, 6 Mov Cx, 5 ; lặp lại 5 lần TT: Add Dx, Cx
; cộng thêm giá trị thanh ghi Cx vào thanh ghi Dx Loop TT
; lặp lại các lệnh sau nhãn lệnh TT đủ 5 lần ; ; Mov Bx, Cx
Kết thúc đoạn lệnh trên Bx = Cx = 0 (khi Cx = 0 thì vòng lặpLoop TT kết thúc) và Dx = 21 (LÁn 1: Dx
= Dx + Cx = 6 + 5;LÁn 2: Dx = Dx + Cx = 11 + 4; LÁn 3: Dx = Dx + Cx = 15 + 3; LÁn 4: Dx = Dx + Cx
= 18 + 2;LÁn 5: Dx = Dx + Cx = 20 + 1 = 21).
Ví dā 4: Các lệnh sau đây thực hiện phép gán: Ax = 2 + 4 + ...+ 100 Mov Ax, 0 Mov Bx, 2 Mov Cx, 50 Lap_TT: Add Ax, Bx Add Bx, 2 Loop Lap_TT
Ví dā 5: Giả sử tại địa chỉ offset 100 trong đoạn nhớ Data (được chỉ bởi thanh ghi đọan DS) có chứa
một mảng dữ liệu, gồm 100 ô nhớ, mỗi ô là một byte. Hãy cộng thêm 50 đ¢n vị vào tất cả các ô nhớ trong mảng này. Mov DI, 0100
; trỏ cặp thanh ghi DS:DI về
; vùng nhớ cần truy xuất (DS:0100) ; Mov Cx, 100
; lặp 100 lần vì mảng gồm 100 ô nhớ lOMoAR cPSD| 36066900 Lap_TangThem: Mov Dl, DS:[DI]
; lấy nôi dung ô nhớ chỉ bởi DS:DI lưu vào DL Add Dl, 50 ; cộng thêm 50 vào Dl Mov DS:[DI], Dl
; đặt giá trị đã tăng thêm vào lại ô nhớ DS:DI Inc DI
; chỉ đến ô nhớ kế tiếp (vì ô nhớ byte nên tăng 1) Loop Lap_TangThem
; lặp lại đủ 100 lần (duyệt qua đủ 100 ô nhớ)
Trong trường hợp này ta có thể sử dụng lệnh Add DS:[DI], 50 để tăng trực tiếp nội dung của ô
nhớ, hợp ngữ cho phép điều này. Nhưng cách đã viết thường được áp dụng h¢n, vì tính tổng quát
của nó. Nói chung, chúng ta nên hạn chế tác động trực tiếp lên nôi dung của ô nhớ.
Ví dā 6: Giả sử tại địa chỉ 0100:0C00 trong bộ nhớ có chứa một xâu kí tự gồm 50 kí tự (tức là, gồm
50 ô nhớ, mỗi ô 1 byte). Hãy copy xâu kí tự này sang vùng nhớ bắt đầu tại địa chỉ 0200:0100. Mov Ax, 0100 Mov DS, Ax
; trỏ cặp thanh ghi DS:SI về Mov SI, 0C00
; đầu vùng nhớ chưa xâu cần copy (0100:0C00) Mov Ax, 0200 Mov ES, Ax
; trỏ cặp thanh ghi ES:DI về Mov DI, 0100
; vùng nhớ chứa xâu kết quả copy 0200:0100 ; Mov Cx, 50
; lặp 50 lần vì xâu gồm 50 kí tự Lap_Copy: Mov Bl, DS:[SI]
; mượn Bl để chuyển tường kí tự từ ô nhớ được Mov ES:[DI], Bl
; chỉ bởi DS:SI sang ô nhớ được chỉ bởi ES:DI Inc SI
; chuyển đến kí tự tiếp theo Inc DI Loop Lap_Copy
; lặp lại đủ 50 lần (để copy đủ 50 kí tự)
Trong ví dụ này, vùng nhớ chứa xâu kí tự cần copy (vùng nhớ nguồn) và vùng nhớ chứa kết quả
copy (vùng nhớ đích) nằm ở hai đoạn (segment) nhớ khác nhau, do đó, ta phải sử dụng hai thanh ghi
đoạn dữ liệu khác nhau: DS và ES.
Qua 2 ví dụ 5 và 6 ta có thể rút ra nguyên tắc c¢ bản để truy xuất dữ liệu trên một vùng nhớ/mảng nhớ:
Chú ý: Lệnh Loop thực hiện vòng lặp cho đến khi Cx = 0, vì thế nó được xem như lệnh lặp không
điều kiện. Thực tế, hợp ngữ còn có các lệnh lặp có điều kiện, nó cho phép kết thúc vòng lặp trước khi
Cx = 0 dựa vào một điều kiện nào đó. Cụ thể: lệnh LoopE (Loop while Equal): Chỉ lặp lại khi Cx <> 0 lOMoAR cPSD| 36066900
và cờ ZF = 1; lệnh LoopZ (Loop while Zero): tư¢ng tự LoopE; LoopNE (Loop while Not Equal): Chỉ
lặp lại khi Cx <> 0 và cờ ZF = 0;... [2 - 154]. 4. Lên
̣ h LEA (LoadEffectiveAddress) Cu´ pha´ p: LEA
[Toa´ n hang đi´ch],[Toa´ n haṇ g nguô` n]
Trong đo´ : [Toa´ n haṇ g đi´ch]: La` ca´ c thanh ghi 16 bi´t. [Toa´ n haṇ g nguô` n]: La` điạ chỉ củ a môṭ vu` ng
nh¢´ hay tên củ a môṭ biê´n. Ta´ c dun
g: Lệnh LEA có tác dụng chuyển điạ chỉ offset củ a [Toa´ n haṇ g nguô` n] va` o [Toa´ n g đi´ch]. han
Lêṇ h na` y thư¢` ng đư¢̣c sử dụng để lâ´ y điạ chỉ offset củ a môṭ biê´n đã đư¢̣c khai ba´ o trong chư¢ng
tri`nh. Thanh ghi đư¢̣c sử dụng trong trư¢` ng h¢̣p na` y la` thanh ghi c¢ s¢̉ (BX) va` thanh ghi chỉ mục (SI va` DI). Vi´ dụ 1: Lea Bx, DS:[0100]
; chuyển tha` nh phâ` n điạ chỉ offset (0100) va` o Bx Lea DI, XauKT
; chuyển điạ chỉ offset củ a biê´n XauKT va` o DI
; thao ta´ c na` y thư¢` ng đư¢̣c goị la` trỏ DI va` đâ` u ; biến XauKT
Khi chư¢ng tri`nh đư¢̣c nap̣ va` o bộ nh¢´ để hoaṭ đôṇ g thi` ca´ c biê´n đư¢̣c khai ba´ o trong chư¢ng tri`nh
sẽ đư¢̣c điṇ h vi ̣(câ´ p pha´ t vu` ng nh¢´ ) taị môṭ điạ chỉ xa´ c điṇ h trong vu` ng nh¢´ Data. Tư` đây, để thao
ta´ c đê´n dữ liêụ trên ca´ c biê´ n củ a chư¢ng tri`nh thi` chư¢ng trinh` phả i xa´ c điṇ h đư¢̣c điạ chỉ segment
va` o offset (hai tha` nh phâ` n củ a điạ chỉ logic) củ a biê´ n. Lêṇ h LEA ¢̉ trên chỉ lâ´ y đư¢̣c điạ chỉ offset củ a
biê´ n, để lâ´ y đư¢̣c điạ chỉ segment củ a no´ ta co´ thể sử dụng lêṇ h Mov v¢´ i toa´ n tử Seg (tư¢ng tự co´ thể
sử dụng lêṇ h Mov v¢´ i toa´ n tử Offset để lâ´ y điạ chỉ offset củ a biê´n). Vi´ dụ: Ca´ c lêṇ h sau lâ´ y điạ chi
Segment:Offset củ a biê´n XauKT (hay trỏ DS:SI vê` đâ` u biê´ n XauKT): Mov Ax, Seg XauKT
; đưa điạ chỉ Segment củ a biê´n XauKT Mov DS, Ax ; va` o thanh ghi DS Mov SI, Offset XauKT
; đưa điạ chỉ Offset củ a biê´ n XauKT va` o SI
Vi´ dụ 2: Giả sử biê´n TenGom (la` biê´ n kiểu byte) đã đư¢̣c khai ba´ o như sau: TenGom DB 8Nguyen Kim Le Tuan9
Xem ca´ c lêṇ h sau đây (1): Mov Bx, 0 Mov Al, TenGom[Bx] ; Al =8N9 Add Bx, 7 ; Mov Bl, TenGom[Bx] ; Bl =8K9
Xem ca´ c lêṇ h sau đây (2): Lea DI, TenGom lOMoAR cPSD| 36066900 Mov Al, [DI] ; Al =8N9 Mov Bl, [DI + 7] ; Bl =8K9
Ta co´ thể thâ´ y, nho´ m ca´ c lêṇ h (1) va` nho´ m ca´ c lêṇ h (2) la` tư¢ng đư¢ng nhau vê` ta´ c dụng củ a no´ ,
nhưng (1): sử dụng trực tiê´ p tên biê´n để truy xuâ´ t đê´n ca´ c phâ` n tử củ a no´ ; (2): sử dụng thanh ghi chi
muc̣ DI để truy xuâ´ t đê´ n ca´ c phâ` n tử củ a biê´ n. Trong trư¢` ng h¢̣p na` y điạ chỉ segment điṇ h đư¢̣c măc̣
chỉ b¢̉ i DS, điê` u na` y phu` h¢̣p v¢´ i viêc̣ sử dụng điạ chỉ gia´ n tiê´p thanh ghi chỉ mục.
Vi´ dụ 3: Giả sử taị điạ chỉ 0100:0C00 trong bộ nh¢´ co´ chư´ a môṭ xâu ki´ tự gô` m 50 ki´ tự (tư´ c la` , gô` m
50 ô nh¢´ , mỗi ô 1 byte). Hãy copy xâu ki´ tự na` y va` o môṭ biê´n trong chư¢ng tri`nh.
V¢´ i yêu câ` u na` y chư¢ng tri`nh phả i khai ba´ o môṭ biê´n byte co´ độ l¢´ n 50 byte: LuuTru DB 50 Dup (8 8) Mov Ax, 0100 Mov DS, Ax
; trỏ căp̣ thanh ghi DS:SI vê Mov SI, 0C00
; đâ` u vu` ng nh¢´ chưa xâu câ` n copy (0100:0C00) ; Mov Ax, Seg LuuTru
; trỏ căp̣ thanh ghi ES:DI vê Mov ES, Ax ; đâ` u biê´ n LuuTru Lea DI, LuuTru ; Mov Cx, 50 Lap_Copy: Mov Bh, DS:[SI]
; mư¢̣n Bh để chuyển tư¢` ng ki´ tự tư` ô nh¢´ đư¢̣c Mov ES:[DI], Bh
; chỉ b¢̉ i DS:SI sang ô nh¢´ đư¢̣c chỉ b¢̉ i ES:DI Inc SI
; chuyển đê´n ki´ tự tiê´ p theo Inc DI Loop Lap_Copy
; lăp̣ laị đủ 50 lâ` n (để copy đủ 50 ki´ tự)
Chu´ y´ : H¢̣p ngữ co` n cung câ´ p ca´ c lêṇ h LDS (Load Pointer use DS) để lâ´ y nôị dungtoa´ n haṇ g bộ
nh¢´ 32 bi´t đưa va` o ca´ c thanh ghi 16 bi´t (măc̣ điṇ h 16 bi´t cao va` o thanh ghi đoaṇ dữ liêụ DS); va` lêṇ h
LES (Load Pointer use DS) tư¢ng tự LDS nhưng măc̣ điṇ h 16 bi´t cao va` o thanh ghi đoaṇ dữ liêụ (thư´ hai) ES [2 - 137]. 5. Lên ̣ h Mul va` Div Cu´ pha´ p:
• Mul [Toa´ n haṇ g nguô` n] lOMoAR cPSD| 36066900
• IMul [Toa´ n hạng nguô` n] • Div
[Toa´ n hạng nguô` n]
• IDiv [Toa´ n hạng nguô` n]
Trong đo´ : [Toa´ n haṇ g nguô` n]co´ thể la` thanh ghi hay ô nh¢´ . V¢´ i ca´ c lêṇ h nhân: [Toa´ n haṇ g đi´ch]
ngâ` m điṇ h la` thanh ghi Al
Ax. V¢´ i ca´ c lêṇ h chia: [Toa´ n haṇ g đi´ch] la` môṭ trong ca´ c thanh ghi đa hoăc̣ năng Ax, Bx,... Ta´ c dung: -
Lêṇ h Mul (Multiply): Thực hiêṇ phe´ p nhân trên sô´ không dâ´ u. Nê´u [Toa´ n haṇ g nguô` n] la` toa´ n
haṇ g 8 bi´t thi` lêṇ h sẽ nhân nôị dung củ a [Toa´ n han g nguô` n] v¢´ i gia´ tri ̣thanh ghi AL, kê´t quả 16 bi´t chư´ a ¢̉ thanh ghi Ax.
Nê´u [Toa´ n haṇ g nguô` n] la` toa´ n haṇ g 16 bi´t thi` lêṇ h sẽ nhân nôị dung củ a [Toa´ n haṇ g nguô` n] v¢´ i gia
tri ̣thanh ghi Ax, kê´t quả 32 bi´t chư´ a ¢̉ căp̣ thanh ghi Dx:Ax, phâ` n thâ´ p ¢̉ Ax, phâ` n cao ¢̉ Dx. Nê´ u
phâ` n cao củ a kê´t quả (AH hoăc̣ DX) bă` ng 0 thi` ca´ c c¢` CF = 0 va` OF = 0.
- Lêṇ h IMul (Interger Multiply): Tư¢ng tự lêṇ h Mul nhưng thực hiêṇ phe´p nhân trên hai sô´ co´ dâ´ u.
Kê´t quả cũng la` môṭ sô´ co´ dâ´ u.
- Lêṇ h Div (Divide): Thực hiêṇ phe´p chia trên sô´ không dâ´ u. Nê´u [Toa´ n haṇ g nguô` n] la` toa´ n
haṇ g 8 bi´t thi` lêṇ h sẽ lâ´ y gia´ tri ̣củ a thanh ghi Ax (sô´ bi ̣chia) chia cho [Toa´ n han g nguô` n] (sô´ chia),
kê´t quả thư¢ng sô´ chư´ a trong thanh ghi Al, sô´ dư chư´ a trong thanh ghi Ah.
Nê´u [Toa´ n haṇ g nguô` n] la` toa´ n haṇ g 16 bi´t thi` lêṇ h sẽ lâ´ y gia´ tri ̣củ a
thanh ghi Dx:Ax (sô´ bi ̣chia) căp̣
chia cho [Toa´ n haṇ g nguô` n] (sô´ chia), kê´t quả thư¢ng sô´ chư´ a trong thanh ghi Ax, sô´ dư chư´ a trong thanh ghi Dx.
Nê´u phe´p chia cho 0 xả y ra hay thư¢ng sô´ vư¢̣t qua´ gi¢´ i haṇ củ a thanh ghi AL (chia 8 bi´t) hay Ax
(chia 16 bi´t) thi` CPU sẽ pha´ t sinh lỗi - Lêṇ h Idiv (Integer Divide): Tư¢ng tự lêṇ h Div nhưng thực hiêṇ phe´p chia trên hai sô´ co´ dâ´ u. Kê´t
quả cu` ng la` ca´ c sô´ co´ dâ´ u. Vi´ dụ 1: - Mul Bl
; Ax ßAL * Bl: sô´ bi ̣nhân ngâ` m điṇ h trong Al - Mul Bx
; Dx:Ax ßAx * Bx: sô´ bi ̣nhân ngâ` m điṇ h trong Ax - Idiv Bl
; Ax/Bl, thư¢ng sô´ chư´ a trong Al, sô´ dư chư´ a trong Ah - Idiv Bx
; Dx:Ax/Bx, thư¢ng sô´ chư´ a trong Ax, sô´ dư trong Dx
Vi´ dụ 2: Dãy ca´ c lêṇ h dư¢´ i đây sẽ thực hiêṇ phe´p ga´ n A = 4*A – 3*B, trong đo´ A va` B la` ca´ c biê´n kiểu word: Mov Ax, 4
; sô´ nhân phả i đư¢̣c chư´ a trong Ax IMul A
; thực hiêṇ phe´p nhân Mov Bx, Ax ; lưu tam ̣ kê´t quả va` o Bx lOMoAR cPSD| 36066900 Mov Ax, 3 ; Ax = 3 Imul B ; Ax = Ax * B Sub Bx, Ax Mov A, Bx
; đăṭ kê´t quả cuô´ i cu` ng va` o A
Trong trư¢` ng h¢̣p na` y ta đã giả sử tư¢̣ng tra` n đã không xả y ra va` kê´t quả phe´ p nhân chỉ chư´ a hiêṇ trong thanh ghi Ax.
Vi´ dụ 3: Ca´ c lêṇ h sau đây thực hiêṇ phe´p: chia -123 cho 24: Mov Ax, -123
; đăṭ sô´ bi ̣chia va` o Ax Mov Bl, 24
; đăṭ sô´ chia va` o Bl (thanh ghi 8 bi´t) Idiv Bl
; chia Ax cho Bl, kê´t quả chư´ a ¢̉ Al va` Ah
Vi´ dụ 4: Dãy lêṇ h dư¢´ i đây sẽ thực hiêṇ phe´ p ga´ n A = N! (ti´nh N giai thư` a). A la` môṭ biê´n word: Mov Ax, 1
; chuẩ n bi ̣Ax để lưu kê´t quả Mov Cx, N ; ti´nh N! LapNhan: Mul Cx ; Ax ßAx * Cx Loop LapNhan ; Mov A, Ax
; trả kê´t quả va` o biê´n A
Trong trư¢` ng h¢̣p na` y chu´ ng ta giả sử kê´t quả không vư¢̣t qua´ g¢´ i 16 bi´t. haṇ
Chu´ ng ta đã biê´ t: N! = 1 nê´ u N = 1, N! = N*(N-1)*(N-2)*...*1 nê´u N>1, điê` u na` y hoa` n toa` n phu` h¢̣p v¢´ i
sự thay đổ i củ a thanh ghi CX trong lêṇ h lăp̣ Loop. Do đo´ , ¢̉ đây ta co´ thể sử dụng CX như la` N trong
biểu thư´ c ti´nh giai thư` a.
Chu´ y´ : H¢̣p ngữ cung câ´ p lêṇ h AAM (Asci Adjust for Multiple) để điê` u chinh kê´t quả phe´ p nhân trên
2 sô´ BCD dan g không dô` n. Va` lêṇ h AAD (Asci Adjust for Division) để điê` u h kê´ t quả phe´p chia chin
trên 2 sô´ BCD dan g không dô` n. Ngoa` i ra co` n co´ lêṇ h CBW (Convert Byte to Word) va` lêṇ h CWD
(Convert Word to Doubleword) để hỗ tr¢̣ cho phe´ p chia trên ca´ c sô´ co´ dâ´ u [2 – 187-200].
Lêṇ h IMul củ a vi xử ly´ Intel 80286 cho phe´ p ghi rõ [Toa´ n hang đi´ch], [Toa´ n haṇ g nguô` n] trong câu
lêṇ h, ca´ c lêṇ h na` y co´ thể co´ đê´ n 3 toa´ n han g [1 - 541]. 6. Lên
̣ h logic: NOT – AND – OR – XOR – TEST Trư¢´ c khi tim
hiểu vê` ca´ c lêṇ h logic chu´ ng ta xem laị kê´t quả thực hiêṇ ca´ c phe´ p ti´nh logic trên 2 bi´t
nhi ̣phân A va` B thông qua bả ng sau đây: A B A And B A Or B A Xor B NotA lOMoAR cPSD| 36066900 0 0 0 0 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 0
Bả ng trên cho thâ´ y: V¢´ i phe´p And: kê´t quả = 1 chỉ khi cả hai bi´t = 1; V¢´ i phe´p Or: kê´t quả = 0 chỉ khi
cả hai bi´t = 0; V¢´ i phe´p Xor: kê´t quả = 0 khi hai bi´t giô´ ng nhau, kê´t quả = 1 khi hai bi´t kha´ c nhau. V¢´ i
phe´p Not: 0 tha` nh 1, 1 tha` nh 0. Cu´ pha´ p:
• Not [Toa´ n hạng đi´ch]
• And [Toa´ n hạng đi´ch], [Toa´ n hạng nguô` n] • Or
[Toa´ n haṇ g đi´ch], [Toa´ n hạng nguô` n]
• Xor [Toa´ n hạng đi´ch], [Toa´ n hạng nguô` n]
• Test [Toa´ n hạng đic´ h], [Toa´ n hạng nguô` n]
Trong đo´ : [Toa´ n haṇ g đi´ch], [Toa´ n hang nguô` n] co´ thể la` hă` ng sô´ (trực hă` ng), biê´ n, thanh ghi hay
điạ chỉ ô nh¢´ . [Toa´ n haṇ g đi´ch] không thể la` hă` ng sô´ . Ta´ c dun g: Mỗi lên
̣ h logic thực hiêṇ phe´p ti´nh logic tư¢ng ư´ ng trên ca´ c bi´t (tư¢ng ư´ ng vê` vi ̣tri´) củ a
[Toa´ n haṇ g đi´ch] va` [Toa´ n haṇ g nguô` n], kê´t quả đư¢̣c ghi va` o laị [Toa´ n haṇ g đi´ch]. Riêng lêṇ h Not,
thực hiêṇ phe´p đả o bi´t ngay trên ca´ c bi´t củ a [Toa´ n han g đi´ch]. Hâ` u hê´t ca´ c lêṇ h logic đê` u ả nh hư¢̉ ng
đê´n ca´ c c¢` CF, OF, ZF,... -
Lêṇ h Not (Logical Not): Thực hiêṇ viêc̣ đả o ngư¢̣c tư` ng bi´t trong nôị dung củ a [Toa´ n haṇ g đi´ch].
Lêṇ h na` y không la` m ả nh hư¢̉ ng đê´n ca´ c c¢` .
Lêṇ h Not thư¢` ng đư¢̣c sử dụng để taọ daṇ g bu` 1 củ a [Toa´ n haṇ g đi´ch]. -
Lêṇ h And (Logical And): Thực hiêṇ phe´p ti´nh logic And trên tư` ng căp̣ bi´t (tư¢ng ư´ ng vê` vi ̣tri´)
củ a [Toa´ n hang nguô` n] v¢´ i [Toa´ n haṇ g đi´ch], kê´t quả lưu va` o [Toa´ n haṇ g đi´ch].
Lêṇ h And thư¢` ng đư¢̣c sử dụng để xo´ a (= 0) môṭ hoăc̣ nhiê` u bi´t xa´ c điṇ h na` o đo´ trong môṭ thanh ghi. -
Lêṇ h Or (Logical Inclusive Or):Thực hiêṇ phe´ p ti´nh logic Or trên tư` ng căp̣ bi´t (tư¢ng ư´ ng vê` vi ̣tri´)
củ a [Toa´ n hang nguô` n] v¢´ i [Toa´ n haṇ g đi´ch], kê´t quả lưu va` o [Toa´ n haṇ g đi´ch].
Lêṇ h Or thư¢` ng du` ng để thiê´t (= 1) môṭ hoăc̣ nhiê` u bi´t xa´ c điṇ h na` o đo´ trong môṭ thanh ghi. lâp̣ -
Lêṇ h Xor (eXclusive OR):Thực hiêṇ phe´p ti´nh logic Xor trên tư` ng căp̣ bi´t (tư¢ng ư´ ng vê` vi ̣tri´) củ a
[Toa´ n haṇ g nguô` n] v¢´ i [Toa´ n haṇ g đi´ch], kê´t quả lưu va` o [Toa´ n hang đi´ch]. lOMoAR cPSD| 36066900
Lêṇ h Xor thư¢` ng du` ng để so sa´ nh (bă` ng nhau hay kha´ c nhau) gia´ tri ̣củ a hai toa´ n haṇ g, no´ cũng giu´ p
pha´ t hiêṇ ra ca´ c bi´t kha´ c nhau giữa hai toa´ n haṇ g na` y. -
Lêṇ h Test: Tư¢ng tự như lêṇ h And nhưng không ghi kê´t quả va` o laị [Toa´ n haṇ g đi´ch], no´ chỉ ả nh
hư¢̉ ng đê´ n ca´ c c¢` CF, OF, ZF,... Vi´ dụ 1: Mov Al,0 ; Al ß0 Not Al
; Al = Not Al. Tư´ c la` Al = 0FFh
Vi´ dụ 2: Cho AL = (10010011)2, BL = (11001100)2. - And Al, Bl
; Al ß 10010011 And 11001100. Al = - And Al, 0 ; Al ß 10010011 And 0. Al = - Or Bl, Al
; Bl ß 11001100 Or 10010011. Al = - Or Bl, 4 ; Bl ß 11001100 Or 100. Al = - Xor Al, Bl
; Al ß 10010011 Xor 11001100. Al = - Xor Bl, Bl
; Bl ß 11001100 Xor 11001100. Bl = 00000000
Vi´ dụ 3: Để xo´ a nôị dung thanh ghi na` o đo´ , trong h¢̣p ngữ ta co´ thể sử dụng môṭ trong ca´ c lêṇ h sau đây: - Mov Ax, 0 - Sub Ax, Ax - Xor Ax, Ax
; ca´ c căp̣ bi´t giô´ ng nhau thi` đê` u = 0
Vi´ dụ 5: Lêṇ h sau đây sẽ xo´ a (= 0) ca´ c bi´t 3 va` 6 củ a thanh ghi AL, ca´ c bi´t kha´ c giữ nguyên gia´ tri:̣ - And AL, 10110111b
; AL ßAL And 10110111
Trong trư¢` ng h¢̣p na` y: dãy bi´t 10110111 đư¢̣c goị la` dãy bi´t măṭ na,̣ ca´ c bi´t 3 (= 0) va` 6 (= 0) đư¢̣c
goị la` ca´ c bi´t măṭ na.̣ Như vâỵ muô´ n la` m cho bi´t na` o = 0 ta cho bi´t măṭ nạ tư¢ng ư´ ng v¢´ i no´ = 0, ca´ c
bi´t co` n laị trong dãy bi´t măṭ nạ đê` u = 1.
Vi´ dụ 6: Lệnh sau đây sẽ thiê´t lâp
̣ (= 1) ca´ c bi´t 3 va` 6 củ a thanh ghi AL, ca´ c bi´t kha´ c giữ nguyên gia tri:̣ - Or AL, 01001000b
; AL ßAL Or 01001000
Trong trư¢` ng h¢̣p na` y: dãy bi´t 01001000 đư¢̣c goị la` dãy bi´t măṭ na,̣ ca´ c bi´t 3 (= 1) va` 6 (= 1) đư¢̣c
goị la` ca´ c bi´t măṭ na.̣ Như vâỵ muô´ n la` m cho bi´t na` o = 1 ta cho bi´t măṭ nạ tư¢ng ư´ ng v¢´ i no´ = 1, ca´ c
bi´t co` n laị trong dãy bi´t măṭ nạ đê` u = 0.
Vi´ dụ 7: Lêṇ h sau đây sẽ kiểm tra bi´t 12 củ a thanh ghi AX la` = 0 hay = 1: - And AX, 0001000000000000b
; AX ßAX And 0001000000000000
V¢´ i dãy bi´t măṭ nạ như trên, nê´u bi´t 12 củ a Ax = 0 thi` kê´t quả : Ax = 0, nê´u bi´t 12 củ a Ax = 1 thi` kê´t quả : Ax <> 0. lOMoAR cPSD| 36066900
Ca´ ch du` ng lêṇ h And như trên để kiểm tra bi´t i´t đư¢̣c sử dụng, vi` no´ la` m thay đổi gia´ tri ̣củ a thanh ghi
câ` n kiểm tra (điều này có thể khắc phục bằng lệnh Test) va` phả i thêm bư¢´ c kiểm tra gia´ tri ̣củ a Ax (=
0 hay <> 0) m¢´ i biê´t đư¢̣c kê´t quả kiểm tra. Ngoa` i ra, no´ cũng chỉ kiểm tra đư¢̣c 1 bi´t.
Trong thực tê´ ngư¢` i ta thư¢` ng sử dụng kê´t h¢̣p giữ a ca´ c lêṇ h dic̣ h bi´t, lêṇ h quay bi´t, lêṇ h nhả y,... để
kiểm tra ca´ c bi´t trong môṭ thanh ghi. 7. Lên
̣ h chuyể n dũ liêu qua cổ ng: IN và OUT Cu´ pha´ p: • IN
AL, <Địa chỉ cổng> • OUT
<Địa chỉ cổng>, AL
Trong đo´ :<Địa chỉ cổng> chính là số hiệu cổng (port) mà lệnh nhận nhiệm vụ trao đổi dữ liệu qua nó.
Địa chỉ cổng có thể được ghi trực tiếp dưới dạng một hằng số hoặc được ghi thông qua thanh ghi Dx. Ta´ c dung: -
LênhIn (Input): Đọc một lượng dữ liệu 8 bít từ cổng được chỉ ra ở <Địa chỉ cổng> đưa vào lưu trữ trong thanh ghi AL.
Nếu địa chỉ cổng nằm trong giới hạn từ 0 đến FF (hệ thập lục phân) thì có thể viết trực tiếp trong câu
lệnh, nếu địa chỉ cổng lớn h¢n FF thì ta phải dùng thanh ghi Dx để chỉ định địa chỉ cổng.
- LệnhOut (Output): Gởi một lượng dữ liệu 8 bít từ thanh ghi AL ra cổng được chỉ ra ở <Địa chỉ
cổng>. Tư¢ng tự lệnh In, địa chỉ cổng có thể được viết trực tiếp trong câu lệnh hoặc thông qua thanh ghi Dx. Vi´ dụ 1: - In Al, 40h ; - Mov Dx, 3B0h ; In Al, Dx ; Ví dā 2: - Out 40h, Al ; - Mov Dx, 3B0h ; Out Dx, Al ; Ví dā 3: Các khai báo hằng: DAT EQU 13h ; POR EQU 7Ch ; Các lệnh: Mov Al, POR ; lOMoAR cPSD| 36066900 Mov Bl, DAT ; Out Bl, Al ;
Vi´ dụ 1: Giả sử taị điạ chỉ 0100:0120 trong bộ nh¢´ co´ chư´ a môṭ mả ng dữ liêụ , gô` m 100 ô nh¢´ , mỗi ô
la` 1 word. Hãy ti´nh tổ ng nôị dung củ a ca´ c ô nh¢´ na` y, kê´t quả chư´ a trong thanh ghi Dx. Mov Ax, 0100 Mov DS, Ax
; trỏ cẳp thanh ghi DS:DI vể Mov DI, 0120
; đẩu vủng nhở cẩn truy xuẩt (0100:0120) ; Mov Dx, 0
; chuẩn bỉ Dx để lưu tổng Mov Cx, 100
; lẳp 100 lẩn vỉ mảng gổm 100 ô nhở Lap_TT: Add Dx, DS:[DI]
; cổng thêm n/dung ô nhở chỉ bởi DS:DI vảo Dx nên Add DI, 2
; chỉ đển ô nhở kể tiểp (vỉ ô nhở word tăng 2) Loop Lap_TT
; lẳp lải đủ 100 lẩn (duyểt qua đủ 100 ô nhở) ;
Kê´t thu´ c đoaṇ lêṇ h trên tổ ng nôị dung củ a 100 word nh¢´ bă´t đâ` u taị điạ chỉ 0100:0120 trong bộ nh¢´
đư¢̣c lưu va` o thanh ghi Dx. ¡̉ đây chu´ ng ta bỏ qua khả năng tra` n dữ liêụ trong thanh ghi kê´t quả Dx.
Ví dā 2: Các lệnh sau đây sẽ copy 100 word dữ liệu từ vùng nhớ bắt đầu tại địa chỉ 0100:0120 sang
vùng nhớ bắt đầu tại 0100:0500: Mov Ax, 0100 Mov DS, Ax Mov SI, 0120
; trỏ DS:SI về vùng nhớ nguồn 0100:0120 Mov DI, 0500
; trỏ DS:DI về vùng nhớ địch 0100:0500 ; Mov Cx, 100 Lap_Copy: Mov Ax, DS:[SI]
; lưu tạm nội dụng word nhớ tại DS:SI vào Ax Mov DS:[DI], Ax
; ghi giá trị Ax và word nhớ tại DS:DI Add SI, 2
; đến word nhớ tiếp theo Add DI, 2 lOMoAR cPSD| 36066900 Loop Lap_Copy ;
Hai vùng bộ nhớ đã cho đều ở trong cùng một segment nên ở đây ta chỉ cần sử dụng một thanh ghi đoạn dữ liệu DS.
Vi´ dụ 3: Các lệnh sau đây sẽ tính tổng nội dung của 100 ô nhớ (100 byte nhớ) trong bộ nhớ, bắt đầu
tại địa chỉ 0A00:0120. Kết quả được lưu vào word nhớ ngay trước vùng nhớ này: Mov Ax, 0A00h Mov ES, Ax Mov SI, 0120h
; trỏ DS:SI về vùng nhớ nguồn 0A00:0120 Mov DI, SI
; trỏ DS:DI về vùng nhớ nguồn 0A00:0120 Sub DI,2
; trỏ DS:DI về word nhớ trước vùng nhớ nguồn ; Mov Cx, 100 Mov Dx, 0 ; DX chứa tổng TTong: Add Dx, Byte PTR ES:[SI]
; cộng n.d của byte nhớ tại ES:SI vàoDX Inc SI ; ô nhớ byte Loop TTong ; Mov Word PTR ES:[DI], DX ;
Trong đoạn lệnh trên chúng ta sử dụng toán tử PTR để định kiểu ô nhớ cần truy xuất.
Lệnh Add Dx, Byte PTR ES:[SI]: lấy nội dung của byte nhớ tại ô nhớ được chỉ bởi ES:SI cộng
thêm vào thanh ghi DX. Nếu ta viết Add Dx, ES:[SI] thì hệ thống lấy giá trị cả 1 word tại ES:SI
cộng thêm vào DX (vì DX là thanh ghi word (16 bít), điều này không đúng với thực tế, vì chúng ta
đang cần truy xuất đến các byte nhớ. Trong trường hợp này, nếu không dùng toán tử PTR thì lệnh
Add phải viết như sau: Add DL, ES:[SI], khi đó hệ thống chỉ lấy giá trị cả 1 byte tại ES:SI cộng
thêm vào DL (vì DL là thanh ghi byte: 8 bít),
Ví dā 4: Các lệnh sau đây sẽ copy toàn bộ 20 kí tự Ascii từ biến Xau1 vào biến Xau2. Giả sử Xau1
và Xau2 đã được khai báo trước như sau: Xau1 DB 8Khoa CNTT – DHKH Hue9 Xau2 DB 20 Dup (8 9) Các lệnh: Mov Ax, @Data lOMoAR cPSD| 36066900 Mov DS, Ax Lea SI, Xau1 Lea DI, Xau2 ; Mov Cx, 20 Lap_Copy: Mov Al, Byte PTR DS:[SI] Mov Byte PTR DS:[DI], Al Inc SI Dec DI Loop Lap_Copy ;
Các biến của chư¢ng trình h¢̣p ngữ được khai báo trong đoạn Data. Hai lệnh đầu tiên ở trên có tác
dụng lấy địa chỉ segment của đoạn Data đưa vào thanh ghi đoạn DS. Do đó, DS sẽ chứa địa chỉ
segment của hai biến Xau1 và Xau2. Hay cụ thể: DS:SI trỏ về biến Xau1 và DS:DI trỏ về Xau2.
Ví dā 5: Chư¢ng trình sau đây: Để in một xâu kí tự từ trong chư¢ng trình ra màn hình văn bản đ¢n sắc (môi trường MSDOS). Chú ý:
- BIOS và MSDOS đã cung cấp nhiều hàm/ngắt để chư¢ng trình h¢̣p ngữ ghi một kí tự hoặc một
xâu kí tự ra màn hình, tại vị trí hiện tại của con trỏ màn hình hoặc tại một tọa độ màn hình xác định
nào đó. Kỹ thuật này được gọi là kỹ thuật ghi ra màn hình gián tiếp thông qua các hàm/ngắt màn hình.
- Biết rằng, tất cả những thông tin mà chúng ta thấy được trên màn hình của một máy tính đều
được lưu trữ trong bộ nhớ màn hình của máy tính đó, theo một cách nào đó. Tức là, nọi thao tác
ghi/đọc trên màn hình đều phải thông qua bộ nhớ màn hình. Như vậy: Muốn ghi một xâu kí tự ra màn
hình chư¢ng trình có thể ghi nó vào bộ nhớ màn hình. Muốn đọc một xâu kí tự từ màn hình chư¢ng
trình có thể đọc nó từ bộ nhớ màn hình. Kỹ thuật này được gọi là kỹ thuật truy xuất trực tiếp bộ nhớ màn hình.
- Mỗi hệ điều hành, mỗi chế độ màn hình sử dụng một đoạn bộ nhớ xác định (thường là khác
nhau) cho bộ nhớ màn hình. Và cách tổ chức lưu trữ thông tin trên màn hình trong bộ nhớ màn hình
cũng khác nhau với các hệ điều hành, các chế độ màn hình khác nhau.
- Trên môi trường hệ điều hành MSDOS, bộ nhớ màn hình của chế độ nàm hình đ¢n sắc 25 dòng
(0 đến 24) x 80 cột (0 đếm 79) được lưu trữ tại segment nhớ 0B800, bắt đầu tại offset 0000.
- Tổ chức lưu trữ thông tin trên màn hình trong bộ nhớ màn hình loại này như sau: Mỗi kí tự trên
nàm hình chiếm 1 word trong bộ nhớ màn hình: byte thấp chứa mã ASCII của kí tự, byte cao chứa
thuộc tính (màu chữ, màu nền,...) của kí tự đó. Từng dòng kí tự trên màn hình, từ trái sang phải (từ
cột 0 đến cột 79), được lưu tữ lần lượt tại các offset liên tiếp nhau trong bộ nhớ màn hình: 80 kí tự lOMoAR cPSD| 36066900
của dòng 0 được lưu trữ tại 80 word đầu tiên, 80 kí tự của dòng 1được lưu trữ tại 80 word tiếp
theo,... , 80 kí tự của dòng 79ược lưu trữ tại 80 word cuối cùng trong bộ nhớ nàm hình. Như vậy ta
dễ dàng tính ra được offset trong bộ nhớ màn hình, tư¢ng ứng với một kí tự trên màn hình khi ta biết
được tọa độ (dòng, cột) của nó. Cụ thể, offset của kí tự tạo tọa độ (Dòng, Cßt) được tính theo công thức sau:
(80*(Dong - 1) + (Cot - 1))*2 -
Bộ nhớ màn hình loại này có kích thước là 25 x 80 x 2 (byte) = 40000 byte. lOMoAR cPSD| 36066900
NG¾T TRONG CH¯¡NG TRÌNH ASSEMLBLY
Ngắt (Interrupt) là các tín hiệu mà các thành phần trong hệ thống, như: thiết bị ngoại vi, hệ điều hành,
chư¢ng trình người sử dụng, ..., gửi đến vi xử lý (họ Intel) mỗi khi nó cần trao đổi thông tin với vi xử
lý hay cần được sự phục vụ từ vi xử lý. Ngắt cũng có thể phát sinh từ chính bên trong vi xử lý khi nó
phát hiện một lỗi nghiêm trong xảy ra trong quá trình xử lý của nó. Khi nhận được một tín hiệu yêu
cầu ngắt vi xử lý sẽ dừng ngay thao tác (lệnh) hiện tại để xem xét và đáp ứng yêu cầu ngắt đó, sau
đo mới tiếp tục lại từ thao tác (lệnh) mà nó bị dừng trước đó.
Các máy IBM_PC, sử dụng vi xử lý Intel, bao gồm nhiều loại ngắt: Ngắt phần cứng, ngắt phần mềm,
ngắt bên trong, ngắt không chắn được (ngắt có độ ưu tiên cao nhất). Trong đó ngắt phần mềm là các
ngắt được phát sinh từ hệ điều hành và chư¢ng trình của người sử dụng, điều này thường thấy trong
các chư¢ng trình h¢̣p ngữ .
Hệ thống các ngắt mềm bên trong máy tính IBM_PC được cung cấp bởi BIOS và hệ điều hành, gồm
256 ngắt, chúng được đánh số từ 0 đến 255 và được gọi là số hiêu ngắt. Mỗi ngắt mềm tư¢ng ứng
với một chư¢ng con được gọi là chư¢ng trình con phục vụ ngắt, tức là, để đáp ứng một yêu cầu ngắt
mềm thì hệ thống sẽ phải thực hiện một chư¢ng trình con phục vụ ngắt tư¢ng ứng.
H¢̣p ngữ cung cấp lệnh Int để các chư¢ng trình gọi một ngắt mềm khi cần. Khi gọi ngắt mềm chư¢ng
trình phải chỉ ra số hiệu ngắt cần gọi, khi đó hệ thống sẽ phải gọi thực hiện chư¢ng trình con phục vụ
ngắt tư¢ng ứng để đáp ứng yêu cầu gọi này.
Trong môi trường hệ điều hành 16 bít MSDOS, ngay sau khi khởi động, hệ điều hành nạp tất cả 256
chư¢ng trình con phục vụ ngắt vào bộ nhớ. Khi đó 256 địa chỉ (Segment:Offset) các vùng nhớ,
n¢i định vị của 256 chư¢ng trình con phục vụ ngắt, sẽ được lưu trữ tại một không gian nhớ đặc biệt
trong bộ nhớ, vùng nhớ này được gọi là bảng vector ngắt (mỗi địa chỉ định vị của một chư¢ng trình
con phục vụ ngắt được gọi là một vector ngắt.
Như vậy khi hệ thống nhận được một yêu cầu ngắt n (Int n) từ chư¢ng trình thì nó phải: 1. Dừng
lệnh hiện tại, đưa giá trị con trỏ lệnh CS:IP hiện tại (đó chính là địa chỉ của lệnh sau lệnh gọi ngắt)
vào lưu trữ ở hai phần tử trên đỉnh Stack; 2. Xác định phần tử (địa chỉ) trong bảng vector ngắt chứa
vector ngắt của ngắt n, lấy giá trị tại đây (2 phần tử liên tiếp) để đưa vào cặp thanh ghi con trỏ lệnh
CS:IP (đây chính là địa chỉ của lệnh đầu tiên của chư¢ng trình con phục vụ ngắt n trong bộ nhớ); 3.
Bắt đầu thực hiện chư¢ng trình con phục vụ ngắt cho đến khi gặp lệnh kết thúc chư¢ng trình này, đó
là lệnh Iret; 4. Lấy nội dung của hai phần tử trên đỉnh Stack đặt vào lại cặp thanh ghi con trỏ lệnh để
quay trở lại tiếp tục thực hiện lệnh sau lệnh gọi ngắt trong chư¢ng trình.
Những vấn đề liên quan đến Stack và cơ chế của một lßi gọi ngắt sẽ được chúng tôi làm rõ hơn á
phần sau của tài liệu này. 1.Lên ̣ h Int Cu´ pha´ p: Int <n>
Trong đo´ : Trong đó là số hiệu ngắt của một ngắt mềm cần gọi. Tức là, n có thể là một trong các
giá trị từ 0 đến 255, đó chính là số hiệu ngắt của 256 ngắt mềm mà BIOS và MSDOS cung cấp. lOMoAR cPSD| 36066900 Ta´ c dun
g: Lệnh Int (Interrupt) được sử dụng để gọi một ngắt mềm (của BIOS hoặc MSDOS) trong
chư¢ng trình h¢̣p ngữ. Khi một ngắt mềm được gọi thì hệ thống sẽ thực hiện chư¢ng trình con phục
vụ ngắt tư¢ng ứng với nó. Ví dā 1: Int 10h ; gọi ngắt 10h của BIOS Int 20h
; gọi ngắt 20h của MSDOS
Một ngắt mềm, hay chính xác h¢n là một chư¢ng trình con phục vụ ngắt, có thể thực hiện nhiều chức
năng khác nhau, mỗi chức năng này được thể hiện thông qua một con số, được gọi là số hiệu hàm
của ngắt. Do đó, trước khi gọi ngắt chư¢ng trình phải chỉ rõ gọi ngắt với hàm nào, bằng cách đặt số
hiệu hàm cần gọi vào thanh ghi AH. Ví dā 2: Mov Ah, 01
; gọi ngắt 21h với hàm 01, Hay nói ngược lại: gọi hàm Int 21h ; 01 của ngắt 21h
Trước khi gọi hàm/ngắt chư¢ng trình cần cung cấp đầy đủ dữ liệu vào (nếu có) cho nó, sau khi
hàm/ngắt được thực hiện chư¢ng trình cần xác định rõ nó có trả về dữ liệu kết quả (dữ liệu ra) hay
không, nếu có thì chứa ở đâu: thanh ghi hay ô nhớ, và có tác động đến các cờ hiệu hay không. Vi´ dụ 3: Mov Ah, 02
; đặt số hiệu hàm cần gọi vào AH Mov DH, 10
; cung cấp dữ liệu vào thứ nhất vào DH Mov DL, 20
; cung cấp dữ liệu vào thứ hai vào DL Int 10h
; gọi ngắt 10h với hàm 02. Hàm/ngắt này không
; trả về dữ liệu kết quả.
Dãy lệnh trên thực hiện việc gọi hàm 02 của ngắt 10h (ngắt của BIOS), nó thực hiện việc dịch chuyển
con trỏ đến dòng 10 cột 20 của màn hình văn bản. Ví dā 4: Mov Ah, 2Bh ; gọi ngắt 21h với Int 21h ; hàm 2Bh
Hàm này trả về ngày-tháng-năm hiện tại (theo đồng hồ hệ thống trên máy tính). Cụ thể: Thanh ghi CX
(1980-2099) chứa năm hiện tại, thanh ghi DH (1-12) chứa tháng hiện tại, thanh ghi DL (1-31) chứa
ngày hiện tại. Đồng thời AL cho biết ngày trong tuần (0 : chủ nhật, 6 : thứ 7).
Môt sô´ ha` m củ a ngă´t 21h (MSDOS)
Ngắt 21h của MSDOS là ngắt thưßng được sử dụng nhất, nên á đây chúng tôi chọn giới thiệu
về ngắt này, nhưng chỉ với các hàm vào/ra kí tự/xâu kí tự cơ bản. Chức năng của mỗi ngắt, chức lOMoAR cPSD| 36066900
năng của các hàm trong một ngắt, các yêu cầu dữ liệu vào/ra của từng hàm trong mỗi ngắt,... dễ
dàng tìm thấy trong các tài liệu về lập trình hệ thống.
Ha` m 02 cũa ng¿t 21h: Ta´ c dun
g: In một kí tự ra màn hình. Kí tự (hoặc mã ASCII của kí tự) cần in được đặt trước trong
thanh ghi DL. Kí tự ở đây có thể là kí tự thường hoặc kí tự điều khiển. Sủ dung: Va` o: Ah = 02 Dl = Ra: Không có
Vi´ dụ 1: Các lệnh sau đây in ra màn hình kí tự A: Mov Ah, 02 Mov Dl, 8A9
;có thể viết lệnh Mov Dl, 41h Int 21h
; 41h là mã ASCII của kí tự A
Ví dā 2: Các lệnh sau đây in ra màn hình 10 kí tự, bắt đầu từ kí tự A: Mov Cx, 10 Mov Ah, 02 Mov Dl, 8A9 Lap_In: Int 21h INC DL Loop Lap_In
Ví dā 3: Các lệnh sau đây in xâu kí tự từ trong biến TieuDe ra màn hình. Giả sử rằng biến TieuDe đã được khai báo như sau: TieuDe DB 8Khoa CNTT Truong DHKH Hue9 Các lệnh: Lea DI, TieuDe Mov Cx, 25 Mov Ah, 02 Lap_In: Mov Dl, Byte PTR [DI] Int 21h INC DI lOMoAR cPSD| 36066900 Loop Lap_In ;
Ví dā 4: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một xâu kí tự ASCII, gồm 50 kí tự. Các
lệnh sau đây sẽ in xâu kí tự nói trên ra màn hình. Mov Ax, 0A00h Mov ES, Ax Mov DI, 0100h ; Mov Cx, 50 Mov Ah, 02 Lap_In: Mov Dl, Byte PTR ES:[DI] Int 21h INC DI Loop Lap_In ;
Ha` m 09 cũa ng¿t 21h: Ta´ c dun
g: In một dãy kí tự (một xâu kí tự) ra màn hình. Địa chỉ của xâu cần in này phải được chỉ bởi
cặp thanh ghi DS:DX và xâu phải được kết thúc bởi dấu $. Sủ dung: Va` o: Ah = 09 DS:DX = Ra: Không có
Vi´ dụ 1: Giả sử chư¢ng trình đã khai báo biến TieuDe. Viết lệnh in nội dung của biến TieuDe ra màn hình: -
Trong trường hợp này biến TieuDe phải được khai báo trước như sau: TieuDe DB 8Truong DH Khoa hoc Hue$9 -
Và đoạn lệnh gồm các lệnh sau: Mov Ah, 09 Mov Ax, Seg TieuDe Mov DS, Ax Mov Dx, Offset TieuDe
; có thể dùng lệnh Lea TieuDe lOMoAR cPSD| 36066900 Int 21h
Trong thự tế, với các chư¢ng trình h¢̣p ngữ viết ở dạng đoạn giản đ¢n, thì không cần đưa địa chỉ
Segment của biến cần in vào DS. Bởi vì: -
Đối với các chư¢ng trình dạng COM: -
Đối với các chư¢ng trình dạng EXE:
Ví dā 2: Giả sử biến TieuDe đã được khai báo như sau: TieuDe DB 8Khoa CNTT Truong DHKH Hue$9
Các lệnh sau chỉ in các kí tự Mov Ax, Segment TieuDe Mov DS, Ax Mov Dx, TieuDe Add Dx, 11 Mov Ah, 09 Int 21h
Các lệnh sau chỉ in các kí tự Mov Ax, Segment TieuDe Mov DS, Ax Mov Dx, TieuDe Mov DI, Dx Add DI, 10 Mov Byte PTR DS:[DI], 8$9 Mov Ah, 09 Int 21h
Ví dā 3: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một xâu kí tự ASCII, gồm 50 kí tự. Các
lệnh sau đây sẽ in xâu kí tự nói trên ra màn hình. Mov Ax, 0A00h Mov DS, Ax Mov Dx, 0100h ; Mov DI, Dx Mov Cx, 50 Add DI, Cx lOMoAR cPSD| 36066900 Mov Byte PTR DS:DX, 8$9 ; Mov Ah, 09 Int 21h ;
Ha` m 01 cũa ng¿t 21h: Ta´ c dun
g:Nhập một kí tự từ bàn phím vào lưu trong thanh ghi AL. Cụ thể là, AL sẽ chứa mã ASCII
của kí tự ghi trên phím nhập. Sủ dung: Va` o: Ah = 01 Ra:
Al = 0: nếu phím nhập là một trong các phím chức năng
Al = < mã ASCII của kí tự ghi trên phím nhập>
Cụ thể như sau: Khi chư¢ng trình gọi ngắt 21h với hàm 01 thì màn hình sẽ xuất hiện một con trỏ
nhập, để người sử dụng nhập vào một kí tự từ bàn phím. Khi đó, nếu người sử dụng gõ một trong
các phím chức năng thì AL nhận được giá trị 0, nếu người sử dụng gõ một phím kí tự nào đó thì AL
nhận được mã ASCII của kí tự đó.
Chú ý: Hàm 08 của ngắt 21h có chức năng tư¢ng tự hàm 01 ngắt 21h nhưng kí tự trên phím gõ
không xuất hiện trên màn hình, tất cả đều được xuất hiện bởi kí tự 8*9. Vi´ dụ 1: Mov Ah, 01
; với hai lệnh này màn hình sẽ xuất hiện con trỏ Int 21h
; nhập để người sử dụng nhập một kí tự vào AL
Ví dā 2: Các lệnh sau đây cho phép nhập một xâu kí tự, với số lượng kí tự được ấn định trước, vào
biến LuuXau đã khai báo trước trong chư¢ng trình
Giả sử biến LuuXau được khai báo như sau: LuuXau DB 30 Dup (8 8) Các lệnh: Mov Ax, Seg LuuXau Mov DS, Ax Lea DI, LuuXau ; Mov Cx, 30 Mov Ah, 01 Nhap_Xau: lOMoAR cPSD| 36066900 Int 21h Mov Byte PTR DS:[DI], Al INC DI Loop Nhap_Xau ;
Trong trường hợp này chúng ta đã giả sử: Người sử dụng chỉ nhập các kí tự (gõ phím kí tự để nhập),
không nhập các phím chức năng.
Trong thực tế, không bao giờ chúng ta sử dụng hàm 01 ngắt 21h để nhập xâu, vì nó tồn tại hai hạn
chế: không thể kết thúc nhập bằng cách gõ phím Enter; số kí tự của xâu nhập phải được ấn định
trước trong chư¢ng trình. Để khắc phục, MSDOS cung cấp hàm 0Ah của ngắt 21h để hỗ trợ nhập xâu kí tự.
Ha` m 0Ah cũa ng¿t 21h: Ta´ c dun
g:Nhập một xâu kí tự vào một biến đệm cho trước trong chư¢ng trình, biến này phải được
chỉ bởi cặp thanh ghi DS:DX. Và biến đệm phải có dạng như sau: -
Byte 0: chứa số kí tự tối đa của xâu nhập vào -
Byte 1: chứa một trị không (= 0) -
Byte 2, byte 3, byte 4, ... chứa một trị rỗng (để trống), để chứa các kí tự sẽ được nhập vào sau
này (khi chư¢ng trình thực hiện).
Để có được một biến như trên chư¢ng trình phải khai báo biến (tên biến là Xau_Nhap) như sau: Xau_Nhap DB 256, 0, 256 Dup (8 8)
Như vậy Xau_Nhap là một biến kiểu byte, gồm 258 byte. Byte đầu tiên (byte) chứa trị 256, byte 1
chứa trị 0, 256 byte tiếp theo chứa kí tự trống, được sử dụng để chứa các kí tự sẽ được nhập sau
này. Xau_Nhap chứa tối đa 256 kí tự.
Cũng có thể sử dụng hàm 0Ah/21h để nhập một xâu kí tự vào vùng nhớ có địa chỉ xác định trong bô nhớ. Sủ dung: Va` o: Ah = 0Ah
DS:DX = <Địa chỉ Segment:Offset của xâu nhập> Ra: DS:DX không thay đổi
Biến đệm bây giờ có dạng như sau: - Byte 0: không thay đổi -
Byte 1: chứa tổng số kí tự đã được nhập vào -
Byte 2, byte 3, byte 4, ... chứa các kí tự đã được nhập vào. lOMoAR cPSD| 36066900
Vi´ dụ 1: Với khai báo biến đệm Xau_Nhap như trên, nếu sau này chư¢ng trình nhập vào xâu: hoc= thì: - Byte 0: vẫn chứa số 256 -
Byte 1: chứa giá trị 7, đó chính là 7 kí tự trong xâu -
Từ byte 2 đến 8 chứa lần lượt các kí tự trong xâu
Ví dā 2: Giả sử chư¢ng trình đã khai báo xâu TieuDe như sau: TieuDe DB 100, 0, 100 Dup (8 8)
Các lệnh sau đây sử dụng hàm 0Ah/21h để nhập một xâu kí tự vào biến TieuDe: Mov Ax, Seg TieuDe Mov Ds, Ax Lea Dx, TieuDe Mov Ah, 0Ah Int 21h
Các lệnh sau đây lấy số kí tự thực sự nhập đưa vào lưu trữ trong thanh ghi Cx: Mov Cx, 0 Mov Cl, TieuDe[1]
Các lệnh sau đây sử dụng hàm 02/21h để in xâu kí tự vừa nhập ra lại màn hình: Lea DI, TieuDe Mov Cx, 0 Mov Cl, TieuDe[1] Add DI, 2 Mov Ah, 02 Lap_In: Mov Dl, DS:[DI] Int 21h INC DI Loop Lap_In
Các lệnh sau đây sử dụng hàm 09/21h để in xâu kí tự vừa nhập ra lại màn hình: Mov Ax, Seg TieuDe Mov Ds, Ax Lea Dx, TieuDe lOMoAR cPSD| 36066900 Add Dx, 2 Mov DI, Dx Mov Cx, 0 Mov Cl, TieuDe[1] Add DI, Cx Mov Byte PTR DS:[DI], 8$9 Mov Ah, 09h Int 21h
Ví dā 3: Chư¢ng trình dạng COM sau đây sử dụng hàm 0Ah ngắt 21h để nhập một xâu kí tự vào
biến Buff. Sau đó sử dụng hàm 09h ngắt 21h để in xâu kí tự vừa nhập ra lại màn hình.
Để đ¢n giản, chư¢ng trình khai báo biến Buff gồm toàn kí tự 8$9, nhờ đó, khi dùng hàm 09h/21h để
in ra chư¢ng trình không cần đưa thêm kí tự 8$9 vào cuối xâu nhập, mà chỉ cần trỏ DS:DX về đầu
vùng/xâu kí tự cần in ra. .Model small .Code ORG 100h Start: JMP Main
TBN DB 'Nhap vao mot xau ki tu: $'
TBX DB 0Ah,0Dh,'Xau vua nhap: $' Buff DB 100,0,100 Dup ('$') Main Proc Mov Ah, 09h Lea Dx, TBN Int 21h ; Mov Ah, 0Ah Lea Dx, Buff Int 21h ; Mov Ah, 09h Lea Dx, TBX lOMoAR cPSD| 36066900 Int 21h ; Mov Ah, 09h Lea Dx, Buff Add Dx, 2 Int 21h ; Int 20h Main Endp End Start Mßt sß ví dā:
Vi´ dụ 1: Giả sử hàm X của ngắt Y khi được gọi sẽ trả về trong cặp thanh ghi ES:SI địa chỉ của vùng
nhớ chứa tên của nhà sản xuất (nhãn hiệu) của vi xử lý đang sử dụng trên máy tính hiện tại, tên này dài không quá 8 kí tự.
Các lệnh sau đây sẽ in tên nhà sản xuất nói trên ra màn hình: Mov Ah, X
; hàm cần gọi được đưa vào thanh ghi ah Int Y ; gọi ngắt Y với hàm X ; Mov Cx, 8
; tên dài không quá 8 kí tự Mov Ah, 02
; dùng hàm 02/21h để in kí tự ra nàm hình LapIn: Mov Dl, Byte PTR ES:[SI] Int 21h INC SI ; đến kí tự tiếp theo Loop LapIn ;
Ví dā 2: Giả sử hàm X của ngắt Y khi được gọi với AL = 1 (được gọi là hàm con) sẽ trả về trong cặp
thanh ghi ES:DI địa chỉ của vùng nhớ chứa ngày-tháng-năm sản xuất ROM-BIOS đang sử dụng trên
máy tính hiện tại. Đồng thời hàm/ngắt này cũng cho biết số kí tự trong xâu ngày-tháng-năm trong thanh ghi BX.
Các lệnh sau đây sẽ in xâu ngày-tháng-năm nói trên ra màn hình: Mov Ah, X
; hàm cần gọi được đưa vào thanh ghi ah Mov Al, 1 ; gọi hàm X với Al = 1 lOMoAR cPSD| 36066900 Int Y ; gọi ngắt Y với hàm X ; Mov Cx, Bx
; đưa số kí tự của xâu ngày-tháng-nămvào Cx Mov Ah, 02
; dùng hàm 02/21h để in kí tự ra nàm hình LapIn: Mov Dl, Byte PTR ES:[DI] Int 21h INC DI ; đến kí tự tiếp theo Loop LapIn ;
Ví dā 3: Hàm 39h của ngắt 21h được sử dụng để tạo thư mục con trên đĩa. Hàm này quy định:
DS:DX chứa xâu tên thư mục cần tạo, bao gồm cả đường dẫntìm đến thư mục này, xâu này kết thúc
bởi trị 0. Nếu việc tạo không thành công thì Cf = 1, khi đó thanh ghi Ax sẽ chứa mã lỗi.
Các lệnh sau đây sẽ tạo ra thư mục con BTCB trong thư mục ASSEM trên thư mục gốc ổ đĩa D.
Chư¢ng trình phải khai báo biến TenTM, chứa xâu tên thư mục cần tạo như sau: TenTM DB 8D:\ASSEM\BTCB9,0 Các lệnh: Mov Ax, Seg TenTM Mov DS, Ax Mov Dx, Offset TenTM Mov Ah, 39h Int 21h ; Jc TB_Loi
; nếu CF = 1 thì việc tạo bị lỗi Jmp KetThuc TB_Loi: KetThuc: ... ; MÞT SÞ VÍ DĀ lOMoAR cPSD| 36066900
Ví dā 1: Giả sử hàm X của ngắt Y khi được gọi sẽ trả về trong cặp thanh ghi ES:SI địa chỉ của vùng
nhớ chứa tên của nhà sản xuất (nhãn hiệu) của vi xử lý đang sử dụng trên máy tính hiện tại, tên này dài không quá 8 kí tự.
Các lệnh sau đây sẽ in tên nhà sản xuất nói trên ra màn hình: Mov Ah, X
; hàm cần gọi được đưa vào thanh ghi ah Int Y ; gọi ngắt Y với hàm X ; Mov Cx, 8
; tên dài không quá 8 kí tự Mov Ah, 02
; dùng hàm 02/21h để in kí tự ra nàm hình LapIn: Mov Dl, Byte PTR ES:[SI] Int 21h INC SI ; đến kí tự tiếp theo Loop LapIn ;
Ví dā 2: Giả sử hàm X của ngắt Y khi được gọi với AL = 1 (được gọi là hàm con) sẽ trả về trong cặp
thanh ghi ES:DI địa chỉ của vùng nhớ chứa ngày-tháng-năm sản xuất ROM-BIOS đang sử dụng trên
máy tính hiện tại. Đồng thời hàm/ngắt này cũng cho biết số kí tự trong xâu ngày-tháng-năm trong thanh ghi BX.
Các lệnh sau đây sẽ in xâu ngày-tháng-năm nói trên ra màn hình: Mov Ah, X
; hàm cần gọi được đưa vào thanh ghi ah Mov Al, 1 ; gọi hàm X với Al = 1 Int Y ; gọi ngắt Y với hàm X ; Mov Cx, Bx
; đưa số kí tự của xâu ngày−tháng−nămvào Cx Mov Ah, 02
; dùng hàm 02/21h để in kí tự ra nàm hình LapIn: Mov Dl, Byte PTR ES:[DI] Int 21h INC DI ; đến kí tự tiếp theo Loop LapIn ; lOMoAR cPSD| 36066900
Ví dā 3: Hàm 39h của ngắt 21h được sử dụng để tạo thư mục con trên đĩa. Hàm này quy định:
DS:DX chứa xâu tên thư mục cần tạo, bao gồm cả đường dẫntìm đến thư mục này, xâu này kết thúc
bởi trị 0. Nếu việc tạo không thành công thì Cf = 1, khi đó thanh ghi Ax sẽ chứa mã lỗi.
Các lệnh sau đây sẽ tạo ra thư mục con BTCB trong thư mục ASSEM trên thư mục gốc ổ đĩa
D. Chư¢ng trình phải khai báo biến TenTM, chứa xâu tên thư mục cần tạo như sau: TenTM DB 8D:\ASSEM\BTCB9,0 Các lệnh: Mov Ax, Seg TenTM Mov DS, Ax Mov Dx, Offset TenTM Mov Ah, 39h Int 21h ; Jc TB_Loi
; nếu CF = 1 thì việc tạo bị lỗi
‹In ra thong bao hoan thanh> Jmp KetThuc TB_Loi:
‹In ra thong bao khong tao duoc thu muc> KetThuc: ... ;
Ví dā 4: Sau đây là chư¢ng trình dạng COM: In ra tất cả (256) các kí tự có trong bảng mã ASCII: .Model Small .Code ORG 100h Start: Jmp Main TB
DB 'Noi dung Bang ma ASCII:',0Ah,0Dh,'$' Main Proc Mov Ah, 09h Lea Dx, TB Int 21h lOMoAR cPSD| 36066900 ; Mov Cx, 256 Mov Ah, 02 Mov Dl, 0 LapIn: Int 21h ; Mov Bl, Dl Mov Dl, ' ' Int 21h Mov Dl, Bl ; INC Dl Loop LapIn ; Int 20h Main Endp End Start
Ví dā 5.1: Sau đây là chư¢ng trình dạng COM: Nhập vào một xâu kí tự bất kỳ. Sau đó in ra lại chính
xâu kí tự vừa được nhập vào nhập. Sử dụng hàm 09/21h để in ra. .Model Small .Code ORG 100h Start: JMP Main TBN
DB 'Nhap vao mot xau ki tu: $' TBX DB 0Ah,0Dh,'Xau vua nhap: $' Buff DB 100,0,100 Dup (' ') Main Proc Mov Ah, 09h lOMoAR cPSD| 36066900 Lea Dx, TBN Int 21h ; Mov Ah, 0Ah Lea Dx, Buff Int 21h ; Mov Cx, 0 Mov Cl, Buff[1] Add Dx, 2 Mov DI, Dx Add DI, Cx Mov Byte PTR [DI], 8$9 ; Mov Ah, 09h Lea Dx, TBX Int 21h ; Mov Ah, 09h Lea Dx, Buff Add Dx, 2 Int 21h ; Int 20h Main Endp End Start
Ví dā 5.2: Sau đây là chư¢ng trình dạng EXE: Nhập vào một xâu kí tự bất kỳ. Sau đó in ra lại chính
xâu kí tự vừa được nhập vào nhập. Sử dụng hàm 02/21h để in ra. .Model Small .Stack 100h lOMoAR cPSD| 36066900 .Data TBN
DB 'Nhap vao mot xau ki tu: $' TBX DB 0Ah,0Dh,'Xau vua nhap: $' Buff DB 100,0,100 Dup (' ') .Code Main Proc Mov Ax,@Data Mov DS, Ax ; Mov Ah, 09h Lea Dx, TBN Int 21h ; Mov Ah, 0Ah Lea Dx, Buff Int 21h ; Mov Cx, 0 Mov Cl, Buff[1] Add Dx, 2 Mov DI, Dx ; Mov Ah, 09h Lea Dx, TBX Int 21h ; Mov Ah, 2 Lap_In: Mov Dl, [DI] Int 21h INC DI lOMoAR cPSD| 36066900 Loop Lap_In ; Mov Ah, 4Ch Int 21h Main Endp End
Ví dā 6: Sau đây là chư¢ng trình dạng COM: Nhập vào một kí tự thường, chư¢ng trình sẽ in ra kí tự in hoa tư¢ng ứng. .Model Small .Code ORG 100h Start: Jmp Main TB1
DB 'Nhap vao mot ki tu thuong: $' TB2
DB 0Ah,0Dh,'Ki tu hoa tuong ung: $' Main Proc Mov Ah, 09h Lea Dx, TB1 Int 21h ; Mov Ah, 01 Int 21h Mov Bl, Al ; Mov Ah, 09h Lea Dx, TB2 Int 21h ; Mov Ah, 02 Mov Dl, Bl Sub Dl, 20h lOMoAR cPSD| 36066900 Int 21h ; Int 20h Main Endp End Start
Ví dā 7: Sau đây là chư¢ng trình dạng COM: Nhập vào một xâu kí tự sau đó in ra lại xâu đã nhập
theo thứ tự đảo ngược. .Model Small .Code ORG 100h Start: JMP Main TBN
DB 'Nhap vao mot xau ki tu: $' TBX DB 0Ah,0Dh,'Xau vua nhap: $' Buff DB 100,0,100 Dup (' ') Main Proc Mov Ah, 09h Lea Dx, TBN Int 21h ; Mov Ah, 0Ah Lea Dx, Buff Int 21h ; Mov Cx, 0 Mov Cl, Buff[1] ; Mov Ah, 09h Lea Dx, TBX Int 21h ; lOMoAR cPSD| 36066900 Lea Dx, Buff Add Dx, 2 Mov DI, Dx Add DI, Cx Dec DI Mov Ah, 02 Lap_In_Nguoc: Mov Dl, [DI] Int 21h DEC DI Loop Lap_In_Nguoc ; Int 20h Main Endp End Start
Ví dā 8:,Sau đây là chư¢ng trình dạng COM: Nhập vào hai số (số thứ nhất: nhỏ h¢n 5; số thứ hai:
nhỏ h¢n hoặc bằng 5), sau đó in ra tổng của hai số vừa nhập. .Model Small .Code ORG 100h Start: Jmp Main TBN1 DB
'Nhap so hang thu nhat (nho hon 5): $' TBN2 DB
0Ah,0Dh,'Nhap so hang thu hai (nho hon bang 5): $' TBX DB
0Ah,0Dh,'Tong cua hai so la: $' Main Proc Mov Ah, 09h Lea Dx, TBN1 Int 21h Mov Ah, 01 lOMoAR cPSD| 36066900 Int 21h Mov Bl, Al Sub Bl, 30h ; Mov Ah, 09h Lea Dx, TBN2 Int 21h Mov Ah, 01 Int 21h Sub Al, 30h Add Bl, Al ; Mov Ah, 09h Lea Dx, TBX Int 21h Mov Ah, 02 Mov Dl, Bl Add Dl, 30h Int 21h ; Int 20 Main Endp End Start lOMoAR cPSD| 36066900
TẬP LÞNH ASSEMBLY CŨA INTEL 8088/8086 - (Tiếp theo) 8. Lßnh so sánh: Cu´ pha´ p: Cmp
[Toa´ n haṇ g đi´ch], [Toa´ n haṇ g nguô` n]
• Trong đo´ : [Toa´ n hạng đic´ h], [Toa´ n hạng nguô` n] có thể là hằng, biến, thanh ghi hay ô nhớ. [Toa´ n hạng đic´ h]
không thể là hằng số. [Toa´ n hạng đic´ h] và [Toa´ n haṇ g nguô` n] không thể đồng thời là ô nhớ.
• Ta´ c duṇ g: Lệnh Cmp (Compare) được sử dụng để so sánh giá trị/nội dung của [Toa´ n haṇ g đi´ch] so với
[Toa´ n hạng nguô` n]. Tư¢ng tự như lệnh Sub, nó lấy [Toa´ n haṇ g đic´ h] trừ đi [Toa´ n hạng nguô` n] nhưng kết
quả không làm thay đổi [Toa´ n hạng đi´ch] mà chỉ làm thay đổi giá trị của một số cờ hiệu: CF, ZF, OF,...
Kết quả so sánh của lệnh Cmp là: [Toa´ n hang đi´ch] > [Toa´ n haṇ g nguô` n]; [Toa´ n haṇ g đi´ch] g[Toa´ n
haṇ g nguô` n]; [Toa´ n haṇ g đi´ch] < [Toa´ n han g nguô` n]; [Toa´ n haṇ g đi´ch] f[Toa´ n han g nguô` n]; [Toa´ n
haṇ g đi´ch] = [Toa´ n han g nguô` n]; [Toa´ n han g đi´ch] ≠ [Toa´ n haṇ g nguô` n];... mỗi kết quả sẽ tác động (0
→1, 1→0) đến một cờ tư¢ng ứng cụ thể nào đó.
Do đó, để biết được kết quả so sánh chư¢ng trình phải sử dụng các lệnh kiểm tra cờ (đó là cá lệnh
nhảy), và chúng phải được đặt ngay sau lệnh so sánh. Như vậy lệnh Cmp sẽ không có ý nghĩa khi nó đứng độc lập.
Có thể nói ngược lại, lệnh Cmp được sử dụng để cung cấp điều kiện nhảy (thay đổi giá trị các cờ)
cho các lệnh nhảy có điều kiện. Vi´ dụ 1: • Cmp Ax, Bx
; so sánh giá tị thanh ghi Ax với Bx • Cmp Ax, 20
; so sánh giá trị thanh ghi Ax với 20 • Cmp Ax, [SI]
; so sánh Ax với nội dung ô nhớ được chỉ bởi SI Ví dā 2: • Cmp Al, 8A9
; so sánh giá trị thanh ghi Al với 8A9 • Cmp Al, Var1
; so sánh giá trị thanh ghi Al với giá trị biến Var1
Tất cả cá lệnh Cmp ở trên điều không có ý nghĩa, vì nó không cho biết kết quả so sánh một cách trực
tiếp mà phải ánh thông qua các cờ.
Lệnh Cmp không thể sử dụng để so sánh hoặc kiểm tra giá trị của các cờ. 9. Các lßnh nhảy
Lßnh nhảy không điều kißn: Cu´ pha´ p: Jmp
<Vị trí đích>
• Trong đo´ : có thể là nhãn của một lệnh, tên của một thủ tục hoặc có thể là một thanh ghi, một ô
nhớ (đã được định nghĩa) nào đó. cũng có thể là một biến nào đó, giá trị của nó thường là địa
chỉ của một ô nhớ trong đoạn Code. lOMoAR cPSD| 36066900
• Ta´ c duṇ g: Khi gặp lệnh này chư¢ng trình chuyển điều khiển (nhảy đến) đến thực hiện lệnh sau đích> mà không phụ thuộc vào bất kỳ điều kiện nào.
C¢ chế thực hiện của lệnh Jmp là thay đổi nội dung của cặp thanh ghi con trỏ lệnh CS:IP. CS:IP mới
sẽ là địa chỉ của lệnh sau trong bộ nhớ.
Lệnh Jmp có 3 dạng: Short, Near và Far. Đối với dạng Short và Far thì chỉ có thanh ghi IP bị thay đổi
khi lệnh thực hiện, ngược lại, với dạng Far, khi lệnh thực hiện thì cả thanh ghi IP và thanh ghi đoạn
CS đều bị thay đổi. Hay nói cách khác: Đối với dạng Short và Near thì lệnh Jmp và phải
nằm trong cùng Segment nhớ, ngược lại, với dạng Far thì lệnh Jmp và có thể nằm ở các Segment nhớ khác nhau. Vi´ dụ 1: Start: Jmp Main TieuDe DB 8Khoa CNTT – DHHH9 Main Proc .................. Main Endp End Start Ví dā 2: • Jmp short Main • Jmp Ax • Jmp word PTR [BX] • Jmp dword PTR [BX] Ví dā 3: Reset DD 5BE000F0h Jmp Reset Ví dā 4: Mov Ax, 15 Mov Bx, 20 Jmp TTong Add Ax, Bx TTong: Sub Ax, Bx Mov Cx, Ax lOMoAR cPSD| 36066900
Kết thúc đoạn lệnh trên Cx = Ax = -5, vì lệnh Add
Ax, Bx không được thực hiện. Khi gặp lệnh Jmp
TTong chư¢ng trình nhảy đến thục hiện lệnh sau nhãn TTong, đó chính là lệnh Sub Ax, Bx.
Lßnh nhảy có điều kißn: Cu´ pha´ p chung:
<Vị trí đích>
• Trong đo´ : : Tư¢ng tự như lệnh Jmp.
• Ta´ c duṇ g: Khi gặp một lệnh nhảy có điều kiện, đầu tiên chư¢ng trình sẽ kiểm tra điều kiện nhảy của nó,
nếu thỏa mãn thì sẽ nhảy đến thực hiện lệnh ở , nếu không thì bỏ qua không thực hiện lệnh nhảy này.
Điều kiện nhảy của các lệnh nhảy này chính là sự thay đổi giá trị của các cờ hiệu, do đó để tạo điều
kiện nhảy cho một lệnh nhảy xác định thì chư¢ng trình phải làm thay đổi giá trị của cờ hiệu tư¢ng
ứng với nó. Chư¢ng trình thường dùng các lệnh địch bít, quay bít, so sánh,... để làm thay đổi giá trị
các cờ hiệu để tạo điều kiện nhảy cho các lệnh nhay. Cách đ¢n giản nhất là sử dụng lệnh Cmp ngay trước lệnh nhảy.
Sau đây là các lßnh nhảy có điều kißn vái dữ lißu có d¿u: • Lệnh JG:
Nhảy nếu [Đích] > [Nguồn] ; (SF = 0F và ZF = 0) • Lệnh JL:
Nhảy nếu [Đích] < [Nguồn] ; (SF <> 0F) • Lệnh JGE:
Nhảy nếu [Đích] g[Nguồn] ; (SF = 0F) • Lệnh JLE:
Nhảy nếu [Đích] f[Nguồn] ; (CF <> 0F và ZF = 1) • ...
Trong đó: [Đích] và [Nguồn] chính là hai toán hạng: [Toa´ n haṇ g đi´ch] và [Toa´ n han g nguô` n] trong
lệnh Cmp đứng ngay trước lệnh nhảy. Tức là, chư¢ng trình sử dụng lệnh Cmp để tạo điều kiện nhảy
cho các lệnh này. Cụ thể: lệnh nhảy có thực hiện được hay không (có chuyển điều khiển đến đích> hay không) phụ thuộc vào giá trị của [Đích] và [Nguồn] trong lệnh Cmp đứng ngay trước nó.
Với việc sử dụng lệnh Cmp để tạo điều kiện nhảy cho các lệnh nhảy thì ta không cần quan tâm đến
các cờ điều kiện nhảy của chúng.
Sau đây là các lßnh nhảy có điều kißn vái dữ lißu không d¿u: • Lệnh JA:
Nhảy nếu [Đích] > [Nguồn] ; (CF = 0 và ZF = 0) • Lệnh JB:
Nhảy nếu [Đích] < [Nguồn] ; (CF = 0) • Lệnh JNA:
Nhảy nếu [Đích] không lớn h¢n [Nguồn]; (CF =1 or ZF =1) • Lệnh JNB:
Nhảy nếu [Đích] không nhỏ h¢n [Nguồn] ; (CF = 0)
Các lệnh nhảy với dữ liệu có dấu có thể áp dụng với các dữ liệu không dấu.
Sau đây là các lßnh nhảy có điều kißn dùng chung: • Lệnh JC: Nhảy nếu cờ CF = 1 • Lệnh JNC: Nhảy nếu cờ CF = 0 • Lệnh JZ: Nhảy nếu cờ ZF = 1 lOMoAR cPSD| 36066900 • Lệnh JNZ: Nhảy nếu cờ ZF = 0 • Lệnh JE:
Nhảy nếu [Đích] = [Nguồn]; Tư¢ng tự JZ; (ZF = 1) • Lệnh JNE:
Nhảy nếu [Đích] ≠[Nguồn]; Tư¢ng tự JNZ; (ZF = 0) • ... [2 - 150]
Với các lệnh này, chư¢ng trình thường sử dụng các lệnh dịch bít hoặc lệnh quay bít để tạo điều kiện nhảy nó.
Ví dā 1a: Dãy lệnh sau đây thực hiện việc gán giá trị cho thanh ghi Cx dựa vào giá trị của thanh ghi Ax và Dx: Mov Ax, 12 Mov Dx, 25 ; Cmp Ax, Dx ; Ax ? Bx Jg Nhan1 ; nếu Ax > Dx Jle Nhan2 Nhan1: Mov Cx, Ax Jmp Tiep_Tuc Nhan2: Mov Cx, Dx Jmp Tiep_Tuc Tiep_Tuc: Mov Bx, Cx ;
Có thể thấy, ở đây không cần dùng lệnh Jle Nhan2, vì nếu Ax không lớn h¢n Dx thì chắc chẵn nó
sẽ nhỏ h¢n hoặc bằng Dx. Ngoài ra cũng không cần dùng lệnh Jmp
Tiep_Tuc sau nhãn Nhan2, vì
việc chuyển đến lệnh sau nhãn Tiep_Tuc ở đây là tất nhiên. Vì thế đoạn lệnh trên có thể được viết
rút gọn như trong Ví dā 1b sau đây.
Ví dā 1b: Dãy lệnh sau đây là trường hợp rút gọn của dãy lệnh trên: Mov Ax, 12 Mov Dx, 25 ; Cmp Ax, Dx Jg Nhan1 ; nếu Ax > Dx lOMoAR cPSD| 36066900 Mov Cx, Dx ; khi Ax fDx Jmp Tiep_Tuc Nhan1: Mov Cx, Ax ; khi Ax > Dx Tiep_Tuc: Mov Bx, Cx ;
Trong cả hai ví dụ trên: khi kết thúc, Bx = Cx = Dx = 25. Nhưng nếu cho
Ax = 120 (Ax > Bx) thì Bx = Cx = Ax = 120.
Ví dā 2: Giả sử tại địa chỉ 0A000:0100 trong bộ nhớ có chứa một mảng các số nguyên kiểu byte,
gồm 100 phần tử (100 byte).
Các lệnh sau đây tính tổng của các phần tử trong mảng này mà giá trị của nó lớn h¢n 123. Kết quả chứa ở thanh ghi Dx. Mov Ax, 0A000h Mov DS, Ax Mov DI, 0100h ; Mov Dx, 0 Mov Cx, 100 Lap_TT: Mov Al, Byte PTR DS:[DI] Cmp Al, 123 Jle Tiep_Tuc Add Dx, Al Tiep_Tuc: INC DI
; trỏ đến phần tử kế tiếp Loop Lap_TT
; lặp lại: kiển tra và tính tổng ;
Ví dā 3: Giả sử tại địa chỉ 0C000:00120 trong bộ nhớ có chứa một xâu kí tự, xâu này được kết thúc bởi giá trị 0 (số 0).
Các lệnh sau đây sẽ đếm xem xâu nói trên gồm bao nhiêu kí tự. Kết quả ghi vào ô nhớ ngay trước vùng nhớ chứa xâu này: Mov Ax, 0C000h lOMoAR cPSD| 36066900 Mov ES, Ax Mov DI, 00120h Mov SI, DI ; Mov Dx, 0 Lap_Dem: Mov Al, Byte PTR ES:[DI] Cmp Al, 0 ; so sánh Al với 0 Je KetThuc
; nếu Al = 0: đã đến cuối xâu INC Dx ; khi Al <> 0: đếm INC DI
; trỏ đến kí tự kế tiếp Jmp Lap_Dem
; lặp lại: kiển tra và đếm KetThuc: Mov Byte PTR DS:[SI - 1], Dx ;
Ví dā 4: Các lệnh sau đây in nội dung bản bản ASCII ra màn hình, nhưng không in ra các kí tự có mã 07h, 0Ah, 0Dh. Mov Cx, 256 Mov Ah, 02 Mov Dl, 0 Lap_In: Cmp Dl, 07h Je TTuc Cmp Dl, 0Ah Je TTuc Cmp Dl, 0Dh Je TTuc Int 21h TTuc: INC DL Loop Lap_In lOMoAR cPSD| 36066900 ;
Ví dā 5: Các lệnh sau đây cho phép nhập một xâu kí tự bất kỳ, dài không quá 200 kí tự, từ bàn phím
vào biến XauNhap. Sau đó copy các kí tự là chữ cái in hoa trong xâu vừa nhập vào biến XauHoa.
Trước hết chư¢ng trình phải khai báo các biến: XauNhap DB 200, 0, 200 Dup (8 9) XauHoa DB 200 Dup (8 9) Các lệnh: Mov Ax, Seg XauNhap Mov DS, XauNhap Mov Dx, Offset XauNhap Mov Ax, Seg XauHoa Mov ES, XauHoa Mov DI, Offset XauHoa ; Mov Ah, 0Ah Int 21h ; Mov Cx, 0 Mov Cl, XauNhap[1] Mov SI, Dx Add SI, 2 ; Lap_Copy: Mov Al, DS:[SI] Cmp Al, 8A9 Jl TTuc Cmp Al, 8Z9 Jg TTuc Mov ES:[DI], Al INC DI TTuc: lOMoAR cPSD| 36066900 INC SI ; Loop Lap_Copy ;
Nên nhớ, trong bảng mã ASCII các kí tự là chữ cái in hoa nằm ở những vị trí liên tiếp nhau: A, B, C,
..., Z, chúng có mã lần lượt là 65, 66, 67, ..., 90.
Ví dā 6: Giả sử tại địa chỉ 0F000:FFFE trong bộ nhớ ROM-BIOS có chứa một byte dữ liệu. Byte này
cho biết loại của máy tính đang sử dụng. Cụ thể, nếu byte này: chứa trị 0FBh: máy PC/XT; chứa trị
0FCh: máy PC/AT; chứa trị 0FFh: máy PC classic;...
Các lệnh sau đây cho biết máy tính đang sử dụng thuộc loại máy nào:
Trước hết chư¢ng trình phải khai báo các biến trả lời: TB1 DB 8Day la may PC/XT.$9 TB2 DB 8Day la may PC/AT.$9 TB3 DB 8Day la may PC classic.$9 Các lệnh: Mov Ax, 0F000h Mov ES, Ax Mov SI, 0FFFEh ; Mov Al, Byte PTR ES:[SI] Cmp Al, 0FBh Je TraLoi1 Cmp Al, 0Fch Je TraLoi2 Cmp Al, 0Ffh Je TraLoi3 ... TraLoi1: Mov Ax, Seg TB1 Mov DS, Ax Lea Dx, Offset TB1 Mov Ah, 09 Int 21h lOMoAR cPSD| 36066900 Jmp KetThuc TraLoi2: Mov Ax, Seg TB2 Mov DS, Ax Lea Dx, Offset TB2 Mov Ah, 09 Int 21h Jmp KetThuc TraLoi1: Mov Ax, Seg TB3 Mov DS, Ax Lea Dx, Offset TB3 Mov Ah, 09 Int 21h Jmp KetThuc ..... KetThuc: Mov Ah, 4Ch Int 21h ;
Có thể nói, ví dụ trên đây là một thao tác điển hình trong lập trình h¢̣p ngữ. Nó cũng cho thấy chức
năng và thế mạnh của ngôn ngữ này. Đây cũng là mục tiêu mà người lập trình h¢̣p ngữ nhắm tới.
Việc truy xuất vào các vùng nhớ dữ liệu để lấy các byte/word thông tin cấu hình hệ thống là một yêu
cầu c¢ bản với các ngôn ngữ lập trình cấp thấp, và nó được thực hiện một cách khá đ¢n giản trong
ngôn ngữ h¢̣p ngữ. Ví dụ trên đây cũng cho thấy nguyên tắc để làm việc này.
10. Các lßnh Dịch bít – Quay bít
Các lệnh dịch bít là các lệnh làm cho các bít trong một thanh ghi bị dịch về bên trái (lệnh ShR) hoặc
về bên phải (lệnh ShL) một hoặc nhiều bít. Lệnh quay bít làm cho các bít trong một thanh ghi quay
theo quay đều lần lượt được đưa qua cờ CF. Do đo, các lệnh dịch bít và quay bít thường được sử dụng
để kiểm tra giá trị bít (= 0 hay = 1) của các bít trong thanh ghi.
H¢̣p ngữ cung cấp hai dạng lệnh quay bít, quay không qua cờ CF (lệnh RcL và Rcl) và quay có qua cờ CF (lệnh ShL và ShR). lOMoAR cPSD| 36066900 Cu´ pha´ p: • Shr
[Toa´ n hạng đi´ch], <n> • Shl
[Toa´ n hạng đic´ h], <n>
• Rcr [Toa´ n hạng đi´ch], <n> • Rcl
[Toa´ n hạng đic´ h], <n>
Trong đo´ : [Toa´ n haṇ g đi´ch] là một thanh ghi 8 bít hoặc 16 bít. là số bít cần dịch, nếu = 1
thì chỉ định trực tiếp trong câu lệnh, nếu lớn h¢n 1 phải chỉ định thông qua thanh ghi CL. Ta´ c dung:
• Lệnh ShR (Shift Logical Right): Dịch chuyển các bít trong thanh ghi [Toa´ n hạng đi´ch] sang phải một hoặc
nhiều bít. Các bít được dịch lần lượt được đưa vào cờ CF, cờ CF sẽ chứa bít của lần dịch cuối cùng. Sau
khi dịch các bít bị khuyết (ở bên đối diện) sẽ được thay bằng các bít có trị 0. Tức là, với thanh ghi 8 bít thì
sau 8 lần dịch nó sẽ nhận được một dãy 8 bít = 0, tư¢ng tự với thanh ghi 16 bít thì sau 16 lần dịch nó sẽ
nhận được một dãy 16 bít = 0. Nếu thanh ghi AL = 01001001 thì sau khi bị dịch về bên trái 2 bít nó sẽ như
sau: AL = 00100100, khi đó CF = 1.
• Lệnh ShL (Shift Logical Left): Tư¢ng tự như lệnh ShR nhưng các bít được dịch về phía bên trái.
• Lệnh RCR (Rotate through Carry Right): Tư¢ng tự như lệnh ShR, nhưng bít được dịch sẽ được đặt vào lại
bít bị khuyết ở bên đối diện. Tức là, với thanh ghi 8 bít thì sau 8 lần dịch nó sẽ nhận lại dãy bít ban đầu,
tư¢ng tự với thanh ghi 16 bít thì sau 16 lần dịch nó sẽ nhận lại dãy bít ban đầu. Nếu thanh ghi AL
= 01001001 thì sau khi bị quay về bên trái 2 bít nó sẽ như sau: AL = 00100101, khi đó CF = 1.
• Lệnh RCL (Rotate through Carry Left): Tư¢ng tự như lệnh RCR nhưng các bít được quay về phía bên trái. Ví dā 1: • Shr Al, 1 • Mov Cl, 2 Shr Al, CL Shl Bl, CL Rcl AL, CL • Rcr Dl, 1
Ví dā 2: Các lệnh sau đây đếm số bít bằng 1 trong thanh ghi BX, kết quả chứa ở thanh ghi Al mà
không làm thay đổi giá trị của nó: Mov Al, 0 Mov Cx, 16 DemBit1: Rcl Bx, 1 Jnc TiepTuc lOMoAR cPSD| 36066900 Inc Al TiepTuc: Loop DemBit1 ;
Ví dā 3: Các lệnh sau đây đếm số bít giống nhau (tư¢ng ứng) giữa hai thanh ghi Ax và Bx. Kết quả
chứa trong biến Dem (Dem DB 0): Mov Dem, 0 Xor Ax, Bx Mov Cx, 16 KTra: Shl Ax, 1 Jnc Bit_0 Jmp TiepTuc Bit_0: Inc Dem TiepTuc: Loop KTra ;
Ví dā 4: Các lệnh sau đây in nội dung của thanh ghi BX ra màn hình dưới dạng sô nhị phân: Mov Cx, 16 ; lặp in đủ 16 bít Mov Ah, 02
; in ra kí tự với hàm 02/21h In_NhiPhan: Shl Bx, 1
; dịch trái để bít cần in r¢i vào cờ CF Jc In_1
; lệnh JC nhảy khi cờ CF = 1 Mov Dl, 809
; CF = 0 tức là bít cần in có trị = 0, nên in ra kí tự 809 Int 21h Jmp In_Tiep In_1: Mov Dl, 819 Int 21h In_Tiep: lOMoAR cPSD| 36066900 Loop In_NhiPhan ;
Ví dā 5: Các lệnh sau đây nhập một số nhị phân từ bàn phím đưa vào lưu trữ trong thanh ghi BX: Mov Bx, 0 Mov Cx, 0 Mov Ax, 0 Mov Ah, 1 Nhap: Int 21h Cmp Al, 0Dh
; nếu nhập Enter thì kết thúc Je KetThuc ; Cmp Al, 819 Je Them_Bit Cmp Al, 809 Je Them_Bit ; Mov Ah,09 Mov
Dx, Seg TB ; Biến TB được khai báo như sau: Mov DS, Dx ; TB
DB 8Ban phai nhap 0/1 hoac Enter$9 Mov Dx, Offset TB Int 21h Jmp Nhap Them_Bit: Sub Al, 30h
; có thẻ sử dụng lệnh And Al, 0Fh Shl Bx, 1 Or Bx, Al Inc Cx Cmp Cx, 17 Jne Nhap KetThuc: lOMoAR cPSD| 36066900 Mov 4Ch Int 21h ;
Ví dā 6: Giả sử tại địa chỉ 0B000:0010 trong bộ nhớ có chứa một byte dữ liệu, nó cho biết một số
thông tin liên quan đến cấu hình hệ thống. Byte 3 cho biết máy tính hiện tại có (byte 3 = 1) hay không
có (byte 3 = 0) cổng cắm USB.
Các lệnh sau đây cho biết máy tính hiện tại (máy thực hiện đoạn lệnh) có hay không có cổng cắm
USB. Kết quả được thông báo thông qua thanh ghi Cx: Cx = 0: không có cổng cắm USB; Cx = 0FFFFh: có cổng cắm USB. Mov Ax, 0B000h Mov DS, Ax Mov DI, 0010h ; Mov Ax, 0 Mov Al, byte PTR DS:[DI] ; Mov Cl, 4 Shr Al, Cl Jc CoUSB ; co cong USB Mov Cx, 0 ; khong co cong USB Jmp KetThuc CoUSB: Mov Cx, 0FFFFh KetThuc: ... ;
Trong thực tế, kết quả kiểm tra này thường được trả lời thông qua ngay chính cờ CF. CF = 0: không
có cổng USB, CF = 1: có cổng USB.
Chú ý 1: Có thể sử dụng lệnh dịch bít ShR/ShL để thực hiện phép chia/phép nhân giá trị của một
thanh ghi (chứa số nguyên không dấu) với một số là bội số của 2.
Ví dā: Hai lệnh sau đây sẽ dịch AL sang trái 3 bít, tức là nhân AL với 8: Mov Cl, 3 Shl Al, Cl
; Al ß AL * 8, 8 = 23. lOMoAR cPSD| 36066900
Ví dā: Hai lệnh sau đây sẽ dịch AL sang phải 3 bít, tức là chia AL với 8: Mov Cl, 3 Shr Al, Cl
; Al ß AL * 8, 8 = 23.
Chú ý 2: H¢̣p ngữ còn cung cấp các lệnh dịch chuyển số học SAL (Shift Arithmetic Left) và SAR
(Shift Arithmetic Right) . SAL tư¢ng tự hoàn toàn ShL, có thể sử dụng để thực hiện nhân 2 với các số
âm. SAR tư¢ng tự ShR nhưng bít cuối cùng của [Toa´ n hang đi´ch] không bị thay bằng bít 0 mà vẫn
giữ nguyên giá trị cũ, có thể sử dụng để thực hiện chia 2 với các có dấu.
Các lệnh dich bít, quay bít của các vi xử lý Intel 80286/80386/. .... cho phép viết số bít cần dịch, trong
trường hợp lớn h¢n một, trực tiếp trong lệnh dịch, quay mà không cần thông qua thanh ghi Cl [1 – 540]. lOMoAR cPSD| 36066900 Thanh Ghi
Thanh ghi nằm bên trong CPU tùy theo đội dài 8 hay 16 bít vàtùy theo chức năng. Khi
đó, thanh ghi được dùng để chứa dữ liệu, lưu trữ dữliệu, kết quả trung gian của máy tính
hoặc đơn vị địa chỉ bộ nhớ 8088 có 14thanh ghi được chia làm 5 nhóm
I. Nhóm thanh ghi đa dụng : (General Register)
Gồm 4 thanh ghi đa dụng : AX,BX,CX và DX (có 16 bít)
Công dụng chung của các thanh ghi này là : dùng trong các phép
toán số học, logic, chứa dữ liệu.
Một thanh ghi 16 bít có thể được xem là 2 thanh ghi 8bít và
chúng được chia như sau : Thanh ghi 16 bít 2 thanh ghi 8 bít ========================= Byte cao Byte thấp AX AH AL BX BH BL CX CH CL DX DH DL ========================== Ví dụ : AX = 0x1234 AH = 0x12 AL = 0x34
Mỗi thanh ghi còn có những công dụng riêng của nó :
a. Thanh ghi AX : (Auxliary Register)
Công dụng riêng dùng trong các phép toán số học, lưu kết
quả của các phép toán *, chia, ...
b. Thanh ghi BX : (Base Regiser)
Dùng trong phép định địa chỉ cơ sở của bộ nhớ, nó đóng vai
trò như 1 thanh ghi địa chỉ offset của bộ nhớ
c. Thanh ghi CX : (Count Regiser)
Dùng để chứa số vòng lặp trong chương trình, nó đóng vai
tròn như một biến đếm cho việc lặp vòng. Ngoài ra, thanh ghi
CL còn được dùng trong các phép dịch chuyển với số lần dịch
chuyển là nội dung của thanh ghi CL.
d. Thanh ghi DX : (Data Regiser)
Dùng để lưu trữ kết quả của phép toán * hoặc /, định địa chỉ
cổng trong các lệnh xuất nhập cổng.
II. Nhóm thanh ghi đoạn : (Segiment regiset)
Gồm 4 thanh ghi : CS, DS, ES, SS
a. Thanh ghi CS : (Code Segment)
Dùng để chứa địa chỉ Segment của đoạn mã của đoạn mã chương trình. lOMoAR cPSD| 36066900
b. Thanh ghi DS : (Data Segment)
Chứa địa chỉ Segment của đoạn dữ liệu
c. Thanh ghi ES : (Extra Segment)
Chứa địa chỉ Segment của đoạn dữ liệu bổ sung. Như vậy nếu
ta có hai đoạn dữ liệu thì một sẽ do thanh ghi DS và hai sẽ do thanh ghi ES quản lý
d. Thanh ghi SS (Stack Segment)
Dùng lưu địa chỉ Segment của đoạn Stack
Bốn thanh ghi này có thể truy xuất trên bốn đoạn khác nhau.
Như vậy một chương trình làm việc cùng một lúc tối đa là bốn đoạn
III. Nhóm thanh ghi con trỏ và chỉ mục :
a. Thanh ghi SI : (Source Index)
Dùng để trỏ đến ô nhớ trong đoạn dữ liệu định bởi thanh ghi
DS, trong xử lí chuỗi thanh ghi SI dùng để trỏ đến địa chỉ bắt đầu của chuỗi nguồn
b. Thanh ghi DI : (Distination Index)
Dùng để trỏ đến ô nhớ có địa chỉ Segment định bởi thanh ghi
ES, trong xử lí chuỗi nó dùng để trỏ đến địa chỉ của chuỗi đích
c. Thanh ghi SP : (Stack pointer)
Dùng để trỏ đến phần tử ở trên đỉnh của Stack
d. Thanh ghi BP : (Base pointer)
Dùng trong phép định địa chỉ cơ sở, trong việc truy xuất
phần tử trên Stack. Nó được dùng trong các phép gọi chương trình con
e. Thanh ghi IP : (Instruction Pointer)
Chứa đến địa chỉ ô nhớ được định bởi thanh ghi CS để chỉ
đến mã lệnh của chương trình. Khi thực thi một lệnh CPU sẽ tự
động thay đổi nội dung của thanh ghi IP để trỏ đến lệnh kế tiếp
của chương trình, thanh ghi này không bị tác động trực tiếp bởi
các lệnh. Vì vậy, nó thường không có mặt trong những lệnh của hợp ngữ.
Những cặp thanh ghi thường đi chung : DS : SI ES : DI SS : SP SS : BP CS : IP IV. Thanh ghi cờ :
Mục đích của việc sử dụng cờ là việc chỉ ra trạng thái của CPU.
Để làm được điều đó bộ vi xử lí đã dành riêng ra một thanh ghi
gọi là thanh ghi cờ. Những bit trên thanh ghi này được gọi là các
cờ . Có hai loại cờ : Cờ trạng thái, cờ điều khiển
Cờ trạng thái phản ánh kết quả của phép toán
Cờ điều khiển dùng để cho phép hay không cho phép một thao tác nào đó lOMoAR cPSD| 36066900
Chúng ta chỉ quan tâm đến nhóm cờ trạng thái gồm 6 cờ là : CF, AF, SF, OF, PF, ZF.
a. Cờ CF : (Carry Flag) " Cờ nhớ "
Cờ CF được bật lên một nếu kết quả của phép toán có mượn
hay có nhớ đối với bít cao
b. Cờ AF : (Awiliary Flag) " Cờ nhớ phụ "
Bật lên một khi có mượn hay có nhớ ở bít 3
c. Cờ SF : (Sign Flag) " Cờ dấu "
Cờ SF được bật lên một nếu như kết quả của một phép tính có
bít cao nhất bằng một (số âm)
d. Cờ OF : (Over Flag) " Cờ tràn "
Được bật lên một nếu như kết quả của phép toán có dấu bị sai Ví dụ : 01010000 = AL (dương) + 01110000 = BL (dương) 11000000
e. Cờ PF : (Parity Flag) " Cờ chẵn le "
Cờ PF được bật lên một nếu như kết quả của một phép toán có
tổng 8 bít thấp là một số chẵn f. Cờ ZF : (Zero Flag)
ZF = 1 nếu như kết quả của phép toán bằng không Ví dụ : AX = FFFFh + BX = FFFFh 1FFFEh