1. Trang chủ
  2. » Kỹ Thuật - Công Nghệ

Tự học lập trình assembly

65 1,6K 5

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 65
Dung lượng 159,67 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

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 1

Tự 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 2

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)

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 3

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 (“?”) <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 4

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):

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 7

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 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 8

Nế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 9

LINK [đườ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 10

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:

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 11

Cấu trúc này cho thấy rõ, một chương trình hợp ngữ dạng gồm 3 đoạn: đoạn Code, chứa toàn bộ

mã lệnh của chương trình Đoạn Data, chứa phần khai báo dữ liệu của chương trình Đoạn Stack, nơi chứa stack (ngăn xếp) của chương trình khi chương trình được nạp vào bộ nhớ để hoạt động Chỉ dẫn Stackđặt ở đầu chương trình với mục đích khai báo kích thước của Stack dùng cho chương trình sau này Kích thước thường được chọn là 100h (256) byte

Chỉ dẫn Model được đặt ở đầu cả cấu trúc chương trình dạng COM và EXE với mục đích khai báo chế độ bộ nhớ mà chương trình sử dụng

Ví dụ: Sau đây là hai chương trình hợp ngữ đơn giản, dạng COM và dạng EXE, cùng thực hiện nhiệm vụ in ra màn hình 3 dòng văn bản như sau :

Nguyen Kim Le Tuan

Nguyen Le Tram Thanh Nguyen Le Tram Uyen

Hai chương trình dưới đây chỉ có tác dụng minh họa cho việc sử dụng các hướng dẫn biên dịch định nghĩa đoạn đơn giản và giúp các bạn thấy được những điểm giống nhau, khác nhau giữa hai dạng cấu trúc chương trình dạng COM và EXE, vì vậy, ở đây các bạn chưa cần quan tâm đến ý nghĩa của các lệnh và các hàm/ngắt trong nó Phần lệnh hợp ngữ và các hàm/ngắt sẽ được trình bày ngay sau đây.

Chương trình viết theo cấu trúc dạng COM:

MyChildren DB ‘Nguyen Kim Le Tuan’,0Ah,0Dh

DB ‘Nguyen Le Tram Thanh’,0Ah,0Dh

DB ‘Nguyen Le Tram Uyen’,’$’

Trang 12

End Start

Chương trình này chọn chế độ bộ nhớ Small Tên thủ tục chính là Main (tên thủ tục chính là tùy ý) Nhãn chính của chương trình là Start (tên thủ tục chính là tùy ý), đó chính là nhãn của lệnh Jmp Phần khai báo dữ liệu chỉ khai báo một biến, đó là MyChildren

Chương trình này gọi hàm 4Ch của ngắt 21h để kết thúc chương trình Có thể gọi ngắt 20h để kết thúc các chương trình dạng COM

Chương trình viết theo cấu trúc dạng EXE:

.Model Small

.Stack 100h

.Data

MyChildren DB ‘Nguyen Kim Le Tuan’,0Ah,0Dh

DB ‘Nguyen Le Tram Thanh’,0Ah,0Dh

DB ‘Nguyen Le Tram Uyen’,’$’

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 13

09h của ngắt 21h để in ra màn hình một xâu kí tự (xâu này chính là giá trị khởi tạo của biến MyChildren).

Chú ý 1: Trình biên dịch hợp ngữ (Macro Assembler) cho phép các chương trình được dịch bởi nóc họn sử dụng một trong các chế độ bộ nhớ sau:

- Small: Đoạn mã lệnh (Code) và đoạn dữ liệu (Data) của chương trình đều chỉ có thể chứa trong một đoạn (segment) bộ nhớ Tức là, kích thước của chương trình chỉ có thể tối đa là hai đoạn bộ nhớ Tuy vậy chế độ bộ nhớ này đủ dùng cho hầu hết các chương trình hợp ngữ

- Medium: Đoạn Code của chương trình có thể chiếm nhiều hơn một đoạn bộ nhớ Trong khi đó, đoạn Data chỉ có thể chiếm 1 đoạn bộ nhớ

- Compact: Đoạn Data của chương trình có thể chiếm nhiều hơn một đoạn bộ nhớ Trong khi đó, đoạn Code chỉ có thể chiếm 1 đoạn bộ nhớ

- Large: Đoạn Code và đoan Data của chương trình đều có thể chiếm nhiều hơn một đoạn bộ nhớ Nhưng trong trường hợp này không thể định nghĩa một mảng dữ liệu có kích thước lớn hơn

64 Kbyte

- Huge: Tương tự như Large, nhưng trong trường hợp này có thể định nghĩa một mảng dữ liệu có kích thước lớn hơn 64 Kbyte

Chế độ bộ nhớ Small là đơn giản nhất, được hầu hết các chương trình lựa chọn

Chú ý 2: Với các chương trình hợp ngữ sử dụng định nghĩa đoạn đơn giản: Khi được nạp vào bộ nhớ để hoạt động thì các thanh ghi đoạn sẽ tự động trỏ về các đoạn chương trình tương ứng Cụ thể: Thanh ghi đoạn CS chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Code của chương trình Thanh ghi đoạn DS (và có thể cả ES) chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Data của chương trình Thanh ghi đoạn SS chứa địa chỉ segment của đoạn bộ nhớ chứa đoạn Stack của chương trình

Tuy nhiên, trong thực tế, khi nạp chương trình EXE vào bộ nhớ DOS luôn dành ra 256 byte đầu tiên của vùng nhớ, mà DOS cấp phát cho chương trình, để chứa PSP (Program Segment Prefix) của chương trình PSP chứa các thông tin cần thiết mà trình biên dịch chuyển đến cho DOS để hỗ trợ DOS trong việc thực hiện chương trình này, đặc biệt, chương trình cũng có thể truy xuất vùng nhớ PSP Do đó, DOS phải đưa địa chỉ segment của vùng nhớ chứa PSP vào cả DS và ES trước khi chương trình được thực hiện Tức là, ngay khi chương trình được nạp vào bộ nhớ DS không phải chứa địa chỉ segment của đoạn Data của chương trình mà chứa địa chỉ segment của PSP

Vì vậy, để trỏ DS về lại đoạn Data chương trình chúng ta phải đặt ngay hai lệnh sau đây ở đầu chương trình viết theo cấu trúc EXE:

Mov Ax, @Data

Mov DS, Ax

Với việc khởi tạo thanh ghi đoạn DS ở trên, địa chỉ segment của tất cả các biến khai báo trong đoạn Data đều được chứa trong thanh ghi DS, do đó, trong các thao tác xử lý biến sau này

chương trình không cần quan tâm đến địa chỉ segment của nó nữa

Chú ý 3: Hợp ngữ còn cho phép các chương trình sử dụng các hướng dẫn biên dịch định nghĩa

đoạn toàn phần, các định nghĩa này phù hợp với hầu hết các trình biên dịch hợp ngữ hiện nay Định nghĩa đoạn toàn phần giúp cho việc viết chương trình hợp ngữ trở nên mềm dẻo và linh

Trang 14

hoạt hơn, nó giúp người lập trình có thể điều khiển thứ tự các đoạn chương trình, kết hợp các đoạn chương trình, liên kết các đoạn chương trình trong bộ nhớ, , ngay trong khi lập trình.

Chi tiết về cách sử dụng và mục đích sử dụng của các hướng dẫn biên dịch nói chung và các định nghĩa đoạn toàn phần nói riêng dễ dàng tìm thấy trong rất nhiều tài liệu về lập trình hợp ngữ [1], [2] Ở đây chúng tôi chỉ giới thiệu sơ lược về nó thông qua ví dụ dưới đây

Ví dụ: Sau đây là một chương trình dạng EXE sử dụng các hướng dẫn biên dịch định nghĩa đoạn

toàn phần (phù hợp với Macro Assembler):

S_Seg Segment Stack

DB 100h DUP (?)

S_Seg Ends

D_Seg Segmet

MyChildren DB ‘Nguyen Kim Le Tuan’,0Ah,0Dh

DB ‘Nguyen Le Tram Thanh’,0Ah,0Dh

DB ‘Nguyen Le Tram Uyen’,’$’

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 15

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

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 16

Lenh_VD: Mov AX,BX ; đặt giá trị thanh ghi BX vào thanh ghi AX

Trong đó:

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

lệnh Mov

Mov: Là tên lệnh.

AX và BX: Là các toán hạng (đích và nguồn) Trong trường hợp này toán hạng là các

thanh ghi đa năng 16 bít

“đặt giá trị thanh ghi BX vào thanh ghi AX”: Là lời giải thích cho lệnh này Trong thực

tế lời giải thích thường là tiếng Việt không dấu

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

- NOP ; đây là lệnh không có toán hạng

- Mov Ax, Bl ; lệnh này có hai toán hạng, [Toán hạng đích] là thanh

; ghi 16 bít, [Toán hạng nguồn] là thanh ghi 8 bít

- Add Cl, Spt ; lệnh này có hai toán hạng, [Toán hạng đích] là thanh

; ghi 8 bít, [Toán hạng nguồn] là một biến byte

- Mov Ax, [SI] ; lệnh này có hai toán hạng, [Toán hạng đích] là thanh

; ghi 16 bít, [Toán hạng nguồn] là một ô nhớ

- Sub Dl, ‘a’ – ‘A’ ; lệnh này có hai toán hạng, [Toán hạng đích] là thanh

; ghi 8 bít, [Toán hạng nguồn] là một hằng số

- IMul Ax, Bx, 20 ; lệnh này có ba toán hạng, [Toán hạng đích] là thanh

; ghi 16 bit (Ax), [Toán hạng nguồn] là thanh ghi 16 bít

; (Bx) và một hằng số (20)

Lệnh Imul ở trên là một lệnh nhân mới của vi xử lý Intel 80286 Lệnh này thực hiện như sau: lấy nội dung/giá trị hai [Toán hạng nguồn] nhân với nhau, kết quả chứa ở [Toán hạng đích] (trong lệnh trên là: Bx*20, tích kết quả chứa ở thanh ghi Ax (chỉ lấy 16 bít thấp của tích để đưa vào Ax))

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 19

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)

- 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 20

Ví dụ 3: Cộng thêm giá trị của thanh ghi Ax vào nội dung của ô nhớ tại địa chỉ offset 0100 trong

đoạn DS:

Mov DI, 0100 ; trỏ DI về ô nhớ offset 0100

Mov Bx, DS:[DI] ; lưu tạm ndung ô nhớ DS:DI vào thanh ghi Bx Add Bx, Ax ; cộng thêm Ax vào Bx

Mov DS:[DI], Bx ; đặt kết quả vào lại ô nhớ DS:DI (DS:0100)

Trong trường hợp này ta có thể sử dụng lệnh Add DS:[DI],Ax.

Ví dụ 4: Giả sử tại ô nhớ 0B800:0100 trong bộ nhớ có chứa một word dữ liệu Hãy tăng nội

dung của ô nhớ này lên một đơn vị

Mov Ax, 0B800h ; mượn thanh ghi Ax làm trung gian để chuyển

Mov ES, Ax ; địa chỉ đoạn của ô nhớ cần truy xuất vào ES

Mov DI, 01 ; đặt địa chỉ offset của ô nhớ cần truy xuất vào DI

; -; (gọi ngắn gọn: trỏ ES:DI về ô nhớ cần truy xuất)

Mov Dx, ES:[DI] ; chuyển tạm nội dung ô nhớ cần tăng vào Dx

Inc Dx ; tăng giá trị thanh ghi Dx lên 1 đơn vị

Mov ES:[DI], Dx ; đặt giá trị Dx đã tăng vào lại ô nhớ cần tăng

Ví dụ 5: Giả sử tại địa chỉ 0A00:0100 trong bộ nhớ có chứa một byte dữ liệu Hãy chuyển nội

dung của ô nhớ này vào thành ghi AL

Mov Ax, 0A00h ; (1); Các lệnh (1), (2), (3) trỏ cặp thanh

Mov ES, Ax ; (2); ghi ES:DI về ô nhớ có địa chỉ 0A00:0100 Mov DI, 0100h ; (3); trong đó 0A00 là địa chỉ Segment và

; - ; 0100 là địa chỉ Offset Lệnh (4) chuyển nội

Mov Al, ES:[DI] ; (4); dung ô nhớ được chỉ bởi ES:DI vào Al

Ví dụ 6: Giả sử tại địa chỉ 0100:0100 trong bộ nhớ có chứa 2 word dữ liệu liên tiếp (hai ô nhớ

liên tiếp) Hãy tính tổng nội dung hai word nhớ này, rồi lấy kết quả ghi vào ô nhớ tại địa

chỉ 0100:0120.

Mov Ax, 0100

Mov ES, Ax ; trỏ cặp thanh ghi ES:SI về đầu vùng nhớ

Mov SI, 0100 ; cần truy xuất

Mov DI,0120 ; trỏ cặp thanh ghi ES:DI về ô nhớ chứa kết quả

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 22

Trong đó: <Nhãn đích> là một nhãn lệnh và nó phải đứng trước lệnh lặp Loop không quá 126

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 23

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ề

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 24

Lap_Copy:

Mov Bl, DS:[SI] ; mượn Bl để chuyển tường kí tự từ ô nhớ được Mov ES:[DI], Bl ; chỉ bởi DS:SI sang ô nhớ được chỉ bởi ES:DI Inc SI ; chuyển đến kí tự tiếp theo

Inc DI

Loop Lap_Copy ; lặp lại đủ 50 lần (để copy đủ 50 kí tự)

Trong ví dụ này, vùng nhớ chứa xâu kí tự cần copy (vùng nhớ nguồn) và vùng nhớ chứa kết quả copy (vùng nhớ đích) nằm ở hai đoạn (segment) nhớ khác nhau, do đó, ta phải sử dụng hai thanh ghi đoạn dữ liệu khác nhau: DS và ES

Qua 2 ví dụ 5 và 6 ta có thể rút ra nguyên tắc cơ bản để truy xuất dữ liệu trên một vùng

nhớ/mảng nhớ:

Chú ý: Lệnh Loop thực hiện vòng lặp cho đến khi Cx = 0, vì thế nó được xem như lệnh lặp

không điều kiện Thực tế, hợp ngữ còn có các lệnh lặp có điều kiện, nó cho phép kết thúc vòng lặp trước khi Cx = 0 dựa vào một điều kiện nào đó Cụ thể: lệnh LoopE (Loop while Equal): Chỉ lặp lại khi Cx <> 0 và cờ ZF = 1; lệnh LoopZ (Loop while Zero): tương tự LoopE; LoopNE (Loop while Not Equal): Chỉ lặp lại khi Cx <> 0 và cờ ZF = 0; [2 - 154]

4 Lệnh LEA (LoadEffectiveAddress)

Cú pháp:

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

Trong đó: [Toán hạng đích]: Là các thanh ghi 16 bít [Toán hạng nguồn]: Là địa chỉ của một

vùng nhớ hay tên của một biến

Tác dụng: Lệnh LEA có tác dụng chuyển địa chỉ offset của [Toán hạng nguồn] vào [Toán hạng

đích] Lệnh này thường được sử dụng để lấy địa chỉ offset của một biến đã được khai báo trong chương trình Thanh ghi được sử dụng trong trường hợp này là thanh ghi cơ sở (BX) và thanh ghi chỉ mục (SI và DI)

Ví dụ 1:

Lea Bx, DS:[0100] ; chuyển thành phần địa chỉ offset (0100) vào Bx Lea DI, XauKT ; chuyển địa chỉ offset của biến XauKT vào DI ; thao tác này thường được gọi là trỏ DI và đầu ; biến XauKT

Khi chương trình được nạp vào bộ nhớ để hoạt động thì các biến được khai báo trong chương trình sẽ được định vị (cấp phát vùng nhớ) tại một địa chỉ xác định trong vùng nhớ Data Từ đây, để thao tác đến dữ liệu trên các biến của chương trình thì chương trình phải xác định được địa chỉ segment vào offset (hai thành phần của địa chỉ logic) của biến Lệnh LEA ở trên chỉ lấy được địa chỉ offset của biến, để lấy được địa chỉ segment của nó ta có thể sử dụng lệnh Mov với toán tử Seg (tương tự có thể sử dụng lệnh Mov với toán tử Offset để lấy địa chỉ offset của biến) Ví dụ: Các lệnh sau lấy địa chỉ Segment:Offset của biến XauKT (hay trỏ DS:SI về đầu biến

XauKT):

Mov Ax, Seg XauKT ; đưa địa chỉ Segment của biến XauKT

Mov DS, Ax ; vào thanh ghi DS

Mov SI, Offset XauKT ; đưa địa chỉ Offset của biến XauKT vào SI

Trang 25

Ví dụ 2: Giả sử biến TenGom (là biến kiểu byte) đã được khai báo như sau:

TenGom DB ‘Nguyen Kim Le Tuan’

Xem các lệnh sau đây (1):

Mov Bx, 0

Mov Al, TenGom[Bx] ; Al =‘N’

Add Bx, 7 ;

Mov Bl, TenGom[Bx] ; Bl =‘K’

Xem các lệnh sau đây (2):

Lea DI, TenGom

Mov Al, [DI] ; Al =‘N’

Mov Bl, [DI + 7] ; Bl =‘K’

Ta có thể thấy, nhóm các lệnh (1) và nhóm các lệnh (2) là tương đương nhau về tác dụng của nó, nhưng (1): sử dụng trực tiếp tên biến để truy xuất đến các phần tử của nó; (2): sử dụng thanh ghi chỉ mục DI để truy xuất đến các phần tử của biến Trong trường hợp này địa chỉ segment mặc định được chỉ bởi DS, điều này phù hợp với việc sử dụng địa chỉ gián tiếp thanh ghi chỉ mục

Ví dụ 3: Giả sử tại địa chỉ 0100:0C00 trong bộ nhớ có chứa một xâu kí tự gồm 50 kí tự (tức là,

gồm 50 ô nhớ, mỗi ô 1 byte) Hãy copy xâu kí tự này vào một biến trong chương trình

Với yêu cầu này chương trình phải khai báo một biến byte có độ lớn 50 byte:

LuuTru DB 50 Dup (‘ ‘)

Mov Ax, 0100

Mov DS, Ax ; trỏ cặp thanh ghi DS:SI về

Mov SI, 0C00 ; đầu vùng nhớ chưa xâu cần copy (0100:0C00) ; -

Mov Ax, Seg LuuTru ; trỏ cặp thanh ghi ES:DI về

Mov ES, Ax ; đầu biến LuuTru

Lea DI, LuuTru

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 26

Tự 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 27

Ví dụ 2: Dãy các lệnh dưới đây sẽ thực hiện phép gán A = 4*A – 3*B, trong đó A và B là các

biến kiểu word:

Mov Ax, 4 ; số nhân phải được chứa trong Ax

IMul A ; thực hiện phép nhân

Mov Bx, Ax ; lưu tạm kết quả vào Bx

Mov Ax, 3 ; Ax = 3

Imul B ; Ax = Ax * B

Sub Bx, Ax

Mov A, Bx ; đặt kết quả cuối cùng vào A

Trong trường hợp này ta đã giả sử hiện tượng tràn đã không xảy ra và kết quả phép nhân chỉ chứa trong thanh ghi Ax

Ví dụ 3: Các lệnh sau đây thực hiện phép: chia -123 cho 24:

Mov Ax, -123 ; đặt số bị chia vào Ax

Mov Bl, 24 ; đặt số chia vào Bl (thanh ghi 8 bít)

Idiv Bl ; chia Ax cho Bl, kết quả chứa ở Al và Ah

Ví dụ 4: Dãy lệnh dưới đây sẽ thực hiện phép gán A = N! (tính N giai thừa) A là một biến word:

Mov Ax, 1 ; chuẩn bị Ax để lưu kết quả

Mov 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 28

Word) và lệnh CWD (Convert Word to Doubleword) để hỗ trợ cho phép chia trên các số có dấu [2 – 187-200].

Lệnh IMul của vi xử lý Intel 80286 cho phép ghi rõ [Toán hạng đích], [Toán hạng nguồn] trong câu lệnh, các lệnh này có thể có đến 3 toán hạng [1 - 541]

6 Lệnh logic: NOT – AND – OR – XOR – TEST

Trước khi tìm hiểu về các lệnh logic chúng ta xem lại kết quả thực hiện các phép tính logic trên 2 bít nhị phân A và B thông qua bảng sau đây:

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 29

Lệnh Not thường được sử dụng để tạo dạng bù 1 của [Toán hạng đích]

- Lệnh And (Logical And): Thực hiện phép tính logic And trên từng cặp bít (tương ứng về vị

trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào [Toán hạng đích]

Lệnh And thường được sử dụng để xóa (= 0) một hoặc nhiều bít xác định nào đó trong một thanh ghi

- Lệnh Or (Logical Inclusive Or):Thực hiện phép tính logic Or trên từng cặp bít (tương ứng

về vị trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào [Toán hạng đích]

Lệnh Or thường dùng để thiết lập (= 1) một hoặc nhiều bít xác định nào đó trong một thanh ghi

- Lệnh Xor (eXclusive OR):Thực hiện phép tính logic Xor trên từng cặp bít (tương ứng về vị

trí) của [Toán hạng nguồn] với [Toán hạng đích], kết quả lưu vào [Toán hạng đích]

Lệnh Xor thường dùng để so sánh (bằng nhau hay khác nhau) giá trị của hai toán hạng, nó cũng giúp phát hiện ra các bít khác nhau giữa hai toán hạng này

- Lệnh Test: Tương tự như lệnh And nhưng không ghi kết quả vào lại [Toán hạng đích], nó chỉ

ảnh hưởng đến các cờ CF, OF, ZF,

Ví dụ 1:

Mov Al,0 ; Al ß0

Not Al ; Al = Not Al Tức là Al = 0FFh

Ví dụ 2: Cho AL = (10010011)2, BL = (11001100)2

- And Al, Bl ; Al ß 10010011 And 11001100 Al =

- And Al, 0 ; Al ß 10010011 And 0 Al =

- 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 31

Một số ví dụ minh họa:

Ví dụ 1: Giả sử tại địa chỉ 0100:0120 trong bộ nhớ có chứa một mảng dữ liệu, gồm 100 ô nhớ,

mỗi ô là 1 word Hãy tính tổng nội dung của các ô nhớ này, kết quả chứa trong thanh ghi Dx Mov Ax, 0100

Mov DS, Ax ; trỏ cặp thanh ghi DS:DI về

Mov DI, 0120 ; đầu vùng nhớ cần truy xuất (0100:0120)

; -

Mov Dx, 0 ; chuẩn bị Dx để lưu tổng

Mov Cx, 100 ; lặp 100 lần vì mảng gồm 100 ô nhớ

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

Ngày đăng: 29/05/2016, 16:58

TỪ KHÓA LIÊN QUAN

w