Ngôn ngữ lập trình Assembly

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,...

doc59 trang | Chia sẻ: hunglv | Lượt xem: 2963 | Lượt tải: 3download
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:

  • docAssermbler.doc
Tài liệu liên quan