Tài liệu Ngôn ngữ lập trình Assembly: Bài 1 : Mở đầu
I. Giới thiệu về Assembly Language (Hợp ngữ)
1. Giới thiệu về Assembly Language (Hợp ngữ)
Việc lập trình bằng ngôn ngữ máy đòi hỏi người lập trình cần phải nhớ các mã lệnh bằng số, phải sắp đặt vị trí của mã lệnh và tất cả các số liệu trong bộ nhớ của máy tính, ngay cả các số liệu cũng phải viết dưới dạng số. Công việc này hết sức nặng nhọc và rất dễ gây nhầm lẫn.
Chính vì vậy người ta cần đến Assembly Language, nó cho NSD các khả năng sau:
Cho phép dùng các ký hiệu gợi nhớ thay cho các mã lệnh bằng số của bộ VXL, ví dụ ADD thay cho 00000000; INT 13h thay cho 11001101 00010011 .v.v... các ký hiệu loại này được gọi là mã lệnh (Op-Code).
Cho phép dùng các tên gọi (nhãn: Label) để chỉ các địa chỉ nhớ, NSD gọi đến các tên này như việc gọi các thủ tục hoặc như là việc truy nhập đến các biến hay hằng số trong các ngôn ngữ lập trình bậc cao.
Cho phép dùng các chỉ thị cho chính Assembler (Assembler Directive), để nó biết cần phải chuyển các mã của NSD như thế nào,...
59 trang |
Chia sẻ: hunglv | Lượt xem: 3073 | Lượt tải: 3
Bạn đang xem trước 20 trang mẫu tài liệu Ngôn ngữ lập trình Assembly, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
Bài 1 : Mở đầu
I. Giới thiệu về Assembly Language (Hợp ngữ)
1. Giới thiệu về Assembly Language (Hợp ngữ)
Việc lập trình bằng ngôn ngữ máy đòi hỏi người lập trình cần phải nhớ các mã lệnh bằng số, phải sắp đặt vị trí của mã lệnh và tất cả các số liệu trong bộ nhớ của máy tính, ngay cả các số liệu cũng phải viết dưới dạng số. Công việc này hết sức nặng nhọc và rất dễ gây nhầm lẫn.
Chính vì vậy người ta cần đến Assembly Language, nó cho NSD các khả năng sau:
Cho phép dùng các ký hiệu gợi nhớ thay cho các mã lệnh bằng số của bộ VXL, ví dụ ADD thay cho 00000000; INT 13h thay cho 11001101 00010011 .v.v... các ký hiệu loại này được gọi là mã lệnh (Op-Code).
Cho phép dùng các tên gọi (nhãn: Label) để chỉ các địa chỉ nhớ, NSD gọi đến các tên này như việc gọi các thủ tục hoặc như là việc truy nhập đến các biến hay hằng số trong các ngôn ngữ lập trình bậc cao.
Cho phép dùng các chỉ thị cho chính Assembler (Assembler Directive), để nó biết cần phải chuyển các mã của NSD như thế nào, chương trình bắt đầu ở đâu; dự trữ khoảng trống bao nhiêu cho dữ liệu; báo rằng không còn lệnh để chuyển mã ngữ tiếp nữa .v.v. Ví dụ ORG 100h: đoạn mã lệnh bắt đầu ở địa chỉ 100h,Code_seg: segment: tên Code_seg là đại diện cho 1 segment trong bộ nhớ
end: hết mã ngữ.
Cho phép viết các chú thích trong chương trình, làm cho chương trình dễ đọc, dễ bảo trì.
Khi hợp dịch, các mã lệnh được chuyển trực tiếp thành các lệnh của bộ vxl để nó có thể thực hiện trực tiếp được. Các nhãn, các chỉ thị cho Assembler chỉ được chính Assembler sử dụng, không chuyển thành mã máy; còn các chú thích (comment) thì bị bỏ qua. Chính vì vậy mà các chương trình viết bằng Assembly rất ngắn gọn, chạy rất nhanh.
Chương trình được viết bằng các ký hiệu của ngôn ngữ Assembly được gọi là chương trình nguồn (Source Program). Chương trình ở dạng số tương tương với chương trình nguồn mà bộ VXL có thể hiểu được (microprocessor-compatible form) gọi là chương trình đích(Object Program). Assembler là chương trình thực hiện chuyển (convert) chương trình nguồn(Source Program) thành chương trình đích (Object Program).
2. Một số vấn đề về hệ đếm 2, số có dấu, số bù 2
2.1 Hệ đếm 2
Như ta đã biết, các phần tử nhớ cơ bản cấu tạo nên bộ nhớ cũng như các phần tử trong khối tính toán của máy tính đều là các phần tử có 2 trạng thái cân bằng ổn định 'on' và 'off' (ta có thể dùng để biểu diễn số 1 và 0) giống như trạng thái đóng mạch và cắt mạch của một cái công tắc điện. Tất cả chương trình và số liệu mà chương trình sử dụng đều đặt trong bộ nhớ trước khi được bộ VXL đọc. Như vậy sự kết hợp của một dãy các số 0 và 1 có thể biểu diễn bất cứ con số nào. Hệ đếm chỉ sử dụng các số 0 và 1 được gọi là hệ nhị phân (Binary numbering system) hay hệ đếm 2 , đó là hệ đếm cơ sở cho mọi loại MTĐT ngày nay.
Một phần tử nhớ chỉ có 1 trong hai giá trị 0, 1 được gọi là 1 bit, đó là chữ viết tắt của Binary digit. Cũng tương tự như trong hệ thập phân, trong một số nhị phân giá trị của mỗi bit trong con số phụ thuộc vào vị trí của nó trong con số đó như được thể hiện sau đây:
7 6 5 4 3 2 1 0 vị trí bit trong con số
x x x x x x x x
27 26 25 24 23 22 21 20 trọng số của bit
128 64 32 16 8 4 2 1 giá trị tương ứng trong hệ thập phân
Để chuyển một số từ hệ thập phân sang hệ nhị phân ta thực hiện một loạt các phép MOD 2 đối với số đó cho tới khi số thập phân bằng 0:
Giá trị của phép tính MOD 2 đầu tiên là giá trị của bit 0
Giá trị của phép tính MOD 2 tiếp theo là giá trị của bit 1.v.v...
Trong các lĩnh vực tin học, thường sử dụng một đơn vị lớn hơn, đó là byte, 1 byte gồm 8 bit, như vậy 1 byte có thể biểu diễn một con số có giá trị từ 00000000 đến 11111111 (0 - 255 trong hệ thập phân).
Ngoài ra còn có các đơn vị 1KB = 210 byte, 1MB = 220 byte ...
2.2 Số có dấu
Khi sử dụng 1 byte để biểu diễn số có dấu, người ta chỉ sử dụng 7 bit thấp để biểu diễn số, còn bit cao nhất (bit 7) để biểu diễn dấu của con số đó, nếu bit này = 0 thì con số là dương, nếu bit này = 1 thì con số là âm. Như vậy với 1 byte có thể biểu diễn các số từ -128 đến +127.
Theo quy ước này số -1 có giá trị như sau: 10000001 nếu ta đem cộng
với 1 thì kết quả lại = 10000010 = -2D! không như chúng ta chờ đợi là = 0; chính vì lý do này mà người ta phải dùng một dạng số đặc biệt - dạng bù 2 (two's complemented) để biểu diễn số âm.
Để tìm dạng biểu diễn nhị phân của một số âm (nghĩa là tìm dạng bù 2 của nó), ta chỉ việc lấy số dương tương ứng rồi đảo giá trị các bit, sau đó cộng 1 vào kết quả là xong. Thí dụ tìm biểu diễn nhị phân của số -1: 0000 0001 đảo các bit thành 1111 1110, đem cộng thêm 1 trở thành 1111 1111.Nếu ta đem số -1 này cộng với +1 thì kết quả là 1 0000 0000, ta lờ số nhớ (1) đi thì được số 0000 0000 như mong muốn!
đó là cách biểu diễn số âm trong máy tính!
Nhìn vào số âm dạng bù 2 ta khó mà hiểu được số thập phân (âm) tương ứng bằng bao nhiêu, nếu muốn tính, ta vẫn áp dụng quy tắc trên để tính được số dương tương ứng.Thí dụ muốn biết 1101 0000 có giá trị thập phân tương ứng bằng bao nhiêu?
Đảo các bit 0010 1111
Cộng thêm 1 = 0000 0001
Kết quả = 0011 0000 = 32 + 16 = +48 , số thập phân tương ứng là -48
2.3. Hệ đếm thập lục phân (hệ 16)
Biểu diễn số trong hệ cơ số 2 có nhiều điểm bất tiện cho người lập trình, vì vậy người ta tìm cách biểu diễn khác cho tiện lợi hơn, tuy vậy cần phải nhấn mạnh rằng bên trong máy tính tất cả lệnh và số liệu đều được biễu diễn dưới dạng nhị phân.
Ngay từ khi máy tính điện tử mới ra đời, người lập trình cũng đã thường thao tác trên từng nhóm bit chứ không phải trên từng bit. Các bộ VXL đầu tiên là loại 4 bit, vì thế người ta có thể nhóm 4 bit lại và biểu diễn bằng một ký hiệu duy nhất. Với 4 bit có thể biểu diễn 16 giá trị khác nhau, người ta dùng các ký hiệu 0..9, A, B, C, D, E, F để biểu diễn các giá trị nhị phân từ 0000 đến 1111. Hệ đếm như vậy được gọi là hệ thập lục phân (hệ đếm cơ số 16) - hexadecimal numbering system.
3. Một số vấn đề cần nhắc lại (sau giáo trình CTMT) về bộ VXL
Các Mode làm việc: Các bộ VXL 80286 (và các bộ VXL mới hơn của INTEL) có thể hoạt động trong 2 mode khác nhau: Real Address Mode và Protected Virtual Address Mode.
Real Address Mode : Trong Mode này 80286 hoạt động cũng giống như 8086, nhưng thực hiện các chương trình nhanh hơn từ 2 đến 5 lần, nó hỗ trợ tất cả các lệnh của 8086, thêm vào đó là các lệnh của riêng 80286. Khi chúng ta mới bật máy, 80286 bắt đầu ở Mode này, chỉ khi nào chương trình ra lệnh cho nó chuyển sang Mode kia (Protected Mode).
Protected Mode : Trong Mode này 80286 vẫn có mọi tính năng như ở Real Address Mode nhưng được thêm vào một số tính năng tinh vi để bảo vệ dữ liệu và quản lý bộ nhớ. Tính năng nổi bật nhất của Mode này là nó cho phép 80286 truy cập một bộ nhớ rất lớn (so với 8086) bằng cách sử dụng một kỹ thuật gọi là Virtual Addressing.Bằng kỹ thuật này 80286 có thể làm việc với 2 loại bộ nhớ: không gian địa chỉ vật lý (Physical address space) và không gian địa chỉ ảo (Virtual address space).
Không gian địa chỉ vật lý là miền bộ nhớ mà 80286 có thể làm việc với nó tại một thời điểm, bằng 224 = 16MB
Không gian địa chỉ ảo là miền bộ nhớ mà 80286 có thể đánh địa chỉ, bằng 230 = 1GB (1 Giga byte). Thuật ngữ ảo (Virtual) được sử dụng bởi vì nếu một chương trình cần truy cập bộ nhớ mà địa chỉ không thuộc không gian địa chỉ nhớ vật lý, hệ điều hành có thể sử dụng khả năng quản lý bộ nhớ của 80286 để chuyển đổi (swap) một phần thích hợp (applicable piece) của bộ nhớ ảo. Việc chuyển đổi bộ nhớ (swapping) này người lập trình không thấy được vì vậy anh ta có thể viết chương trình truy cập bất cứ địa chỉ nhớ nào trong toàn bộ không gian địa chỉ lớn đến 1GB. Virtual Disk từ Version 3.0 trở lên, DOS hỗ trợ việc sử dụng bộ nhớ trên 1MB, ta có thể sử dụng nó như 'Virtual Disk' (đĩa ảo hay RAM Disk). Để truy cập vùng nhớ cao này, bộ VXL tạm thời (momentarily) chuyển (switch) sang Protected Mode, thực hiện việc (vận) chuyển dữ liệu rồi lại chuyển trở về Real Mode như bình thường. Kết quả chung là ta có thể sử dụng một bộ nhớ RAM có kích thước lớn, tốc độ thao tác cực nhanh so với tốc độ thao tác trên đĩa. * Protected Mode được người thiết kế OS quan tâm, với người lập trình bình thường chỉ cần quan tâm tới Real Mode là đủ.
II Tập lệnh của bộ vi xử lý 8088/8086
1. Bảng lệnh 8088/8086 (xem tài liệu chụp)
2. Phân loại các lệnh
Căn cứ vào công việc mà lệnh thực hiện, có thể chia làm 5 nhóm
lệnh như sau:
1/ Các lệnh số học:
ADD dest,src cộng 2 toán hạng, đặt kết quả trong dest
dest:= dest + src
ADC dest, src cộng 2 toán hạng, cộng thêm số nhớ (Carry) từ lệnh ADD trước đó
dest:= dest + src +Cf
INC dest tăng destination lên 1 (dest có thể là thanh ghi hoặc địa chỉ)
dest:= dest + 1
DEC dest giảm destination bớt 1 (dest có thể là thanh ghi hoặc địa chỉ)
dest:= dest - 1
SUB dest,src lấy dest trừ đi src, để kết quả trong dest
dest:= dest - src
SBB dest,src lấy dest trừ đi src và trừ thêm số mượn từ lệnh SUB trước đó ,kết quả để trong dest
dest:= dest - src - CF
NEG dest đổi dấu của toán hạng
dest:= 0-dest
CMP dest, src so sánh (bằng cách trừ, nhưng không làm thay đổi dest và src) để đặt các cờ hiệu là nhớ, tràn, v.v...
MUL src nhân AL hay AX với một số dương (không có dấu) là src (src là reg/mem)
AX:= (AL*src8); DX:AX:=(AX*Immed16)
IMUL src nhân AL hay AX với một số nguyên có dấu
DIV src chia AL hoặc AX cho giá trị trong reg/mem không dấu nếu chia cho 0 hoặc kết quả quá lớn sẽ gây ra INT 0
AL < (AX*src8); AH < (AX MOD src8)
AX < (DX:AX*src16); DX < (DX:AX MOD src16)
IDIV src chia AL hoặc AX cho giá trị trong reg/mem có dấu CBW đổi byte thành Word
AL mở rộng theo dấu vào AH
AH được điền đầy theo bit 7 của AL
CWD đổi Word thành Word
AX mở rộng theo dấu vào DX
DX được điền đầy theo bit 15 của AX
.v.v...
2/ Các lệnh logic
AND dest,src : nhân logic từng bit trong dest và src
OR dest,src : OR logic từng bit trong dest và src
XOR dest,src : XOR logic từng bit trong dest và src
NOT dest : đảo từng bit trong dest (lấy bù 1)
.v.v...
3/ Các lệnh vận chuyển số liệu
MOV dest,src : copy dữ liệu từ src (reg/mem) tới dest. Lưu ý rằng không thể MOV từ ô nhớ (mem) vào ô nhớ
XCHG dest,src đổi (chỗ) giá trị giữa 2 thanh ghi hoặc giữa thanh ghi và bộ nhớ
IN Port8 (or DX) nhận dữ liệu từ cổng vào AL hay AX
byte : AL port
word : AL [port]
AH [port+1]
OUT Port8 (or DX) gửi dữ liệu từ AL hay AX ra cổng
byte : AL port
word : AL [port]
AH [port+1]
LEA reg16, mem1 nạp địa chỉ vào thanh ghi, mem1 phải là một nhãn. reg16 được nạp địa chỉ của mem1 chứ
không phải dữ liệu chứa trong mem1
( nạp kết quả việc tính toán EA của địa chỉ tương đối vào
reg16)
PUSH src cất giá trị thanh ghi 16 bit (hoặc r/mem16) vào stack PUSH immed cất giá trị 16 bit (hoặc 8 bit mở rộng dấu) vào stack
PUSHF cất giá trị của thanh ghi cờ vào stack
POP dest chuyển giá trị từ stack vào dest (reg16 hoặc r/mem
hoặc thanh ghi đoạn)
POPF chuyển giá trị từ stack vào thanh ghi cờ
.v.v...
4/ Các lệnh thao tác trên xâu ký tự
CLD Clear direction flag to UP (DF:=0)
string Ops auto-increment
STD set direction flag to Down (DF:=1)
string Ops auto-decrement
REP/REPE/REPZ
lặp lại thao tác xâu ký tự (CX:= CX-1) cho đến khi C=0
REPNE/REPNZ
lặp lại thao tác xâu ký tự ,sử dụng kết hợp với lệnh CMPS và
SCAS
.v.v...
5/ Các lệnh điều khiển sự thực hiên chương trình
JMP short/near/far target nhảy không điều kiện đến nhãn
short : IP (IP + (target displacement sign-extended))
báo cho assembler là nhãn đích của lệnh nhảy JMP không xa
hơn 127 bytes kể từ địa chỉ của lệnh tiếp theo.
near : IP (IP + (target displacement)
far : SS target_seg; IP target_offset
indirect : IP (register or value in memory)
JCXZ: nhảy tới nhãn khi CX=0, không kiểm tra cờ
LOOP short_label CX CX-1
JA/JNBE JAE/JNB
JB/JC JE/JZ
JG/JNGE JGE/JNL
.V.V...
INT TYPE thực hiện ngắt phần mềm (call a systen function)
INTO type: if OF=OV=1 then perform INTO type
IRET quay trở về từ lệnh gọi ngắt. Nó có ảnh hưởng như
lệnh POP IP; POP CS; POPF
6/ Các lệnh điều khiển bộ VXL
CLS xoá cờ CY thành NC (CF 0)
CMC lấy phần bù (đảo giá trị của) cờ Carry)
STC đặt cờ carry thành CY
CLD clear direction flag
(các lệnh với xâu ký tự tự động tăng)
CLI cấm các ngắt (có thể che)
STI bỏ mặt nạ ngắt (cho phép có thể ngắt)
HLT ngừng xử lý (thực hiện các lệnh NOP cho đến khi một
lời gọi ngắt xảy ra)
.v.v...
7/ Các lệnh điều khiển tính bảo vệ (80286/80386/80486)
(chưa đưa vào chương trình phần này) 3. Phân tích một số mã lệnh - Trong hệ lệnh của 8088/8086... có nhiều lệnh tính chất giống nhau,
chỉ khác nhau ở địa chỉ của các toán hạng. Chính vì vậy có những
lệnh chỉ cần 1 byte mã lệnh là đủ để bộ VXL thực hiện, lại có những
lệnh cần thêm một số thông tin nữa mới có thể thực hiện được, các thông tin này được chứa trong 1 byte, đi ngay sau byte mã lệnh thứ nhất. Việc xác định xem 1 lệnh nào đó có cần đọc thêm byte lệnh thứ 2
hay không được bộ giải mã lệnh quyết định. - Ta phân tích lệnh ADD làm ví dụ:
Có các khả năng sau đối với việc cộng 2 toán hạng
1/ r/m r8 : cộng nội dung thanh ghi 8 bit vào một thanh ghi
hay 1 ô nhớ
2/ r/m r16 : cộng nội dung một thanh ghi 16 bit và một thanh
ghi 16 bit hoặc vào (2) ô nhớ
3/ r8 r/m cộng nội dung từ thanh ghi (8 bit) hoặc từ 1 ô nhớ (tất nhiên là 8 bit) vào 1 thanh ghi 8 bit
4/ r16 r/m cộng nội dung một thanh ghi hoặc ô nhớ vào 1
thanh ghi 16 bit
5/ AL im8 cộng trực tiếp 1 giá trị 8 bit vào AL
6/ AX im16 cộng trực tiếp 1 giá trị 16 bit vào AX
7/ r im8 hoặc im16; trường hợp này r # AX
Cấu tạo của mã lệnh như sau:
1/ trường hợp 1 d: direction
reg/memory with reg to either = 1: 'to' reg
= 0 : 'from' reg
0 0 0 0 0 0 d w mod reg r/m w : word operation
= 1 : with word = 0: with byte
mã lệnh này chứa đựng các khả năng:
nêu ở trên
2/ trường hợp 2
Immediate to reg/mem (not to AX) s: sign
s= 1: signed value
s= 0: unsigned value
1 0 0 0 0 0 s w mod 0 0 0 r/m data data
byte 1 byte 2 nếu
s:w = 01
mã lệnh này chứa đựng các khả năng:
nêu ở trên
3/ trường hợp 3
Immediate to AX (Accumulator)
0 0 0 0 0 1 0 w data data
byte 1 byte 2 nếu
s:w = 01
mã lệnh này chứa đựng các khả năng:
nêu ở trên
III. Cấu tạo của lệnh, các Mode địa chỉ của Assembly Language
Trên thị trường có một số Assembler, trong tài liệu này chúng ta sử dụng
MACRO ASSEMBLER (MASM); về MASM sẽ được đề cập ở bài 2.
1. Cấu tạo của lệnh
nhãn Mã lệnh các toán hạng ghi chú
Data: MOV cx,ax ;nạp vào cx giá trị trong ax
- Trường nhãn (label field), Nhãn dài tối đa 31 ký tự. ấn định một tên đại diện cho 1 lệnh assembly, để cho các lệnh khác có thể dùng tên này thay cho địa chỉ của lệnh đó trong bộ nhớ.
Nhãn không được bắt đầu bằng 1 con số, không chứa dấu phân cách, nhãn kết thúc bằng dấu:; trong nhãn có thể có các ký tự '$', '?', '@','_'... - Mã lệnh (Op-code) dài từ 2-7 ký tự, là những chữ viết tắt gợi trí nhớ, để chỉ các lệnh của bộ vxl. Mã lệnh có thể đứng liền sa dấu ': ' của nhãn hoặc phân cách bởi 1 số dấu cách (space). Giữa mã lệnh và các toán hạng phải có một số dấu cách. - Các toán hạng (operands): báo cho MPU biết phải tìm các toán hạng ở đâu - các ghi chú (comments) dùng để diễn giải dòng lệnh trong chương trình, để đọc lại cho dễ hiểu hơn. Trong các chương trình dài các chú thích rất quan trọng và cần thiết.
ghi chú phải đi sau ký hiệu ';' và kết thúc bởi dấu xuống dòng (Enter)
2. Các Mode địa chỉ
- Phương pháp xác định vị trí trong bộ nhớ của của 1 toán hạng được gọi là Mode địa chỉ (Addressing Mode). Mode địa chỉ của một toán hạng phụ thuộc vào vị trí trong bộ nhớ của dữ liệu ứng với toán hạng đó. Có một số cách phân loại Mode địa chỉ khác nhau, dưới đây nêu ra hai trong các cách đó: - Cách thứ nhất: chia làm 3 loại Mode địa chỉ:
1. Mode địa chỉ tức thời (Immediate Addressing Mode): sử dụng chính
giá trị bằng số ở vị trí của toán hạng.
ví dụ mov ax,0001h
trong mode địa chỉ này phải có 1 vị trí nhớ hay thanh ghi làm
destination.(số chu kỳ máy cần thiết tương ứng là 10 và 4)
2. Mode địa chỉ thanh ghi (register addressing mode) cả dest và src
đều là thanh ghi. Vì cả hai đều ở bên trong MPU nên được thực hiện rất
nhanh.
ví dụ mov ax,cx cần 2 chu kỳ máy
3. Mode địa chỉ bộ nhớ (Memory Addressing Mode)
dest hoặc src là địa chỉ nhớ (hay nhãn cũng vậy)
có 2 loại mode địa chỉ bộ nhớ:
1. Địa chỉ trực tiếp (direct)
2. Địa chỉ gián tiếp (indirect)
-direct: dest hoặc src là một địa chỉ nhớ được xác định bằng số địa
chỉ hoặc nhãn.
ví dụ: mov ax, mem1 thì nội dung ô nhớ có nhãn mem1 được chuyển
vào ax.
-indirect: địa chỉ nhớ không được nêu ra trực tiếp. có một thanh ghi dùng để chứa địa chỉ của ô nhớ chứa dữ liệu. Như vậy thanh ghi có
tác dụng như là một địa chỉ gián tiếp để xác định vị trí dữ liệu.
ví dụ: mov [bx],cx
Có một số biến cách của mode gián tiếp này, tuy vậy ý nghĩa cơ bản
không khác nhau. - Cách thứ 2: chia làm 7 Mode địa chỉ
1. Register addressing địa chỉ thanh ghi
thí dụ MOV AX,BX
2. Immediate addressing địa chỉ tức thời
MOV CL,-30; MOV CL, PI (PI EQU 3.14)
3. Direct addressing địa chỉ trực tiếp
thí dụ MOV AX,TABLE (trong đó TABLE là một nhãn)
4. Register indirect addressing địa chỉ gián tiếp thanh ghi
thí dụ MOV AX,[BX]
5. Base relative addressing địa chỉ tương đối cơ sở
thí dụ MOV AX,[BX] + 4
6. Direct indexed addressing địa chỉ được chỉ số hoá trực tiếp
(MOV DI,2)
MOV AL, TABLE[DI] (trong đó TABLE là một tên của 1 byte (tên biến)
7. Base indexed addressing địa chỉ được chỉ số hoá cơ sở
MOV AX, [BX][DI+2] IV. Các chỉ thị cho Assembler (Assembler Directives)
(có khoảng 60)
Các chỉ thị Assembler trông rất giống các mã lệnh ngôn ngữ Assembly, mỗi chỉ thị gồm có 4 trường (Fields)
tên Chỉ thị đối số (argument) ghi chú
PI EQU 3.14 ;xác định giá trị của PI
- tên (name field) cũng giống như trường nhãn trong dòng lệnh
có chỉ thị assembler cần trường tên, có chỉ thị không cần, tên bắt đầu bằng chữ cái, kết thúc bằng khoảng trống. - Chỉ dẫn Assembler (directive field)
cũng tương tự như Op_code trong dòng lệnh.
bắt đầu bằng khoảng trống và kết thúc bằng khoảng trống hoặc xuống dòng - argument field: chứa 1 địa chỉ nhớ hoặc 1 số để sử dụng cùng với chỉ thị
và do chỉ thị xác định.
bắt đầu bằng khoảng trống và kết thúc bằng xuống dòng
*trong các chương trình đơn giản thường dùng các chỉ thị sau:
ASSUME chỉ định các thanh ghi đoạn; giúp Assembler đổi các nhãn thành các địa chỉ bằng cách báo cho Assembler biết ta định
dùng thanh ghi đoạn nào để đánh địa chỉ các nhãn này.
format: ASSUME Seg-reg: Seg-name [,...]
trong đó Seg-reg là một trong các thanh ghi đoạn sau DS, CS,
ES, SS; Seg-name là tên sẽ đứng trước chỉ thị SEGMENT.
thí dụ assume cs: cseg, ds: dseg
SEGMENT Định nghĩa đoạn
Dạng chung: SEGMENT COMBINE-TYPE ALIGN_TYPE CLASS .. ALIGN_TYPE: chỉ ra biên của segment bắt đầu ở đâu trong bộ nhớ.
- BYTE: begin anywhere.
- WORD: begin at an even-numbered address (word).
- PARA: begin at an address divisible by 16
- PAGE: begin at an address divisible by 256
COMBINE_TYPE: chỉ ra một Segment sẽ được kết hợp với các Segment khác có cùng tên như thế nào. Một chương trình có thể sử
dụng 4 Segment: Data, Code, Extra và Stack.
Code, Data và Extra có thể được nối lại (joined) với nhau
(PUBLIC) hoặc trùm lên nhau (Overlapped) (COMMON).
Stack Segment nhất thiết phải có kiểu STACK.
CLASS TYPE: ảnh hưởng tới thứ tự sắp đặt các Segment trong bộ nhớ. Các Segment có cùng tên Class được sắp đặt liên tiếp, Các Segment khác tên Class được sắp đặt theo thứ tự mà
chương trình LINK gặp.
- CODE - DATA
- EXTRA
Với SEGMENT directive ta có thể dùng như sau:
với DATA SEGMENT: SEGMENT PARA PUBLIC 'DATA' với CODE SEGMENT: SEGMENT PARA PUBLIC 'CODE'
với EXTRA SEGMENT: SEGMENT PARA PUBLIC 'EXTRA' với STACK SEGMENT: SEGMENT PARA STACK 'STACK'
ENDS nơi kết thúc đoạn hay cấu trúc. SEGMENT và ENDS chỉ đánh dấu điểm đầu và điểm cuối của một Segment, chúng không cho Assembler biết đang định nghĩa loại Segment gì; việc này là
nhiệm vụ của Pseudo-Op: ASSUME.
ORG địa chỉ khởi đầu của chương trình
DB định vùng dữ liệu dạng byte
DW định vùng dữ liệu dạng word
DD định vùng dữ liệu dạng 2 word, thí dụ DD 0 nghĩa là 4 byte
bằng 0
DUP lặp lại dữ liệu
EQU Equate: gán tên cho một hằng số (kiểu số hoặc kiểu String) tên này vĩnh viễn nhận giá trị hằng số đó (khác với
Directive = ') thí dụ PI EQU 3.14
Message EQU 'Hay vao so lieu'
PUBLIC khai báo tên dùng chung, làm cho 1 hoặc nhiều symbol đã được ĐN có thể được dùng chung trong nhiều Module mà các Module
đó sẽ được Linked với Module chứa khai báo PUBLIC này.
Dạng: PUBLIC Symbol [,...]
Symbol ở đây có thể là các tên biến, nhãn kể cả các nhãn của Proc và các tên đã được định nghĩa bởi các
Pseudo_Op EQU, = .
PUBLIC có bạn đồng hành là EXTRN
EXTRN External - Báo là tham chiếu ngoài;
format: External name: type[,...]
Như EXTRN get_hex
chỉ ra các SYMBOL đã được định nghĩa trong Modul
get_hex
- NAME là một Symbol đã được định nghĩa và được mô tả là
PUBLIC ở một Modul hợp ngữ khác.
- TYPE có thể là một trong các kiểu sau:
+ nếu NAME là một ký hiệu trong DATA SEGMENT, EXTRA
SEGMENT thì kiểu có thể là BYTE, WORD hoặc DWORD
+ nếu NAME là một nhãn thủ tục thì kiểu có thể là NEAR
hoặc FAR
+ nếu NAME là một hằng được định nghĩa bởi EQU, =, thì
kiểu phải là ABS
* chú ý:
PUBLIC và EXTRN thường được sử dụng để phân chia các thủ tục. Chẳng hạn để chạy một thủ tục có tên là SORT từ chương trình chính thì MODUL trong đó định nghĩa SORT phải chứa khai báo PUBLIC SORT và MODUL chính có chứa lời gọi tới SORT
phải có chứa khai báo:
EXTRN SORT: NEAR (hoặc FAR).
PROC và ENDP đánh dấu chỗ bắt đầu và chỗ kết thúc thủ tục. Thủ tục là một khối các chỉ thị (instructions) có thể được gọi thực
hiện từ các vị trí khác nhau trong chương trình.
END nơi kết thúc chương trình nguồn
Dạng: END Nhãn điểm vào chương trình (nhãn của lệnh đầu
tiên của chương trình)
Nếu chương trình của ta có chứa nhiều MODUL, ta (phải) gán nhãn cho chỉ dẫn END ở cuối MODUL chương trình chính, còn trong các MODUL phụ khác nhau không được gán nhãn cho chỉ
dẫn END
*sau đây là bảng các chỉ thị cho assembler
AND và logic, ví dụ: mov ax,7 and 8 BYTE định dữ liệu dài 1 byte COMMENT bắt đầu của lời bình CREF cross reference:danh sách các ký hiệu dùng để tham chiếu chéo CGROUP
(TLTK, bản dịch của LNH, trg 44) DGROUP
(TLTK, bản dịch của LNH, trg 44) DQ define quadruple:định vùng dữ liệu dạng 4 word
như DQ 0:tám byte 0 DT define ten: định vùng dữ liệu dạng 10 byte
như DT 0: mười byte 0 DW define word: định vùng dữ liệu dạng word (2 byte) DWORD double word: định dữ liệu dài 2 word
như: JMP DWORD PTR[SI] DUP Duplicate: lặp lại một xâu . Như 10 DUP ('stack') ELSE ENDIF ENDM end macro ENDP EQ Equal: so sánh logic EVEN Đặt địa chỉ chẵn EXITM Exit macro. Thoát khỏi macro FAR Báo nhãn nằm ở ngoài đoạn. Như old_e LABEL FAR GE Greater than or Equal. Phép toán logic lớn hơn hay bằng GROUP Nhóm các đoạn thành một đoạn; Như Nhóm GROUP code, data
(TLTK, bản dịch của LNH, trg 44) GT Greater Than. Phép toán logic lớn hơn HIGH Chỉ byte cao của một word ;* Về các Pseudo_Op điều kiện, xem
(TLTK, bản dịch của LNH, trg 45) IF Khối điều kiện IF1 Khối điều kiện cho lần duyệt thứ nhất IF2 Khối điều kiện cho lần duyệt thứ hai IFB If blank. Khối điều kiện khi không có tham số IFDEF If Defined. Khối điều kiện khi đã có định nghĩa IFDIF If diffent. Khối điều kiện khi hai tham số khác nhau IFE If Equal. Khối điều kiện khi bằng 0 IFIDN If Identical. Khối điều kiện khi hai tham số bằng nhau IFNB If not Blank. Khối điều kiện khi có tham số INCLUDE Chèn thêm vào chương trình một số chỉ thị lấy từ một tập tin
khác. Như INCLUDE mylib.mac IRP Indefinite Repeat. Macro có số lần lặp không xác định.
Như IRP para?. sẽ lặp lại 4 lần IRPC Indefinite Repeat Character. Macro có số lần lặp chữ
không xác định. Như IRPC para?. ABCD sẽ lặp lại 4 lần LABEL Nhãn
(TLTK, bản dịch của LNH, trg 44) LE Less than or Equal. Phép toán logic nhỏ hơn hay bằng LENGTH Xác định chiều dài một kí hiệu LOCAL Định các kí hiệu chỉ được dùng trong nội bộ macro LOW Chỉ byte thấp của một word LT Less Than. Phép toán logic nhỏ hơn .LALL Liệt kê toàn bộ macro .LFCOND Liệt kê khối điều kiện .LIST Liệt kê chương trình MACRO Bắt đầu macro MASK Mặt nạ dạng bit MOD Modulo. Cho số dư trong phép chia NAME Định tên của một chương trình hay mô-đun NE Not Equal. Phép toán logic không bằng NEAR Báo nhãn nằm trong đoạn NOT Phép toán logic lấy bù OFFSET Offset trong đoạn OR Phép toán logic hoặc %OUT In xâu ra màn hình PAGE Bắt đầu trang mới và định kích thước trang sẽ được in ra
(màn hình và máy in) khi hợp dịch.
Dạng: PAGE [LINE], [COLLUMN]
ngầm định là: 57,80 (hợp với khổ giấy A4) PTR Pointer. Dùng chung với BYTE, WORD, DWORD, NEAR và FAR để
định kích thước của một nhãn hay một biến .RADIX Định cơ số RECORD Định kiểu bản ghi REPT Repeat. Macro lặp .SALL Không liệt kê macro SEG Giá trị đoạn ứng với một kí hiệu SHL Shift left. Phép dịch chuyển trái.Như MOV AX,7 SHL 1 SHORT Nhảy xuôi gần SHR Shift right. Phép dịch chuyển phải.
Như MOV A,8 SHR 1 .SFCOND Không liệt kê các khối điều khiển sai STRUC Structure. Định nghĩa cấu trúc .TCOND Liệt kê mặc định cho các khối điều kiện TITLE Psuedo_Op để tạo ra một tiêu để được căn thẳng trái trên dòng thứ 2 của mỗi trang (in). TITLE thường được đặt ở đầu chương trình, nhưng ta cũng có thể đặt nó ở bất cứ đâu trong
chương trình.
Độ dài tối đa của TITLE: 60 dòng SUBTTL Sub Title để tạo ra một tiêu đề phụ được định tâm ở dòng thứ 3 (của trang tiếp theo ?), tiêu đề này thường được sử
dụng để mô tả nội dung của trang đó
Độ dài tối đa của SUBTITLE: 60 dòng XOR Phép toán logic hoặc. Như MOV AX,7 XOR 9 .XALL Liệt kê các macro sinh ra các mã lệnh hoặc dữ liệu .XCREF Không liệt kê danh sách để tham chiếu chéo .XLIST Không liệt kê chương trình WIDTH Định độ rộng của trường trong bản ghi WORD Định dữ liệu dài một word = Gán tên cho một hằng (kiểu số hoặc kiểu String) sau này trong
chương trình có thể gán lại
(khác với Directive EQU) .286C giả lệnh chế độ, báo cho Assembler sử dụng tập lệnh của 80286 phải đặt nó tại đầu chương trình, ngay sau các Pseudo-Op liệt
kê danh sách. .8086 giả lệnh chế độ, báo cho Assembler sử dụng tập lệnh của 8086
đây là chế độ ngầm định. $ Offset hiện hành
V. các toán tử trong Assembly language
( Phần này sử dụng để tra cứu, TLTK số 1, trang 48 )
Toán tử là một sự sửa đổi (modifier) được sử dụng trong trường toán hạng (Operand Field) của một Op_code hay Pseudo_Op. Có 5 loại toán tử: Số học, logic, quan hệ, trả lại giá trị, cho thuộc tính. (An operator is a modifier used in the operand field of an assembly language or pseudo-op statement.)
1. Toán tử số học (xem trang 29)
* format: value1 * value2;multiplies value2 by value1 * format: value1 * value2;multiplies value2 by value1
/ format: value1 / value2;divides value1 by value2 and returns the
the quotient.
MOD format: value1 MOD value2; divides value1 by value2 and
returns the remainder.
SHL format: value SHL expression; shifts values left by expression
bit positions.
SHR format: value SHR expression; shifts values right by expression
bit positions. 2. Toán tử logic (xem trang 29)
AND format: value1 AND value2; takes logical AND of value1 and
value2
OR format: value1 OR value2; takes logical inclusive-OR of
value1 and value2
XOR format: value1 XOR value2; takes logical exclusive-OR of
value1 and value2
NOT format: NOT value; reverses the state of each bit
in value; that is, it takes the
one's complement. 3. Toán tử quan hệ (xem trang 30)
EQ format operand1 EQ operand2; true if the two operands are
identical.
NE format operand1 NE operand2; true if the two operands are
not identical.
LT format operand1 LT operand2; true if operand1 is less than
operand2.
GT format operand1 GT operand2; true if operand1 is greater
than operand2.
LE format operand1 LE operand2; true if operand1 is less than
or equal to operand2.
GE format operand1 GE operand2; true if operand1 is greater
than or equal to operand2.
4. Toán tử trả lại giá trị (xem trang 30)
$ format: $
cho lại giá trị hiện thời của bộ đếm vị trí bộ nhớ.
SEG format: SEG Var_name, hoặc
SEG Label_name
trả lại giá trị Segment của biến hoặc nhãn
OFFSET format: OFFSET Var_name. hoặc
OFFSET Label_name
trả lại giá trị OFFSET của biến hoặc nhãn
LENGTH format: LENGTH Var_name
trả lại độ dài tính bằng đơn vị (Byte hoặc Word) của biến đã
được định nghĩa bằng DUP.
TYPE format: TYPE Var_name, hoặc
TYPE Label_name
đối với các biến, TYPE cho lại 1 nếu là kiểu Byte, 2 nếu là kiểu Word, 4 nếu là kiểu Double Word. đối với các nhãn TYPE cho lại
-1 nếu là NEAR, -2 nếu là FAR.
SIZE Dạng SIZE Var_name
trả lại giá trị là tích của Length và Type
5. Toán tử thuộc tính
PTR format: TYPE PTR EXPRESSION
khai báo lại kiểu (type) (BYTE hoặc WORD) hoặc khoảng cách (NEAR hoặc FAR) của một toán hạng địa chỉ bộ nhớ. TYPE là thuộc tính mới còn Expression là một tên (indentifier) mà thuộc tính của nó
bị thay đổi (overriden).
DS: format: seg-reg:addr-expr hoặc ES: seg-reg:label hoặc
SS: seg-reg:variable
CS: thay thế (overrides) thuộc tính đoạn (segment attribute) của một
nhãn, biến hoặc biểu thức địa chỉ.
SHORT xem ở lệnh JMP, trang
HIGH format: HIGH value hoặc
HIGH expression
trả lại giá trị là byte cao của 1 giá trị số hoặc địa chỉ 16 bit
LOW format: LOW value hoặc
LOW expression
trả lại giá trị là byte thấp của 1 giá trị số hoặc địa chỉ 16
bit. VI Hằng số trong chương trình nguồn
Assembler chấp nhận việc sử dụng một vài dạng hằng số sau: 1. Binary
là một dãy số 0,1 kết thúc bởi ký tự B, thí dụ 10111010B 2. Decimal
là một dãy các chữ số từ 0 đến 9, có thể kết thúc bởi ký tự D hoặc không
cũng được, thí dụ 512 , 512D 3. Hexadecimal
là một dãy các chữ số từ 0 đến 9 và các ký tự A, B, C, D, E, F
- phải kết thúc bởi ký tự H
- ký tự đầu tiên nhất thiết phải là chữ số (0..9)
thí dụ 8FH, 0EDH 4. Character
đó là một dãy các chữ cái, chữ số, ký hiệu được ghi trong dấu nháy đơn
hay kép, thí dụ 'Hello World!', "Don't enter a number here" bài tập 1. Đổi các số thập phân sau sang dạng nhị phân:
a) 12; b) 17; c) 45; d) 72 2. Đổi các số nhị phân sau sang dạng thập phân:
a) 00001000; b) 00010101; c) 00011111; d) 11000011 3. Đổi các số ở câu 2 sang dạng thập lục phân 4. Đổi sang dạng thập phân số 0D8H trong trường hợp: - 0D8H biểu diễn số không dấu - 0D8H biểu diễn số có dấu
---------- hết bài 1 ----------
Bài giảng môn Assembly Language, Bài 1 - $ - Nguyễn Đình Việt, Khoa CNTT, ĐHQGVN,HN 1995 ---------------------------------------------------------------------------
Bài 2 Các bước trong quá trình tạo 1 chương trình chạy được
Trên thị trường có khá nhiều Assembler, trong các bài giảng môn học này chúng ta sẽ sử dụng Macro Assembler của MicroSoft (MASM.EXE), nó tương thích với IBM Macro Assembler.
Cho đến nay (1990), IBM đã đưa ra thị trường 2 Version của Macro Assembler, Version 1 , version2. Để xây dựng chương trình cho họ máy AT, chúng ta cần phải dùng version 2 (của IBM Macro Assembler) hoặc version 3 (của MicroSoft Macro Assembler).
Đĩa Macro Assembler của IBM có 2 chương trình Assembler tách biệt: - Small Assembler (ASM): để dùng cho các máy tính có bộ nhớ <= 128 KB và
có tập lệnh ít hơn của MASM. MicroSoft không có Small Assembler như IBM. - Macro Assembler (MASM)
I. Các bước chính
Bứơc 0:
Xác định nhiệm vụ của chương trình, thiết kế chương trình. Trong bước này ta thường cần vẽ lưu đồ (Flowchart). Bước này đặc biệt quan trọng nếu chương trình là dài và phức tạp. Nên thực hiện chiến thuật "Top-Down Program Design".
Bước 1. Soạn chương trình nguồn
Dùng 1 chương trình soạn thảo văn bản, ví dụ SideKick, Turbo Pascal
để soạn thảo chương trình nguồn - Không nên dùng các hệ soạn thảo trong đó có sử dụng các mã ASCII mở
rộng, hoặc các ký tự điều khiển (Như Word Perfect, Bked .v.v...) - dùng SideKick có rất nhiều tiện lợi, vì đó là chương trình thường trú nhỏ
các lệnh soạn thảo khá giống Turbo Pascal Version 3.01a, quen thuộc với
sinh viên. - dùng Turbo Pascal để soạn thảo rất dễ trình bày chương trình nguồn đẹp
vì chức năng TAB của nó rất tiện lợi cho soạn thảo chương trình. - (hướng dẫn sinh viên sử dụng SideKick nếu cần thiết) - ghi file nguồn lên đĩa, ví dụ với tên là vidu1.asm
Bước 2. Hợp dịch chương trình nguồn
- Hợp dịch
A:\> masm vidu1
sau đó ta phải trả lời 3 câu hỏi sau:
Object filename [vidu1.obj]: nếu ta đồng ý thì ấn Enter (nói chung là ta
đồng ý với các gợi ý của MASM)
Source Listing [Nul.lst]: Cross Reference [Nul.crf]:
Sau đó MASM sẽ hiển thị như sau:
Microsoft (R) Macro Assembler Version 5.10 Copyright (C) Microsoft Corp. 1981-1985. All right reserved. 51712 + 154624 Bytes symbol space free
nn nnn Byte Free
m Warning Errors l Severe Errors
A:\>
trường hợp không có lỗi nào xảy ra thì m=0, l=0
- Về File .lst
Đây là File văn bản nguồn do MASM tạo ra, nó chứa các lệnh nguồn và các mã số của chúng để ta có thể in ra xem cách Assembler biên dịch chương
trình của mình.
Trong file .lst Macro Assembler còn đưa ra cả địa chỉ (Segment:Offset) của các lệnh và dữ liệu của chương trình, việc này rất có ích khi chúng ta muốn phân tích các chương trình khó, chẳng hạn phân tích các đoạn mã
Virus .v.v. - cross-reference listing file:
File vidu1.crf (crf: cross reference) còn gọi là File tham khảo chéo, (cross-reference listing), nó cho số dòng tại đó mỗi ký hiệu (symbol) được định nghĩa và số hiệu tất cả các dòng khác, tại đó có tham chiếu
(refer to) tới ký hiệu này. Để MASM.EXE sinh ra file này khi được hỏi:
Cross Reference [Nul.crf]:
ta cần đánh tên file (đuôi ngầm định là .crf). Sau khi đã hợp dịch (chạy
MASM.EXE) cần chạy chương trình CREF.EXE
CREF VIDU1; - .MAP - Map listing file (file bản đồ chương trình)
Việc tạo ra file này là tuỳ chọn đối với LINK.EXE. File này tóm tắt về các đoạn của chương trình, trong mỗi đoạn nó chỉ ra địa chỉ offset 'start' và 'stop' , chiều dài mỗi đoạn của chương trình và class (loại)
của các đoạn.
Để tạo ra file .MAP ta làm như sau:
LINK VIDU1,,; (2 dấu phẩy trước dấu chấm phẩy)
Lưu ý: - trường hợp có đánh dấu '; ' sau tên của chương trình nguồn: MASM.EXE tự động đặt tên file đích là vidu1.obj , không tạo các file .lst và .crf
tương ứng. - trường hợp không đánh dấu '; ' sau tên của chương trình nguồn: MASM.EXE
thực hiện theo các bước nêu trên.
- cách sử dụng các chỉ dẫn cho MASM.EXE để có thể in các thông báo lỗi ra máy in, tạo các file tham chiếu chéo, định trang in (số dòng/1 trang,
số cột mỗi dòng) ...
* sau bước này Assembler mới tạo ra File vidu1.obj; đây là một File trung gian, gọi là File đích (Object file), nó chứa chương trình của chúng ta ở dạng ngôn ngữ máy, nhưng kèm theo một số thông tin mà chương trình Link
của DOS sẽ dùng đến. - sửa các lỗi do Assembler thông báo:
nếu có lỗi, Assembler sẽ thông báo số thứ tự của dòng có lỗi, loại lỗi, sau đó Assembler tiếp tục hợp dịch các dòng chương trình nguồn tiếp sau
và lại thông báo lỗi nếu gặp phải.
Ta nên ghi lại các thông báo lỗi và STT dòng có lỗi để sửa (có thể in
luôn ra giấy bằng lệnh Print Screen) luôn một lần. - hợp dịch lại và lại sửa lỗi cho đến khi hết lỗi
* Một số thông báo lỗi của Macro Assembler:
- Block nesting error:
thông báo lỗi này thường kèm theo thông báo "Open procedures" hoặc "Open
segments", hãy xem giải thích về các thông báo này như ở dưới đây. - End of file, no END directive:
thiếu chỉ thị assembler END ở cuối file hoặc phải thêm 1 dòng trống sau
dòng chứa chỉ thị END. - Must be declared in pass 1:
thông báo này thường liên quan đến giả lệnh GROUP, có thể ta chưa định
nghĩa một tên đoạn được khai báo trong giả lệnh GROUP. - No or Unreadable CS:
MASM cần thấy giả lệnh ASSUME khi hợp dịch một số chỉ thị như CALL hay
JMP. - Open procedures:
thiếu nhãn PROC hay ENDP hoặc các tên không trùng nhau trong cặp giả
lệnh PROC - ENDP. - Open segments:
thiếu giả lệnh Segment hoặc ENDS hoặc tên đi cùng với hai giả lệnh này. - Symbol not defined
Đây là một thông báo lỗi thường thấy khi chúng ta mới học ngôn
ngữ Assmblly, cần kiểm tra các vấn đề sau:
+ sử dụng một tên chưa khai báo hoặc đánh sai một tên đã được khai báo
+ thiếu khai báo EXTRN mà chương trình lại sử dụng một tên nằm trong Modul ngoài; hoặc tên trong modul ngoài và tên được gọi đến không
trùng nhau (do ta đánh sai chẳng hạn)
Bước 3 Link (kết nối chương trình và số liệu .v.v...)
chương trình LINK có 2 nhiệm vụ chính: 1./ DOS có thể để 1 chương trình ở chỗ nào đó do nó sắp đặt cho thuận tiện. Việc này tránh cho NSD không phải chỉ ra việc đặt chương trình ở đâu. Muốn cho chương trình nguồn (và sau đó là dạng .obj) sử dụng được đặc
điểm này (relocatable) cần phải có chương trình LINK của DOS. 2./ NSD thường viết chương trình theo các modul, thử riêng rẽ từng Modul, do vậy sẽ tạo ra nhiều file .obj tương ứng. Nhiệm vụ thứ 2 của LINK chính là kết nối các object module lại thành 1 modul duy nhất, chạy
được.
Thí dụ LINK MOD1 + MOD2 + MOD3 sẽ kết nối các file MOD1.OBJ,
MOD2.OBJ, MOD3.OBJ để tạo thành file MOD1.EXE chạy được.
Ta cần sử dụng chương trình Link.exe (lệnh ngoại trú của DOS) để tạo ra
một file .exe từ File .obj. - A:\> Link vidu1;
Microsoft 8086 Object Linker
Version 3.02 (C) Copyright Microsoft Corp. 1983, 1984, 1985
Warning: No stack segment
There was an error detected
Error là lỗi nhưng chính là điều chúng ta muốn, nó báo rằng chưa có đoạn
stack. * sau khi thực hiện bước này, ta có file vidu1.exe, nói chung có thể cho
chạy ngay được. Tuy nhiên ta cần nghiên cứu cách tạo file .com trước đã. * Xem tuỳ chọn của LINK để tạo ra file .MAP đã trình bày ở bước 2.
* Một số thông báo lỗi của LINK
- Fixup offset exceeds field with:
Với thông báo này ta cũng khó tìm ra được lỗi, có thể là do có một lời gọi xa tới một thủ tục (Call far) nhưng trong thủ tục đó lại khai báo nó là near (Proc near). Lỗi này cũng có thể xảy ra nếu toàn bộ chương
trình vượt quá 64KB, dùng file .MAP có thể thấy được điều này.
- Symbol defined more than once:
xảy ra khi LINK phát hiện thấy 1 biến hay thủ tục được định nghĩa
trong hai file.
- Unresolved externals:
có thể do thiếu khai báo public cho 1 biến hay thủ tục, hoặc tên trong
1 thủ tục external sai.
- Warning: no stack segment
Đây thực ra không phải là thông báo lỗi mà là lời nhắc nhở của LINK, ta
thường luôn thấy nó khi LINK các file .OBJ để tạo ra các file .COM
Bước 4. Dịch chương trình sang File dạng .COM
Để tạo ra file .com, ta cần chương trình Exe2bin.exe của DOS. Chương trình này chuyển 1 file dạng .exe hay .bin sang file dạng .com - Exe2bin vidu1 vidu1.com
Chúng ta dùng lệnh DIR của DOS để kiểm tra sự tồn tại của vidu1.com trên
đĩa.
Đôi khi có thể thấy thông báo lỗi 'File cannot be converted', đây là thông báo lỗi duy nhất của EXE2BIN, nguyên nhân sinh ra lỗi này có thể
như sau:
+ các đoạn xếp sai thứ tự, cần kiểm tra lại bằng file .MAP (xem phụ lục
C trong sách 'Nhập môn Assembler', Peter Norton.
+ chương trình chính không đứng đầu tiên trong lệnh LINK.
+ chương trình chính không có chỉ thị Assembler ORG 100H đứng liền dưới
khai báo CODE_SEG SEGMENT PUBLIC.
Ngoài ra cần kiểm tra lại xem sau chỉ thị END cuối cùng trong modul chương trình chính đã ghi nhãn của lệnh đầu tiên của chương trình chưa.
- Lưu ý: các Version Dos sau 5.0 không có File Exe2bin.exe vì DOS không chủ trương hỗ trợ việc tạo các file .com, muốn thực hiện bước 4 này ta
cần đặt lệnh
DEVICE=PATH_NAME\SETVER.EXE
trong file CONFIG.SYS, tất nhiên cần phải có SETVER.EXE tại đường dẫn
PATH_NAME.
Bước 5. Chạy thử chương trình
- Nếu chương trình không dài, thì tốt nhất là dùng Debug để chạy thử từng bước Lệnh T (Trace), theo dõi quá trình biến đổi nội dung các thanh ghi,
việc chuyển số liệu .v.v... - Nếu chia chương trình thành từng khối, có thể hợp dịch từng khối và cho
chạy thử từng Modul, sau khi chạy tốt mới ghép nối lại với nhau. - Nếu có lỗi thì lặp lại các bước trên
II. Hướng dẫn sử dụng chương trình Debug
Trình bày 19 lệnh của DEBUG, 4 lệnh còn lại liên quan đến bộ nhớ mở rộng
XMS và EMS không cần trình bày.
Bài tập 2.1 In ra màn hình 1 ký tự (ký tự 'A')
Bài tập 2.2 In ra màn hình một xâu ký tự 'Darling, I love you'
Bài tập 2.3 hỏi tên người (từ bàn phím) rồi gửi lời chào người đó
Bài tập 2.4 Nhận vào từ bàn phím 5 xâu ký tự (n <= 5); in ra màn hình 1 trong 5 xâu ký tự đó
Bài tập 2_1 (BT2_1.ASM) In ra màn hình 1 ký tự (ký tự 'A')
Seg_a segment:100 jmp 103
assume cs:seg_a, ds:seg_a:102 db 41
org 100h:103 mov ah,02 start: jmp short loc_1:105 mov dl,[102] data_1 db 41h:109 int 21 loc_1 mov ah,02:10b int 20
mov dl,data_1
int 21h int 20h seg_a ends end start
;--------------------------------------------------------------------------
Bài tập 2.2 In ra màn hình một xâu ký tự 'Darling, I love you'
Seg_a segment:100 jmp 118
assume cs:seg_a, ds:seg_a:102 db'Darling, I love you$'
org 100h:118 mov dx,0102 start: jmp short loc_1:11b mov ah,09 data_1 db'Darling, I love you$':11d int 21 loc_1 mov dx,offset data_1:11f int 20
mov ah,09
int 21
int 20 seg_a ends end start
;--------------------------------------------------------------------------
Bài tập 2.3 hỏi tên người (từ bàn phím) rồi gửi lời chào người đó ;========================================================================== ;== BT2_3.LST == ;== Created: 4-Aug-95 == ;== Passes: 5 Analysis Options on: QRSUX == ;========================================================================== SEG_A SEGMENT BYTE PUBLIC
ASSUME CS:SEG_A, DS:SEG_A
ORG 100h
BT2_3 PROC FAR
94E0:0100 START: 94E0:0100 EB 25 JMP SHORT LOC_1;(0127) 94E0:0102 90 DB 90H 94E0:0103 0D 0A 24 DATA_1 DB 0DH, 0AH, '$' 94E0:0106 57 68 61 74 20 69 DATA_2 DB 'What is your name ?: $' 94E0:010C 73 20 79 6F 75 72;xref 94E0:0129, 012C 94E0:0112 20 6E 61 6D 65 20 94E0:0118 3F 20 3A 20 24 94E0:011D 48 65 6C 6C 6F 20 DATA_3 DB 'Hello Mr.$';xref 94E0:0143, 0146 94E0:0123 4D 72 2E 24 94E0:0127 LOC_1:;xref 94E0:0100 94E0:0127 B4 09 MOV AH,9 94E0:0129 .BA 0106 MOV DX,OFFSET DATA_2 94E0:012C CD 21 INT 21H;DOS Service 09h
;display char string at ds:dx 94E0:012E B4 0A MOV AH,0AH 94E0:0130 .BA 015F MOV DX,OFFSET DATA_4 94E0:0133 8B DA MOV BX,DX 94E0:0135 C6 07 FE MOV BYTE PTR [BX],0FEH 94E0:0138 CD 21 INT 21H; DOS Service 0Ah
; get keybd line, put at ds:dx 94E0:013A B4 09 MOV AH,9 94E0:013C .BA 0103 MOV DX,OFFSET DATA_1 94E0:013F CD 21 INT 21H 94E0:0141 B4 09 MOV AH,9 94E0:0143 .BA 011D MOV DX,OFFSET DATA_3 94E0:0146 CD 21 INT 21H 94E0:0148 B4 09 MOV AH,9 94E0:014A .BB 015F MOV BX,OFFSET DATA_4 94E0:014D 8B D3 MOV DX,BX 94E0:014F 02 5F 01 ADD BL,[BX+1] 94E0:0152 83 C3 02 ADD BX,2 94E0:0155 C6 07 24 MOV BYTE PTR [BX],24H; '$' 94E0:0158 83 C2 02 ADD DX,2 94E0:015B CD 21 INT 21H 94E0:015D CD 20 INT 20H; Program Terminate 94E0:015F 3F DATA_4 DB 3FH; '?'
BT2_3 ENDP SEG_A ENDS
END START
;--------------------------------------------------------------------------
Bài tập 2.4 Nhận vào từ bàn phím 5 xâu ký tự (n <= 5); in ra màn hình 1 trong 5 xâu ký tự đó
;GET 5 STRING FROM KBD, THEN ASK WHICH OF THEM WILL BE SEND TO SCR CGROUP GROUP CODE_SEG
ASSUME CS: CGROUP CODE_SEG SEGMENT BYTE PUBLIC
ORG 100H
START: JMP FIRST
MSG DB "Enter 5 string ",0DH,0AH,"$" MSG1 DB "one:$" MSG2 DB "two:$" MSG3 DB "three:$" MSG4 DB "four:$" MSG5 DB "five:$" ASK_FOR_OUT DB "Which string will be out ?: $" NUMBER DB 0
FIRST: MOV AH, 09H
MOV DX,OFFSET MSG
INT 21H
MOV AH,09H;ask for 1st string
MOV DX,OFFSET MSG1
INT 21H
MOV AH,0AH
MOV DX,OFFSET STR1
INT 21H
CALL CR_LF
MOV AH,09H;ask for 2nd string
MOV DX,OFFSET MSG2
INT 21H
MOV AH,0AH
MOV DX,OFFSET STR2
INT 21H
CALL CR_LF
MOV AH,09H;ask for 3rd string
MOV DX,OFFSET MSG3
INT 21H
MOV AH,0AH
MOV DX,OFFSET STR3
INT 21H
CALL CR_LF
MOV AH,09H;ask for 4th string
MOV DX,OFFSET MSG4
INT 21H
MOV AH,0AH
MOV DX,OFFSET STR4
INT 21H
CALL CR_LF
MOV AH,09H;ask for 5th string
MOV DX,OFFSET MSG5
INT 21H
MOV AH,0AH
MOV DX,OFFSET STR5
INT 21H
CALL CR_LF
MOV AH,09H
MOV DX,OFFSET ASK_FOR_OUT
INT 21H
MOV AH,01;GET STRING NUMBER TO SEND OUT
INT 21H
CALL CR_LF
SUB AL,30H
MOV NUMBER,AL
MOV AX,100H;256d
XOR CX,CX
MOV CL,NUMBER
DEC CL MUL CX
ADD AX,OFFSET STR1
MOV BX,AX
MOV CL,[BX+1]
ADD BX,CX
ADD BX,2
MOV BYTE PTR [BX],"$"
MOV DX,AX
ADD DX,2
MOV AH,09H
INT 21H
INT 20H ;-------------------------------------------------------------------------- CR_LF PROC NEAR
PUSH AX PUSH DX
MOV AH,02 MOV DL,13
INT 21H
MOV DL,10
INT 21H
POP DX POP AX
RET CR_LF ENDP ;-------------------------------------------------------------------------- STR1 DB 0FEH,?,0FEH DUP(0) STR2 DB 0FEH,?,0FEH DUP(0) STR3 DB 0FEH,?,0FEH DUP(0) STR4 DB 0FEH,?,0FEH DUP(0) STR5 DB 0FEH,?,0FEH DUP(0)
CODE_SEG ENDS END START
Bài giảng môn Assembly Language, Bài 2 - $ - Nguyễn Đình Việt, Khoa CNTT, ĐHQGVN,HN 1995 ---------------------------------------------------------------------------
Bài 3 Dạng của 1 chương trình mẫu
Bài này trình bày
-các mô hình tổng quát để xây dựng nên 1 chương trình. Mẫu chương trình này là chung nhất cho mọi chương trình. Khi chúng ta viết chương trình của mình, ta chỉ cần điền số liệu và các chỉ thị cho chương trình cụ thể đó.
-về sự khác nhau giữa .com files và .exe files
I. Main Program Module: Mô dul chương trình chính
Sau đâu là 1 mẫu chung cho 1 modul nguồn, nó bao gồm 1 chương trình đầy đủ hoặc nó sẽ được kết nối với 1 hoặc 1 số modul (phụ) khác để tạo nên 1 chương trình.
PAGE ,132 TITLE (Insert title here) SUBTTL (Insert sub_title here) (Insert EXTRN statement, if approriate)
STACK SEGMENT PARA PUBLIC 'STACK'
DB 64 DUP('STACK ') STACK ENDS
DSEG SEGMENT PARA PUBLIC 'DATA'
(Insert data here) DSEG ENDS
CSEG SEGMENT PARA PUBLIC 'CODE'
ASSUME CS:CSEG, DS:DSEG, SS:STACK
ENTRY PROC FAR;entry point
;set up the stack to contain the proper values so this program ;can return to DOS or Debug
PUSH DS
SUB AX,AX
PUSH AX
;initialize the data segment address
MOV AX,DSEG
MOV DS,AX
(insert instructions here)
RET;return to DOS or Debug ENTRY ENDP CSEG ENDS END ENTRY
chú ý mẫu này là chương trình tổng quát, ta có thể sửa đổi 1 số điểm sau: -Title: Tiêu đề thường chỉ ra tên File trên đĩa của chương trình này -Nếu Modul này có chứa tham chiếu tới các thủ tục hoặc biến, mà chúng được dịnh nghĩa ở Modul phụ, thì nhất thiết phải có chỉ thị EXTRN để liệt kê
danh sách chúng. -Ta dùng AX để khởi tạo STACK và địa chỉ đoạn dữ liệu, nhưng cũng có thể
dùng các thanh ghi đa năng khác.
Vì vậy, nếu chương trình của ta nhận giá trị vào của người sử dụng từ thanh ghi AX, khi đó ta bắt buộc phải dùng thanh ghi khác (ví dụ DI) để
khởi tạo. -Về thủ tục:
Các Pseudo_Op Proc và Endp đánh dấu điểm đầu và điểm cuối của 1 thủ tục. Thủ tục là một khối lệnh mà có thể được gọi thực hiện từ nhiều nơi trong
chương trình.
Nếu thủ tục kết thúc bằng lệnh RET (Return from Procedure) thì ta có thể gọi nó là một chương trình con (Subroutine). Lệnh RET làm cho MPU thực hiện chương trình đã gọi (tới thủ tục)tiếp tục thực hiện lệnh (trình tự)
sau lời gọi đó.
Một thủ tục luôn có một trong hai thuộc tính: NEAR và FAR được chỉ
ra bởi Operand đứng ngay sau Proc directive, trong đó NEAR là ngầm định.
+ Proc_name PROC NEAR: thủ tục Proc_name chỉ có thể được gọi từ trong chính SEGMENT chứa thủ tục đó. Khi gặp lời gọi tới 1 thủ tục NEAR,
MPU chỉ cất Offset (IP) của địa chỉ trở về lên Stack.
Với chương trình dạng .COM, luôn luôn dùng NEAR.
+ Proc_name PROC FAR: thủ tục Proc_name có thể được gọi từ bất cứ
SEGMENT nào. Khi gặp lời gọi tới 1 thủ tục FAR, MPU chỉ cất địa chỉ
trở CS:IP lên Stack.
Với chương trình dạng .EXE, luôn luôn dùng FAR cho thủ tục chính.
II. SECONDARY Module: Mô dul phụ
sau đây là mẫu modul phụ, sẽ được Linked với Main Modul ở ví dụ trên
Page ,132 Title (điền các tiêu đề ở đây.) public pname
(khai báo public cho các biến tại đây nếu cần thiết) dseg segment para public 'data'
(đặt các dữ liệu tại đây) dseg ends cseg segment para public 'code'
assume cs:cseg, ds:dseg pname proc near
(đặt các lệnh ở đây)
ret;trở lại chương trình gọi pname endp cseg ends
end
Chú ý có thể sửa đổi lại một số khai báo như sau:
1. Vì Code segment của modul phụ này có cùng tên (cseg) với tên của code segment thuộc modul chính, nên ta khai báo Pname proc near. Để gọi Pname từ segment khác, phải thay near bằng far. 2. vì data segment của modul phụ này có cùng tên với data segment của modul chính, nên ta không cần khởi tạo DS (không cần assume ds:dseg). Nếu modul phụ này dùng data segment khác tên với data segment của modul chính thì ta phải dùng pseudo op: assume ds: tên data segment dseg. 3. ở đây ta đặt tên thủ tục là Pname, khi lập chương trình của mình, ta có thể thay bằng tên theo ý ta ở các vị trí: + khai báo public ở trên đầu thủ tục + khai báo proc ở giữa + khai báo endp ở cuối 4. ở modul phụ, ta có khai báo public để tương ứng với khai báo Extrn ở modul chính. trong 2 ví dụ này, nếu modul chính gọi modul phụ, thì ở modul chính phải có khai báo
extrn pname: near III. .com file
DOS có thể chạy được 2 loại file chương trình viết bằng assembly language, đó là .com file và .exe file. Nói chung người sử dụng dùng .exe file khi cần tạo các chương trình lớn, dài hơn 64 KB, và dùng .com file khi cần tạo các file chương trình <= 64KB. việc tạo ra .com file cần theo một số quy tắc khác .exe file, sau đây là một số điểm quan trọng:
1. Các quy tắc để tạo ra .com file
1/ bỏ qua tất cả các stack, data và extra segments.
2/ chỉ xác định 1 code segment, nhưng để tất cả lệnh và dữ liệu vào đó
3/ ở assume pseudo-op ta trỏ tất cả 4 thanh ghi đoạn tới code segment.
ví dụ
cseg segment para public 'code'
assume cs:cseg, ds:cseg, es: cseg, ss: cseg
4/ đặt trước điểm vào của chương trình (lệnh đầu tiên của ch tr) với
chỉ thị assembler
org 100h; để dành 256 byte đầu tiên cho PSP (Program segment prefix)
5/ đặt tất cả các dữ liệu trước các lệnh của ch trình. Tất nhiên trước
dữ liệu phải có 1 lệnh JMP để nhảy qua vùng dữ liệu
ví dụ entry: jmp our_prog
source db 10,20,30,40
dest db 4 dup(?)
our_prog proc near
(các lệnh ...)
6/ xác định tất cả các Procedure là near (ngầm định)
7/ cuối chương trình sau END pseudo_op phải có nhãn của lệnh đầu tiên
của chương trình
ví dụ
end entry
2. Các quy tắc tạo các modul phụ của .com file
cũng tuân theo các quy tắc trên, nhưng cần lưu ý: - đặt tên cho segment ở modul phụ giống tên segment ở modul chính
ví dụ tên cseg dùng ở cả modul chính lẫn phụ. - cũng giống như ở .exe file, ở lệnh END cuối modul phụ, không được viết
nhãn sau END
3. Mẫu cho 1 .com file
Mẫu cho 1 mô đun chính
Page ,132 Title (ta để tiêu đề ở đây) (đặt khai báo Extrn tại đây nếu cần)
Cseg segment Para Public 'Code'
Assume cs: cseg, ds: cseg, es:cseg, ss:cseg
org 100h
Entry: jmp start
(vùng data) Start proc near
(các lệnh của thủ tục Start)
ret (trở về ch trình gọi, hoặc DOS, hoặc Debug) Start endp Cseg ends
end Entry
Mẫu cho 1 mô đun phụ (ch trình con) của .com file
Page ,132 Title (ta để tiêu đề ở đây)
public Pname (đặt Public cho các biến tại đây, nếu có)
Cseg segment Para Public 'Code'
Assume cs: cseg, ds: cseg, es:cseg, ss:cseg
jmp Pname; nhảy qua miền dữ liệu
(vùng data)
Pname proc near
(các lệnh của thủ tục Pname)
ret (trở về ch trình gọi) Pname endp Cseg ends
end
3. .EXE file
4. Ưu điểm và nhược điểm của .Com files so với .exe files
4.1 ưu điểm
1) ngắn hơn hẳn so với .exe files
2) nói chung nạp vào bộ nhớ nhanh hơn .exe files
3) dễ viết hơn vì không phải khởi tạo các thanh ghi cho Stack, DS,
ES
4) các ch trình thường trú phải viết dưới dạng .com 4.1 nhược điểm
1) Không thể dài hơn 64 KB
2) chúng yêu cầu phương pháp viết ch trình chặt chẽ hơn
3) vì chỉ sử dụng có 1 segment nên không có sự tách biệt hoàn toàn
giữa các lệnh và số liệu
4) Không truy nhập được các thủ tục hoặc số liệu ở các segment khác, do đó rất khó sử dụng các modul được NSD khác xây dựng, nếu các modul này đặt tên cho segment mà chúng dùng khác tên segment của
ta.
Bài tập 3.1 Nhận vào từ bàn phím 2 con số 8 bit, không dấu (số dương), in ra màn hình tích của chúng.
;BT3_1.ASM Get in 2 un_sign 8 bit number2 (0=< and <= 255) ;Then print out the product
CODE_SEG SEGMENT BYTE PUBLIC
ASSUME CS:CODE_SEG, DS:CODE_SEG
ORG 100H
START: JMP FIRST
MSG DB "Enter 2 number (0 =< x <= 255):$" MSG1 DB "number 1:$" MSG2 DB "number 2:$" msg3 DB "The Product is: $" NUMBER_1 DB 0 NUMBER_2 DB 0 RESULT LABEL BYTE PRODUCT DW 0
FIRST: MOV AH,09H
MOV DX,OFFSET MSG
INT 21H
CALL CR_LF
MOV AH,09H;ask for the 1st number
MOV DX,OFFSET MSG1
INT 21H
call Get_an_Int_num
CMP Ax,255
Ja EXIT
mov number_1,al
CALL CR_LF
MOV AH,09H;ask for the 2nd number
MOV DX,OFFSET MSG2
INT 21H
call Get_an_Int_num
CMP Ax,255
JA EXIT
mov number_2,al
CALL CR_LF
mov bl,number_1
mul bl PUSH ax
MOV AH,09H
MOV DX,OFFSET MSG3
INT 21H
pop ax
CALL WRITE_INT_NUMBER
EXIT: INT 20H
;-------------------------------------------------------------------------- ;This procedure get in an integer number between 0..255 ;Return it to calling prog. in AX ;-------------------------------------------------------------------------- Get_an_Int_num PROC; 0 <= X <= 255
jmp $+4
temp_var Dw 0
push bx push cx push dx
xor dx,dx
mov temp_var,0
mov cx,1;CX counts digits typed in loop_2: Call Get_a_dec_digit
cmp al,0Dh je exit_2 sub al,30h
xor ah,ah mov dx,ax
mov ax,temp_var
cmp cx,1
je sum_up
mov bl,10
push dx
mul bl pop dx sum_up: add ax,dx ; cmp ax,255 ; ja exit_2
mov temp_var,ax
inc cx
CMP CX,3
JA EXIT_2 jmp loop_2 exit_2: mov ax,temp_var
pop dx pop cx pop bx
ret Get_an_Int_num ENDP ;-------------------------------------------------------------------------- ;This procedure gets in a decimal digit between 0..9 ;checks input for digit ;Return it to calling prog. in AL ;-------------------------------------------------------------------------- Get_a_dec_digit PROC loop_1:
push dx
mov ah,08
int 21h
cmp al,0Dh je exit_1 cmp al,30h jb loop_1 cmp al,39h ja loop_1
mov dl,al mov ah,02
int 21h exit_1: pop dx
ret Get_a_dec_digit ENDP
;-------------------------------------------------------------------------- ;This Prog. will Write out an Integer number in AX (Int number) ;let refer to example in 'Nhap mon Assembler', Peter norton, Pg 83. WRITE_INT_NUMBER PROC NEAR
MOV BX,10 XOR CX,CX NONE_ZERO: XOR DX,DX
DIV BX PUSH DX INC CX
OR AX,AX
JNZ NONE_ZERO WRITE_DIGIT_LOOP:
POP DX
ADD DL,'0'
MOV AH,02
INT 21H
LOOP WRITE_DIGIT_LOOP
RET WRITE_INT_NUMBER ENDP
;-------------------------------------------------------------------------- CR_LF PROC NEAR
PUSH AX PUSH DX
MOV AH,02 MOV DL,13
INT 21H
MOV DL,10
INT 21H
POP DX POP AX
RET CR_LF ENDP
;-------------------------------------------------------------------------- CODE_SEG ENDS END START
Bài giảng môn Assembly Language, Bài 3 - $ - Nguyễn Đình Việt, Khoa CNTT, ĐHQGVN,HN 1995 ---------------------------------------------------------------------------
Bài 5 Các thao với File
TLTK: 1. Assembly Language for Pascal Programmers; Steven Holzner, Brady
Page 71
2. PC Intern System Programming, Michael Tischer, Chapter 19
Mục đích của tính toán là tạo ra các OUTPUT có thể sử dụng được bên ngoài chương trình. Output lên màn hình là một trong các phương pháp như vậy, nhưng sử dụng File rất quan trọng vì nó là bộ nhớ ngoài, có thể lưu trữ thông tin ngay cả khi tắt máy.
Ta có thể in ra một File, có thể tổ chức một file để lưu giữ dữ liệu và các ký tự cho 1 Editor, file có thể là chính chương trình.
DOS có một bộ dịch vụ về File rất phong phú, phần lớn là các dịch vụ trong INT 21h.
Các Service của INT 21h represent most of the resources mà những người lập trình sử dụng trong DOS. Bên cạnh INT 20h (kết thúc chương trình) và các ngắt cho phép đặt một chương trình thường trú, những ngắt khác thực sự cần thiết là các ngắt cho phép đọc/ghi đĩa, INT 25h và INT 26h. Trong khi đó số lượng các Service của INT 21h không ngừng tăng lên, ở DOS version 4.0 con số đã lên tới 6Ch (=108d).
I. File Control Blocks (FCB)
Trước DOS 2.0, DOS thường làm việc với File thông qua FCBs. FCBs giữ các thông tin về file: Filename, tên ổ đĩa chứa file, kích thước file. Tuy nhiên FCBs hạn chế độ dài tối đa của File là 11 character. Từ DOS 2.0 trở đi, IBM đưa ra Directory và vì vậy cần phải chỉ ra đường dẫn tới file, tất nhiên 11 ký tự nói chung là không đủ, đó là lý do mà File handles đã được IBM (và Microsoft) đưa ra.
II. File handles
Một File handle là một từ 16 bit làm đại diện cho 1 file trước DOS (A file handle is a 16-bit word that stands, to DOS, for a file.) Khi chúng ta muốn sử dụng một file, ta trao cho DOS filename còn DOS thì trả lại một File handle trong một thanh ghi (thường là AX). Khi chúng ta muốn làm một việc gì đó với file - rename file, open file, read from file ... thì dịch vụ của INT 21h sẽ cần file handle 16 bit đó trong một thanh ghi nào đó (thường là BX).
Trình tự điển hình để Copy một file rất giống với trong ngôn ngữ lập trình Pascal - ta sẽ mở, đọc, ghi và đóng file lại - ngưng ta sẽ sử dụng DOS chứ không phải là PASCAL ở đây.
Ta thực hiện công việc đó như sau: - tạo một xâu ký tự tên file (ASCIIZ string) trong bộ nhớ, ví dụ:
FILE_36 DB "C:\Novel\Chapter.89",0 - mở file và lấy file handle cho nó (INT 21h service 3Dh). - tạo một file mới (INT 21h service 3Ch). - đọc từ file nguồn (file thứ nhất) (INT 21h service 3Fh). - viết vào file mới (file đích hay file thứ hai) (INT 21h service 40h). - sau đó đóng cả hai file lại (INT 21h service 3Eh). service 3Fh giống như Read() service 40h giống như Write() Cũng giống như trong Pascal, chúng ta có thể coi các thiết bị (devices) như các file logic. Đặc biệt chúng ta có thể sử dụng Predefined handle để truy cập tới các thiết bị vật lý khác nhau.
Sau đây là một số Predefined handles:
handle Device
0 Standard Input (STDIN), usually keyboard 1 Standard Output (STDOUT), usually screen
2 Standard Erro device (STDERR)
3 Standard Auxiliary device (STDAUX)
4 Standard Printer
Thí dụ, nếu chúng ta chọn service 40h và trao giá trị 04h cho file handle thì Output sẽ đi ra máy in.
II. Các dịch vụ file handle của DOS và các mã lỗi (error codes)
1. Các dịch vụ File Handle
Có rất nhiều dịch vụ file handle của DOS, sau đây là một số service:
File Handle Service Number You set It Returns --------------------------------------------------------------------- Create Subdirectory 39h DS:DX to ASCIIZ if CY=1, AX chứa mã lỗi
string Delete Subdirectory 3Ah DS:DX to ASCIIZ if CY=1, AX chứa mã lỗi
string Change Directory 3Bh DS:DX to ASCIIZ if CY=1, AX chứa mã lỗi
string Create File 3Ch DS:DX to ASCIIZ if CY=1, AX chứa mã lỗi
string if CY=0
CX= Attribute Open File 3Dh DS:DX to ASCIIZ if CY=1, AX chứa mã lỗi string if CY=0, AX=File handle
AL=mode Close File 3Eh BX=File handle if CY=1, AX chứa mã lỗi Read from File 3Fh BX=Handle if CY=1, AX chứa mã lỗi CX=#Byte wanted if CY=0, AX=#Byte read
DS:DX=Buffer Write to File 40h BX=Handle if CY=1, AX chứa mã lỗi CX=#Byte wanted
DS:DX=Buffer Delete a File 41h DS:DX to ASCIIZ if CY=1, AX chứa mã lỗi string Move Read/Write pointer 42h CX:DX=#Bytes to if CY=1, AX chứa mã lỗi
move if CY=0, DX:AX=new
BX=File Handle location in file.
AL="Methode" Find 1st Matching file 4Eh DS:DX to ASCIIZ if CY=1, AX chứa mã lỗi
CX=Attribute if CY=0 then DTA has
21 bytes reserved
1 byte: file's attrib.
1 word: file's time 1 word: file's date
1 Dword: file's size 13 byte: ASCIIZ name Find next Matching file 4Fh DTA as set same as for 4Eh
by service 4Eh Rename File 56h DS:DX to ASCIIZ if CY=1, AX chứa mã lỗi
ES:DI to new
name (also ASCIIZ) ---------------------------------------------------------------------
Còn có rất nhiều Serice khác nữa, trong đó có các Service tạo các file tạm (temporary file), lấy hoặc đặt lại các thuộc tính Date, Time của file ...
1.1. Các mã lỗi trả về trong AX (Error Codes)
Trong DOS version 4.0 có 91 mã lỗi khác nhau, ở các version sau này con số đó còn lớn hơn. Sau đây là một số mã lỗi thường gặp:
Error codes Means
1 Invalid function number
2 File not found
3 Path was not found
4 Too many files open at once
5 Access denied for this operation
6 File handle used is invalid
7 Memory Control Blocks destroyed
8 Insufficient memory
15 Invalid drive was specified
16 Cannot delete current directory
19 Cannot write on a write-protected diskette
21 Drive not ready 23 Disk data error 25 Disk seek error
27 Sector not found
28 Printer needs paper
29 Write fault
30 Read fault
61 Print queue is full 1.2. File Access Modes
Mode truy cập file được truyền qua AL, có các Mode sau:
Access Mode for Opening Files Means
0 Open file for Read only 1 Open file for Write only
2 Open file for both Read and Write
1.3. Các thuộc tính của File (không được là tên nhãn đĩa) truyền qua CX
File Attribute Means
0 Plain old file
1 Read-Only
2 Hidden File (Hidden from directory searches)
4 A System file
8 Used for the Volume label of a Disk
10 This file name is the name of a subdirectory
1.4. Cách dịch chuyển Read/Write Pointer khi dùng service 42h
( Methods for INT 21h Service 42h)
Method (in AL) Means
0 Set R/W pointer to CX:DX bytes from the beginng of the file
1 Move R/W pointer CX:DX bytes from where it is now
2 Set R/W pointer to CX:DX bytes from end of file
Note: Vị trí mới của R/W pointer được trả về trong DX:AX
if CF=CY=1, mã lỗi trả về trong AX
2. Chương trình thí dụ Program RUBOUT
Đây là một chương trình đơn giản nhất - xoá một file, sử dụng Service 41h. Service này không đòi hỏi 1 file handle để xoá file, nó chỉ cần một ASCIIZ string cho tên file (có cả đường dẫn).
;------------------------------------------------------------------------- ;Program Rubout.asm it will delete a file by specifying file name ;-------------------------------------------------------------------------
Code Segment
Assume CS:Code, DS: Code org 100h start: jmp rubout
the_buffer db 50
byte_typed db 0
characters db 50 dup(0)
prompt db "File to delete ? $"
ok_message db "File deleted $"
not_ok_message db "File NOT deleted $" rubout proc near
mov dx, offset prompt
mov ah,9
int 21h
mov AH,0AH
mov dx, offset the_buffer
int 21h
mov bh,0
mov bl, byte_typed
add bx, offset characters
mov byte ptr[bx],0
mov dx, offset characters
mov ah,41h
int 21h
jnc all_ok
mov dx, offset not_ok_message
jmp print all_ok: mov dx, offset ok_message print: mov ah,09
int 21h exit: int 20h rubout endp
end start
3. Chương trình minh hoạ: Program BACK.ASM
Đây là chương trình minh hoạ cách thức mà Assembly language có thể làm việc với data của file. Chương trinh này sẽ tạo một file lưu kiểu .BAK của một file đã có trên đĩa do ta chỉ định tên của file đó ra. Thí dụ, nếu ta chạy BACK.COM rồi chỉ ra tên 1 file NOVEL.ONE thì BACK.COM sẽ tạo ra một file mới NOVEL.BAK là bản sao của file trên.
BACK.COM chỉ là một chương trình minh hoạ, trong đó tất cả mã lệnh của chương trình và dữ liệu mà nó đọc từ file nguồn (chỉ đọc một lần) đều đặt trong một đoạn, chính vì vậy mà file nguồn làm ví dụ phải có kích thước hạn chế <= 60 KB. Tất nhiên ta có thể cải tiến thêm một chút để BACK.COM có thể COPY các file kích thước tuỳ ý. Ngoài ra để đơn giản hoá chương trình, tên file đưa vào sẽ không có đường dẫn.
Việc sử dụng file handle để làm việc với file giúp ta có thể làm việc trực tiếp với data của file, không còn lớp đệm (buffer) trung gian nữa, ta có thể đọc bao nhiêu byte tuỳ ý, chẳng hạn 12 bytes, 279 bytes ...
3.1 Stack của file .COM (The .COM file stack)
Đây là một điểm quan trọng khi ta thao tác với một lượng số liệu lớn, thí dụ như khi copy file. DOS sử dụng stack để chứa địa chỉ trở về khi nó gặp một lệnh CALL; chúng ta dùng stack cùng với các lệnh PUSH, POP để lưu giữ các thông tin trạng thái, stack của .COM file cùng nằm trong một segment với mã lệnh và data. - Trong segment của chương trình .COM, stack nằm ở trên cùng (phía địa chỉ
cao nhất). địa chỉ hiện thời của stack luôn được trỏ bởi SS:SP
Khi bắt đầu chương trình SP luôn trỏ tới FFFE nghĩa là cách đỉnh của segment 1 word. Stack có kích thước thay đổi tuỳ theo hoạt động của chương trình, khi stack lớn lên (sau một lệnh PUSH chẳng hạn) nó phát
triển xuống phía dưới (nghĩa là SP giảm đi) từng word một. - con trỏ stack bổ sung là BP (Auxilary stack pointer) - điều cực kỳ quan trọng là chương trình không được viết vào stack, nếu không chương trình sẽ bị phá huỷ (crash). Nói chung ta sẽ không được viết gì vào miền 256 byte cao nhất (Top) của segment để tránh viết đè
vào stack.
3.2 Chương trình BACK.ASM (số dòng được ghi chỉ để tiện phân tích)
CODE SEGMENT 1 ASSUME CS: CODE, DS: CODE 2 ORG 100H 3 START: JMP BACK 4 THE_BUFFER DB 13;tạo buffer 13 byte cho INT 21h, 5 BYTE_TYPED DB 0;service 0AH 6 FILE_ONE DB 12 DUP(0) 7 MAKE_ME_ZERO DB 0;byte cuối của ASCIIZ string 8 FILE_TWO DB 8 DUP(0), ".BAK",0 9 HANDLE_1 DW 0 10 HANDLE_2 DW 0 11 PROMPT DB "Filename to back up: $" 12 BACK PROC NEAR 13 MOV DX, OFFSET PROMPT 14 MOV AH, 9;write out a string ended with '$' 15 INT 21H;pointed to by DS:DX 16 MOV AH, 0AH 17 MOV DX, OFFSET THE_BUFFER;read in a string 18 INT 21H;and store at [DS]:[DX] 19 MOV MAKE_ME_ZERO, 0;overwrite CR character (0Dh) 20 MOV CX, 8 21 MOV SI, OFFSET FILE_ONE 22 MOV DI, OFFSET FILE_TWO 23 REP MOVSB 24 MOV DX, OFFSET FILE_ONE;OPEN 1ST FILE 25
MOV AX, 3D00H;AL=0 means open file for read only 26
INT 21H 27 MOV HANDLE_1, AX 28 MOV DX, OFFSET FILE_TWO 29 MOV AH, 3CH;CREATE BACKUP FILE 30
MOV CX, 0;CX=0 (attrib) means Plain old file 31
INT 21H 32 MOV HANDLE_2, AX 33 MOV AH, 3FH 34 MOV CX, 60*1024 35 MOV DX, OFFSET DATA 36 MOV BX, HANDLE_1 37 INT 21H 38
MOV CX, AX;set number of bytes to write to number 39
MOV AH, 40H;actually read. 40 MOV BX, HANDLE_2 41 INT 21H 42 MOV AH, 3EH 43 MOV BX, HANDLE_1 44 INT 21H 45 MOV BX, HANDLE_2 46 INT 21H 47 EXIT: INT 20H 48 BACK ENDP 49 DATA: 50 CODE ENDS 51 END START 52
- các dòng từ 5..12
dành một số byte để chứa tên file mà chương trình nhận vào, tên file mới sẽ tạo ra, dòng ký tự nhắc (prompt), 2 words để chứa File handle của 2
file nguồn và đích.
3.3 Mở file nguồn để đọc, tạo file mới để chép file nguồn sang
- các dòng 25..28
mở file nguồn ở MODE Read Only (sau khi đã nhận vào tên file từ bàn phím), lưu File handle của nó vào word có tên Handle_1 để sử dụng khi
đóng file lại. - các dòng 29..33
tạo một file mới bằng service 3Ch, với thuộc tính CX=0 (plain old file), nghĩa là mở để đọc, ghi, lưu File handle của nó vào word có tên Handle_1 để sử dụng khi đóng file lại. nếu đã tồn tại một file trùng tên thì file đã tồn tại bị cắt giảm kích thước = 0 , nếu file đó có thuộc tính read
only thì tác vụ 3Ch này thất bại.
Tham khảo thêm: nếu muốn lấy/đặt thuộc tính của file thì sử dụng service
43h, INT 21h (CHMOD), như sau:
input
AH:= 43H
AL:= SubFunction = 0: lấy thuộc tính file hiện hành
:= SubFunction = 1: đặt thuộc tính file
DS:DX:= ASCIIZ filename nếu AL:= 0
Attribute nếu AL:= 1
nếu không có tên ổ đĩa và đường dẫn thì
ngầm định là ổ đĩa và đường dẫn hiện thời.
return
AX = mã lỗi nếu CF=CY
CX = attrib. của file hiện thời, nếu AL khi gọi = 0 - dòng 50
chỉ là một nhãn, ta dùng nó để chuẩn bị miền chứa data từ file nguồn sẽ được đọc vào. Nhãn DATA: này cần đặt ở cuối cùng của chương trình, nhưng vẫn phải nằm bên trong cùng segment với chương trình, do đó phải đứng
trước Pseudo-Op
CODE ENDS
Việc dùng một nhãn bên ngoài thủ tục (DATA: đứng sau BACK ENDP) có nghĩa là tất cả dữ liệu của ta sẽ ghi vào ngay sau chương trình trong bộ nhớ,
trên cùng một segment.
Chúng ta không cần dành chỗ cho DATA bằng Directive DATA DB bởi vì sau chương trình chúng ta có thể sử dụng toàn bộ phần còn lại của segment, miễn là không dụng chạm đến Stack. Nếu chúng ta làm ngược lại, sử dụng DATA DB và sau đó là các byte để dành cho dữ liệu thì chương trình của chúng ta bao gồm cả các byte để dành đó, tất nhiên là lớn hơn và hoàn
toàn không cần thiết như vậy.
3.4 Việc đọc từ File
Trong ngôn ngữ lập trình PASCAL có 3 loại file: TEXT, TYPED, UNTYPED. Trong TEXT FILE chúng ta sẽ tìm thấy (0Dh 0Ah) ứng với vị trí cuối mỗi dòng. Các file có định kiểu chỉ chứa dữ liệu của một kiểu cụ thể, kiểu đó ta phải khai báo trước.
Các file không định kiểu là các file không có cấu trúc và có thể xem chúng như là bao gồm các byte không có liên hệ gì đến nhau cả.
Trong assembly language việc sử dụng tất cả các loại file đều giống như việc sử dụng file không định kiểu trong PASCAL. - các dòng lệnh 34..38 (đọc từ file nguồn)
MOV AH, 3FH 34 MOV CX, 60*1024 35 MOV DX, OFFSET DATA 36 MOV BX, HANDLE_1 37 INT 21H 38 chúng ta không biết file nguồn dài bao nhiêu vì vậy ta yêu cầu đọc số lượng byte lớn nhất có thể được (60 KB), service 3Fh sẽ đọc số byte lớn nhất có thể đọc được (cho đến khi gặp dấu END OF FILE), sau khi đọc xong nó sẽ trả về AX số byte thực sự đọc. Con số này ta sẽ sử dụng để báo cho service 40h (Write to file) số byte cần ghi.
Đây là cách chúng ta sẽ thường làm khi làm việc với File trong Assembly language. Tất nhiên ta có thể tính toán ra kích thước file nhưng không nhất thiết phải làm như vậy.
3.5 Việc ghi vào File đích
Ta sẽ sử dụng Service 40h, INT 21h để ghi vào file đích. Vì dữ liệu của cả 2 file nguồn và đích đều không định kiểu, ta chỉ cần quan tâm tới số byte chứ không có vấn đề số dòng hay số phần tử của một kiểu nào đó như các file kiểu text hay record ... trong PASCAL.
MOV CX, AX;set number of bytes to write to number 39
MOV AH, 40H;actually read. 40 MOV BX, HANDLE_2 41 INT 21H 42 3.6 Đóng file
sử dụng service 3Eh, INT 21h
MOV AH, 3EH 43 MOV BX, HANDLE_1 44 INT 21H 45 MOV BX, HANDLE_2 46 INT 21H 47
4. File có định kiểu trong ASSEMBLY LANGUAGE - File of Records
Trong các version DOS trước đây khi các thao tác file sử dụng FCBs, NSD quy định kích thước Record và kích thước khối byte (đọc/ghi). Khi sử dụng file handle chúng ta chỉ phải chọn số byte cho thao tác đọc/ghi ...
Như vậy, thực tế với assembly language chỉ có 1 kiểu file - file không định kiểu, trong đó mỗi phần tử là một byte. Tuy nhiên với người lập trình assembler có thể tổ chức file như kiểu File of Records trong PASCAL.
4.1 File of Records
Giả sử chúng ta cần tổ chức một file dữ liệu chứa Tên và số Phone tương ứng của một số người. Ta có thể coi đó là 2 field của 1 record như trong PASCAL.
Ta có thể định nghĩa một Record như vậy trong MEMORY như sau:
NAME DB 16 DUP(0) NUMBER DB 16 DUP(0) như vậy mỗi record dài 32 byte, bắt đầu bằng nhãn NAME. Ta có thể tạo nhiều Record như thế trong bộ nhớ, điều cần chú ý là với các Record mà số liệu của các Field ngắn hơn ta định, chúng ta cần điền 0 vào để đảm bảo cho tất cả các Record có chiều dài bằng nhau. Chính nhờ kích thước các Record bằng nhau mà ta có thể truy cập đến một record tuỳ ý một cách dễ dàng (RANDOM ACCESS) thông qua STT của nó.
Nếu chúng ta dùng Record có chiều dài thay đổi thì chúng ta phải ghi các dấu hiệu End of Record vào trong file làm ranh giới giữa các record. Trong PASCAL đó là cặp CR,LF. Chính vì lý do này khi cần truy cập một record nào đó ta phải duyệt từ đầu để đếm số record, cách truy cập đó được gọi là Truy cập tuần tự (Sequential Access).
Để viết record trong bộ nhớ vào file, trước hết cần mở (open) hoặc tạo (create) file, cho DS:DX trở tới NAME, CX:= số byte cần ghi (ở đây là 32 byte).
4.2 Đọc số liệu từ file - con trỏ đọc/ghi (Read/Write Pointer)
Tại mỗi thời điểm ta có thể đọc/ghi tại một vị trí nào đó trong file, ta có thể coi đó là 'Cửa sổ file' hay 'cửa sổ đọc/ghi', vị trí đó được trỏ bởi Read/Write Pointer, DOS có Service 42h cho phép ta định vị con trỏ này tới một vị trí tuỳ ý trong file.
File Handle Service Number You set It Returns --------------------------------------------------------------------- Move Read/Write pointer 42h CX:DX=#Bytes to if CY=1, AX chứa mã lỗi
move if CY=0, DX:AX=new
BX=File Handle location in file.
AL="Methode"
Method (in AL) Means ---------------------------------------------------------------------
0 Set R/W pointer to CX:DX bytes from the beginng of the file
1 Move R/W pointer CX:DX bytes from where it is now
2 Set R/W pointer to CX:DX bytes from end of file
Note: Vị trí mới của R/W pointer được trả về trong DX:AX
if CF=CY=1, mã lỗi trả về trong AX
Khi đã biết kích thước (bytes) của Record ta có thể định vị nó như sau:
vị trí (bytes) = số thứ tự Record x kích thước Record
(stt đếm từ 0, kích thước tính bằng Byte)
5. Chương trình minh hoạ: PROGRAM PHONE.ASM
Chúng ta giả sử đã có file NUMBER.DAT chứa các record như mô tả ở trên, mỗi record dài 32 byte, gồm 2 field, mỗi field dài 16 byte. Field 1 chứa tên, Field 2 chứa số phone của tên tương ứng.
Nếu trên đĩa thực hành chưa có NUMBER.DAT, ta có thể sử dụng DEBUG tạo ra một cách nhanh chóng như hướng dẫn sau đây, chỉ cần tạo ra 3 record để thử là được, ngoài ra ta dùng một 'mẹo vặt' để sử dụng INT 21h, service 09h để in ra trường tên một cách thuận lợi, bằng cách điền đầy 3 x 32 byte của miền nhớ sẽ chứa 3 record các ký tự '$', sau đó mới đưa nội dung của 3 record vào.
Hãy làm theo các bước sau:
- DEBUG
- F 100 15F "$" ;
- D 100 ;
- A100 ;
- XXXX:0100 DB "Bo Mon Tin hoc" ;
- XXXX:010E
- A110
- XXXX:0110 DB "581 530"
- XXXX:0117
- A120
- XXXX:0120 DB "Toan Co Tin hoc"
- XXXX:0117
- A130
- XXXX:0130 DB "581 135"
- XXXX:0137
- A140
- XXXX:0140 DB "TAXI PT"
- XXXX:0147
- A150
- XXXX:0150 DB "533 171"
- XXXX:0157
Sau đó ghi vào file NUMBER.DAT bằng cách mà chúng ta biết khi học sử dụng chương trình DEBUG.
CODE SEGMENT
ASSUME CS: CODE, DS: CODE
ORG 100H
START: JMP PHONE
FILENAME DB "NUMBER.DAT",0
FILEHANDLE DW 0
PERSON_NAME DB 16 DUP(0) NUMBER DB 16 DUP(0)
PROMPT DB "Get phone number (1..3) or Quit (Q): $"
PHONE PROC NEAR
MOV DX, OFFSET FILENAME
MOV AL, 0;Read Only MOV AH, 3DH;Open File
INT 21H
MOV FILEHANDLE, AX ASK: MOV DX, OFFSET PROMPT
MOV AH, 9;Write out a string pointed to by DS:DX
INT 21H
MOV AH, 1;Get into AL a typed in character
INT 21H
CMP AL, 'Q'
JE QUIT
SUB AL, "0"
MOV CL, 5
DEC AL
SHL AL, CL;Multiply AL by 2 to the power of 5 (32)
MOV CX, 0 MOV DH, 0
MOV DL, AL
MOV AH, 42H;Move Read/Write Pointer
MOV AL, 0;Set Methode to 0: set R/W Pointer to CX:DX bytes MOV BX, FILEHANDLE; from the beginning of the file
INT 21H
MOV DX, OFFSET PERSON_NAME
MOV BX, FILEHANDLE
MOV CX, 32;Bytes wanted
MOV AH, 3FH;Read from a file
INT 21H
MOV AH, 9;Write out a string pointed to by DS:DX
MOV DX, OFFSET PERSON_NAME
INT 21H
MOV DX, OFFSET NUMBER
INT 21H JMP ASK QUIT: MOV BX, FILEHANDLE
MOV AH, 3EH;Close a file
INT 21H EXIT: INT 20H PHONE ENDP CODE ENDS
END START
Chương trình này hoàn toàn nhằm mục đích minh hoạ các thao tác với File of Record, giao diện với NSD hết sức 'thô sơ', các xâu ký tự in ra màn hình liên tiếp nhau, thiếu cả dấu xuống dòng. Có thể thêm dấu xuống dòng sau mỗi lần in ra màn hình một xâu ký tự bằng các cách sau:
1/ in ra xâu ký tự CR_LF như sau: CR_LF DB ODh, 0Ah, "$"
sử dụng INT 21h, Service 09. 2/ xây dựng 1 thủ tục CR_LF in dấu xuống dòng, bảo vệ các thanh ghi trước
khi thực hiện và phục hồi lại chúng trước khi trở về (RET).
CODE SEGMENT BYTE PUBLIC
ASSUME CS:CODE
CR_LF PROC NEAR
PUSH AX PUSH DX
MOV AH,2;DOS ah=function 02h
MOV DL,0DH
INT 21H
MOV DL,0AH
INT 21H
POP DX POP AX
RET CR_LF ENDP
CODE ENDS
END
Bài giảng môn Assembly Language, Bài 5 - $ - Nguyễn Đình Việt, ĐHQG HN, 1995 -------------------------------
Bài 6 Quản lý màn hình và Đồ hoạ nhanh
( SCREEN HANDLING AND FAST GRAPHICS )
Trong ngôn ngữ lập trình PASCAL có rất nhiều thủ tục và hàm phục vụ cho các thao tác trên màn hình, kể cả trong chế độ đồ hoạ. Tuy nhiên hầu hết các chức năng màn hình đều chạy khá chậm.
Trong Assembly Language thì vấn đề lại ngược lại: có quá ít dịch vụ hỗ trợ chế độ đồ hoạ (không kể trong OS/2 Presentation Manager), tuy nhiên tốc độ lại rất cao - nhất là khi ta viết trực tiếp vào vùng nhớ màn hình. Việc hiển thị trên màn hình PC không ngừng được cải thiện, đã có một số tiêu chuẩn về màn hình đồ hoạ như: MDA, CGA, EGA, VGA và HERCULES, chưa có một tiêu chuẩn nào được coi là duy nhất. Ngay cả các CARD mới ra đời như Super VGA, TIGA cũng chưa có tiêu chuẩn duy nhất.
Sau đây trình bày về các loại VIDEO GRAPHICS CARD
- Monochrome Display Adapter (MDA), cùng với CGA là những Graphics Adapter đầu tiên mà IBM đưa ra như là những chuẩn trong các máy PCs đầu tiên
trong năm 1981.
+ chỉ có 1 Mode hoạt động là TEXT MODE 80x25
+ độ phân giải cao 720 x 350, không bị nhấp nháy.
+ MDA sử dụng bộ nhớ hiển thị 4 KB, chỉ có 1 trang.
+ Sử dụng VIDEO CONTROLLER Motorola MC6485 giống CGA CARD
- Color Graphics Adapter (CGA), được IBM đưa ra thị trường cũng trong năm
1981, có khả năng hiển thị đồ hoạ.
+ có thể hiển thị 4 màu 1 lúc, chọn 4 màu trong số 16 màu ở độ phân giải
là 320 x 200; cảm giác nhấp nháy khá rõ.
+ Độ phân giải cao nhất là 640 x 200, nhưng chỉ có 2 "màu". + Có TEXT MODE với các ký tự là các ma trận điểm tối thiểu.
+ CGA sử dụng bộ nhớ hiển thị 16 KB
+ Có thể nối màn ảnh máy thu hình với đầu ra của CGA CARD.
+ Sử dụng VIDEO CONTROLLER Motorola MC6485 giống MDA CARD
- Hercules Graphics Card (HGC)
Năm 1982, một năm sau khi IBM PC ra đời, hãng Hercules đưa ra CARD đồ hoạ mới mang tên hãng mình và trở thành nổi tiếng. HGC được coi là 1
chuẩn trong các CARD đồ hoạ Monochrome.
+ Chế độ Graphics có độ phân giải 720x348, có 2 trang màn hình.
+ Dựa trên VIDEO CONTROLLER Motorola MC6485, hoàn toàn tương thích với
MDA.
- Enhanced Graphics Adapter (EGA)
Sau khi HGC ra đời, IBM cố gắng thiết kế 1 CARD mới thay cho CGA và vượt các tính năng của HGC. Năm 1985 IBM cho ra đời EGA. Có các tính năng bao
trùm CGA và MDA, ngoài ra còn có các MODE riêng.
+ Đây là loại CARD đồ hoạ đầu tiên có thể điều khiển hiển thị cả màn
hình đơn sắc lẫn màn hình màu.
+ Có khả năng hiển thị đơn sắc trên màn hình đơn sắc, do đó nó giống như HGC. Tất nhiên EGA CARD khi nối ghép với màn hình EGA thì hiệu quả là
cao nhất.
+ Độ phân giải 640x350 pixels , có thể hiển thị cùng một lúc 16 màu trong
số 64 màu, màn hình EGA không nhấp nháy.
+ EGA sử dụng bộ nhớ hiển thị có thể lớn tới 256 KB để hiển thị các
trang màn hình đồ hoạ khác nhau.
+ Không sử dụng VIDEO CONTROLLER Motorola MC6485 mà sử dụng VLSI Chips. Mọi thông tin màn hình đều chứa trong VIDEO RAM, đây là điểm tiến bộ
hơn hẳn của EGA so với các CARD ra đời trước nó.
+ Một điểm khác biệt quan trọng của EGA so với MDA, CGA là nó không được IBM ROM BIOS hỗ trợ, vì vậy EGA CARD có ROM BIOS riêng của nó thay thế cho BIOS nguyên thuỷ (original) của IBM, cho phép truy cập tới tất cả
các tính năng (features) của EGA CARD.
- VGA (VIDEO GRAPHICS ARRAY)
Tháng 4 năm 1987, IBM cho ra đời VGA cùng với việc cho ra đời họ máy PS/2. VGA CARD có tất cả các ưu điểm của EGA, hơn nữa nó có thể thể hiện được nhiều màu sắc hơn, độ phân giải cao hơn, hiển thị TEXT tốt
hơn.
+ Ưu điểm của VGA so với EGA là mức độ tổ hợp (của mạch điện tử) cao hơn,
tất cả các khối logic điều khiển được tổ hợp trong 1 chip.
+ khác với EGA, VGA CARD gửi tiến hiệu analog tới monitor chứ không phải tín hiệu số, VGA CARD có thể sinh ra hơn 260,000 màu khác nhau trong
trường hợp các MODE 2,4,16,256 cùng active.
+ Độ phân giải cao nhất: 640 x 480 ,số màu có thể thể hiện là 2,4 hay 16. + ở độ phân giải thấp (320 x 200) VGA có thể hiển thị đồng thời 256 màu
được chọn trong 256 x 1024 màu có thể.
+ RAM VIDEO: Độ phân giải cao, số màu hiển thị lớn đòi hỏi RAM VIDEO cũng
lớn theo, minimum là 256 KB , MAX là 512 KB.
+ Giống như EGA, VGA không được IBM ROM BIOS hỗ trợ, vì vậy VGA CARD có ROM BIOS riêng của nó thay thế cho BIOS nguyên thuỷ (original) của IBM,
cho phép truy cập tới tất cả các tính năng (features) của VGA CARD.
- Super VGA
Super VGA Card có cấu trúc phần cứng giống như VGA Card thông thường,
nhưng nó hiển thị Pixel nhanh hơn.
Lưu ý:
Các CARD màn hình của IBM luôn luôn bị các hãng khác, đặc biệt là các hãng ở Châu á Copy, họ đưa ra các CARD tương thích để cạnh tranh, các CARD này thường có các MODE bổ sung và rất có thể các MODE này không
được nhiều phần mềm hỗ trợ.
Bài này sẽ chủ yếu khám phá BIOS INT 10H, xem xem Assembly Language có thể làm tốt những việc gì trên màn hình.
BIOS INT 10H chứa các dịch vụ màn hình mà các chương trình Assembly Language phải dùng đến. Tất nhiên có một số Function của INT 21h cũng in được ký tự ra màn hình nhưng nó cũng gọi INT 10H ra thực hiện mà thôi. BIOS nói chung là phần mềm trong ROM nhưng ngày nay thường được tăng cường bằng các phần mềm bổ sung nạp vào từ các file đĩa.
Điều rất cần nhớ là sau khi gọi INT 10H các thanh ghi sau nói chung sẽ bị phá huỷ (giá trị trong đó): AX, SI, DI I. Các dịch vụ màn hình của BIOS trong chế độ TEXT
(The BIOS Screen Handling Services)
Chế độ này còn có tên gọi Alphanumeric-Mode
BIOS và DOS đều có các dịch vụ in ký tự lên màn hình, nhưng BIOS có thể làm được nhiều việc mà DOS không làm được, thí dụ: - in các ký tự có màu - viết vào Screen Buffer - thay đổi thuộc tính của ký tự - di chuyển con trỏ trên màn hình, cuộn màn hình ...
Trang màn hình (Video Memory Page)
Vùng nhớ giành cho màn hình trong một số Video Mode có thể được chia thành các trang. Một Video Adapter thường kèm theo một lượng bộ nhớ nhất định, hơn nữa Adapter này có thể làm việc ở một số Mode khác nhau, các Mode khác nhau cần dung lượng nhớ tương ứng khác nhau, do vậy trong một số Mode còn một số lượng nào đó của bộ nhớ Video chưa được sử dụng, IBM cho phép ta sử dụng bộ nhớ đó như các trang thêm vào (Extra Pages) của bộ nhớ hiển thị.
Thí dụ CGA Adapter trong chế độ đồ hoạ cần 16 KB còn trong chế độ TEXT chỉ cần có 4 KB, do vậy vùng nhớ Video của CGA có thể chia thành 4 trang trong chế độ TEXT. Bình thường thì cả 4 trang này là các bản copy của nhau và Page 0 được hiển thị.
Với BIOS ta có thể lựa chọn để viết vào trang nào là tuỳ ý, trang được ngầm định sử dụng là trang số 0.
1. Service 2,3: đặt và lấy vị trí của con trỏ trên màn hình
lưu ý rằng góc trên bên trái của màn hình có toạ độ Row,Column = 0,0
1.1 Đặt vị trí con trỏ trên màn hình (Set cursor position)
AH:= 2;SERVICE NUMBER
DH,DL:= Row, Column of new position
BH:= Page Number (usually = 0)
service này giống như Gotoxy(x,y) trong PASCAL
1.2 Lấy vị trí con trỏ trên màn hình (Get cursor position)
AH:= 3;SERVICE NUMBER
BH:= Page Number (usually = 0) Return:
DH,DL:= Row, Column of current cursor position
service này giống như WhereX,WhereY trong PASCAL
Service này rất hữu ích khi ta viết Popup Program, nó cần xác định vị trí Cursor trước và sau khi nó thực hiện.
2. Service 6,7: Cuộn màn hình Active lên, xuống
(Scroll Active Page Up or Down)
AH:= 6: Scroll Up
:= 7: Scroll Down AL:= Number of lines to scroll (blank line will be inserted)
(AL:= 0 means blank the whole active Window.) (CH,CL):= Row,Column of upper left corner of scroll window (DH,DL):= Row,Column of lower right corner of scroll window BH:= Attribute to be used on blank line
lưu ý - Ta có thể cuộn một phần tuỳ ý của màn hình độc lập so với phần còn lại - Dòng Blank sẽ được chèn vào Window và đó là nơi sẽ tiếp nhận input - Bằng cách đặt thuộc tính cho dòng Blank, ta chọn được màu cho nó.
Thuộc tính của ký tự hiển thị trên màn hình
Mỗi ký tự hiển thị trên màn hình có 1 byte mô tả thuộc tính của nó, nghĩa là mô tả nó sẽ được hiển thị trên màn hình như thế nào. ý nghĩa như sau:
7 6 5 4 3 2 1 0
Blink Red Green Blue Intens. Red Green Blue
Background Color Foreground Color
( màu nền ) ( màu chữ )
Bit nào được SET = 1 thì thuộc tính tương ứng với bit đó là ON, chúng ta có thể trộn các màu với nhau bằng cách cộng giá trị các bit lại.
- khi đặt thuộc tính cho một dòng mới, mỗi vị trí (ký tự) trên dòng đó được đặt cùng một thuộc tính dù còn chưa có ký tự nào trên dòng đó. Các ký tự được đưa vào dòng này sẽ nhận thuộc tính đã được đặt sẵn của dòng, tất nhiên ta có thể đặt thuộc tính (tuỳ ý) cho từng ký tự khi in chúng
ra nếu ta muốn. - Thí dụ ký tự màu đỏ (Foreground) trên nền Xanh lá cây (Background) có
byte thuộc tính như sau:
0 0 1 0 0 1 0 0 B = 24H - ở màn hình Monochrome ta không thể sử dụng từng màu riêng rẽ được, nhưng
chúng ta có thể sử dụng các thuộc tính sau:
+ 7: normal attrib. + 0FH: high intensity
+ 87H: blinking normal
+ F0H: blinking reverse video
+ 01: underline, thuộc tính này các màn hình đồ hoạ không có
+ 09: intense underline, thuộc tính này các màn hình đồ hoạ không có
Bộ đệm màn hình và các byte thuộc tính
Trong TEXT MODE
Mỗi ký tự hiển thị trên màn hình ứng với 2 byte trong bộ đệm màn hình, một byte là mã ASCII và một là byte thuộc tính. Byte mã ASCII đứng trước byte thuộc tính và chúng xếp xen kẽ như vậy trong bộ nhớ đệm màn hình. Ký tự đầu tiên trong bộ đệm ứng với góc trên bên trái màn hình. - Trong màn hình MDA, bộ đệm màn hình bắt đầu ở B000:0000 - Trong màn hình đồ hoạ, ở CGA Mode, bộ đệm màn hình bắt đầu ở B800:0000 - Trong màn hình EGA, VGA bộ đệm màn hình bắt đầu ở A000:0000 lưu ý: Trừ khi chúng ta đặt lại Video Mode, cả EGA lẫn VGA đều chạy ở Mode CGA, vì vậy bộ đệm màn hình bắt đầu ở B800:0000 Chỉ trong các Mode EGA hoặc VGA, bộ đệm màn hình bắt đầu ở A000:0000
Trong GRAPHICS MODE
Mọi điều cũng tương tự như trong TEXT MODE, nhưng không phải là ký tự mà là với các PIXEL (Picture Elements).
Thí dụ trong MODE CGA 320 x 200 có 320x200 = 64000 Pixels, trong MODE này CGA cho phép hiển thị 4 màu, vì thế mỗi Pixel cần 2 bit trong bộ nhớ, như vậy bộ nhớ mà CGA cần là:
64000 Pixels x 2 bit/Pixel / 8 bits/Byte = 16000 bytes, xấp xỉ 16 KB Ta có thể dùng DEBUG, lệnh E để thử viết vào bộ nhớ đệm màn hình, đồng thời cũng thấy được thuộc tính ngầm định của các byte trong bộ nhớ đó (7).
3. Service 9: Viết ký tự với thuộc tính của nó tại vị trí CURSOR
(Write Attribute/Character at Cursor Position)
- không di chuyển con trỏ sang phải trên màn hình sau khi viết ký tự ra. - Coi các ký tự điều khiển CR (Carriage Return: 0DH) và LF (Line Feed:
0AH) như các ký tự để in ra.
AH:= 9
AL:= ASCII CODE of character to write
BH:= Page Number (Usully = 0)
BL:= Character's attribute
CX:= Number of times to write character (restricted to 1 line on
screen)
Chương trình minh hoạ: CHECKER.ASM
CODE SEGMENT
ASSUME CS: CODE, DS:CODE
ORG 100h
entry: mov cx,255;Loop over all 255 combinations
mov al,0;Start printing with character 0
mov bl,0;and with initial attrib. = 0
mov dl,0;DL:= COLUMN, start at column 0
mov dh,4;DH:= ROW, start at row 4
color_loop: push ax
mov ah,2;Service 2 Set cursor position
int 10h
pop ax
mov ah,9;Print character in AL with
;it's attrib. in BL
push ax
push cx
mov cx,1;write 1 character
int 10h
pop cx pop ax
inc al;Select next character
inc bl;and next attrib.
inc dl;Set next cursor Column
cmp dl,79;Check if cursor is at the end
jb ok_cursor;of line
mov dl,0;Then move to beginning of
inc dh;next line
ok_cursor: loop color_loop
int 20h
CODE ENDS
END ENTRY
4. Service 0ah: Viết ký tự không kèm theo thuộc tính của nó tại vị trí
CURSOR
(Write Character alone at Current Cursor Position)
- không di chuyển con trỏ sang phải trên màn hình sau khi viết ký tự ra. - Coi các ký tự điều khiển CR (Carriage Return: 0DH) và LF (Line Feed:
0AH) như các ký tự để in ra.
Đặt: AH:= 0AH
AL:= ASCII CODE of character to write
BH:= Page Number (Usually 0)
CX:= Number of times to write character
(restricted to 1 line on screen)
Service này cũng giống như Service 9, chỉ khác có một điều là nó không
làm thay đổi thuộc tính của ký tự. Nói cách khác là: nếu ký tự viết ra đè lên 1 ký tự tại vị trí đó thì thuộc tính của ký tự mới lấy luôn thuộc
tính của ký tự bị viết đè.
5. service 0eh - Teletype Write
Đặt: AH:= 0EH
AL:= ASCII CODE of character to write
Sau đây là 2 điểm khác với Service 09 và 0Ah
- In ra một ký tự tại vị trí hiện thời của con trỏ, sau đó dịch chuyển
con trỏ sang phải
- Coi các ký tự CR (Carriage Return) và LF (Line Feed) như các lệnh chứ
không phải ký tự để in ra.
II. Các dịch vụ màn hình của BIOS trong chế độ graphics
Chế độ này còn có tên gọi All-Points-Addressable.
Các hỗ trợ của BIOS IBM PC cho việc lập trình vẽ đồ hoạ thật là nghèo nàn, chỉ có một dịch vụ vẽ 1 điểm (Pixel). Nếu so với máy Macintosh, có Toolbox Routines (like QuickDraw) hay System Routine trong OS/2 Presentation Manager, trong đó có các chương trình con vẽ Circle, Line,
Box ... thì thấy rõ hỗ trợ của BIOS IBM PC là cực tiểu. 1 INT 10H, SERVICE 0 - SET VIDEO MODE
Set: AH:= 0
AL:= New Video Mode
Khi chúng ta muốn làm việc trong chế độ Graphics, trước hết chúng ta phải đặt (Set) Mode ở độ phân giải mong muốn. Sau khi đã Set Graphics Mode, chúng ta sẽ ở trong Mode này cho tới khi chúng ta Set Mode khác.
Như vậy việc Set Mode thường làm đầu tiên trong công việc đồ hoạ.
Sau đây là các Mode của màn hình:
SCREEN MODE
------------------------------------------------------------------------ Mode Display lines Number Adapters Maximum Pages
(in AL) of Colors
------------------------------------------------------------------------
0 40X25 B&W text CGA, EGA, VGA 8 1 40X25 Color text CGA, EGA, VGA 8
2 80X25 B&W text CGA, EGA, VGA 4(CGA),8(EGA,VGA)
3 80X25 Color text CGA, EGA, VGA 4(CGA),8(EGA,VGA)
4 320X200 4 CGA, EGA, VGA 1 5 320X200 B&W CGA, EGA, VGA 1 6 640X200 2 (on or off) CGA, EGA, VGA 1
7 80X25 Monochrome MDA, EGA, VGA 1(MDA),8(EGA,VGA)
8 160X200 16 PCjr 1 9 320X200 16 PCjr 1 A 640X200 1 PCjr 1
B Reserved for future use
C Reserved for future use
D 320X200 16 EGA, VGA 8 E 640X200 16 EGA, VGA 4 F 640X350 Monochrome EGA, VGA 2 10H 640X350 16 EGA, VGA 2 11H 640X480 2 VGA 1 12H 640X480 16 VGA 1 13H 320X200 256 VGA 1
------------------------------------------------------------------------
- Ta thấy các Mode 0,1,2,3,7 là các Mode Text - chỉ hiển thị ký tự.
- Ta có thể vẫn cứ sử dụng Text như bình thường ngay trong Graphics
Mode, nhưng sẽ không có con trỏ.
- Các mode 0..6 được sử dụng trên CGA (tất nhiên cả trên EGA, VGA vì chúng có các khả năng hiển thị bao trùm các khả năng của CGA -tính
tương thích).
- Các mode 8..0A: của PCjr
- Các mode 0DH..10H là của EGA, VGA (ở đây VGA chạy trong mode của EGA)
- Các mode 11H..13H là của chỉ có ở VGA (mode 13H là Mode 256 màu) 2 INT 10H, SERVICE 0BH - SET CGA Color Palette (Colors in CGA 320x200 mode)
Set:
AH:= OBH
BH:= Palette Color ID (0 or 1, see below)
BL:= Color(s) to set with that Palette Color ID (see below)
BH:= 0
BL:= Background color (0..31), from now on, this will be color value 0 to be used in Write and Read dot. What colors 0..15 actually mean (blue,red,etc.) is
discussed below.
In Text mode, this background color will determine the screen border color (0..31; 16..31 means high
intensity.)
BH:= 1
BL: = 0: Selects Green/Red/Yellow Palette. Color value 1 in Write and Read Dot will be Green, color value 2
will be Red, color value 3 will be Yellow.
BL: = 1: Select this Palette and Color values: Cyan
(color value will be 1)/Magenta(2)/White(3).
- Khi EGA, VGA được SET trong MODE này (320x200), chúng có thể thể hiện
nhiều màu hơn.
2.1 palette ??????????
Trong Mode độ phân giải trung bình của CGA (320x200), ta có thể hiển
thị cùng một lúc 4 màu trên màn hình, tại bất kỳ Pixel nào.
Ta có thể chọn tuỳ ý 1 trong 4 màu, với 3 màu còn lại (màu 1,2,3) ta
chỉ có thể chọn được từ 1 trong 2 bảng màu (Palette).
Palette 1: Green, Red, Yellow
Palette 2: Cyan , Magenta, White
2.2 Màu nền của CGA (The CGA Background Color)
Các màu 1..3 có thể chọn từ bảng màu (Palette), còn màu 0 là màu nền
thì chúng ta có thể chọn trong số 16 màu.
Để chọn màu nền, ta sử dụng service 0BH mô tả như trên, với các tham số
cụ thể như sau:
AH:= 0BH
BH:= 0 To inform this service that you want to select the CGA
background color
BL:= (0..31), đó là 16 màu và 16 màu tương ứng với độ sáng tăng cường
0: Black (off) 8: Black 1: Blue 9: Light Blue 2: Green 10: Light Green 3: Cyan 11: Light Cyan 4: Red 12: Light Red 5: Magenta 13: Light Magenta 6: Brown 14: Yellow 7: White 15: Light White
Đây cũng là 16 màu ngầm định đầu tiên của EGA và VGA
2.3 Mode độ phân giải cao của CGA (Mode 6: 640x200)
Do hạn chế về dung lượng bộ nhớ, CGA chỉ có thể dành 1 bit cho 1 Pixel, như vậy mỗi Pixel chỉ có 2 'màu' là màu 0 và 1 ứng với 2 giá trị của 1 bít. Độ phân giải 640x200 tương ứng với 128 000 Pixel trên màn hình và
tương ứng với 128000/8 = 16000 byte trong bộ nhớ hiển thị.
3 INT 10H, SERVICE 0CH - Vẽ 1 điểm ảnh (Write pixel)
Set:
AH:= 0CH
DX:= Row Number (0..199, 0..349, 0..479)
CX:= Column Number (0..319, 0..639)
AL:= Color Value
BH:= Page Number (0 based) for multi-paged graphics modes
ở đây AX được hiểu khác nhau trong các tiêu chuẩn khác nhau CGA, EGA,
VGA.
- CGA: ở độ phân giải 320x200, màu:= 0..3 ở độ phân giải 640x200, màu:= 0, 1
- EGA: màu:= 0..15
- VGA: riêng ở mode 13H, màu:= 0..255
ở các mode khác màu:= 0..15
4. Chương trình minh hoạ BOX.ASM
4.1 Chương trình vẽ một hình chữ nhật lên màn hình trong mode graphics CGA,
sau đó không trả lại Mode màn hình ngầm định.
Code segment
Assume cs:code, ds:code
org 100h
start: jmp box
toprow dw 50
topcol dw 100 botrow dw 100 botcol dw 200
color db 15
box proc
mov ah,0
mov al,6; choose CGA Mode
int 10h
mov cx,botcol sub cx,topcol mov si,toprow mov di,topcol
toploop:
call writedot
inc di
loop toploop
mov cx,botrow sub cx,toprow mov si,toprow
sideloop:
mov di,topcol
call writedot
mov di,botcol
call writedot
inc si
loop sideloop
mov cx,botcol sub cx,topcol mov si,botrow mov di,topcol
botloop:
call writedot
inc di
loop botloop
int 20h box endp
writedot proc
push ax push cx push dx
mov dx,si mov cx,di
mov al,color
mov ah,0ch
int 10h
pop dx pop cx pop ax
ret writedot endp Code Ends
end start
4.2 Chương trình vẽ một hình chữ nhật lên màn hình trong mode graphics VGA,
sau đó trả lại (Reset) Mode màn hình ngầm định.
Code Segment
Assume CS:Code, DS:Code
org 100h
Start: jmp box
toprow dw 0 topcol dw 0
botrow dw 479
botcol dw 639
color db 4
Box proc
mov ah,0
mov al,12h; choose VGA Mode
int 10h
mov cx,botcol sub cx,topcol mov si,toprow mov di,topcol
Toploop:
call writedot
inc di
loop toploop
mov cx,botrow sub cx,toprow mov si,toprow
Sideloop:
mov di,topcol
call writedot
mov di,botcol
call writedot
inc si
loop sideloop
mov cx,botcol sub cx,topcol mov si,botrow mov di,topcol
Botloop:
call writedot
inc di
loop botloop
mov ah, 1;wait for a strike then turn to
int 21h;text mode 2 = B&W text
mov ah, 0 mov al, 2
int 10h;reset video adapter to default text mode
int 20h Box endp
Writedot proc
push ax push cx push dx
mov dx,si mov cx,di
mov al,color
mov ah,0ch
int 10h
pop dx
pop cx pop ax
ret Writedot endp Code Ends
End start
5. EGA, Service 10H - Set Individual EGA Palette Register
(Set 1 of 16 colors for 16-color mode)
Set: AH:= 10H
AL:= 0
BL:= EGA Palette Register to set (0..15)
BH:= rgbRGB value to set it to (0..63)
Với mỗi pixel, EGA có thể điều khiển 2 mức cường độ sáng của từng màu cơ bản: Red, Green, Blue. Mỗi màu cơ bản có 1 bit biểu diễn mức cường độ sáng thấp là r, g, b và 1 bit biểu diễn mức cường độ sáng trung bình là R, G, B, khi cả 2 bit cùng bằng 1 thì ứng với mức sáng cao (High Intensity) (r+R). Như vậy với mỗi màu cơ bản có 4 mức độ sáng là: 0=ff, r=low, R=medium và r+R= high.
EGA sử dụng 6 bit để mô tả trạng thái màu của 1 pixel rgbRGB, với 6 bit có thể tổ hợp được 0..63 màu khác nhau. Do cấu tạo phần cứng của EGA, tại mỗi thời điểm chỉ có thể chọn để hiển thị 16 màu trong số 64 màu có thể, mỗi tập 16 màu đó gọi là 1 bảng màu (Palette), như vậy có 16 Palette hay còn gọi 16 Mode của EGA.
Dưới đây là Mode (Palette) ngầm định của EGA
Color Value Color rgbRGB ---------------------------------------------------------------------------
0 Black 0 0 0 0 0 0
1 Blue 0 0 0 0 0 1 2 Green 0 0 0 0 1 0 3 Cyan 0 0 0 0 1 1
4 Red 0 0 0 1 0 0 5 Magenta 0 0 0 1 0 1 6 Brown 0 1 0 1 0 0 7 White 0 0 0 1 1 1 8 Dark Gray 1 1 1 0 0 0
9 Light Blue 1 1 1 0 0 1 10 Light Green 1 1 1 0 1 0 11 Light Cyan 1 1 1 0 1 1
12 Light Red 1 1 1 1 0 0 13 Light Magenta 1 1 1 1 0 1 14 Yellow 1 1 1 1 1 0 15 Intense White 1 1 1 1 1 1 --------------------------------------------------------------------------- 6. VGA, Service 10H, function 10H (subfunction or subservice)
set DAC Register (set 1 of 256 VGA colors)
Set: AH:= 10H AL:= 10H
BX:= Register to set (0..255)
CH:= Green Intensity CL:= Blue Intensity DH:= Red Intensity
Màn hình VGA khác các loại màn hình mà chúng ta xét đến trước đây ở chỗ nó là màn hình Analog, nghĩa là xét bên trong của VGA nó là bộ biến đổi DAC (Digital to Analog Converter).
DAC này có 256 thanh ghi, chúng có thể làm việc như là các thanh ghi Palette như ở EGA ... (ở EGA các thanh ghi này là 16 bit), nhưng ở VGA các thanh ghi này là 18 bit, vì vậy giá trị của nó là miền 0..256K, nói cách khác nếu ta dùng thanh ghi của DAC để đặt màu thì ta có thể chọn được tới 256K màu để hiển thị.
18 bits DAC register:
r r r r r r g g g g g g b b b b b b
III. viết ảnh đồ hoạ trực tiếp vào bộ nhớ
(Writing Graphics Images Directly in Memory)
Hầu hết các chương trình chuyên xử lý đồ hoạ không sử dụng thủ tục Write Dot của BIOS bởi vì tốc độ thực hiện chậm mà viết trực tiếp vào bộ nhớ bằng việc sử dụng các lệnh MOV để vận chuyển nhanh số liệu giữa Conventional Memory và Display hoặc giữ Display Memory và Display Memory, thường việc này nâng cao tốc độ vẽ lên nhiều lần.
Bình thường thì bộ nhớ hiển thị liên hệ với MPU qua BUS dữ liệu 8 bit (ở các CARD màn hình cắm vào khe cắm mở rộng), ở các máy mà VGA CARD là ON_BOARD thì BUS dữ liệu là 16 bit. Các CARD EGA/VGA CPU có thể thực hiện 1 lệnh để khởi tạo DATA BUS 32 bit bên trong nó.
Tuy nhiên thủ tục viết vào bộ nhớ (hiển thị) phụ thuộc vào loại CARD điều khiển màn hình. Chính vì vậy mà người ta vẫn khuyên sử dụng BIOS Write Dot vì nó làm cho người lập trình không bị phụ thuộc vào loại CARD màn hình cụ thể, chính thủ tục đó đã được thiết kế để giao tiếp với phần cứng.
Bộ nhớ hiển thị được tổ chức bằng hai cách:
- sử dụng Packed Format
- hoặc Bit-Plane Format - Bit-Plane:
Đây là một kỹ thuật phân đoạn bộ nhớ thành các Bit-Plane. Một Bit-plane chứa 1 bit cho mỗi Pixel. Số lượng Bit-Plane bằng số bit cực đại dành cho mỗi Pixel bởi vì mỗi Pixel cần một số bit mô tả và mỗi một trong
số các bit đó lại nằm trong 1 plane khác nhau. - Packed Display Format
Trong Mode này chỉ có 1 Plane trong bộ nhớ và plane này được chia làm
các phần tử (element), mỗi phần tử chứa số bit ứng với 1 pixel. Nếu mỗi pixel chỉ dùng 1 bit thì cả 2 kỹ thuật nêu trên là giống hệt
nhau (identical).
pixel pixel pixel pixel n n+1 n n+1
Bit plane 0
Height Bit plane 1 Height
Bit plane 2
Bit plane 3
Width Width
#bits/pixels
a) Bit-Plane b) Packed Display Format
1. Bộ đệm màn hình CGA trong Graphics Mode
(The CGA screen buffer in Graphics Mode)
1.1 Trong High Resolution Mode (640x200 pixel)
Với mỗi Pixel chúng ta chỉ có thể chọn on hoặc off, trong trường hợp này PC chỉ cần sử dụng 1 bit trong bộ nhớ (VIDEO RAM) cho 1 pixel trên màn hình, như vậy cần 640x200=128000 bit = 16000 byte, xấp xỉ 16KB.
Ta có thể suy luận một cách rất tự nhiên là để bật (turn on) 1 pixel tại vị trí (0,0) trên màn hình thì cần đặt (set) bit đầu tiên của bộ nhớ đệm màn hình =1. Trong thực tế đúng là như vậy.
Để 'bật' pixel bên cạnh (cùng hàng), ta đặt bit tiếp theo =1 .v.v... cho đến bit thứ 640 ứng với cuối dòng 1.
Còn nếu ta muốn 'bật' tiếp các pixel của hàng dưới thì vấn đề lại không hoàn toàn "tự nhiên" như vậy nữa, lý do là như sau:
CGA Buffer Memory Block IBM chia graphics video buffer thành 2 khối, mỗi khối 8KB: - khối thứ nhất bắt đầu ở B800:0000 chứa thông tin ứng với các dòng chẵn
của màn hình - khối thứ hai bắt đầu ở B800:2000 chứa thông tin ứng với các dòng lẻ của
màn hình IBM làm như vậy vì ở CGA, CRTC thực hiện quét xen kẽ, tổ chức như vậy CRTC hoạt động thuận lợi hơn, còn với người lập trình thì quả thật là "phiền toái" hơn. Hình ảnh đồ hoạ sẽ bị chia cắt làm 2 nửa đặt trong 2 block trong bộ nhớ.
1.2 Trong Medium Resolution Mode (320x200 pixel)
Vấn đề cũng khá giống trong Hires (640x200), nhưng khác ở chỗ mỗi điểm ảnh có thể nhận 1 trong 4 màu - cần 2 bit. Như vậy trong bộ nhớ mỗi nhóm 2 bit mô tả trạng thái 1 pi
Các file đính kèm theo tài liệu này:
- Assermbler.doc