Hệ điều hành MS-DOS Dịch vụ ROM-BIOS Chương trình TSR Kỹ xảo lập trình
TRUY XUẤT HỆ ĐIỀU HÀNH MS-DOS
I.1. Môi trường MS-DOS
· Lấy phiên bản MS-DOS
Function DOSVersion : Word;
Hàm này trả về 2 bytes chứa số hiệu phiên bản của hệ điều hành, byte thấp chứa
chỉ số chính (major version), byte cao chứa chỉ số phụ (minor version).
Ví dụ:
Uses DOS;
Begin
Writeln(’DOS Version : ’,Lo(DOSVersion),’.’,Hi(DOSVersion));
End.
· Cài đặt biến môi trường (environment string)
Trong MS-DOS tồn tại các biến môi trường sau:
COMSPEC = đường dẫn đến trình phân giải lệnh nội trú, thường là C:\COMMAND.COM hay
C:\WINDOWS\SYSTEM\COMMAND.COM
OS = hệ điều hành được sử dụng, ví dụ Windows_NT
PATH = đường dẫn đến các thư mục để HĐH truy tìm các chương trình.
PROMPT = định dạng dấu nhắc DOS, thường là $P$G
. . .
Để truy xuất đến các biến môi trường, unit DOS cung cấp một số hàm sau:
Function EnvCount : Integer;
Hàm này trả về số lượng biến môi trường đã được cài đặt.
Function EnvStr(Index : integer) : string;
Hàm này trả về nội dung của biến môi trường có thứ tự Index.
Ví dụ: Chương trình liệt kê tất cả các biến môi trường của MS-DOS.
hai cách chuyển kiểu này có một số quy tắc khác biệt. IX.1. Chuyển kiểu đối với trị Việc chuyển kiểu đối với trị được thực hiện khi giá trị cần chuyển và kiểu được chuyển đều cùng kiểu thứ tự (ordinal type) hoặc cả hai đều mang kiểu con trỏ. Dưới đây là một số ví dụ chuyển kiểu theo trị: Integer(’A’) Chuyển ký tự A sang số Char(48) Chuyển số sang ký tự Boolean(0) Chuyển số sang kiểu luận lý Longint(@Buffer) Chuyển địa chỉ vùng đệm sang kiểu số longint. jishan_vn@msn.com - 71 - Kiểu kết quả sau khi chuyển có thể bị cắt bỏ nếu kiểu được chuyển không thích hợp. Ví dụ: I:=Byte(539) kết quả I mang trị 27, lý do 539 là một số lớn hơn kích thước của một byte (255). Cho nên 539(10) = 1000011011(2) và khi chuyển qua kiểu byte, trình biên dịch chỉ giữ lại 8 bits cuối, kết quả thu được 00011011(2) = 27(10). IX.2. Chuyển kiểu đối với biến Ta có thể chuyển kiểu dữ liệu cho biến sang hầu hết các kiểu dữ liệu khác. Ví dụ: Char(I) Boolean(Count) TSomeDefinedType(MyVariable) Chuyển kiểu cho biến còn được thực hiện ở cả hai vế của câu lệnh gán. Ví dụ: var MyChar:Char; ... Shortint(MyChar):=122; ... Chuyển kiểu theo cách trên sẽ gán mã ASCII cho biến mang kiểu ký tự MyChar. Kết quả MyChar sẽ chứa ký tự ‘z’ (#122). Ta cũng có thể chuyển kiểu của một biến sang kiểu thủ tục (procedural type). Ví dụ ta có khai báo: type Func = function(X:Integer): Integer; var F:Func; P:Pointer; N:Integer; Ta cũng có thể thực hiện chuyển kiểu cho biến thủ tục như trong các phép gán sau: F := Func(P); { Assign procedural value in P to F } Func(P) := F; { Assign procedural value in F to P } @F := P; { Assign pointer value in P to F } P := @F; { Assign pointer value in F to P } N := F(N); { Call function via F } N := Func(P)(N); { Call function via P } Biến có thể được chuyển kiểu thông qua dấu chấm (.) phân định phạm vi như sau: type TByteRec = record Lo, Hi: Byte; end; TWordRec = record Low, High: Word; end; PByte = ^Byte; var B: Byte; W: Word; L: Longint; P: Pointer; begin W := $1234; B := TByteRec(W).Lo; TByteRec(W).Hi := 0; L := $01234567; W := TWordRec(L).Low; B := TByteRec(TWordRec(L).Low).Hi; B := PByte(L)^; end; Ở ví dụ trên, TByteRec được dùng để truy xuất byte thấp và byte cao của biến kiểu Word. TWordRec được dùng để truy xuất word thấp và word cao của biến kiểu LongInt (cách này còn có tên gọi khác là record hóa vùng nhớ). Ta có thể gọi hàm Hi và Lo của Pascal để thực hiện cùng một chức năng trên. Tuy nhiên, với kỹ thuật chuyển kiểu cho biến, ta có thể sử dụng thêm chức năng của phép gán, gán trị cho biến sau khi đã thực hiện phép chuyển kiểu. jishan_vn@msn.com - 72 - X. CÁC QUY TẮC ƯU TIÊN CỦA TOÁN TỬ Trong một biểu thức phức tạp gồm nhiều toán từ, wuy tắc ưu tiên dành cho toán tử được thể hiện như sau: Toán tử Độ ưu tiên @, ^, not Ưu tiên 1 (cao nhất) *, /, div, mod, and, shl, shr Ưu tiên 2 +, -, or, xor Ưu tiên 3 =, , , =, in Ưu tiên 4 (thấp nhất) Toán tử có độ ưu tiên cao sẽ được định giá trước các toán tử có độ ưu tiên thấp. Các toán tử có độ ưu tiên bằng nhau sẽ được định giá từ trái sang phải. Ví dụ với biểu thức: X+Y*Z Biểu thức này thực hiện phép nhân Y với Z rồi sau đó cộng với X, toán tử * được thực hiện trước vì nó có độ ưu tiên cao hơn toán tử +. Ví dụ với biểu thức: X-Y+Z Biểu thức này thực hiện phép trừ giữa X và Y trước rồi mới cộng với Z, toán tử + và – có cùng độ ưu tiên nên biểu thức được thực hiện từ trái sang phải. Ta có thể sử dụng dấu ngoặc đơn để đặt lại độ ưu tiên cho toán tử. Biểu thức trong dấu ngoặc đơn sẽ được định giá trước, sau đó kết quả sẽ được sử dụng như một toán hạng cho các toán tử kế tiếp. Ví dụ: (X+Y)*Z sẽ cộng X với Y, sau đó nhân tổng cộng được cho Z. Trong một số trường hợp dấu ngoặc đôi khi rất cần thiết, nếu không ta có thể nhận được kết quả định giá không như mong muốn. Ví dụ: X=Y or X=Z Dĩ nhiên biểu thức này muốn so sánh với ý nghĩa (X=Y)or(X=Z). Thế nhưng nếu không có dấu ngoặc để phân biệt độ ưu tiên của các nhóm toán tử, trình biên dịch theo quy tắc ưu tiên của toán tử sẽ biên dịch với ý nghĩa (X=(Y or Z))=Z, kết quả trình biên dịch sẽ báo lỗi trừ phi Z mang kiểu Boolean. Nhưng cho dù Z có mang kiểu Boolean đi chăng nữa thì kết quả so sánh vẫn không là những gì mà ta mong muốn. Cách tốt nhất là ta nên dùng dấu ngoặc để phân định các nhóm ưu tiên cho toán tử, nhất là trong trường hợp không nhớ rõ độ ưu tiên của từng toán tử. Cặp dấu ngoặc cũng làm cho biểu thức rõ ràng và dễ đọc hơn là hiểu quy tắc ưu tiên mặc định. Ví dụ: X+(Y*Z). Đối với trình biên dịch, dấu ngoặc trong trường hợp này là không cần thiết, nhưng lập trình viên và người đọc sẽ dễ dàng hiểu tường minh ý nghĩa của biểu thức mà ta muốn thể hiện là thực hiện Y nhân với Z rồi mới cộng với X. XI. KIỂM SOÁT LỖI NHẬP XUẤT (I/O ERROR) Khi nhập dữ liệu từ bàn phím, nếu giá trị nhập vào không đúng với kiểu của biến thì chương trình sẽ bị lỗi xuất nhập không đúng kiểu và bẫy lỗi (error trap) sẽ làm ngưng chương trình. Hoặc khi truy xuất tập tin, thư mục, nếu có lỗi xảy ra trong quá trình truy xuất, chương trình cũng sẽ bị dừng bởi lỗi xuất nhập. Để tránh việc này, cần phải gỡ bỏ bẫy lỗi kiểm soát xuất nhập. Để bật/tắt bẫy lỗi xuất nhập, ta dùng chỉ thị dịch $I: + {$I+} : bật bẫy lỗi kiểm tra xuất nhập. + {$I-} : tắt bẫy lỗi kiểm tra xuất nhập. Khi tắt bẫy lỗi xuất nhập, chương trình sẽ không bi dừng nếu có lỗi xảy ra. Để biết chương trình có bị lỗi hay không, Pascal cung cấp hàm IOResult để kiểm tra: Function IOResult: Integer; Khi xuất nhập dữ liệu đúng, hàm IOResult cho giá trị là 0, ngược lại sẽ trả về số hiệu của lỗi. jishan_vn@msn.com - 73 - Ví dụ: Cú pháp kiểm tra thao tác nhập dữ liệu: {$I-} Repeat Write( {} ); Readln( {} ); Until IOResult=0; {$I+} Cú pháp trên sẽ kiểm tra kiểu dữ liệu nhập vào, chương trình chỉ được tiếp tục khi kiểu dữ liệu phù hợp với biến. Ví dụ: Chương trình kiểm tra sự tồn tại của tập tin: Function FileExists(Filename:string):boolean; var F:file; begin {$I-} Assign(F,Filename); Reset(F); If IOResult0 then FileExists:=false else begin Close(F); FileExists:=true; end; {$I+} end; XII. TRUY XUẤT VÙNG NHỚ MÁY TÍNH XII.1. Biến địa chỉ tuyệt đối Các biến khai báo trong chương trình bằng từ khoá Var hay các biến động được tạo ra bằng các thủ tục New và GetMem đều mang địa chỉ do sự bố trí sắp xếp của máy tính. Nếu ta muốn cố định biến chương trình vào một địa chỉ nhất định thì phải sử dụng cú pháp khai báo biến địa chỉ tuyệt đối bằng từ khoá absolute: Var : absolute :; Hoặc khai báo biến trùng địa chỉ với một biến khác có sẵn: Var : absolute ; Ví dụ 1: Var St:String; Len:byte absolute St; Ta có Len sẽ mang cùng địa chỉ với biến St (hay St[0]), nghĩa là Len luôn mang giá trị là chiều dài của chuỗi St. Khi đó, mọi thay đổi trên Len sẽ thay đổi chiều dài chuỗi St. Ví dụ 2: Nếu ta muốn lấy mode màn hình hiện tại, ngoài cách sử dụng LastMode của thư viện CRT, ta có thể sử dụng biến địa chỉ tuyệt đối truy xuất vào vùng nhớ $0000:$0449 chứa giá trị của mode màn hình: Var Mode:byte absolute $0000:$0449; begin case Mode of 0: writeln(’Mode 40x25 BW’); 1: writeln(’Mode 40x25 Color’); 2: writeln(’Mode 80x25 BW’); 3: writeln(’Mode 80x25 Color’); 7: writeln(’Mode 80x25 Hercules Mono’); end; end; Lưu ý rằng vùng nhớ $0000:$0449 chỉ mang giá trị của mode màn hình chứ không có tác dụng làm thay đổi mode màn hình nên việc thay đổi giá trị của biến Mode sẽ không làm đổi mode màn hình. jishan_vn@msn.com - 74 - XII.2. Biến truy xuất vùng nhớ Ngoài cú pháp trên, Pascal còn cung cấp cho ta những biến bộ nhớ để truy xuất trực tiếp vào bất kỳ địa chỉ nào của bộ nhớ. Đó là 3 biến kiểu mảng MEM, MEMW và MEML. + MEM: Mỗi thành phần của biến này có kiểu byte, dùng để truy xuất vùng nhớ 1 byte. + MEMW: Mỗi thành phần của biến này có kiểu word, dùng để truy xuất vùng nhớ 2 bytes. + MEML: Mỗi thành phần của biến này có kiểu longint, dùng để truy xuất vùng nhớ 4 bytes. Chỉ số của các biến mảng này được khai báo bằng cú pháp đặc biệt có dạng Segment:Offset, là địa chỉ vùng nhớ mà biến truy xuất đến. Ví dụ: If Mem[$0000:$0449]=7 then Writeln(’Hercules MonoChrome’) Else Writeln(’CGA, EGA or VGA’); Khi sử dụng các biến này, ta có thể lấy dữ liệu từ vùng nhớ ra để sử dụng và cũng có thể ghi trực tiếp dữ liệu vào vùng nhớ bằng lệnh gán hay các cú pháp làm thay đổi giá trị của biến. Nhưng cần phải lưu ý khi sử dụng các biến truy xuất bộ nhớ: + Phải biết rõ địa chỉ, độ lớn và công dụng của dữ liệu trong vùng nhớ. + Phải biết rõ dữ liệu trong vùng nhớ này có được phép sửa đổi hay không vì nếu không biết rõ, việc thay đổi giá trị trong vùng nhớ có thể gây sụp đổ chương trình lẫn cả hệ thống. + Không được truy xuất MEM, MEMW và MEML mà không có chỉ số kèm theo. XII.3. Biến truy xuất các cổng dữ liệu Ngoài việc sử dụng Assembly thì Pascal còn cung cấp các biến để truy xuất các cổng dữ liệu, đó là 2 biến kiểu mảng PORT và PORTW. + PORT: Mỗi thành phần của biến này có kiểu byte, dùng để truy xuất các giá trị 1 byte. + PORTW: Mỗi thành phần của biến này có kiểu word, dùng để truy xuất các giá trị 2 bytes. Chỉ số của các biến mảng này đều mang kiểu word. Ta không thể truy xuất Port và PortW mà không có chỉ số đi kèm. Khi gán một giá trị vào Port(PortW), giá trị đó sẽ được xuất ra cổng tương ứng, ngược lại ta có thể nhận giá trị ở cổng bằng cách đọc từ Port(PortW). Ví dụ: Port[$60] là cổng bàn phím, Port[$378] là cổng LPT1, Port[$3F8] là cổng COM1, Port[$2F8] là cổng COM2... Lưu ý rằng Port và PortW chỉ được giới hạn sử dụng trong phép gán và tham chiếu giá trị trong các biểu thức, điều đó có nghĩa là không được sử dụng Port và PortW như tham số biến trong lời gọi chương trình con.
File đính kèm:
- System Programming with Pascal.pdf