HAY HOC LAP TRINH ASEMBLY NGAY HOM NAY. VO NO LA MOT NGON NGU LAP TRINH MUC THAP CO UH DUNG RAT NHIEU TRONG LINH VUC DIEN TU VOI LAP TRINH PHAN CUNG. VA DO BAO MAT RAT CAO.VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
Trang 1Tự học lập trình Assembly - Bài 1: Bước đầu với lập trình Assembly trên vi xử lý Intel
8086/8088
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 (“nhúng”) mã lệnh hợp 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
Trang 2hà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)
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: <Tên biến> DB <Trị khởi tạo>
• b: <Tên biến> DW <Trị khởi tạo>
• c: <Tên biến> DD <Trị khởi tạo>
• d: <Tên hằng> EQU <Hằng số>
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 <Trị khởi tạo> có thể một hoặc nhiều giá trị, nó có thể là
Trang 3mộ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 (“?”) <Hằng số> có thể là một số, một kí tự hay một xâu kí tự.
Trong ví dụ trên, hai biến Spt và Kitu đều là biến kiểu byte, kích thước 1byte BiếnTieuDe 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ụ 3: TenKhoa DB ‘Cong nghe Thong tin’, 0Ah, 0Dh, ‘$’
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
Trang 4Như 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):
Spt[2] = KiTu[1] = ‘T’, KiTu[5] = ‘h’, DaySo[-5] = ‘h’, TieuDe[11] = 1345,
Tự học lập trình Assembly - Bài 2: Biên dịch chương trình Assembly chạy trên môi trường hệ điều hành 16bit
Thứ Năm, 29/08/2013 18:47
Bài viết này hướng dẫn sử dụng các chương trình biên dịch 16 bít: Microsoft
MacroAssembler và A86 MacroAssembler V4.05 để dịch các chương trình hợp ngữ (*.asm) sang chương trình thực thi (*.exe và *.com), chạy được trên nền hệ điều hành MS_DOS:
· Chương trình A86 Macro Assembly (tập tin chính là: A86.com) thường được sử dụng để
dịch chương trình hợp ngữ sang chương trình thực thi dạng COM
· Chương trình Macro Assembly (tập tin chính là: MASM.exe) thường được sử dụng để
dịch chương trình hợp ngữ sang chương trình thực thi dạng EXE Tuy nhiên, MASM chỉ có thể dịch tập tin chương trình hợp ngữ sang dạng tập tin đối tượng mã máy dạng Obj Để chuyển tập tin Obj sang tập tin chương trình thực thi EXE ta phải sử dụng chương trình liên kết của
MSDOS, đó là Link.exe Để chuyển tập tin thực thi dạng EXE sang tập tin thực thi dạng COM
ta phải sử dụng thêm một chương trình khác của MS_DOS, đó là EXE2Bin.com
Có thể sử dụng các tập tin TASM.Exe và TLINK.Exe để thay thế cho MASM.exe và Link.exe Các tập tin này, và cả tập tin EXE2Bin.com, có thể tìm thấy trong bộ chương trình Turbo Pascal
Chức năng của các tập tin biên dịch hợp ngữ liên quan:
Trang 5• Tập tin MASM.exe/TASM.exe: Hỗ trợ dịch chương trình hợp ngữ nguồn sang tập tin mã máy dạng Obj tương ứng
• Tập tin Link.exe/TLink.exe: Hỗ trợ liên kết các tập tin mã máy, dạng tập tin đối tượng (*.Obj), thành tập tin thực thi dạng EXE
• Tập tin EXE2Bin.exe: Hỗ trợ chuyển các tập tin EXE, viết theo cấu trúc dạng COM, thành tập tin thực thi dạng COM
MASM có thể dịch tập tin chương trình hợp ngữ sang các tập tin: tập tin đối tượng (*.Obj), tập tin liệt kê thông tin (*.Lst), tập tin tham khảo chéo (*.Crf)
• Tập tin đối tượng (Object File): Chứa bảng dịch mã máy của các lệnh trong chương trình nguồn hợp ngữ, và các thông tin cần thiết để có thể tạo nên một tập tin thực thi Đây là tập tin chính để tạo nên tập tin thực thi
• Tập tin liệt kê thông tin (List File): Là một tập tin văn bản cho biết địa chỉ offset của từng lệnh trong đoạn Code; mã lệnh của các lệnh trong chương trình; danh sách các tên/nhãn dùng trong chương trình; các thông báo lỗi và một số thông tin khác Đây là tập tin cơ sở hỗ trợ việc gỡ rối chương trình
• Tập tin tham khảo chéo (Cross Reference File): Liệt kê các tên sử dụng trong chương trình và dòng mà chúng xuất hiện
Hầu hết các chương trình biên dịch hợp ngữ 16 bít đều không hỗ trợ màn hình soạn thảo chương trình Nhưng, nó cho phép chúng ta sử dụng bất kỳ chương trình/màn hình soạn thảo nào
đó, như Turbo Pascal, C,…, để viết chương trình Chúng ta có thể sử dụng chương trình soạn thảo Notepad trên Windows (Windows XP) để viết và chỉnh sửa chương trình hợp ngữ, trong trường hợp này ta nên đặt phần mở rộng cho tập tin chương trình là asm (*.asm)
Sử dụng A86 để dịch chương trình hợp ngữ sang chương trình dạng COM:
- Trước hết, các bạn nên viết chương trình hợp ngữ theo cấu trúc chương trình dạng COM và lưu tập tin với phần tên mở rộng là asm
Các bạn nên đặt tập tin chương trình hợp ngữ cần dịch ở cùng thư mục với tập tin dịch a86.com Ở đây chúng tôi giả sử chúng được đặt ở E:\ASSEM
- Sử dụng A86 để biên dịch chương trình:
Cú pháp : A86 [đường dẫn]<Tập tin>;
Ví dụ: E:\>\ASSEM\A86 In_Ascii.asm
Trên môi trường hệ điều hành Windows các bạn phải chuyển về cửa sổ MSDOS (Cmd)
để thực hiện việc biên dịch này
Trang 6- Nếu chương trình không có lỗi thì màn hình sẽ xuất hiện thông báo như sau:
Thông báo này cho biết việc biên dịch chương trình đã thành công Tập tin chương trình hợp ngữ In_Ascii.asm đã được dịch thành tập tin chương trình In_Ascii.COM (chính xác hơn là hình thành thêm 2 tập tin mới In_Ascii.COM và In_Ascii.SYM)
- Nếu chương trình bị lỗi thì A86 sẽ xuất hiện thông báo lỗi như sau:
Thông báo này cho biết, trong quá trình biên dịch A86 đã phát hiện thấy lỗi trong chương trình In_Ascii.asm, tức là, chưa thể dịch chương trình In_Ascii.asm sang chương trình thực thi Cụ thể:
- Thông báo “Error message inserted into In_Asscii.asm” cho biết thông báo lỗi đã được chèn vào ngay trong tập tin chương trình In_Asscii.asm
- Thông báo “Original source renamed as In_Ascii.OLD” cho biết tập tin chương trình gốc
đã được đổi tên thành In_Asscii.OLD
Trang 7Trong trường hợp này chúng ta phải quay về chương trình Notepad để mở (Open) lại tập tin chương trình In_Ascii.asm và tiến hành tìm và chỉnh sửa lỗi Tập tin In_Ascii.asm xuất hiện các dòng thông báo lỗi như sau:
- Sau khi sửa lỗi và ghi lại chúng ta tiếp tục dịch lại chương trình như ban đầu
Chúng ta có thể sử dụng kết hợp cả 3 tập tin Masm.exe, Link.exe và EXE2Bin.com để dịch một chương trình hợp ngữ viết theo dạng COM thành tập tin thực thi dạng COM.
Sử dụng MASM để dịch chương trình hợp ngữ sang chương trình dạng EXE:
- Trước hết, các bạn nên viết chương trình hợp ngữ theo cấu trúc của chương trình dạng EXE
và lưu vào file với phần mở rộng là asm
- Quá trình biên dịch chương trình gồm 2 bước:
Bước 1: Dùng MASM hoặc TASM để dịch chương trình hợp ngữ sang tập tin đối tượng
ngôn ngữ máy Cú pháp đơn giản như sau:
- MASM [đường dẫn] <File1.Asm> <File2.Obj>;
- TASM [đường dẫn] <File1.Asm> <File2.Obj>;
Ví dụ: E:\>\ASSEM\Tasm In_Ascii.asm In_Ascii.obj
Trang 8Nếu chương trình không có lỗi thì TASM sẽ xuất hiện thông báo như trên
Thông báo này cho biết quá trình biên dịch chương trình đã thành công Tập tin chương trình hợp ngữ In_Ascii.asm đã được dịch sang tập tin đối tượng ngôn ngữ máy In_Ascii.obj
Nếu chương trình bị lỗi thì TASM sẽ xuất hiện thông báo lỗi, cùng với số thứ tự của dòng
chương trình bị lỗi và nội dung lỗi trên màn hình, thông báo sau đây cho biết In_Ascii.asm bị lỗi tại dòng 10 và 11:
Trong trường hợp này chúng ta phải quay về chương trình Notepad để mở (Open) lại tập tin chương trình In_Ascii.asm và tiến hành tìm và chỉnh sửa lỗi
Chú ý: Để MASM/TASM tạo ra các tập tin LST và CRF trong khi biên dịch thì ta phải viết ra tên của các tập tin này trong câu lệnh biên dịch
Ví dụ:
E:\>\ASSEM\Tasm In_Ascii.asm, In_Ascii.obj, In_Ascii.lst, In_Ascii.crf
Với câu lệnh dịch này Tasm sẽ đồng thời tạo ra cả 3 tập tin In_Ascii.obj, In_Ascii.lst,
In_Ascii.crf, các tập tin này rất thiết cho việc kiểm tra lỗi và gỡ rối chương trình
Bước 2: Dùng Link hoặc Tlink để liên kết một hoặc nhiều file đối tượng thành một file thực thi
duy nhất Cú pháp đơn giản:
Trang 9LINK [đường dẫn]<File1.Obj>;
TLINK [đường dẫn]<File1.Obj>;
Ví dụ: E:\>\ASSEM\TLink In_Ascii.obj;
Nếu việc liên kết không bị lỗi (đối với các chương trình đơn giản chỉ có một file đối tượng thì bước này thường không gây ra lỗi) thì màn hình sẽ xuất hiện thông báo:
Microsoft (R) Overlay Linker Version 3.64
Copyright (c) Microsoft Corp 1981, 1988 All right reserved
Thông báo này cho biết việc liên kết các tập tin đối tượng chương trình đã thành công Tập tin chương trình hợp ngữ In_Ascii.asm đã được dịch thành tập tin chương trình In_Ascii.exe
Sau khi biên dịch chương trình hợp ngữ thành chương trình thực thi EXE hoặc COM chúng ta tiến hành cho chạy thử chương trình để kiểm tra, nếu kết quả không đúng như mong muốn thì phải xem và thay đổi lại nội dung chương trình và sau đó thực hiện biên dịch trở lại từ đầu
Tự học lập trình Assembly - Bài 3: 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
Trang 10mỗ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:
Chỉ dẫn ORG 100h và lệnh JMP <Thủ tục chính> sẽ được đề cập trở lại ở các phần sau đây của tài liệu này.
Cấu trúc chương trình dạng EXE:
Trang 11Cấu trúc này cho thấy rõ, một chương trình hợp ngữ dạng gồm 3 đoạn: đoạn Code, chứa toàn bộ
mã lệnh của chương trình Đoạn Data, chứa phần khai báo dữ liệu của chương trình Đoạn Stack, nơi chứa stack (ngăn xếp) của chương trình khi chương trình được nạp vào bộ nhớ để hoạt động Chỉ dẫn Stackđặt ở đầu chương trình với mục đích khai báo kích thước của Stack dùng cho chương trình sau này Kích thước thường được chọn là 100h (256) byte
Chỉ dẫn Model được đặt ở đầu cả cấu trúc chương trình dạng COM và EXE với mục đích khai báo chế độ bộ nhớ mà chương trình sử dụng
Ví dụ: Sau đây là hai chương trình hợp ngữ đơn giản, dạng COM và dạng EXE, cùng thực hiện nhiệm vụ in ra màn hình 3 dòng văn bản như sau :
Nguyen Kim Le Tuan
Nguyen Le Tram Thanh Nguyen Le Tram Uyen
Hai chương trình dưới đây chỉ có tác dụng minh họa cho việc sử dụng các hướng dẫn biên dịch định nghĩa đoạn đơn giản và giúp các bạn thấy được những điểm giống nhau, khác nhau giữa hai dạng cấu trúc chương trình dạng COM và EXE, vì vậy, ở đây các bạn chưa cần quan tâm đến ý nghĩa của các lệnh và các hàm/ngắt trong nó Phần lệnh hợp ngữ và các hàm/ngắt sẽ được trình bày ngay sau đây.
Chương trình viết theo cấu trúc dạng COM:
MyChildren DB ‘Nguyen Kim Le Tuan’,0Ah,0Dh
DB ‘Nguyen Le Tram Thanh’,0Ah,0Dh
DB ‘Nguyen Le Tram Uyen’,’$’
Trang 12End Start
Chương trình này chọn chế độ bộ nhớ Small Tên thủ tục chính là Main (tên thủ tục chính là tùy ý) Nhãn chính của chương trình là Start (tên thủ tục chính là tùy ý), đó chính là nhãn của lệnh Jmp Phần khai báo dữ liệu chỉ khai báo một biến, đó là MyChildren
Chương trình này gọi hàm 4Ch của ngắt 21h để kết thúc chương trình Có thể gọi ngắt 20h để kết thúc các chương trình dạng COM
Chương trình viết theo cấu trúc dạng EXE:
.Model Small
.Stack 100h
.Data
MyChildren DB ‘Nguyen Kim Le Tuan’,0Ah,0Dh
DB ‘Nguyen Le Tram Thanh’,0Ah,0Dh
DB ‘Nguyen Le Tram Uyen’,’$’
Thao tác đầu tiên của chương trình là trỏ thanh ghi đoạn DS về đầu đoạn Data, hay còn gọi là khởi tạo thanh ghi đoạn DS:
Mov Ax, @Data
Mov DS, Ax
thao tác này được xem như là bắt buộc đối với cấu trúc chương trình dạng EXE sử dụng định nghĩa đoạn đơn giản Các chương trình viết theo cấu trúc dạng EXE phải gọi hàm 4Ch của ngắt 21h để kết thúc
Có thể thấy, cấu trúc chương trình dạng COM và cấu trúc chương trình dạng EXE chỉ khác phần hướng dẫn biên dịch, phần khai báo biến và phần lệnh thao tác chính hoàn toàn giống nhau Hai chương trình đơn giản ở trên hoàn toàn giống nhau ở biến là MyChildren và các lệnh gọi hàm
Trang 1309h của ngắt 21h để in ra màn hình một xâu kí tự (xâu này chính là giá trị khởi tạo của biến MyChildren).
Chú ý 1: Trình biên dịch hợp ngữ (Macro Assembler) cho phép các chương trình được dịch bởi nóc họn sử dụng một trong các chế độ bộ nhớ sau:
- Small: Đoạn mã lệnh (Code) và đoạn dữ liệu (Data) của chương trình đều chỉ có thể chứa trong một đoạn (segment) bộ nhớ Tức là, kích thước của chương trình chỉ có thể tối đa là hai đoạn bộ nhớ Tuy vậy chế độ bộ nhớ này đủ dùng cho hầu hết các chương trình hợp ngữ
- Medium: Đoạn Code của chương trình có thể chiếm nhiều hơn một đoạn bộ nhớ Trong khi đó, đoạn Data chỉ có thể chiếm 1 đoạn bộ nhớ
- Compact: Đoạn Data của chương trình có thể chiếm nhiều hơn một đoạn bộ nhớ Trong khi đó, đoạn Code chỉ có thể chiếm 1 đoạn bộ nhớ
- Large: Đoạn Code và đoan Data của chương trình đều có thể chiếm nhiều hơn một đoạn bộ nhớ Nhưng trong trường hợp này không thể định nghĩa một mảng dữ liệu có kích thước lớn hơn
64 Kbyte
- Huge: Tương tự như Large, nhưng trong trường hợp này có thể định nghĩa một mảng dữ liệu có kích thước lớn hơn 64 Kbyte
Chế độ bộ nhớ Small là đơn giản nhất, được hầu hết các chương trình lựa chọn
Chú ý 2: Với các chương trình hợp ngữ sử dụng định nghĩa đoạn đơn giản: Khi được nạp vào bộ nhớ để hoạt động thì các thanh ghi đoạn sẽ tự động trỏ về các đoạn chương trình tương ứng Cụ thể: Thanh ghi đoạn CS chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Code của chương trình Thanh ghi đoạn DS (và có thể cả ES) chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Data của chương trình Thanh ghi đoạn SS chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Stack của chương trình
Tuy nhiên, trong thực tế, khi nạp chương trình EXE vào bộ nhớ DOS luôn dành ra 256 byte đầu tiên của vùng nhớ, mà DOS cấp phát cho chương trình, để chứa PSP (Program Segment Prefix) của chương trình PSP chứa các thông tin cần thiết mà trình biên dịch chuyển đến cho DOS để hỗ trợ DOS trong việc thực hiện chương trình này, đặc biệt, chương trình cũng có thể truy xuất vùng nhớ PSP Do đó, DOS phải đưa địa chỉ segment của vùng nhớ chứa PSP vào cả DS và ES trước khi chương trình được thực hiện Tức là, ngay khi chương trình được nạp vào bộ nhớ DS không phải chứa địa chỉ segment của đoạn Data của chương trình mà chứa địa chỉ segment của PSP
Vì vậy, để trỏ DS về lại đoạn Data chương trình chúng ta phải đặt ngay hai lệnh sau đây ở đầu chương trình viết theo cấu trúc EXE:
Mov Ax, @Data
Mov DS, Ax
Với việc khởi tạo thanh ghi đoạn DS ở trên, địa chỉ segment của tất cả các biến khai báo trong đoạn Data đều được chứa trong thanh ghi DS, do đó, trong các thao tác xử lý biến sau này
chương trình không cần quan tâm đến địa chỉ segment của nó nữa
Chú ý 3: Hợp ngữ còn cho phép các chương trình sử dụng các hướng dẫn biên dịch định nghĩa
đoạn toàn phần, các định nghĩa này phù hợp với hầu hết các trình biên dịch hợp ngữ hiện nay Định nghĩa đoạn toàn phần giúp cho việc viết chương trình hợp ngữ trở nên mềm dẻo và linh
Trang 14hoạt hơn, nó giúp người lập trình có thể điều khiển thứ tự các đoạn chương trình, kết hợp các đoạn chương trình, liên kết các đoạn chương trình trong bộ nhớ, , ngay trong khi lập trình.
Chi tiết về cách sử dụng và mục đích sử dụng của các hướng dẫn biên dịch nói chung và các định nghĩa đoạn toàn phần nói riêng dễ dàng tìm thấy trong rất nhiều tài liệu về lập trình hợp ngữ [1], [2] Ở đây chúng tôi chỉ giới thiệu sơ lược về nó thông qua ví dụ dưới đây
Ví dụ: Sau đây là một chương trình dạng EXE sử dụng các hướng dẫn biên dịch định nghĩa đoạn
toàn phần (phù hợp với Macro Assembler):
S_Seg Segment Stack
DB 100h DUP (?)
S_Seg Ends
D_Seg Segmet
MyChildren DB ‘Nguyen Kim Le Tuan’,0Ah,0Dh
DB ‘Nguyen Le Tram Thanh’,0Ah,0Dh
DB ‘Nguyen Le Tram Uyen’,’$’
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
Trang 15DS: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
Tự học lập trình Assembly - Bài 4: Tập lệnh assembly của Intel 8086/8088 (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:] <Tên lệnh> [Các toán hạng] [;Lời giải thích]
Trong đó:
- [Nhãn lệnh:]: Là một dãy các kí tự đứng trước câu lệnh (kết thúc bởi dấu hai chấm (:)), nó
được chỉ định thay thế cho địa chỉ của câu lệnh trong các đoạn lệnh lặp, rẽ nhánh, Do đó, nó chỉ được sử dụng khi cần
Trong một chương trình hợp ngữ không thể có hai nhãn lệnh trùng tên, tên của các nhãn cũng không thể trùng với tên của các thủ tục trong chương trình
- <Tên lệnh>: Là một trong các lệnh thuộc tập lệnh hợp ngữ (lệnh gợi nhớ: Mnemonic) của vi
xử lý trên máy tính thực hiện lệnh này
Lệnh hợp ngữ không phân biệt chữ hoa hay chữ thường Trong chương trình hợp ngữ mỗi dòng chỉ có thể chứa một lệnh và mỗi lệnh phải được đặt trên một dòng
- [Các toán hạng]: Là đối tượng mà lệnh tác động vào Một lệnh hợp ngữ của Intel 8088/8086
có thể không có toán hạng, có một toán hạng, hoặc có hai toán hạng Nếu có hai toán hạng thì toán hạng đứng trước gọi là [Toán hạng đích], toán hạng đứng sau gọi là [Toán hạng nguồn] [Toán hạng đích] không thể là một hằng số
Một số lệnh hợp ngữ của các Intel 80286/80386/ có thể có đến 3 toán hạng, trong trường hợp này cũng chỉ có một [Toán hạng đích]
- [;Lời giải thích]: Chỉ có tác dụng với người viết và người đọc chương trình, nó không có ý
nghĩa với trình biên dịch, tức là, không được dịch sang mã máy Lời giải thích thường được sử dụng để làm rõ ý nghĩa của câu lệnh (khi người viết thấy cần) Lời giải thích phải nằm sau dấu
chấm phảy (;).
Ví dụ 1: Xét lệnh sau đây:
Trang 16Lenh_VD: Mov AX,BX ; đặt giá trị thanh ghi BX vào thanh ghi AX
Trong đó:
• Lenh_VD: Trong trường hợp này dãy kí tự Lenh_VD được sử dụng làm nhãn lệnh cho
lệnh Mov
• Mov: Là tên lệnh.
• AX và BX: Là các toán hạng (đích và nguồn) Trong trường hợp này toán hạng là các
thanh ghi đa năng 16 bít
• “đặt giá trị thanh ghi BX vào thanh ghi AX”: Là lời giải thích cho lệnh này Trong thực
tế lời giải thích thường là tiếng Việt không dấu
Ví dụ 2: Xem các lệnh sau đây:
- NOP ; đây là lệnh không có toán hạng
- Mov Ax, Bl ; lệnh này có hai toán hạng, [Toán hạng đích] là thanh
; ghi 16 bít, [Toán hạng nguồn] là thanh ghi 8 bít
- Add Cl, Spt ; lệnh này có hai toán hạng, [Toán hạng đích] là thanh
; ghi 8 bít, [Toán hạng nguồn] là một biến byte
- Mov Ax, [SI] ; lệnh này có hai toán hạng, [Toán hạng đích] là thanh
; ghi 16 bít, [Toán hạng nguồn] là một ô nhớ
- Sub Dl, ‘a’ – ‘A’ ; lệnh này có hai toán hạng, [Toán hạng đích] là thanh
; ghi 8 bít, [Toán hạng nguồn] là một hằng số
- IMul Ax, Bx, 20 ; lệnh này có ba toán hạng, [Toán hạng đích] là thanh
; ghi 16 bit (Ax), [Toán hạng nguồn] là thanh ghi 16 bít
; (Bx) và một hằng số (20)
Lệnh Imul ở trên là một lệnh nhân mới của vi xử lý Intel 80286 Lệnh này thực hiện như sau: lấy nội dung/giá trị hai [Toán hạng nguồn] nhân với nhau, kết quả chứa ở [Toán hạng đích] (trong lệnh trên là: Bx*20, tích kết quả chứa ở thanh ghi Ax (chỉ lấy 16 bít thấp của tích để đưa vào Ax))
Trang 17- [Toán hạng nguồn]: Có thể là hằng số, biến, thanh ghi, ô nhớ (chính xác hơn là địa chỉ của một ô nhớ) nào đó.
Tác dụng: Lấy nội dung (giá trị) của [Toán hạng nguồn] đặt vào [Toán hạng đích] Nội dung của
[Toán hạng nguồn] không bị thay đổi
Ví dụ 1:
- Mov Ax, 5 ; Ax ß 5: đặt giá trị 5 vào thành ghi Ax
- Mov Ax, 5*2 ; Ax ß 5*2: đặt giá trị 10 vào thành ghi Ax
- Mov Bx, (80*(Dong - 1) + (Cot - 1))*2
; Dong, Cot là các biến
- Mov Dl, ‘A’ ; Dl = 41h: đặt mã ASCII của ‘A’ vào thanh ghi Dl
- Mov Cx, Var1 ; Cx = Var1: đặt giá trị của biến Var1 vào thanh ghi Cx
- Mov Ax, Bx ; Ax = Bx: đặt giá trị của thanh ghi Bx vào Ax
- Mov Ax, Dl ; Ax = Dl: đặt giá trị của Dl (8 bít) vào Ax (16 bít)
- Mov Bl, Dx ; Bl = Dx: không hợp lệ, vì: Dx (16 bít) mà Bl (8 bít)
- Mov Dl, 300 ; Dl = 300: không hợp lệ, vì 300 vượt giới hạn 1 byte
Ví dụ 2: Giả sử DI = 100; Ô nhớ tại địa chỉ offset 100 trong đoạn nhớ Data (được chỉ bởi DS)
chứa kí tự B Thì :
- Mov Ax, DI ; (1) đặt giá trị thanh ghi DI vào thanh ghi Ax: Ax = 100
- Mov Ax, [DI] ; (2) Ax = <nội dung của ô nhớ được chỉ bởi DI (DI
; chứ địa chỉ offset của ô nhớ)> Tức là, đặt nội dung của
; ô nhớ được chỉ bởi DI vào thanh ghi Ax: Ax = 41h
Hãy phân biệt sự khác nhau giữa hai lệnh trên: Lệnh (1) sử dụng chế độ địa chỉ thanh ghi Lệnh (2) sử dụng chế độ địa chỉ gián tiếp thanh ghi
Nhớ lại rằng: Trong chế độ địa chỉ gián tiếp thanh ghi, các thanh ghi chỉ có thể là BX, DI, SI (địa chỉ đoạn chứa trong DS) hay BP (địa chỉ đoạn chứa trong SS) Như vậy lệnh (2) tương đương với lệnh (3) nhưng khác lệnh (4):
- Mov Ax, DS:[DI] ; (3)
- Mov Ax, ES:[DI] ; (4)
Ví dụ 3:
- Mov Ax, [SI] ; đặt nội dung ô nhớ được chỉ bởi SI vào thanh ghi Ax
- Mov [DI], Bx ; đặt giá trị của thanh ghi bx vào ô nhớ được chỉ bởi DI
Trang 18- Mov [DI], [SI] ; [DI] ß [SI] : lệnh không hợp lệ, vì: không thể chuyển
; nội dung của ô nhớ vào một ô nhớ một cách trực tiếp
- Mov Var1, Ax ; Var1 ß Ax : đặt giá trị t/ghi Ax vào biến word Var1
Chú ý:
- Lệnh Mov không làm ảnh hưởng đến các cờ
- Mov DS:[DI], ES:[SI] ; lệnh không hợp lệ, vì: không thể chuyển dữ liệu
; trực tiếp giữa hai toán hạng bộ nhớ với nhau
- Mov DS, ES ; DS ß ES: lệnh không hợp lệ,
- Mov ES, 0100 ; lệnh không hợp lệ, vì: không thể chuyển
; trực tiếp một hằng số vào thanh ghi đoạn
Để chuyển giá trị của hai thanh ghi đoạn hay nội dung của hai ô nhớ ta có thể mượn một thanh ghi đa năng làm trung gian:
- Mov Ax, ES ; hai lệnh này chuyển nội dung của thanh ghi đoạn ES Mov DS, Ax ; vào thanh ghi đoạn DS thông qua thanh ghi Ax
Theo cách thông thường, để hoán đổi giá trị của hai thanh ghi đoạn hay nội dung của hai ô nhớ người ta thường sử dụng hai thanh ghi đa năng làm trung gian:
- Mov Ax, [DI] ; lưu tạm nội dung ô nhớ được chỉ bởi DI và Ax
Mov Bx, [SI] ; lưu tạm nội dung ô nhớ được chỉ bởi SI và Bx
Mov [DI], Bx ; chuyển giá trị của t/ghi Bx và ô nhớ được chỉ bởi DI
Mov [SI], Ax ; chuyển giá trị của t/ghi Ax và ô nhớ được chỉ bởi SI
Bốn lệnh trên có tác dụng hoán đổi nội dung của hai ô nhớ trong đoạn Data (DS) được chỉ bởi DI
và SI (DI và SI chứa địa chỉ Offset của các ô nhớ)
- Không thể dùng thanh ghi đoạn CS làm [Toán hạng đích] trong lệnh Mov
2 Các lệnh Inc – Dec – Add và Sub
Cú pháp lệnh:
• Inc [Toán hạng đích]
• Add [Toán hạng đích],[Toán hạng nguồn]
• Dec [Toán hạng đích]
• Sub [Toán hạng đích],[Toán hạng nguồn]
Trong đó: [Toán hạng đích], [Toán hạng nguồn]: tương tự lệnh Mov.
Trang 19Tá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)
- 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
Trang 20Ví dụ 3: Cộng thêm giá trị của thanh ghi Ax vào nội dung của ô nhớ tại địa chỉ offset 0100 trong
đoạn DS:
Mov DI, 0100 ; trỏ DI về ô nhớ offset 0100
Mov Bx, DS:[DI] ; lưu tạm ndung ô nhớ DS:DI vào thanh ghi Bx Add Bx, Ax ; cộng thêm Ax vào Bx
Mov DS:[DI], Bx ; đặt kết quả vào lại ô nhớ DS:DI (DS:0100)
Trong trường hợp này ta có thể sử dụng lệnh Add DS:[DI],Ax.
Ví dụ 4: Giả sử tại ô nhớ 0B800:0100 trong bộ nhớ có chứa một word dữ liệu Hãy tăng nội
dung của ô nhớ này lên một đơn vị
Mov Ax, 0B800h ; mượn thanh ghi Ax làm trung gian để chuyển
Mov ES, Ax ; địa chỉ đoạn của ô nhớ cần truy xuất vào ES
Mov DI, 01 ; đặt địa chỉ offset của ô nhớ cần truy xuất vào DI
; -; (gọi ngắn gọn: trỏ ES:DI về ô nhớ cần truy xuất)
Mov Dx, ES:[DI] ; chuyển tạm nội dung ô nhớ cần tăng vào Dx
Inc Dx ; tăng giá trị thanh ghi Dx lên 1 đơn vị
Mov ES:[DI], Dx ; đặt giá trị Dx đã tăng vào lại ô nhớ cần tăng
Ví dụ 5: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một byte dữ liệu Hãy chuyển nội
dung của ô nhớ này vào thành ghi AL
Mov Ax, 0A00h ; (1); Các lệnh (1), (2), (3) trỏ cặp thanh
Mov ES, Ax ; (2); ghi ES:DI về ô nhớ có địa chỉ 0A00:0100 Mov DI, 0100h ; (3); trong đó 0A00 là địa chỉ Segment và
; - ; 0100 là địa chỉ Offset Lệnh (4) chuyển nội
Mov Al, ES:[DI] ; (4); dung ô nhớ được chỉ bởi ES:DI vào Al
Ví dụ 6: Giả sử tại địa chỉ 0100:0100 trong bộ nhớ có chứa 2 word dữ liệu liên tiếp (hai ô nhớ
liên tiếp) Hãy tính tổng nội dung hai word nhớ này, rồi lấy kết quả ghi vào ô nhớ tại địa
chỉ 0100:0120.
Mov Ax, 0100
Mov ES, Ax ; trỏ cặp thanh ghi ES:SI về đầu vùng nhớ
Mov SI, 0100 ; cần truy xuất
Mov DI,0120 ; trỏ cặp thanh ghi ES:DI về ô nhớ chứa kết quả
Trang 21; - ; 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ớ)
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
Tự học lập trình assembly - Bài 4: Tập lệnh assembly của Intel 8086/8088 (2)
3 Lệnh LOOP
Cú pháp:
Loop <Nhãn đích>
Trang 22Trong đó: <Nhãn đích> là một nhãn lệnh và nó phải đứng trước lệnh lặp Loop không quá 126
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
Add Ax, Bx ; cộng thêm giá trị thanh ghi Bx vào thanh ghi Ax
Inc Bx ; tăng Bx lên 1 đơn vị
Loop Lap_TT ; lặp lại các lệnh sau nhãn lệnh Lap_TT đủ 4 lần
; - ; sau lệnh lặp này Ax = 24, Bx = 7
Mov Dx, Ax ; Dx ß Ax
Mov Cx, Bx ; Cx = 7, sau Loop Cx = 0, được thay bằng Bx = 7
Kết thúc đoạn lệnh trên: Ax = 24 (Lần 1: Ax = 6 + 3;Lần 2: Ax = 9 + 4; Lần 3: Ax = 13 + 5; Lần 4: Ax = 18 + 6) và Dx = Ax = 24
Khi gặp lệnh Loop Lap_TT chương trình sẽ quay lại (nếu Cx <> 0) thực hiện lệnhAdd 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
; -;
Trang 23Ví 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ề
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ự
Trang 24Lap_Copy:
Mov Bl, DS:[SI] ; mượn Bl để chuyển tường kí tự từ ô nhớ được Mov ES:[DI], Bl ; chỉ bởi DS:SI sang ô nhớ được chỉ bởi ES:DI Inc SI ; chuyển đến kí tự tiếp theo
Inc DI
Loop Lap_Copy ; lặp lại đủ 50 lần (để copy đủ 50 kí tự)
Trong ví dụ này, vùng nhớ chứa xâu kí tự cần copy (vùng nhớ nguồn) và vùng nhớ chứa kết quả copy (vùng nhớ đích) nằm ở hai đoạn (segment) nhớ khác nhau, do đó, ta phải sử dụng hai thanh ghi đoạn dữ liệu khác nhau: DS và ES
Qua 2 ví dụ 5 và 6 ta có thể rút ra nguyên tắc cơ bản để truy xuất dữ liệu trên một vùng
nhớ/mảng nhớ:
Chú ý: Lệnh Loop thực hiện vòng lặp cho đến khi Cx = 0, vì thế nó được xem như lệnh lặp
không điều kiện Thực tế, hợp ngữ còn có các lệnh lặp có điều kiện, nó cho phép kết thúc vòng lặp trước khi Cx = 0 dựa vào một điều kiện nào đó Cụ thể: lệnh LoopE (Loop while Equal): Chỉ lặp lại khi Cx <> 0 và cờ ZF = 1; lệnh LoopZ (Loop while Zero): tương tự LoopE; LoopNE (Loop while Not Equal): Chỉ lặp lại khi Cx <> 0 và cờ ZF = 0; [2 - 154]
4 Lệnh LEA (LoadEffectiveAddress)
Cú pháp:
LEA [Toán hạng đích],[Toán hạng nguồn]
Trong đó: [Toán hạng đích]: Là các thanh ghi 16 bít [Toán hạng nguồn]: Là địa chỉ của một
vùng nhớ hay tên của một biến
Tác dụng: Lệnh LEA có tác dụng chuyển địa chỉ offset của [Toán hạng nguồn] vào [Toán hạng
đích] Lệnh này thường được sử dụng để lấy địa chỉ offset của một biến đã được khai báo trong chương trình Thanh ghi được sử dụng trong trường hợp này là thanh ghi cơ sở (BX) và thanh ghi chỉ mục (SI và DI)
Ví dụ 1:
Lea Bx, DS:[0100] ; chuyển thành phần địa chỉ offset (0100) vào Bx Lea DI, XauKT ; chuyển địa chỉ offset của biến XauKT vào DI ; thao tác này thường được gọi là trỏ DI và đầu ; biến XauKT
Khi chương trình được nạp vào bộ nhớ để hoạt động thì các biến được khai báo trong chương trình sẽ được định vị (cấp phát vùng nhớ) tại một địa chỉ xác định trong vùng nhớ Data Từ đây, để thao tác đến dữ liệu trên các biến của chương trình thì chương trình phải xác định được địa chỉ segment vào offset (hai thành phần của địa chỉ logic) của biến Lệnh LEA ở trên chỉ lấy được địa chỉ offset của biến, để lấy được địa chỉ segment của nó ta có thể sử dụng lệnh Mov với toán tử Seg (tương tự có thể sử dụng lệnh Mov với toán tử Offset để lấy địa chỉ offset của biến) Ví dụ: Các lệnh sau lấy địa chỉ Segment:Offset của biến XauKT (hay trỏ DS:SI về đầu biến
XauKT):
Mov Ax, Seg XauKT ; đưa địa chỉ Segment của biến XauKT
Mov DS, Ax ; vào thanh ghi DS
Mov SI, Offset XauKT ; đưa địa chỉ Offset của biến XauKT vào SI
Trang 25Ví dụ 2: Giả sử biến TenGom (là biến kiểu byte) đã được khai báo như sau:
TenGom DB ‘Nguyen Kim Le Tuan’
Xem các lệnh sau đây (1):
Mov Bx, 0
Mov Al, TenGom[Bx] ; Al =‘N’
Add Bx, 7 ;
Mov Bl, TenGom[Bx] ; Bl =‘K’
Xem các lệnh sau đây (2):
Lea DI, TenGom
Mov Al, [DI] ; Al =‘N’
Mov Bl, [DI + 7] ; Bl =‘K’
Ta có thể thấy, nhóm các lệnh (1) và nhóm các lệnh (2) là tương đương nhau về tác dụng của nó, nhưng (1): sử dụng trực tiếp tên biến để truy xuất đến các phần tử của nó; (2): sử dụng thanh ghi chỉ mục DI để truy xuất đến các phần tử của biến Trong trường hợp này địa chỉ segment mặc định được chỉ bởi DS, điều này phù hợp với việc sử dụng địa chỉ gián tiếp thanh ghi chỉ mục
Ví dụ 3: Giả sử tại địa chỉ 0100:0C00 trong bộ nhớ có chứa một xâu kí tự gồm 50 kí tự (tức là,
gồm 50 ô nhớ, mỗi ô 1 byte) Hãy copy xâu kí tự này vào một biến trong chương trình
Với yêu cầu này chương trình phải khai báo một biến byte có độ lớn 50 byte:
LuuTru DB 50 Dup (‘ ‘)
Mov Ax, 0100
Mov DS, Ax ; trỏ cặp thanh ghi DS:SI về
Mov SI, 0C00 ; đầu vùng nhớ chưa xâu cần copy (0100:0C00) ; -
Mov Ax, Seg LuuTru ; trỏ cặp thanh ghi ES:DI về
Mov ES, Ax ; đầu biến LuuTru
Lea DI, LuuTru
Inc DI
Loop Lap_Copy ; lặp lại đủ 50 lần (để copy đủ 50 kí tự)
Chú ý: Hợp ngữ còn cung cấp các lệnh LDS (Load Pointer use DS) để lấy nội dungtoán hạng bộ
nhớ 32 bít đưa vào các thanh ghi 16 bít (mặc định 16 bít cao vào thanh ghi đoạn dữ liệu DS); và lệnh LES (Load Pointer use DS) tương tự LDS nhưng mặc định 16 bít cao vào thanh ghi đoạn dữ liệu (thứ hai) ES [2 - 137]
Trang 26Tự học lập trình assembly - Bài 4: Tập lệnh assembly của Intel 8086/8088 (3)
5 Lệnh Mul và Div
Cú pháp:
• Mul [Toán hạng nguồn]
• IMul [Toán hạng nguồn]
• Div [Toán hạng nguồn]
• IDiv [Toán hạng nguồn]
Trong đó: [Toán hạng nguồn]có thể là thanh ghi hay ô nhớ Với các lệnh nhân: [Toán hạng đích]
ngầm định là thanh ghi Al hoặc Ax Với các lệnh chia: [Toán hạng đích] là một trong các thanh ghi đa năng Ax, Bx,
Tác dụng:
- Lệnh Mul (Multiply): Thực hiện phép nhân trên số không dấu Nếu [Toán hạng nguồn] là
toán hạng 8 bít thì lệnh sẽ nhân nội dung của [Toán hạng nguồn] với giá trị thanh ghi AL, kết quả
16 bít chứa ở thanh ghi Ax
Nếu [Toán hạng nguồn] là toán hạng 16 bít thì lệnh sẽ nhân nội dung của [Toán hạng nguồn] với giá trị thanh ghi Ax, kết quả 32 bít chứa ở cặp thanh ghi Dx:Ax, phần thấp ở Ax, phần cao ở Dx Nếu phần cao của kết quả (AH hoặc DX) bằng 0 thì các cờ CF = 0 và OF = 0
- Lệnh IMul (Interger Multiply): Tương tự lệnh Mul nhưng thực hiện phép nhân trên hai số có
dấu Kết quả cũng là một số có dấu
- Lệnh Div (Divide): Thực hiện phép chia trên số không dấu Nếu [Toán hạng nguồn] là
toán hạng 8 bít thì lệnh sẽ lấy giá trị của thanh ghi Ax (số bị chia) chia cho [Toán hạng nguồn] (số chia), kết quả thương số chứa trong thanh ghi Al, số dư chứa trong thanh ghi Ah
Nếu [Toán hạng nguồn] là toán hạng 16 bít thì lệnh sẽ lấy giá trị của cặp thanh ghi Dx:Ax (số bị chia) chia cho [Toán hạng nguồn] (số chia), kết quả thương số chứa trong thanh ghi Ax, số dư chứa trong thanh ghi Dx
Nếu phép chia cho 0 xảy ra hay thương số vượt quá giới hạn của thanh ghi AL (chia 8 bít) hay
Ax (chia 16 bít) thì CPU sẽ phát sinh lỗi “Divice overflow”
- Lệnh Idiv (Integer Divide): Tương tự lệnh Div nhưng thực hiện phép chia trên hai số có
dấu Kết quả cùng là các số có dấu
Ví dụ 1:
- Mul Bl ; Ax ßAL * Bl: số bị nhân ngầm định trong Al
- Mul Bx ; Dx:Ax ßAx * Bx: số bị nhân ngầm định trong Ax
- Idiv Bl ; Ax/Bl, thương số chứa trong Al, số dư chứa trong Ah
- Idiv Bx ; Dx:Ax/Bx, thương số chứa trong Ax, số dư trong Dx
Trang 27Ví dụ 2: Dãy các lệnh dưới đây sẽ thực hiện phép gán A = 4*A – 3*B, trong đó A và B là các
biến kiểu word:
Mov Ax, 4 ; số nhân phải được chứa trong Ax
IMul A ; thực hiện phép nhân
Mov Bx, Ax ; lưu tạm kết quả vào Bx
Mov Ax, 3 ; Ax = 3
Imul B ; Ax = Ax * B
Sub Bx, Ax
Mov A, Bx ; đặt kết quả cuối cùng vào A
Trong trường hợp này ta đã giả sử hiện tượng tràn đã không xảy ra và kết quả phép nhân chỉ chứa trong thanh ghi Ax
Ví dụ 3: Các lệnh sau đây thực hiện phép: chia -123 cho 24:
Mov Ax, -123 ; đặt số bị chia vào Ax
Mov Bl, 24 ; đặt số chia vào Bl (thanh ghi 8 bít)
Idiv Bl ; chia Ax cho Bl, kết quả chứa ở Al và Ah
Ví dụ 4: Dãy lệnh dưới đây sẽ thực hiện phép gán A = N! (tính N giai thừa) A là một biến word:
Mov Ax, 1 ; chuẩn bị Ax để lưu kết quả
Mov A, Ax ; trả kết quả vào biến A
Trong trường hợp này chúng ta giả sử kết quả không vượt quá gới hạn 16 bít
Chúng ta đã biết: N! = 1 nếu N = 1, N! = N*(N-1)*(N-2)* *1 nếu N>1, điều này hoàn toàn phù hợp với sự thay đổi của thanh ghi CX trong lệnh lặp Loop Do đó, ở đây ta có thể sử dụng CX như là N trong biểu thức tính giai thừa
Chú ý: Hợp ngữ cung cấp lệnh AAM (Ascii Adjust for Multiple) để điều chỉnh kết quả phép
nhân trên 2 số BCD dạng không dồn Và lệnh AAD (Ascii Adjust for Division) để điều chỉnh kết quả phép chia trên 2 số BCD dạng không dồn Ngoài ra còn có lệnh CBW (Convert Byte to
Trang 28Word) và lệnh CWD (Convert Word to Doubleword) để hỗ trợ cho phép chia trên các số có dấu [2 – 187-200].
Lệnh IMul của vi xử lý Intel 80286 cho phép ghi rõ [Toán hạng đích], [Toán hạng nguồn] trong câu lệnh, các lệnh này có thể có đến 3 toán hạng [1 - 541]
6 Lệnh logic: NOT – AND – OR – XOR – TEST
Trước khi tìm hiểu về các lệnh logic chúng ta xem lại kết quả thực hiện các phép tính logic trên 2 bít nhị phân A và B thông qua bảng sau đây:
Cú pháp:
• Not [Toán hạng đích]
• And [Toán hạng đích], [Toán hạng nguồn]
• Or [Toán hạng đích], [Toán hạng nguồn]
• Xor [Toán hạng đích], [Toán hạng nguồn]
• Test [Toán hạng đích], [Toán hạng nguồn]
Trong đó: [Toán hạng đích], [Toán hạng nguồn] có thể là hằng số (trực hằng), biến, thanh ghi
hay địa chỉ ô nhớ [Toán hạng đích] không thể là hằng số
Tác dụng: Mỗi lệnh logic thực hiện phép tính logic tương ứng trên các bít (tương ứng về vị trí)
của [Toán hạng đích] và [Toán hạng nguồn], kết quả được ghi vào lại [Toán hạng đích] Riêng lệnh Not, thực hiện phép đảo bít ngay trên các bít của [Toán hạng đích] Hầu hết các lệnh logic đều ảnh hưởng đến các cờ CF, OF, ZF,
- Lệnh Not (Logical Not): Thực hiện việc đảo ngược từng bít trong nội dung của [Toán hạng
đích] Lệnh này không làm ảnh hưởng đến các cờ
Trang 29Lệnh Not thường được sử dụng để tạo dạng bù 1 của [Toán hạng đích]
- Lệnh And (Logical And): Thực hiện phép tính logic And trên từng cặp bít (tương ứng về vị
trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào [Toán hạng đích]
Lệnh And thường được sử dụng để xóa (= 0) một hoặc nhiều bít xác định nào đó trong một thanh ghi
- Lệnh Or (Logical Inclusive Or):Thực hiện phép tính logic Or trên từng cặp bít (tương ứng
về vị trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào [Toán hạng đích]
Lệnh Or thường dùng để thiết lập (= 1) một hoặc nhiều bít xác định nào đó trong một thanh ghi
- Lệnh Xor (eXclusive OR):Thực hiện phép tính logic Xor trên từng cặp bít (tương ứng về vị
trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào [Toán hạng đích]
Lệnh Xor thường dùng để so sánh (bằng nhau hay khác nhau) giá trị của hai toán hạng, nó cũng giúp phát hiện ra các bít khác nhau giữa hai toán hạng này
- Lệnh Test: Tương tự như lệnh And nhưng không ghi kết quả vào lại [Toán hạng đích], nó chỉ
ảnh hưởng đến các cờ CF, OF, ZF,
Ví dụ 1:
Mov Al,0 ; Al ß0
Not Al ; Al = Not Al Tức là Al = 0FFh
Ví dụ 2: Cho AL = (10010011)2, BL = (11001100)2
- And Al, Bl ; Al ß 10010011 And 11001100 Al =
- And Al, 0 ; Al ß 10010011 And 0 Al =
- Xor Ax, Ax ; các cặp bít giống nhau thì đều = 0
Ví dụ 5: Lệnh sau đây sẽ xóa (= 0) các bít 3 và 6 của thanh ghi AL, các bít khác giữ nguyên giá
trị:
Trang 30- And AL, 10110111b ; AL ßAL And 10110111
Trong trường hợp này: dãy bít 10110111 được gọi là dãy bít mặt nạ, các bít 3 (= 0) và 6 (= 0)
được gọi là các bít mặt nạ Như vậy muốn làm cho bít nào = 0 ta cho bít mặt nạ tương ứng với nó
= 0, các bít còn lại trong dãy bít mặt nạ đều = 1
Ví dụ 6: Lệnh sau đây sẽ thiết lập (= 1) các bít 3 và 6 của thanh ghi AL, các bít khác giữ nguyên
giá trị:
- Or AL, 01001000b ; AL ßAL Or 01001000
Trong trường hợp này: dãy bít 01001000 được gọi là dãy bít mặt nạ, các bít 3 (= 1) và 6 (= 1)
được gọi là các bít mặt nạ Như vậy muốn làm cho bít nào = 1 ta cho bít mặt nạ tương ứng với nó
= 1, các bít còn lại trong dãy bít mặt nạ đều = 0
Ví dụ 7: Lệnh sau đây sẽ kiểm tra bít 12 của thanh ghi AX là = 0 hay = 1:
- And AX, 0001000000000000b ; AX ßAX And 0001000000000000
Với dãy bít mặt nạ như trên, nếu bít 12 của Ax = 0 thì kết quả: Ax = 0, nếu bít 12 của Ax = 1 thì kết quả: Ax <> 0
Cách dùng lệnh And như trên để kiểm tra bít ít được sử dụng, vì nó làm thay đổi giá trị của thanh ghi cần kiểm tra (điều này có thể khắc phục bằng lệnh Test) và phải thêm bước kiểm tra giá trị của Ax (= 0 hay <> 0) mới biết được kết quả kiểm tra Ngoài ra, nó cũng chỉ kiểm tra được 1 bít.Trong thực tế người ta thường sử dụng kết hợp giữa các lệnh dịch bít, lệnh quay bít, lệnh nhảy, để kiểm tra các bít trong một thanh ghi
7 Lệnh chuyển dữ liệu qua cổng: IN và OUT
Cú pháp:
• IN AL, <Địa chỉ cổng>
• OUT <Địa chỉ cổng>, AL
Trong đó:<Địa chỉ cổng> chính là số hiệu cổng (port) mà lệnh nhận nhiệm vụ trao đổi dữ liệu
qua nó Địa chỉ cổng có thể được ghi trực tiếp dưới dạng một hằng số hoặc được ghi thông qua thanh ghi Dx
Tác dụng:
- LênhIn (Input): Đọc một lượng dữ liệu 8 bít từ cổng được chỉ ra ở <Địa chỉ cổng> đưa vào
lưu trữ trong thanh ghi AL
Nếu địa chỉ cổng nằm trong giới hạn từ 0 đến FF (hệ thập lục phân) thì có thể viết trực tiếp trong câu lệnh, nếu địa chỉ cổng lớn hơn FF thì ta phải dùng thanh ghi Dx để chỉ định địa chỉ cổng
- LệnhOut (Output): Gởi một lượng dữ liệu 8 bít từ thanh ghi AL ra cổng được chỉ ra ở <Địa
chỉ cổng> Tương tự lệnh In, địa chỉ cổng có thể được viết trực tiếp trong câu lệnh hoặc thông qua thanh ghi Dx
Trang 31Một số ví dụ minh họa:
Ví dụ 1: Giả sử tại địa chỉ 0100:0120 trong bộ nhớ có chứa một mảng dữ liệu, gồm 100 ô nhớ,
mỗi ô là 1 word Hãy tính tổng nội dung của các ô nhớ này, kết quả chứa trong thanh ghi Dx Mov Ax, 0100
Mov DS, Ax ; trỏ cặp thanh ghi DS:DI về
Mov DI, 0120 ; đầu vùng nhớ cần truy xuất (0100:0120)
; -
Mov Dx, 0 ; chuẩn bị Dx để lưu tổng
Mov Cx, 100 ; lặp 100 lần vì mảng gồm 100 ô nhớ
Trang 32
; -Kết thúc đoạn lệnh trên tổng nội dung của 100 word nhớ bắt đầu tại địa chỉ 0100:0120 trong bộ nhớ được lưu vào thanh ghi Dx Ở đây chúng ta bỏ qua khả năng tràn dữ liệu trong thanh ghi kết quả Dx
Ví dụ 2: Các lệnh sau đây sẽ copy 100 word dữ liệu từ vùng nhớ bắt đầu tại địa chỉ 0100:0120
sang vùng nhớ bắt đầu tại 0100:0500:
Mov Ax, 0100
Mov DS, Ax
Mov SI, 0120 ; trỏ DS:SI về vùng nhớ nguồn 0100:0120
Mov DI, 0500 ; trỏ DS:DI về vùng nhớ địch 0100:0500
Ví dụ 3: Các lệnh sau đây sẽ tính tổng nội dung của 100 ô nhớ (100 byte nhớ) trong bộ nhớ, bắt
đầu tại địa chỉ 0A00:0120 Kết quả được lưu vào word nhớ ngay trước vùng nhớ này:
Mov Ax, 0A00h
Mov ES, Ax
Mov SI, 0120h ; trỏ DS:SI về vùng nhớ nguồn 0A00:0120
Mov DI, SI ; trỏ DS:DI về vùng nhớ nguồn 0A00:0120
Sub DI,2 ; trỏ DS:DI về word nhớ trước vùng nhớ nguồn ; -
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