Giáo trình GTASM
Trang 1CHƯƠNG 1 : SỐ NHỊ PHÂN
Trong Computer dữ liệu được ghi nhớ bằng các mạch điện có khả năng thể hiện một số trạng thái xác định Mỗi mạch điện như vậy là một phần tử nhớ căn bản Số giá trị một phần tử nhớ căn bản có thể biểu diễn cũng chính là số trạng thái mạch điện tạo nên nó có thể xác định Do khả năng của kỹ thuật chế tạo mạch, và cũng do giá thành của mạch điện, các phần tử nhớ trên các Computer hiện nay đều được thiết lập từ các mạch điện có hai trạng thái xác định bởi mức điện thế cao và thấp
Tương ứng với hai trạng thái của mạch điện, mỗi phần tử nhớ mang một trong hai giá trị 0 và 1 Một phần tử nhớ như vậy được gọi là một bit
* Các toán tử trên bit :
A B NOT A A AND B A OR B A XOR B A XOR (A XOR B)
1.1_ Đ ƠN VỊ LƯU CHỨA THÔNG TIN :
Để có thể biểu diễn được nhiều giá trị hơn, thông thường người ta lưu chứa thông tin dưới dạng từng nhóm bit, mỗi nhóm biểu thị cho một phần tử của thông tin
- Một nhóm 4 bit được gọi là nibble
- Một nhóm 8 bit được gọi là byte Byte là đơn vị lưu chứa thông tin cơ sở trên các Computer hiện
nay Các bội số của byte là :
1 KB = 1024 byte ( 2 lũy thừa 10 )
1 MB = 1024 KB
1 GB = 1024 MB
- Một nhóm 16 bit được gọi là word
- Một nhóm 32 bit được gọi là double word (dword)
Computer truy nhập đến các thông tin theo đơn vị nhỏ nhất là byte
1.2_H Ệ ĐẾM :
Các giá trị số lưu chứa trong Computer dưới dạng từng nhóm các bit với cách biểu diễn 0 và 1 khác hẳn cách biểu diễn số thông thường của chúng ta (dùng các ký số 0,1 9) Cách biểu diễn số của chúng ta dựa trên cơ sở mười ký số 0,1 9 được gọi là hệ đếm thập phân, cách biểu diễn số trong Computer dựa trên cơ sở hai ký số 0 và 1 được gọi là hệ đếm nhị phân
1.2.1_Quy tắc của một hệ đếm theo vị trí:
Hệ đếm của chúng ta hiện nay là một hệ đếm theo vị trí, dựa trên cơ sở mười ký số 0,1 9 Giá trị của một số được biểu diễn bằng một dãy các ký số, trong đó giá trị của mỗi ký số tùy thuộc vào vị trí của nó ở trong dãy
Thí dụ :
1*100000 + 9*10000 + 7*1000 + 4*100 + 3*10 + 5
Trang 2Gọi số các ký hiệu để biểu diễn một hệ đếm theo vị trí gọi là cơ số C của hệ đếm ta có công thức tổng quát :
x = Σ Ci * Ki (Ki là các ký số của hệ đếm)
1.2.2_Các hệ đếm thông dụng :
Hệ nhị phân : sử dụng các ký số 0 và 1 (bin : binary )
Hệ bát phân : sử dụng các ký số 0,1 7 (Oct: )
Hệ thập phân: sử dụng các ký số 0,1 9 (dec : decimal)
Hệ thập lục : sử dụng các ký số 0,1 9,A F (hex : hexa )
Bảng đối chiếu các hệ đếm:
1.2.3_Quy ước biểu diễn các hệ đếm :
• Các số dạng bin kết thúc bằng b
• Các số dạng hex kết thúc bằng h
• Các số hex nên bắt đầu bởi ký số 0 (bắt buộc khi ký số đầu tiên là A F)
1 1 1 1 1 0 1 0 0 1 0 0 1 1 0 1
1.3_ C ÁC PHÉP TÍNH TRÊN HỆ NHỊ PHÂN
Các phép tính cơ bản + - * / trên số nhị phân được thực hiện với các quy tắc như trên hệ thập phân Tuy vậy, phải chú ý rằng các số nhị phân lưu trong Computer được ghi nhận bằng một số các bit xác định : có số các ký số xác định Do đó các phép tính chỉ có thể thực hiện trên khuôn khổ các là các bit tràn Các số nhị phân chúng ta đề cập kế tiếp là các số nhị phân được lưu trong một số bit xác định (8,16,32), tức là có chiều dài không đổi
1.3.1_Phép cộng nhị phân:
Trang 31 1 0 1 0 1 1 1
0 0 0 0 0 0 0 0
1 1 0 1 0 1 1 1 _
1 0 0 0 0 1 1 0 0 0 1
1.3.4_Phần bù nhị phân:
Phần bù của một số nhị phân được định nghĩa là một số nhị phân được tạo thành từ số ban đầu theo quy tắc các bit đều được đảo ngược (NOT)
Thí dụ : phần bù của 01001101 là 10110010
Trang 40 1 0 0 1 1 0 1
1 0 1 1 0 0 1 0 < - Phần bù Một số cộng với phần bù của nó sẽ cho kết quả là một giá trị nhị phân có tất cả các bit bằng 1 Phần bù của một số nhị phân cũng được gọi là bù 1 của số nhị phân đó
1.4_S Ố ÂM TRONG HỆ NHỊ PHÂN :
Các số nhị phân trên là các số không dấu (nguyên dương) Để biểu diễn số có dấu (nguyên âm) ta không thể đơn thuần thêm một dấu trừ trước một số nhị phân bởi vì Computer chỉ có hai ký hiệu 0 và 1 để ghi nhớ thông tin!
Yêu cầu cần phải có một cách mô tả các số âm cũng trên cơ sở các ký hiệu 0 và 1 đã phát sinh từ đây
Người ta định nghĩa số âm qua khái niệm số đối trong đại số : một số khi cộng với số đối của nó sẽ bằng 0 Như vậy số nhị phân biểu diễn cho giá trị -3 phải là một số khi cộng với số nhị phân biểu diễn cho giá trị +3 tạo ra kết quả là 0
Từ nhận xét một số cộng với phần bù của nó sẽ cho kết quả là một số nhị phân có tất cả các bit đều bằng 1, ta có kết luận sau:
số-nhị-phân + phần-bù + 1 = 0 ( có một bit tràn ! )
===> số đối = phần bù + 1 ( bù 2 )
Trong quá trình xác định cách ký hiệu cho các số âm người ta nhận thấy các số âm tạo thành bao giờ cũng có bit cao nhất là 1, và các số dương tương ứng có bit cao nhất là 0
Các nhận xét trên đưa đến các hệ quả về số nhị phân có dấu như sau :
• Các số âm bao giờ cũng có bit cao nhất là 1
• Một số âm cộng số đối tương ứng của nó sẽ cho kết quả là 0
• Các số có dấu cộng với nhau sẽ cho kết quả đúng như phép cộng các số nguyên ( Hãy chứng
minh -3+2 = -1 )
Trang 5CHƯƠNG 2 : CẤU TRÚC MỘT MÁY VI TÍNH IBM PC
2.1_B Ộ XỬ LÝ TRUNG ƯƠNG
Trung tâm của một máy vi tính IBM PC là bộ xử lý trung ương (CPU) 8088 do Intel sản xuất 8088 là một vi mạch đặc biệt, có khả năng tự thực hiện một loạt các mệnh lệnh được liệt kê trước,có khả năng tự phán đoán để chọn cách đáp ứng với các tình huống trong quá trình thực hiện lệnh Mỗi mệnh lệnh
8088 được biểu thị bằng một nhóm các byte Các byte ấy được gọi là mã lệnh (opcode) của bộ xử lý Khi thực hiện công việc của mình CPU đọc các mã lệnh được lưu chứa trong bộ nhớ của máy tính và thực hiện chúng theo thứ tự liên tiếp nhau
2.2_B Ộ NHỚ
Bộ nhớ là nơi lưu chứa các mệnh lệnh mà CPU sẽ phải thực hiện,cũng là nơi ghi nhớ các thông tin, các giá trị trung gian, các kết quả của quá trình làm việc
Bộ nhớ được tạo thành từ hai loại chính :
- Bộ nhớ chỉ đọc - ROM (Read Only Memory)
- Bộ nhớ đọc/ghi - RAM (Random Access Memory)
Các thông tin ghi trên ROM là cố định và không xóa được: mang tính chất “vĩnh cửu”
Các thông tin trên RAM có thể được sửa đổi tùy thích, nhưng những gì đang được lưu giữ trong RAM sẽ mất đi khi tắt máy Trong một máy tính, mỗi phần tử nhớ thuộc ROM hoặc RAM được ghi nhận bởi một số thứ tự (gọi là địa chỉ)
2.3_C ÁC MẠCH HỖ TRỢ
Để có thể hoạt động được, CPU cần đến sự hỗ trợ của nhiều vi mạch khác Đồng thời, CPU cũng không thể giao tiếp với người sử dụng bằng các giá trị nhị phân biểu diễn dưới dạng các xung điện Nó phải nhờ đến các mạch giao diện (Interface) như keyboard controller,video controller Các mạch giao diện này sẽ điều động trực tiếp đến các phương tiện giao tiếp cụ thể với người sử dụng (như màn hình,bàn phím,máy in ) theo các yêu cầu,các thông tin được gởi đến từ CPU
2.4_P ORT
Để liên lạc với các vi mạch hỗ trợ,các mạch giao diện, CPU dùng một hệ thống địa chỉ riêng ( khác với hệ thống địa chỉ định vị của bộ nhớ ) để xác định đối tượng cần đối thoại Mỗi địa chỉ trong hệ thống này được gọi là một cổng (port) Mỗi vi mạch hỗ trợ, mỗi mạch giao diện sẽ chiếm dụng một số cổng xác định Khi cần giao tiếp, CPU chỉ cần sử dụng đến các cổng tương ứng
SYSTEM BUS
VIDEO CONTROLLER KEYBOARD
CONTROLLER CONTROLLER DISKETTE CONTROLLER HARD DISK INTERFACE IO
Trang 62.5_C LOCK
Các mạch điện ít khi nào có thời gian thực hiện giống nhau Do đó, để các phần tử trong một máy tính hoạt động nhịp nhàng ăn khớp với nhau phải có một bộ phận đếm nhịp Đảm nhiệm công việc này là một bộ phận tạo xung nhịp cho tất cả các mạch điện trong máy Bộ phận này được gọi là đồng hồ của máy Máy có đồng hồ với tần số càng cao thì sẽ chạy càng nhanh
Hệ thống quản lý bus gọi là Bus controller
Giao tiếp thông qua bus được thực hiện như sau :
Giả sử CPU cần tăng giá trị từ một phần tử nhớ tại địa chỉ X lên 1 đơn vị, nó phải trải qua các bước sau :
1 Đặt địa chỉ X vào address bus
2 Đưa yêu cầu đọc memo vào command bus
3 Đợi Bus thông báo sẵn sàng : bus controller lấy giá trị tại địa chỉ X đặt vào Data bus
4 Tiếp nhận giá trị cần đọc từ data bus
5 Tăng giá trị ấy lên 1 ( phép toán được thực hiện trong CPU )
6 Đặt giá trị mới trở lại data bus
7 Đưa yêu cầu ghi memo vào command bus (address bus đang giữ giá trị cũ)
Trang 78 CHƯƠNG 3 : BỘ VI XỬ LÝ 8088
3.1_C ÁC THANH GHI CỦA BỘ VI XỬ LÝ 8088
Bộ vi xử lý 8088 có một số phần tử dùng để ghi nhớ các giá trị trung gian trong quá trình làm việc,tạo ra các địa chỉ,làm các phép tính Các phần tử này gọi là các thanh ghi (Register) 8088 là bộ vi xử lý 16 bit, các tác vụ của 8088 thường dược thực hiện trên 16 bit dồng thời,các thanh ghi của 8088 đều là các phần tử nhớ 16 bit.Các thanh ghi hoàn toàn có thể sử dụng như các phần tử nhớ thuộc về RAM Điểm khác nhau giữa thanh ghi và các phần tử nhớ thuộc RAM là Thanh ghi là các phần tử thuộc bộ vi xử lý (về mặt vật lý)
Một số tác vụ đặc biệt như nhân hay chia, kết quả bắt buộc phải đặt trong thanh ghi
Một số thanh ghi chỉ dùng để xác định địa chỉ cho 8088
Các tác vụ trên thanh ghi nhanh hơn nhiều so với các tác vụ trên RAM Bởi vì để thực hiện các tác vụ trên RAM,8088 phải đi qua một quá trình như sau :
a) Đọc thông tin từ RAM (thông qua Bus)
b) Thực hiện tác vụ
c) Ghi trả lại thông tin vào RAM (thông qua Bus)
Bộ vi xử lý 8088 có 14 thanh ghi 16 bit,được chia thành nhóm theo chức năng như sau:
3.1.1_Nhóm thanh ghi đa dụng :
AX,BX,CX,DX : Mỗi thanh ghi có thể sử dụng như hai thanh ghi 8 bit với các tên AH,AL,BH,BL,CH,CL,DH,DL
3.1.2_Nhóm thanh ghi con trỏ và chỉ mục :
SP,BP,SI,DI : thường dùng để tạo các địa chỉ, cũng có thể dùng để lưu chứa các số liệu như nhóm thanh ghi đa dụng
3.1.3Nhóm thanh ghi phân đoạn :
CS,DS,SS,ES : Chỉ dùng để xác định Segment, không cho phép thực hiện các phép tính số học,logic
3.1.4Thanh ghi con trỏ lệnh IP :
Chỉ dùng để xác định offset của mã lệnh
3.1.5Thanh ghi cờ hiệu Flag :
Ghi lại trạng thái của các phép tính số học, kết quả của các phép so sánh
OF DF IF TF SF ZF AF PF CF
3.2C ÁCH XÁC ĐỊNH ĐỊA CHỈ CỦA 8088
Trang 8Bộ vi xử lý 8088 dùng một số 20 bit để xác định địa chỉ cho các phần tử trong vùng nhớ (memory), tương đương với 1MB vị trí Bằng các giá trị 16 bit,ta chỉ có thể tạo ra địa chỉ cho 0FFFFh phần tử ( 64 KB hay 65536 byte ) Do đó để có thể định vị 1 MB địa chỉ bằng các thanh ghi 16 bit, 8088 sử dụng nguyên tắc truy nhập bộ nhớ theo từng segment,mỗi segment có kích thước 64 KB, điểm bắt đầu của segment phải là một địa chỉ chia hết cho 010h ( 16 thập phân ) Địa chỉ của segment được tính bằng paragraph ( 1 paragraph = 16 byte )
Địa chỉ một phần tử nào đó thuộc đoạn sẽ được xác định thông qua địa chỉ tương đối (offset) của phần tử đó trong đoạn và vị trí của đoạn Từ nguyên tắc trên, ta có các kết quả sau :
Địa chỉ 20 bit = Segment*16 + Offset
Cách biểu diễn : Segment:offset
Cùng một địa chỉ, có thể biểu diễn dưới nhiều hình thức khác nhau
Thí dụ: 0040:006C và 0000:046C là hai cách biểu diễn một địa chỉ Segment chỉ là khái niệm logic,
do đó không có vị trí cố định cho từng segment Ta hoàn toàn có thể mô tả các segment phủ lấp lên nhau Bộ vi xử lý 8088 luôn xác định segment thông qua giá trị của các thanhghi segment Do đó để xác định segment ta phải gán các giá trị tương ứng cho các thanh ghi segment
Thanh ghi CS chỉ định segment của đoạn mã chương trình đang thực hiện,IP mang giá trị offset Vì vậy CS:IP luôn là địa chỉ của lệnh đang được thực hiện
Thanh ghi DS chỉ định segment của dữ liệu,thông thường các địa chỉ đều được căn cứ theo segment
DS
Thanh ghi SS chỉ định segment của STACK ( sẽ nói rõ hơn trong phần sau)
Thanh ghi ES dùng trong các tác vụ về chuỗi hoặc theo yêu cầu riêng
3.3C ÁC XÁC ĐỊNH OFFSET CỦA 8088
Offset của bộ vi xử lý 8088 được xác định theo nguyên tắc sau :
Offset là một hằng số
TD : [046Ch]
Offset là giá trị của một trong các thanh ghi chỉ mục BX,SI,DI,BP
TD : [BX]
Offset là giá trị của một tổng được tạo thành từ các hằng số,các thanh ghi chỉ mục
Nguyên tắc này có một số hạn chế :
- SI và DI không được cùng có mặt trong tổng
- BX và BP không được cùng có mặt trong tổng
Nếu chúng ta chỉ định rõ ràng thanh ghi segment đi kèm với offset, các giá trị mặc nhiên về segment sẽ bị vô hiệu hoá
3.4 B Ộ 8088 THỰC HIỆN MỘT CHƯƠNG TRÌNH RA SAO ?
3.4.1Thế nào là một chương trình :
Chương trình là một tập hợp các mã lệnh và các dữ liệu Mã lệnh (code) : là các lệnh 8088 sẽ thực hiện Dữ liệu (data) : là các thông tin chương trình sẽ sử dụng đến Thí dụ : chương trình của bạn sẽ in
Trang 9dòng chữ “Do you like me ?” ra màn hình thì dòng chữ ấy phải có mặt trong chương trình của bạn Vùng lưu chứa dòng chữ này trong chương trình được gọi là một vùng dữ liệu (data) Chương trình của bạn cũng cần đến một số vùng nhớ để lưu chứa các giá trị trong quá trình chương trình được thực hiện, các vùng nhớ này cũng được gọi là vùng dữ liệu
Phải chú ý, bộ 8088 không thể phân biệt đâu là code, đâu là data !
3.4.2Cách thức 8088 thực hiện một chương trình :
Giả sử đã có chương trình trong RAM và CS:IP chỉ đến lệnh đầu tiên của chương trình 8088 đọc byte xác định bởi CS:IP,nhận diện lệnh, từ đó suy ra chiều dài của lệnh 8088 cộng thêm vào IP chiều dài của lệnh, rồi bắt đầu thực hiện lệnh Sau khi thực hiện lệnh, 8088 đọc tiếp byte đang được xác định bởi CS:IP và cứ thế quá trình này lặp đi lặp lại mãi Chương trình của chúng ta được thực hiện từ lệnh đầu tiên đến lệnh cuối cùng theo phương cách này Nhưng làm cách nào để đưa một chương trình vào RAM và làm cách nào để ấn định CS:IP chỉ đến lệnh đầu tiên của chương trình ấy ? Tất cả các tác vụ này đều
do hệ điều hành thực hiện Chương trình của chúng ta được lưu chứa trên đĩa thành một file có phần mở rộng (đuôi đặc tính) là COM hay EXE Khi ta gõ từ bàn phím tên của file này và nhấn ENTER, hệ điều hành sẽ đọc file ấy vào RAM, sau đó đưa vào CS và IP các giá trị để CS:IP chỉ vào lệnh đầu tiên của chương trình vừa được nạp vào Sau đó là quá trình thực hiện chương trình của chúng ta như đã nêu trên Làm cách nào để kết thúc chương trình của chúng ta ?
Như đã nêu trên, 8088 không ngừng đọc và phân giải các lệnh theo địa chỉ được CS và IP xác định,do đó kết thúc một chương trình nào đó không có nghĩa là 8088 ngừng hoạt động (!) Quá trình kết thúc một chương trình được thực hiện bằng cách làm cho CS:IP chỉ vào một điểm xác định trước trong vùng mã của hệ điều hành ( còn gọi là chuyển điều khiển cho hệ điều hành ) Sau đó là quá trình đợi lệnh của hệ điều hành Quá trình đợi lệnh của hệ điều hành như chúng ta nhìn thấy, có vẻ như “máy tính đang ngừng làm việc,chờ chúng ta ra lệnh tiếp” ! Đây là điều lầm tưởng của nhiều người mới bắt đầu làm quen với Computer Chúng ta phải luôn nhớ rằng máy tính liên tục phân giải và thực hiện các lệnh được CS:IP chỉ đến và chỉ ngừng khi chúng ta tắt máy !
3.4.3Chúng ta tạo ra chương trình bằng cách nào :
Các mã lệnh trong chương trình của chúng ta là các số nhị phân biểu diễn thành từng byte Chúng
ta sẽ rất khó khăn nếu phải viết một chương trình bằng từng số nhị phân như vậy Trong thực tế chúng ta sẽ viết một chương trình nguồn, đó là một file với các đặc điểm :
- Sự trình bày tuân thủ theo một số quy tắc chung
- Mỗi lệnh của 8088 sẽ được gọi bằng một cái tên gợi nhớ
- Các địa chỉ được xác định bởi các tên riêng do người viết quy định
- Chương trình nguồn sẽ được một chương trình đặc biệt biên dịch (compile) thành một file với
đầy đủ các mã lệnh và các dữ liệu đã mô tả trong chương trình nguồn Vì vậy chúng ta không cần phải thắc mắc lệnh này hoặc lệnh kia có mã máy là gì !
3.4.4Một chương trình nguồn sẽ được biên dịch bằng Assembler 86 :
Code Segment Assume CS:Code,DS:Code org 0100h
Main proc near M1: mov dx,offset chuoi1 ; in chuoi1 ra màn hình mov ah,9 ;
int 021h ; mov ah,1 ; Read Keyboard
Trang 10int 021h ;
cmp al,’Y’ ; Kiểm tra ký tự dọc vào
je KT ; nếu là ‘Y’ hay ‘y’ thì nhảy dến KT
Chuoi1 db 13,’Do you like me ? $’ ; Mã 13 để trở về đầu dòng
Chuoi2 db ‘ Thank you !$’
Code ends
end main
Trang 11CHƯƠNG 4 : ASSEMBLER 86
Ngữ pháp của các trình hợp dịch thông dụng hiện nay như MASM,TASM khá phức tạp, do đó tài liệu này sẽ chỉ trình bày các phần căn bản mà hầu hết trình hợp dịch thông dụng đều chấp nhận Những phần căn bản nhất của ngữ pháp của assembler cho bộ xử lý 8088/86 sẽ được trình bày trong chương này Các phần khác sẽ được đề cập đến trong các chương sau
4.1N GỮ PHÁP CỦA A SSEMBLER 86
4.1.1Khai báo segment
Chương trình của chúng ta có thể sử dụng đến nhiều segment khi được thực hiện :
Để truy nhập đến mỗi segment, ta phải gán địa chỉ của segment tương ứng cho các thanh ghi segment Sau đó, các phần tử thuộc về segment sẽ được truy nhập thông qua offset của nó trong segment ấy
Lưu ý là việc gán giá trị cho các thanh ghi segment chỉ được thực hiện trong lúc chương trình đang thực hiện Trong quá trình hợp dịch, trình hợp dịch không thể kiểm soát được giá trị thực của thanh ghi segment Do đó sự phân chia chương trình thành các segment hoàn toàn là các ý niệm của người lập trình
Một trong các nhiệm vụ quan trọng của trình hợp dịch là xác định chính xác các offset theo các segment của chương trình chúng ta Nếu không có một sự lưu ý nào về segment sẽ được sử dụng, trình hợp dịch sẽ cho rằng toàn bộ chương trình ở trong cùng một segment Khi ấy các phần tử thuộc chương trình đều được trình hợp dịch xác định địa chỉ tương đối (offset) theo điểm đầu của chương trình Thí dụ : một vị trí thuộc SEGMENT-B sẽ được tính offset theo cự ly từ đầu SEGMENT-A cho đến vị trí của nó trong toàn bộ đoạn mã chương trình được tạo ra Khai báo về Segment sẽ lưu ý trình biên dịch về phạm vi của các segment cùng các phần tử thuộc về chúng.Theo đó trình biên dịch sẽ xác định đúng các offset tương ứng theo nguyên tắc : phần tử được xét thuộc segment nào sẽ được xác định cự ly theo điểm khởi đầu của segment ấy
Offset Var_X nếu không khai báo Segment
Trang 12Một Segment được khai báo với trình hợp dịch theo dạng thức sau :
Tên-segment Segment [word/byte] [Public]
( Phạm vi của segment )
Tên-segment ends
Các phần tử thuộc về segment nào phải được mô tả bên trong cấu trúc segment đó Để xác định thanh ghi segment sẽ dùng cho segment được mô tả ta dùng khai báo sau :
4.1.2Khai báo thủ tục
Chương trình của chúng ta được viết thành các thủ tục (procedure) Một procedure có cấu trúc như sau :
Tên-procedure proc [near/far]
Tên-procedure endp
Bên trong thủ tục là các lệnh, các khai báo về dữ liệu Mỗi một lệnh, mỗi một khai báo đều được viết dưới dạng một dòng lệnh
Khai báo thủ tục trong hợp ngữ chỉ là hình thức Chúng ta có thể bỏ qua các khai báo ấy mà vẫn tạo lập được một số chương trình đơn giản Tuy vậy viết chương trình dưới dạng các thủ tục sẽ làm chương trình của chúng ta trở nên rõ ràng và dễ quản lý hơn
Khi chương trình được viết dưới dạng các thủ tục, việc khai báo thủ tục rất quan trọng Trình hợp dịch sẽ căn cứ trên các khai báo ấy mà xác định chính xác các mã lệnh tương ứng với các chỉ thị JMP, CALL và RET
4.1.3Cấu trúc một dòng lệnh
A Dòng lệnh mô tả :
Mô tả hằng ( không tạo ra mã lệnh )
Tên EQU Giá trị
Mô tả hằng dùng để định nghĩa một số giá trị sẽ được dùng đến trong chương trình Sau khi được mô tả, mỗi khi sử dụng đến giá trị nào chúng ta chỉ cần dùng tên tương ứng Trình hợp dịch sẽ tự động thay thế giá trị thực của hằng vào đoạn mã tương ứng
Mô tả biến
Tên Loại Danh sách giá trị
( Trong đó Loại có thể là [Byte/Word/Dword] )
Mô tả biến dùng để xác định một địa chỉ,từ đó bắt đầu lưu chứa các thông tin với tính chất nêu trong
“Loại” và chi tiết được liệt kê trong “Danh sách giá trị” Khi cần truy nhập đến các thông tin này chúng ta dùng tên biến như một mốc địa chỉ
Trang 13Thí dụ : Biến-A địa chỉ tại nơi khai báo Biến-A
Biến-A[1] địa chỉ Biến-A + 1
Biến-A[2] địa chỉ Biến-A + 2
Biến-A[-1] địa chỉ Biến-A - 1
Trình hợp dịch sẽ tự động đổi ra địa chỉ thực của biến trong các chỉ thị có dùng đến biến
Mô tả nhãn
Tên LABEL Loại
( Trong đó Loại có thể là [Byte/Word/Dword/Near/Far] )
Mô tả nhãn dùng để xác định một vị trí trong chương trình,tính chất của địa chỉ ấy tùy thuộc vào
“Loại” được khai báo Trình hợp dịch sẽ tạo ra các địa chỉ,các cự ly thực trong các chỉ thị có liên hệ đến tên nhãn
Mô tả biến và nhãn cho phép chúng ta chỉ định các vị trí trong chương trình theo vị trí logic của chúng chứ không theo địa chỉ thực của chúng Khi hợp dịch, trình hợp dịch sẽ tính các cự ly thực sự và thay thế chúng vào các mã lệnh tương ứng
Để lấy địa chỉ một phần tử dùng cấu trúc :
Offset Tên-phần-tử
Thí dụ : Mov dx,offset Biến-A
Các quy định chung :
Tên : là một chuỗi tạo bởi các ký tự, các ký số và - (dấu gạch dưới) Tên không được bắt đầu bằng ký số
Trình hợp dịch quy định sau dấu chấm phẩy (;) là các lời chú thích Ta có thể đặt bất cứ ghi chú gì ở đấy
Mỗi giá trị trong mô tả hằng hoặc biến có thể là :
- Một hằng số, một tên hằng đã khai báo
- Một biểu thức gồm các hằng số,tên hằng tạo thành từ các phép toán:
B Dòng lệnh thực hiện
[Nhãn:] Lệnh Đối tượng
Tên : là một chuỗi tạo bởi các ký tự, các ký số và - (dấu gạch dưới)
Tên không được bắt đầu bằng ký số Sau dấu chấm phẩy (;) là các lời chú thích Ta có thể đặt bất cứ ghi chú gì ở đấy
4.1.4Các loại (kiểu) cơ bản
Loại :
Loại byte : khai báo bằng từ khóa DB
Mỗi phần tử trong danh sách giá trị chiếm 1 byte
Trang 14- Chỉ có kiểu byte mới chấp nhận chuỗi ký tự
- Để biểu diễn một byte,ta có thể ghi giá trị của byte đó dưới dạng số thập phân, số thập lục, số
nhị phân hoặc là một ký tự (có mã tương ứng) đặt giữa hai dấu nháy đơn
TD : 65, 041h, ‘A’ là các cách biểu diễn khác nhau của cùng một giá trị
- Các vùng nhớ lưu chứa giá trị được khai báo được gọi là các biến của chương trình
- Kiểu được khai báo chỉ dùng làm giá trị ngầm định để xác định loại dữ liệu được thao tác
trong các lệnh Ta hoàn toàn có thể chỉ định kiểu bất kỳ cho một biến trong các lệnh một cách tường minh
Loại word : khai báo bằng từ khóa DW
Mỗi phần tử trong danh sách giá trị chiếm 2 byte (1 word)
Loại double word : khai báo bằng từ khóa DD Mỗi phần tử trong danh sách giá trị chiếm 4 byte (2
word)
Chú ý :
Dữ liệu lưu chứa trong RAM có một số điểm khác với cách biểu diễn thông thường của chúng ta : những số biểu diễn bằng nhiều byte như loại word, double word được ghi nhớ trong RAM theo quy tắc byte thấp đứng trước, byte cao đứng sau
Thí dụ : giá trị hai byte 01234h được ghi nhớ tại địa chỉ 0462:0180 như sau :
0462:0180 034h 0462:0181 012h
Chú ý : Khi nói một giá trị được ghi nhớ tại một địa chỉ nào đó ta phải hiểu là “được ghi nhớ bắt đầu từ
địa chỉ đã chỉ ra” !
4.1.5Phân loại chương trình
Đối với hệ điều hành DOS, chương trình của chúng ta được chia thành hai loại chính :
- Loại COM : chỉ sử dụng 1 segment, có kích thước tối đa gần 64 KB khi được thực thi, các thanh ghi segment đều được gán giá trị của segment hiện lưu chứa chương trình Do đó trong một file COM, nếu không có nhu cầu truy nhập đến các vùng nhớ ngoài segment của chương trình, không cần phải chú ý đến việc xác định giá trị các thanh ghi segment Giá trị của IP khi chương trình được thực thi luôn luôn bằng 0100h
- Loại EXE : sử dụng nhiều segment, không hạn chế về kích thước khi được thực thi, các thanh ghi segment cần được gán giá trị của segment có liên quan Giá trị của IP khi chương trình được thực thi tùy thuộc từng chương trình cụ thể
Trong tài liệu này, các chương trình thí dụ đều được viết dưới dạng COM
4.2C ÁC LỆNH CĂN BẢN
4.2.1Lệnh MOV
Dạng lệnh : MOV đích,nguồn
Trang 15Kết quả : dữ liệu sẽ được đem từ “nguồn” vào “đích”
(giá trị của nguồn vẫn giữ nguyên)
Trong đó “nguồn” và “đích” có thể là :
- Thanh ghi
- Số
- Offset
Nếu là offset thì phải chỉ ra sẽ sử dụng byte hay word bằng cách sử dụng các khai báo :
Byte PTR offset ( 8 bit )
Word PTR offset ( 16 bit )
Thí dụ :
MOV AX,BX MOV AX,word PTR [SI+3]
MOV AH,0Fh MOV word PTR [SI+BX],BX MOV AH,byte PTR [BX] MOV byte PTR [DI+4],9
Để lấy địa chỉ của một tên ( nhãn hoặc biến ) ta thực hiện như sau :
MOV DX,offset chuoi1 hoặc LEA DX,chuoi1
Lệnh trên sẽ đưa vào DX offset của chuoi1
Chú ý :
- Đích và nguồn phải có kích thước bằng nhau
- Bộ vi xử lý 8088 không thể thực hiện các tác vụ với cả hai đối tượng cùng được định vị bởi
offset Do đó lệnh sau là không hợp lệ :
Mov byte ptr [SI],byte ptr [DI+2]
4.2.2Lệnh XCHG
Dạng lệnh : XCHG đích,nguồn
Kết quả : dữ liệu giữa “nguồn” vào “đích” sẽ được đổi chỗ cho nhau
4.2.3Các lệnh cộng,trừ
Dạng lệnh : ADD đích,nguồn ADC đích,nguồn (cộng)
SUB đích,nguồn SBB đích,nguồn (trừ) Kết quả : giá trị của “đích” được cộng (hay trừ) với nguồn, kết quả được đặt vào “đích” Giá trị của
“nguồn” không đổi Nếu là ADC hay SBB, kết quả được cộng thêm hay trừ bớt 1 khi bit CF = 1
4.2.4Các lệnh nhân,chia
*Lệnh nhân : MUL giá trị 8 bit (thanh ghi/offset)
Thanh ghi AL được nhân với giá trị 8 bit,kết quả là giá trị 16 bit đặt trong AX
Thí dụ :
Mov AL,3 Mov CL,2 Mul CL
kết quả AX=6
MUL giá trị 16 bit (thanh ghi/offset)
Thanh ghi AX nhân với giá trị 16 bit, kết quả là giá trị 32 bit đặt trong DX và AX
Trang 16Thí dụ :
Mov AX,3 Mov CX,0100h Mul CL
kết quả DX=0 AX=0300h
*Lệnh chia : DIV giá trị 8 bit (thanh ghi/offset)
Thanh ghi AX được chia cho giá trị 8 bit, kết quả là giá trị 8 bit đặt trong AL,số dư đặt trong AH Thí dụ :
Mov AX,035h Mov CL,010h Div CL
Kết quả AL=3 AH=5
DIV giá trị 16 bit (thanh ghi/offset)
Giá trị 32 bit đặt trong thanh ghi DX và AX (giống như kết quả phép nhân 16 bit) chia cho giá trị 16 bit, kết quả là giá trị 16 bit đặt trong AX,số dư đặt trong DX
Thí dụ :
Mov DX,038h ; giá trị 32 bit trong DX và Mov AX,02147h ; AX là 0382147h Mov CX,01000h
Div CX kết quả AX=0382h (thương số)
DX=0147h (số dư)
4.2.5Lệnh CMP và lệnh TEST
Dạng lệnh : CMP đích,nguồn
TEST dích,nguồn
Cách thực hiện :
CMP thực hiện phép tính : nguồn – đích,
TEST lấy nguồn AND với đích Kết quả : Không làm thay đổi giá trị của đích và nguồn, kết quả thực hiện được ghi nhận trong thanh ghi cờ hiệu Flag,để nhận định kết quả phương pháp thông thường là dùng nhóm lệnh chuyển điều khiển có điều kiện
4.2.6Lệnh JMP (chuyển điều khiển không điều kiện)
Dạng lệnh : JMP Nhãn
4.2.7Nhóm lệnh chuyển điều khiển có điều kiện
Dạng lệnh :
Các giá trị không dấu Các giá trị có dấu Ý nghĩa
JBE Nhãn, JNA Nhãn JLE Nhãn, JNG Nhãn <=
JAE Nhãn, JNB Nhãn JGE Nhãn, JNL Nhãn >=
Trang 17JNB Nhãn JNL Nhãn >=
Thường được dùng ngay sau lệnh CMP hoặc lệnh TEST
Thí dụ :
CMP AX,BX ; So sánh AX với BX
JB M-1 ; Nếu AX < BX thì chuyển điều khiển đến M-1
M-0:
MOV Max,AX ; Nếu AX >= BX thì gán Max = AX
JMP M-2 ; Rồi chuyển điều khiển đến M-2
M-1:
MOV Max,BX ; Gán Max = BX, sau đó thực hiện tiếp tại M-2
M-2:
;
Kết quả : Max là giá trị lớn nhất
Max dw 0 ; giữa AX và BX
4.2.8Nhóm lệnh logic
Dạng lệnh :
AND đích,nguồn XOR đích,nguồn,
OR đích,nguồn NOT đích
4.2.9Nhóm lệnh shift
Dạng lệnh :
SHL đích,1 hay SHL đích,CL SHR đích,1 hay SHR đích,CL
Tác dụng : “đẩy” các bit của đích theo chiều từ bit thấp đến bit cao (sang trái/SHL) hay theo chiều từ bit cao qua bit thấp(sang phải/SHR) với số bit là 1 hay là giá trị trong CL Bit bị đẩy “lọt ra ngoài” được đặt trong CF Các khoảng trống do lệnh shift tạo ra được điền bằng giá trị 0
Trang 18Tác dụng : Bit cao nhất và bit thấp nhất của đích được xem như đứng cạnh nhau trong một vòng tròn ROL và ROR sẽ đẩy các bit theo chiều từ bit thấp đến bit cao (ROL) hoặc theo chiều từ bit cao sang bit thấp (ROR) trong vòng tròn ấy với số bit là 1 hay là giá trị trong CL
1 0 0 1 1 0 1 1 0 0 1 1 0 1 1 1 0 1 1 0 1 1 1 0
1 0 0 1 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 0 1 1 0 Dạng lệnh :
RCL đích,1 hay RCL đích,CL RLR đích,1 hay RCR đích,CL Tác dụng : tương tự ROL và ROR, nhưng bit giá trị của CF được đặt chèn vào giữa điểm nối của bit cao nhất và bit thấp nhất.Vòng quay gồm 9 ( hay 17 ) bit
1 0 0 1 1 0 1 1 0 0 1 1 0 1 1 0 0 1 1 0 1 1 0 1 Trước khi RCR Sau khi RCR 1 Sau khi RCR 2
1 0 0 1 1 0 1 1 0 1 0 0 1 1 0 1 1 0 1 0 0 1 1 0
Trang 19CHƯƠNG 5 : STACK - PROCEDURE - INTERRUPT
5.1Khái niệm về STACK
Bộ vi xử lý 8088 dành riêng các thanh ghi SS và SP cho việc quản lý STACK, đó là một cấu trúc đặc biệt được quy định như sau :
STACK là một vùng nhớ thuộc về segment do SS xác định Thanh ghi SP đóng vai trò một con trỏ xác định điểm làm việc của STACK
Có hai tác vụ chuẩn trên STACK :
- PUSH thanhghi/offset (16 bit) :Cất giá trị 16 bit trong thanhghi/offset vào stack
- POP thanhghi/offset (16 bit) :Lấy giá trị 16 bit trong stack đặt vào thanhghi/offset
Cách thực hiện :
Tác vụ Push : trừ SP cho 2, sau đó đặt một giá trị 16 bit vào địa chỉ SS:SP
Tác vụ Pop : lấy giá trị 16 bit tại địa chỉ SS:SP rồi cộng SP với 2
Nếu ta dùng tác vụ Push để cất nhiều giá trị vào STACK, các giá trị này được xếp thành một hàng, giá trị nào được push vào trước đứng đầu hàng, giá trị nào push vào sau đứng cuối hàng Tác vụ Pop sẽ lấy các giá trị từ STACK theo thứ tự từ cuối hàng Do đó thứ tự lấy các giá trị từ STACK bằng Pop phải ngược lại thứ tự đã cất chúng bằng Push
Trong quá trình thực hiện chương trình để tránh hiện tượng các quá trình trung gian làm sai lạc giá trị các thanh ghi, biện pháp thông thường là cất giá trị các thanh ghi này vào STACK trước khi thực hiện quá trình trung gian Thực hiện quá trình trung gian xong, lấy lại giá trị đầu cho các thanh ghi từ STACK Thí dụ :
push DX ; cất DX push AX ; cất AX mov ah,2 ; mov dl,7 ; beep int 021h ; pop AX ; lấy lại AX pop DX ; lấy lại DX
Chú ý :
- Số phần tử có thể cất vào stack chỉ tùy thuộc vào kích thước của stack
- Đã cất giá trị nào vào stack thì phải nhớ lấy ra !
5.2T HỦ TỤC
Chương trình của chúng ta có một số đoạn được sử dụng nhiều lần Đối với các đoạn chương trình này, tốt nhất là tổ chức thành các thủ tục (procedure) Mỗi lần cần sử dụng ta chỉ cần gọi đến chúng theo các tên đã được đặt Việc tổ chức thành thủ tục sẽ giúp cho chương trình ngắn gọn và rõ ràng hơn nhiều Bộ vi xử lý 8088 cung cấp các lệnh có liên quan đến thủ tục như sau :
5.2.1Chỉ thị CALL
Trang 20CALL Tên-procedure
Tác vụ CALL sẽ chuyển điều khiển chương trình đến procedure được chỉ định bằng cách cất địa chỉ của lệnh kế tiếp vào stack,rồi chỉ CS:IP đến địa chỉ của procedure được chỉ định
CALL word ptr <địa-chỉ>
Chuyển điều khiển bằng cách push IP vào stack,sau đó nạp cho IP word lưu chứa tại địa-chỉ
CALL dword ptr <địa-chỉ>
Chuyển điều khiển bằng cách push CS, push IP, sau đó nạp cho IP word ptr địa-chỉ[0], nạp cho CS word ptr địa-chỉ[2]
5.2.2Chỉ thị RET
RET [n]
Quay trở lại lệnh kế tiếp lệnh CALL đã gọi procedure.Một procedure thông thường sẽ kết thúc bằng lệnh RET Thực hiện bằng cách lấy từ stack địa chỉ đã được lưu nhớ trong lệnh CALL rồi gán lại cho CS:IP
Nếu chỉ thị RET đi kèm với một hằng số thì sau khi CS và IP được phục hồi, SP sẽ được cộng thêm hằng số ấy ( tương đương với việc loại bỏ khỏi stack ngần ấy byte )
5.2.3NEAR và FAR :
Nếu procedure được gọi bởi lệnh CALL ở trong cùng segment,thì chỉ cần thay đổi IP để chuyển điều khiển.Do đó chỉ có IP được cất vào stack, và lệnh RET tương ứng cũng chỉ lấy lại giá trị cho IP từ stack Kết quả là CS không thay đổi trong khi thực hiện các lệnh trên Trong trường hợp này lệnh CALL và RET tương ứng được gọi là NEAR
Khi thực hiện một CALL near, IP được cất vào stack (push)
Khi thực hiện một RET near, IP được lấy ra từ stack (pop)
Ngược lại với NEAR, là FAR
Khi thực hiện một CALL far, trước tiên CS được cất vào stack, kế tiếp là IP Sau đó cả IP lẫn CS đều được gán giá trị mới Khi thực hiện một RET far, trước tiên IP được lấy ra từ stack, kế tiếp là CS
5.2.3Quy định của trình hợp dịch về NEAR và FAR
Nếu procedure-A là NEAR thì mặc nhiên các lệnh RET thuộc procedure-A là NEAR và các lời gọi tới procedure-A (CALL procedure-A) là NEAR
Nếu procedure-B là FAR thì mặc nhiên các lệnh RET thuộc procedure-B là FAR và các lời gọi tới procedure-B (CALL procedure-B) là FAR
5.3I NTERRUPT
5.3.1Interrupt trên 8088/8086
Bộ vi xử lý 8088 dành riêng vùng nhớ từ 0000:0000 đến 0000:03FF để tạo thành một bảng ngắt (interrupt table) Bảng ngắt có 256 phần tử liên tiếp được đánh số từ 0-255 (0-0FFh) Mỗi phần tử của bảng ngắt chiếm 4 byte tạo thành địa chỉ (hai byte đầu chứa offset và hai byte sau chứa segment) của một procedure đặc biệt gọi là một interrupt Interrupt là một procedure loại FAR, nhưng chỉ được gọi bằng
lệnh : INT số-hiệu
Khi thực hiện lệnh INT, Flag được cất vào stack kế tiếp là CS và IP Điều khiển được chuyển hướng bằng cách nạp cho CS và IP các giá trị tương ứng trong bảng ngắt (lấy từ phần tử có thứ tự bằng số hiệu đi kèm với chỉ thị INT )
Interrupt kết thúc bằng lệnh IRET, khi thực hiện IRET, IP, CS và Flag được trả lại giá trị cũ
Một interrupt có thể được phát động bằng hai cách : do phần cứng hay do phần mềm
Trang 215.3.2Interrupt cứng (hard interrupt)
Khi một thiết bị ngoại vi cần thông báo một đôi điều gì đó với CPU thông thường một interrupt sẽ
được phát động.Chương trình đang được thực hiện bởi CPU sẽ tạm dừng lại, chỉ thị INT Int-no được thực
hiện ngay tức khắc với Int-no là số hiệu của interrupt được phát động.Các interrupt được phát động theo kiểu này được gọi là các interrupt cứng Các interrupt cứng luôn luôn cất tất cả các thanh ghi được sử dụng đến vào stack trước khi thực hiện công việc của mình Công việc một interrupt cứng thực hiện thường chiếm một thời gian rất ngắn.Sau đó các thanh ghi sẽ được hoàn trả từ stack và interrupt kết thúc bằng tác vụ IRET Như vậy, các interrupt cứng đã tạm ngắt đoạn chương trình hiện thời để thực hiện một số tác vụ nào đó đáp ứng với nguyên nhân đã tạo ra interrupt, sau đó lại cho đoạn chương trình ấy tiếp tục Người sử dụng và bản thân chương trình đang chạy vẫn cứ tưởng mình đang làm chủ hoàn toàn Computer !
Các interrupt cứng được điều khiển bởi interrupt controller Trên IBM PC đó là vi mạch 8259A của Intel 8258A cho phép quản lý 8 interrup cứng Trên IBM AT, hai vi mạch 8259A được sử dụng cung cấp
16 interrupt cứng
Mỗi interrupt cứng được ký hiệu : IRQ <n> ( n = 0 7 hoặc 0 15 )
Mỗi IRQ sẽ được chỉ định tương ứng với một số hiệu từ 0 đến 255 Đó chính là số hiệu của interrupt được phát động Để tránh trường hợp interrupt cứng bị lồng vào nhau ( một interrupt cứng được phát động trở lại trong khi đoạn chương trình của nó chưa kết thúc) Interrupt controller sẽ không cho phép phát động thêm bất cứ interrupt cứng nào khác sau khi một interrupt cứng được gọi Do đó trước khi kết thúc, một interrupt cứng phải cho phép interrupt controller hoạt động trở lại (enable interrupt) bằng cách gởi ra port 020h giá trị 020h
mov al,020h out 020h,al
iret 5.3.2 Interrupt mềm (soft interrupt)
Một interrupt được phát động bởi một chỉ thị INT ngay bên trong đoạn chương trình đang được thực hiện gọi là một interrupt mềm Nói một cách khác, interrupt mềm là một interrupt được chương trình sử dụng chủ động gọi đến
Lẽ dĩ nhiên, một chương trình cũng có thể cố ý gọi đến một interrupt cứng Nhưng hành động ấy sẽ chẳng làm interrupt ấy trở thành “mềm” ! Thông thường việc gọi thẳng đến một interrupt cứng từ chương trình sẽ tạo ra các hậu quả không tốt đẹp gì cho lắm : đoạn chương trình của interrupt cứ đinh ninh rằng một thiết bị ngoại vi, hay một tác nhân ngoài nào đó đang chờ đợi nó trả lời !
Số lượng interrupt cứng rất ít so với số các phần tử của bảng ngắt,do đó hầu hết các interrupt còn lại đều được sử dụng như các interrupt mềm
Các tác vụ điều khiển các thiết bị ngoại vi như màn hình, bàn phím, máy in,đĩa cứng,đĩa mềm cần phải có các procedure phức tạp đảm nhận.Các procedure này một phần được cung cấp bởi ROM BIOS,một phần thuộc về hệ điều hành
Chương trình của chúng ta khi có yêu cầu sử dụng đến các thiết bị ngoại vi chỉ cần gọi đến procedure tương ứng Sử dụng lệnh CALL để truy nhập đến các procedure thuộc về ROM BIOS hay hệ điều hành đòi hỏi địa chỉ của các thủ tục ấy phải cố định.Yêu cầu ấy hầu như không thể thực hiện được bởi vì với mỗi version mới của ROM BIOS hay hệ điều hành, ta lại có một bộ các địa chỉ mới ! Và như vậy các chương trình viết theo cách này chỉ có thể chạy được với một ROM BIOS và một hệ điều hành xác định Để giải quyết vấn đề trên, ta có thể viết các procedure ấy dưới dạng các interrupt và để ROM BIOS hay hệ điều hành tự cài đặt địa chỉ của chúng vào bảng ngắt theo từng số hiệu đã quy định trước Khi cần sử dụng đến các procedure ấy, chương trình của chúng ta chỉ cần thực hiện chỉ thị INT tương ứng
Trang 22Các interrupt theo nghĩa nêu trên chính là các interrupt mềm Thông thường một interrupt mềm được gọi kèm theo một số tham số, các tham số ấy được đặt trong các thanh ghi theo một quy tắc đã định trước
Mỗi interrupt có thể cung cấp nhiều chức năng khác nhau, các chức năng này được gọi là các service hoặc các function Chức năng được chỉ định bằng số hiệu đặt trong AH khi thực hiện lệnh INT
5.3.3Các interrupt trên IBM AT ( Theo Norton Utilities )
Int-no Interrup Name Int-no Interrup Name
09 Keyboard < > IRQ 1 Keyboard
0A Reserved < > IRQ 2 [Cascade]
0B Reserved < > IRQ 3 COM2
0C Reserved < > IRQ 4 COM1
23 Ctrl-Break Handler Address
24 Critical Error Handler
25 Absolute Disk Read
26 Absolute Disk Write
27 Terminate and Stay Resident
33 Microsoft Mouse Driver 34-3E Reserved for DOS 3F Overlay Manager
40 Diskette BIOS Revector
41 Fixed Disk Parameters
42 Relocated Video Handler
67 Expanded Memory 68-6F Reserved
70 Real-Time Clock 71-74 Reserved
75 Redirect to NMI Interrupt 76-79 Reserved
7A Novell Netware (API) 7B-7F Reserved 80-85 Reserved for BASIC 86-F0 Reserved for BASIC interpreter F1-FF Reserved for User Programs
Trang 235.3.4Lấy địa chỉ một interrupt
Truy nhập trực tiếp bảng ngắt :
xor ax,ax mov es,ax mov ax,word ptr es:[Int-no*4]
mov Offs-Int,ax mov ax,word ptr es:[Int-no*4+2]
mov Seg-Int,ax
Offs-Int dw 0 Seg-Int dw 0
Dùng chức năng 035h của interrupt 021h
mov ah,035h ; AH = 035h : get int-addr mov al,Int-no ; AL = Int-no
int 021h ; Return : mov Offs-Int,bx ; BX = Offset mov Seg-Int,es ; ES = Segment
5.3.5Thay địa chỉ một interrupt
Truy nhập trực tiếp bảng ngắt :
CLI ; Cấm interrupt xor ax,ax
mov es,ax mov word ptr es:[Int-no*4],Offset-New-Int mov word ptr es:[Int-no*4+2],Segment-new-Int STI ; Cho phép interrupt
Dùng chức năng 025h của interrupt 021h
mov ah,025h ; AH = 025h : set int-addr mov al,Int-no ; AL = Int-no
mov dx,Offset-new-Int ;
; DS:DX = New-Int address int 021h
5.3.6Thay thế một interrupt
Một vài chương trình trong quá trình thực hiện đã thay thế một số interrupt của hệ thống bằng các đoạn chương trình của mình Nếu interrupt này không thuộc về ROM BIOS lẫn hệ điều hành thì đây chỉ là một cách cài đặt các thủ tục dưới dạng interrupt.Khi cần gọi đến thủ tục nào chỉ cần gọi interrupt tương ứng
5.3.7“Xỏ mũi” interrupt ( Hook interrupt vector )
Nếu interrupt bị thay thế thuộc về ROM BIOS hay hệ điều hành, thông thường đoạn chương trình thay thế phải đảm nhiệm tất cả các khả năng của interrupt cũ : phải viết lại những gì đã có Trong trường hợp này người ta thường chọn một giải pháp khác : mỗi khi interrupt được phát động chương trình thay thế sẽ được chuyển quyền, interrupt cũ sẽ được gọi thực hiện từ chương trình thay thế nếu có yêu cầu Giải pháp này cho phép chúng ta bổ sung thêm một số chức năng, hay tạo một số hiệu ứng phụ cho một interrupt mà vẫn giữ được tất cả các khả năng của nó
Trang 24Nguyên tắc này có thể được thực hiện như sau :
Lấy địa chỉ interrupt cất vào Old-Int
mov ah,035h mov al,Int-no int 021h mov word ptr Old-Int[0],bx mov word ptr Old-Int[2],es
Old-Int dd 0
Thay địa chỉ interrupt bằng địa chỉ của New-Int
mov ah,025h mov al,Int-no mov dx,offset New-Int int 021h
Khi New-Int được gọi :
New-Int sẽ làm một số tác vụ cần thiết như kiểm tra,thay đổi tham số rồi chuyển quyền cho Int bằng cách nhảy thẳng đến đầu vào của Old-Int
Old-New-Int proc near
; Thực hiện các tác vụ cần thiết jmp dword ptr cs:Old-Int New-Int endp
Hoặc Old-Int sẽ được gọi như một thủ tục, khi quyền điều khiển được trả về, New-Int bắt đầu kiểm tra và biến đổi kết quả trả về từ Old-Int Cuối cùng kết thúc bằng IRET
New-Int proc near pushf
call dword ptr cs:Old-Int
; Thực hiện các tác vụ cần thiết iret
New-Int endp
5.4M ỘT SỐ CHỨC NĂNG CỦA INTERRUPT 021 H
Interrupt 021h trên IBM PC được dành riêng cho hệ điều hành DOS Hầu hết các chức năng của DOS được cung cấp qua interrupt này Trong các thí dụ kế tiếp chúng ta sẽ sử dụng đến một số các chức năng căn bản sau :
5.4.1Read keyboard and echo
Input : AH = 1 Output : AL = ASCII code Chức năng : đọc một ký tự từ bàn phím, nếu chưa có thì chờ cho đến khi một phím được gõ Phím được đọc cũng đồng thời được in ra màn hình tại vị trí con trỏ hiện thời
5.4.2Print character
Input : AH = 2
DL = ASCII code
Trang 25Output : None Chức năng : in một ký tự ra màn hình tại vị trí con trỏ hiện thời Các ký tự điều khiển sẽ ảnh hưởng lên chức năng này
Một số ký tự điều khiển trên IBM PC :
7 : Bell, có tác dụng làm loa beep một tiếng
8 : Backspace, có tác dụng kéo lùi con trỏ sang trái một ký tự
10 : Line feed (LF), có tác dụng dời con trỏ xuống hàng kế tiếp
13 : Carry return (CR), có tác dụng đưa con trỏ về đầu dòng hiện tại
5.4.3Print string
Input : AH = 9 DS:DX = string terminated by ‘$’
Output : None Chức năng : in một chuỗi ra màn hình bắt đầu từ vị trí con trỏ hiện thời Chuỗi được quy định kết thúc bởi ký tự ‘$’ Trong chuỗi chấp nhận các ký tự điều khiển
Trang 26CHƯƠNG 6 : MỘT SỐ CẤU TRÚC VÀ GIẢI THUẬT CĂN BẢN
6.1C ẤU TRÚC IF
6.1.1IF Biến-A <= 5 THEN Biến-B:=1 ELSE Biến-B:=0
cmp Biến-A,5 ; So sánh Biến-A với 5
ja L1 mov Biến-B,1 ; Nhỏ hơn hay bằng jmp L2
L1:
mov Biến-B,0 ; Lớn hơn L2:
6.1.2IF Biến-A <= 5 THEN ELSE Biến-B:=0
cmp Biến-A,5 ; So sánh Biến-A với 5 jbe L1
jmp L2 L1: ; Nhỏ hơn hay bằng ;
; Kích thước đoạn mã này lớn hơn ; 128 byte
jmp L3 L2: ; Lớn hơn mov Biến-B,0 L3:
6.2C ẤU TRÚC C ASE
6.2.1Case Biến-A of 0 : Loại-A:=Loại-A+1;
inc Loại-A ; Loại-A:=Loại-A+1 jmp L4
L1:
cmp Biến-A,1 jne L2
inc Loại-B ; Loại-B:=Loại-B+1 jmp L4
L2:
Trang 27cmp Biến-A,2 jne L3
inc Loại-C ; Loại-C:=Loại-C+1 jmp L4
L3:
cmp Biến-A,3 jne L4
inc Loại-D ; Loại-D:=Loại-D+1 jmp L4
L4: ; End-case
6.2.2Case Biến-A of ‘0’ ’9’ : Loại:=0;
‘A’ ’Z’ : Loại:=1;
end;
cmp Biến-A,’0’
jae L1 jmp L2 L1: ; Biến-A >=’0’
cmp Biến-A,’9’
ja L2 Mov Loại,0 ; Biến-A <=’9’
jmp L4 L2:
cmp Biến-A,’A’
jae L3 jmp L4 L3: ; Biến-A >=’A’
cmp Biến-A,’Z’
ja L4 Mov Loại,0 ; Biến-A <=’Z’
L4:
Chú ý : Để kiểm tra xem một giá trị có thuộc một khoảng cho trước hay không ta có thể dùng giải
thuật sau :
Khoảng cho trước : M1 M2
Giá trị cần kiểm tra : X đặt trong AL ( hoặc AX )
sub AL,M2+1 ; Kết quả âm nếu X <=M2 (1) add AL,(M2-M1)+1 ; Kết quả âm nếu X < M1 (2) jnc Không-thuộc-về ; tương đương với JAE
jc Thuộc-về ; tương đương với JB
Để chứng minh, hãy xem ảnh hưởng của các phép tính số học lên các cờ hiệu và tác dụng của cờ hiệu lên các lệnh chuyển điều khiển có điều kiện
Nếu X thuộc về M1 M2 thì phép tính (1) phải cho kết quả âm (2) phải cho kết quả dương Kết quả cộng hai phần tử, trong đó có một phần tử âm tạo thành một kết quả dương sẽ có bit tràn > CF = 1
Chỉ thị JB được thực hiện nếu CF = 1
Chỉ thị JAE được thực hiện nếu CF = 0
Trang 286.2.3Chuyển điều khiển theo nhiều giá trị và cự ly chuyển điều khiển lớn hơn 128
cmp ah,0
je L1 cmp ah,1
je L2 cmp ah,2
je L3 cmp ah,010h
je L4 cmp ah,011h
je L5 cmp ah,012h
je L6 jmp End-case
L1: jmp Case-0 L2: jmp Case-1 L3: jmp Case-2 L4: jmp Case-10 L5: jmp Case-11 L6: jmp Case-12
Case-0:
jmp End-case Case-1:
jmp End-case Case-2:
jmp End-case Case-10:
jmp End-case Case-11:
jmp End-case Case-12:
End-case:
Trang 29
6.3C ẤU TRÚC FOR
6.3.1FOR Biến-A:=0 TO 100 DO
mov Biến-A,0 L1:
inc Biến-A cmp Biến-A,100
ja L2 jmp L1 L2:
6.3.2Chỉ thị LOOP <nhãn>
Chỉ thị LOOP <nhãn> thực hiện như sau : trừ cx 1,kiểm tra cx đã bằng 0 chưa, nếu chưa sẽ chuyển quyền điều khiển đến <nhãn> Nếu cx = 0 thì tiếp tục làm các lệnh kế tiếp Dùng chỉ thị LOOP để điều khiển các vòng lặp làm chương trình của chúng ta trở nên ngắn gọn và thực hiện nhanh hơn Trong khi dùng chỉ thị LOOP để điều khiển vòng lặp hãy cẩn thận khi làm thay đổi giá trị của CX
Thí dụ :
mov cx,10 ; Lặp 10 lần L1:
push cx ; Bảo toàn CX
mov ah,2 ; Chức năng 02h interrupt 021h mov dl,7 ; In mã số 7 : beep một tiếng int 021h
pop cx ; Phục hồi CX loop L1
Chú ý :
Khởi đầu quá trình LOOP nếu CX=0 thì vòng lặp sẽ thực hiện 65536 lần trước khi kết thúc ! Để kiểm tra CX có bằng 0 hay không, ta có thể dùng chỉ thị JCXZ <nhãn> Đây là một lệnh chuyển điều khiển có điều kiện.Chuyển điều khiển được thực hiện nếu CX=0
Mỗi lần “loop” CX bị giảm 1 nhưng đây là phép toán nội của 8088, thanh ghi cờ hiệu hoàn toàn không bị ảnh hưởng Do đó khi kết thúc quá trình lặp mặc dù CX luôn bằng 0 nhưng hiện tượng này không được ghi nhận bởi cờ hiệu ZF
6.4M ỘT SỐ KỸ THUẬT CĂN BẢN
6.4.1Xóa/Dựng/Đảo một bit
Xóa bit 2 của AL
Trang 306.4.2Kiểm tra một bit
test al,8 ; kiểm tra bit 3 của AL
jz L2 L1: ; Bằng 1
jmp L3 L2: ; Bằng 0
jmp L3 L2: ; Có ít nhất 1 bit bằng 0
L3:
6.4.4Kỹ thuật nhân và chia trên số nhị phân
Tác vụ nhân và chia thường chiếm nhiều thời gian thực hiện hơn các tác vụ cộng trừ, shift Do đó các chương trình assembler thường cố gắng chuyển các tác vụ nhân chia thành những đoạn chương trình chỉ gồm các tác vụ cộng trừ và shift, rol
Ta có các số tính chất sau :
- Shift left 1 bit trên một giá trị nhị phân cho kết quả tương tự nhân giá trị nhị phân đó cho 2
- Shift right 1 bit trên một giá trị nhị phân cho kết quả tương tự chia giá trị nhị phân đó cho 2
Thí dụ 1 :
Nhân một giá trị với 10 Dùng quy tắc A*10 = A*8+A*2
mov ax,giá-trị-đầu ; shl ax,1 ; AX = giá-trị-đầu*2 mov bx,ax ; BX = giá-trị-đầu*2 shl ax,1 ; AX = giá-trị-đầu*2*2 shl ax,1 ; AX = giá-trị-đầu*2*2*2 add ax,bx ; AX = giá-trị-đầu*(2*2*2+2)
; AX = giá-trị-đầu*10
Thí dụ 2 : Nhân một giá trị với 14 Dùng quy tắc A*14 = A*16-A*2
mov ax,giá-trị-đầu ; shl ax,1 ; AX = giá-trị-đầu*2 mov bx,ax ; BX = giá-trị-đầu*2 shl ax,1 ; AX = giá-trị-đầu*2*2 shl ax,1 ; AX = giá-trị-đầu*2*2*2
Trang 31shl ax,1 ; AX = giá-trị-đầu*2*2*2*2 add ax,bx ; AX = giá-trị-đầu*(2*2*2*2-2)
; AX = giá-trị-đầu*14
6.5M ỘT SỐ GIẢI THUẬT CHUYỂN ĐỔI
6.5.1Tạo chuỗi số dec từ một giá trị nhị phân 2 byte (Không định dạng)
Num-to-Dec proc near ;
; Input : ; AX = giá trị cần chuyển đổi ( 2 byte ) ; DS:SI = Buffer (Kích thước 5 byte,xóa trống bằng 020h)
; Output :
; Dec-string được đặt vào buffer và canh theo lề phải ;
push ax push bx push cx
mov bx,5 mov cl,10 NTD1:
div cl ; add ah,’0’ ; Chuyển số dư thành ký số ; Chú ý : ‘0’ ’9’ là các mã liên tục mov byte ptr [si+bx],ah
dec bx ; bx = bx-1 xor ah,ah ; Chia al cho 10,số dư trong ah cmp al,0
jne NTD1 ; Nếu AL # 0 thì lặp lại pop cx
pop bx pop ax ret Num-to-Dec endp
6.5.2Tạo chuỗi số dạng dec từ một giá trị nhị phân 4 byte (Có định dạng)
Num-to-Dec proc near ;
; Input : ; DX,AX = giá trị cần chuyển đổi ( 4 byte ) ; DS:SI = Buffer ( Không cần phải xóa trống trước ) ; CX = Kích thước chuỗi số ( format/picture )
; Cho CX = 0 sẽ tạo ra lỗi
; Output :
; Dec-string được đặt vào buffer và canh theo lề phải ;
Trang 32push ax push bx push cx push dx push si push di mov di,10 add si,cx NTD1:
mov bx,ax ; BX = 2 byte thấp mov ax,dx ; Chia 2 byte cao cho 10 xor dx,dx
div di xchg ax,bx ; (số-dư*65536 + 2 byte thấp)/10 div di
add dl,’0’ ; Số dư chính là ký số cần tìm dec cx
jcxz NTD3 NTD2:
mov byte ptr ds:[si],dl dec si
mov dx,bx
or dx,ax jnz NTD1 mov dl,’ ‘ loop NTD2 NTD3:
pop di pop si pop dx pop cx pop bx pop ax ret Num-to-Dec endp
6.5.3Tạo chuỗi số dạng dec từ một giá trị nhị phân 4 byte (Có định dạng)
NUM-TO-DEC proc near ;
; Input : ; ; DX,AX = Giá trị cần chuyển đổi ; (DX : 2 byte cao, AX : 2 byte thấp) ; ES:DI = Địa chỉ của buffer
; CH = Kích thước chuỗi số ( format/picture ) ;
; Output :
Trang 33; Dec-string được đặt vào buffer và canh theo lề phải
cld
mov bx,ax ; DX,BX chứa giá trị cần đổi
mov al,’ ‘
mov cl,9 ; Chuỗi số có chiều dài tối
mov si,offset NTD-table ; đa 9 ký số
NTD1:
cmp dx,word ptr cs:[si+2] ; So sánh DX,BX với dword
jb NTD2 ; đang được DS:SI chỉ đến
ja NTD3 ; ( Bắt đầu từ hàng tỉ )
cmp bx,word ptr cs:[si] ; So sánh 2 byte cao trước
jae NTD3 ; 2 byte thấp sau
jnc NTD5 ; Kết quả vẫn chưa âm
add bx,word ptr cs:[si] ; Nếu kết quả âm, trả lại
adc dx,word ptr cs:[si+2] ; kết quả dương nhỏ nhất
; > chuyển xuống hàng
; kế tiếp
stosb ; AL = ký số hàng đang xét
dec cl ; CL = hàng đang xét
add si,4 ; Dời đến hàng kế tiếp
Trang 34dd 10000000 ; hàng chục triệu
dd 1000000 ; hàng triệu
dd 100000 ; hàng trăm ngàn
dd 10000 ; hàng chục ngàn
Thí dụ : giá trị cần chuyển đổi là 170900 thì giá trị tìm thấy là 100000 (tiêu biểu cho hàng trăm ngàn)
Lấy giá trị cần chuyển đổi trừ cho giá trị tìm thấy và lặp lại cho đến khi hiệu số nhỏ hơn số trừ Số lần lặp chính là giá trị của ký số tương ứng
Trong thí dụ trên, số lần lặp là 1, hiệu số là 70900
Cho số trừ là phần tử kế tiếp trong bảng, lặp lại quá trình trên cho đến khi dò hết bảng
Giải thuật của 6.5.3 chỉ dùng các tác vụ cộng trừ và có thể mở rộng cho các số có kích thước tùy ý
6.5.4Tạo chuỗi số dạng hex từ một giá trị nhị phân 1 byte
Num-To-Hex proc near ;
; Input : ; AL = giá trị cần chuyển đổi ( 1 byte ) ; DS:SI = Buffer
; Output :
; Hex-string được đặt vào buffer ( 2 ký số hex ) ;
push ax push cx mov ah,al ; AH = AL mov cl,4
shl ah,4 ; Xóa 4 bit cao (nibble cao) shr ah,4 ; AH = nibble thấp
shr al,4 ; AL = nibble cao
add ax,03030h ; AH = AH+’0’, AL = AL +’0’
cmp ah,’9’
jbe NTH1 add ah,’A’-‘9’-1 NTH1:
Trang 35cmp al,’9’ ; Nếu AL >’9’ > ‘A’ ’F’
jbe NTH2 add al,’A’-‘9’-1 ; Cộng thêm khoảng cách từ ‘A’
NTH2: ; đến ‘9’ trừ 1 mov word ptr [si],ax ; ký số tương ứng với nibble pop cx ; cao sẽ đặt trước
pop ax ret Num-To-Hex endp
6.5.5Tạo chuỗi số dạng bin từ một giá trị nhị phân 1 byte
Num-to-Bin proc near ;
; Input : ; AL = giá trị cần chuyển đổi ( 1 byte ) ; DS:SI = Buffer ( 8 byte )
; Output :
; Bin-string được đặt vào buffer ( 8 ký số bin ) ;
push ax push bx xor bx,bx ; BX = 0 NTB1:
pop bx pop ax ret Num-to-Bin endp
6.5.6Đổi chuỗi số dec thành giá trị nhị phân 4 byte
DTN proc near
; Input : ; DS:SI = Buffer chứa num-str dạng decimal ; CL = chiều dài của num-str
; Output:
; CF=1 > lỗi chuyển đổi ; CF=0 > DX:AX = giá trị nhị phân tương ứng push bx
push cx
Trang 36cmp byte ptr [si],’ ‘ ; Bỏ qua các khoảng trống
jne DTN1 ; bên trái chuỗi số
pop bx ; cộng dồn
add ax,bx ; a*2 + a*8 = a*10
DTN3: ; Nếu gặp lỗi, CF=1, kết quả
stc ; chuyển đổi dở dang trong
DTN4: ; DX|AX
pop si
pop cx
pop bx
Trang 37ret DTN endp
6.5.7Đổi chuỗi số hex thành giá trị nhị phân 4 byte
HTN proc near
; Input : ; DS:SI = Buffer chứa num-str dạng hexa ; CL = chiều dài của num-str
; Output:
; CF=1 > lỗi chuyển đổi ; CF=0 > DX:AX = giá trị nhị phân tương ứng ;
push bx push cx push si
xor ch,ch xor bx,bx ; DX|BX chứa kết quả trung xor dx,dx ; gian
HTN1:
mov al,byte ptr [si]
inc si sub al,’0’
jc HTN4 ; AL < ‘0’ > lỗi cmp al,9
jb HTN2 sub al,’A’-‘9’-1 cmp ah,al
jc HTN4 ; AL > ‘F’ > lỗi HTN2:
push cx mov cl,4 HTN3:
shl bx,1 ; Nhân DX|BX với 16 rcl dx,1 ;
loop HTN3 ;
or bl,al ; Cộng dồn giá trị mới pop cx
Trang 38loop HTN1 ; Cho đến khi hết chuỗi số HTN4:
mov ax,bx pop si pop cx pop bx ret HTN endp
6.5.8Đổi chuỗi số bin thành giá trị nhị phân 4 byte
BTN proc near
; Input : ; DS:SI = Buffer chứa num-str dạng binary ; CL = chiều dài của num-str
; Output:
; CF=1 > lỗi chuyển đổi ; CF=0 > DX:AX = giá trị nhị phân tương ứng ;
push bx push cx push si
xor ch,ch xor bx,bx xor dx,dx
BTN0:
cmp byte ptr [si],’ ‘ ; Bỏ qua các khoảng trống jne BTN1 ; bên trái chuỗi số
inc si loop BTN0 jmp BTN2 ; Chuỗi số “rỗng”
BTN1:
mov al,byte ptr [si]
inc si sub al,’0’
jc BTN2 ; AL < 0 > lỗi cmp al,1
ja BTN2 ; AL > 1 > lỗi
shr al,1 ; Bit giá trị đặt trong CF rcl bx,1 ;
rcl dx,1 ; Chuyển vào DX|BX loop BTN1
BTN2:
Trang 40CHƯƠNG 7 : CÁC TÁC VỤ VỀ CHUỖI
7.1LODSB/LODSW
Tác dụng : nạp vào AL (đối với LODSB) hay AX (đối với LODSW) giá trị tại địa chỉ xác định bởi DS:SI Sau đó:
Nếu DF = 0 tăng SI 1 đơn vị đối với LODSB, 2 đơn vị đối với LODSW
Nếu DF = 1 giảm SI 1 đơn vị đối với LODSB, 2 đơn vị đối với LODSW
7.2STOSB/STOSW
Tác dụng : lấy giá trị của AL (đối với LODSB) hay AX (đối với LODSW) đặt vào byte hay word tại địa chỉ xác định bởi ES:DI Sau đó
Nếu DF = 0 tăng DI 1 đơn vị đối với STOSB, 2 đơn vị đối với STOSW
Nếu DF = 1 giảm DI 1 đơn vị đối với STOSB, 2 đơn vị đối với STOSW
7.3SCASB/SCASW
Tác dụng : So sánh giá trị của AL (đối với SCASB) hay AX (đối với SCASW) với byte hay word tại địa chỉ xác định bởi ES:DI Sau đó
Nếu DF = 0 tăng DI 1 đơn vị đối với SCASB, 2 đơn vị đối với SCASW
Nếu DF = 1 giảm DI 1 đơn vị đối với SCASB, 2 đơn vị đối với SCASW
7.4CMPSB/CMPSW
Tác dụng : so sánh 1 byte (đối với CMPSB) hay 1 word (đối với CMPSW) tại DS:SI với giá trị tương ứng tại ES:DI Sau đó
Nếu DF = 0 tăng SI và DI 1 đối với CMPSB, 2 đối với CMPSW
Nếu DF = 1 giảm SI và DI 1 đối với CMPSB, 2 đối với CMPSW
7.5MOVSB/MOVSW
Tác dụng : lấy 1 byte (đối với MOVSB) hay 1 word (đối với MOVSW) tại địa chỉ xác định bởi DS:SI đặt vào địa chỉ xác định bởi ES:DI Sau đó
Nếu DF = 0 tăng SI và DI 1 đối với MOVSB, 2 đối với MOVSW
Nếu DF = 1 giảm SI và DI 1 đối với MOVSB, 2 đối với MOVSW
Để thay đổi cờ hiệu DF bộ vi xử lý 8088/8086 có các lệnh sau :
- STD : dựng cờ hiệu DF -> DF = 1
- CLD : xóa cờ hiệu DF -> DF = 0
7.6T IỀN TỐ R EPEAT (REP)
Các tác vụ về chuỗi thường được sử dụng trên nhiều byte/word liên tiếp Do đó cần một vòng lặp để thực hiện nhiều lần một tác vụ về chuỗi
Thí dụ :
mov al,020h ; dùng mã ASCII 020h (khoảng trống) mov cx,30 ; để xóa trống biến buffer1 có chiều lea di,Buffer1 ; dài 30 byte
cld L1: ; stosb ; Có thể thay đoạn chương trình lặp loop L1 ; này bằng chỉ thị