Tải bản đầy đủ (.pdf) (22 trang)

Lập trình bằng Turbo Pascal part 10 pot

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (262.16 KB, 22 trang )

Lập trình bằng Turbo Pascal
BP
IP

CS
DS
SS
ES

Flags

1.1.2 Cấu trúc của địa chỉ trong bộ nhớ.
Bộ vi xử lí họ 8086 dùng 20 bit để đánh địa chỉ bộ nhớ. Suy ra nó
quản lí đợc một không gian địa chỉ gồm 2
20
bit = 1 MB hay 1.048.576 địa
chỉ. Nhng các thanh ghi chỉ có 16 bit, chỉ có thể quản lí một không gian địa
chỉ gồm 2
16
bit = 64 KB hay 65.536 địa chỉ. Vì lí do ny, bộ nhớ đợc chia
lm nhiều đoạn - segment, mỗi đoạn gồm 64 KB. Địa chỉ một ô nhớ gồm hai
phần, có dạng Segment: offset. Segment l địa chỉ đoạn, đợc ghi trong các
thanh ghi đoạn, còn offset l độ dịch chuyển, đợc ghi trong các thanh ghi
offset. Địa chỉ thực của ô nhớ sẽ nhận đợc sau khi nhân địa chỉ đoạn với 16
(hay l dịch trái 4 bit) rồi cộng với điạ chỉ offset.
Trong Pascal ngời ta dùng dấu hiệu $ đứng trớc một số hệ đếm 16
(hexadecimal) để phân biệt với số thập phân thông thờng. Các địa chỉ ô nhớ
thờng đợc viết bằng số trong hệ đếm cơ số 16. Ví dụ $F12A:$E82B. Địa
chỉ thực tơng ứng sẽ l $F12A0 + $E82B = $FFACB.
1.2 Các ngắt - interrupt


1024 byte đầu tiên trong bộ nhớ đợc chia thnh 256 bộ 4 byte, chứa
đợc 256 địa chỉ, trỏ đến các chơng trình xử lí các công việc cơ sở của hệ
thống gọi l các véc tơ ngắt - Interrupt. Các ngắt đợc đánh số từ 0 đến 255
hay l từ $00 đến $FF.
Một ngắt lại có thể lm vi việc khác nhau, mỗi việc nh thế cũng gán
tơng ứng với một số nguyên, gọi l chức năng của ngắt. Ví dụ, ngắt số 16 =
$10 l ngắt phục vụ mn hình. Ngắt ny lại có nhiều chức năng khác nhau
nh
$00: Thiết lập chế độ mn hình.
$03: Lấy toạ độ con chạy trên mn hình văn bản.
$0F: Lấy chế độ hiện tại của mn hình.




Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 245
Lập trình bằng Turbo Pascal
Dới đây l bảng một số véc tơ ngắt.

Ngắt số Tác dụng
Thập
phân
Hex.
0
1
9
16

32
33


$00
$01
$09
$10

$20
$21



Xử lí khi có phép chia cho 0
Cho chạy chơng trình từng bớc (để sửa lỗi)
Đọc mã quét từ bn phím
BIOS gọi các phục vụ mn hình

DOS chấm dứt chơng trình .COM
Thực hiện rất nhiều chức năng của DOS

1.3 Thâm nhập trực tiếp qua thanh ghi và ngắt
1.3.1 Thủ tục gọi ngắt.
Ta nhắc lại cách gọi ngắt thực hiện các chức năng của chúng từ một
chơng trình viết bằng hợp ngữ. Sau khi gán số hiệu chức năng cho thanh ghi
AH, câu lệnh
INT số hiệu ngắt
đợc dùng để gọi thực hiện ngắt. Các kết quả nếu có đợc trả về trong các
thanh ghi.
Việc gọi thực hiện ngắt từ trong một chơng trình Pascal cũng qua
những bớc tơng tự. Unit Dos của TurboPascal cung cấp các hỗ trợ cần
thiết để thực hiện gọi ngắt.

Trớc hết, để truy cập các thanh ghi của bộ vi xử lí, có kiểu dữ liệu
thanh ghi - Registers đợc định nghĩa trong Unit Dos nh sau

Type registers = record
Case Integer of
0: (AX, BX, CX, DX, BP, SI, DI, DS, ES, Flags: word);
1: (AL, AH, BL, BH, CL, CH, DL, DH: byte);
End;

Đây l một kiểu bản ghi có cấu trúc thay đổi nhằm có thể thâm nhập
riêng rẽ byte thấp v byte cao của các thanh ghi AX,BX,CX,DX.

Unit Dos cung cấp hai thủ tục Intr v MsDos để gọi thực hiện các
ngắt.

Intr( IntNum: Word; Var Regs: Registers);
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 246
Lập trình bằng Turbo Pascal
MsDos( Var Regs: Registers);

Biến thanh ghi Regs đóng vai trò cung cấp dữ liệu đầu vo cho ngắt
đợc gọi v cũng l nơi để nhận kết quả trả về nếu có.
MsDos dnh riêng để gọi các phục vụ của Dos, ứng với ngắt số $21.
Lời gọi MsDos(Regs) tơng đơng với lời gọi Intr( $21, Regs) vì ngắt số
$21 l các phục vụ của DOS.
1.3.2
1.4.1
Các bớc gọi ngắt
- Khai báo một biến kiểu thanh ghi, ví dụ Regs
- Đặt các giá trị cần thiết cho các thanh ghi thông qua các trờng của

biến Regs. Quy ớc:
AH chứa số hiệu của chức năng.
AL chứa số hiệu của chức năng phụ (nếu có).
Các thanh ghi còn lại dùng đến hay không tuỳ theo từng ngắt.
- Gọi thực hiện ngắt bằng Intr hay MsDos
- Nhận kết quả trả về trong các trờng của Regs (nếu cần).

Để biết công việc của mỗi ngắt, các chức năng ngắt khác nhau, ý nghĩa
của thông tin trả về trong các thanh ghi,v.v cần tham khảo các ti liệu tra
cứu về hệ thống, hệ điều hnh DOS.

1.4 Các ví dụ minh hoạ.

Ví dụ 1.
Xem v đặt lại chế độ mn hình.
Ngắt thực hiện các dịch vụ mn hình có số hiệu $10. Ngắt ny có
nhiều chức năng khác nhau.
a) Xem chế độ mn hình hiện hnh.
Chức năng: AH:=$0F;
Giá trị trả về: AL = Số hiệu của chế độ mn hình ;
AH = số cột;
BH = số hiệu của trang active;


program LayCheDoManHinh;
uses dos;
var regs: registers;
BEGIN
regs.AH:=$0F; {$0F= chức năng lấy chế độ mn hình}
Intr($10,regs); {$10 l ngắt về dịch vụ mn hình}

With regs do
begin
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 247
Lập trình bằng Turbo Pascal
writeln(' mode man hinh la:',AL);
writeln(' so cot :',AH);
writeln(' active page :',BH);
end;
readln
END.

b) Thiết lập chế độ cho mn hình
Chức năng: AH:=$00;
Tham số đầu vo AL:= số hiệu mode mn hình muốn thiết lập;

program ThietLapCheDoManHinh;
uses dos;
var i: integer;
procedure SetVideoMode(mode: integer);
var regs: registers;
begin with regs do
begin AH:=0;
AL:=mode;
Intr($10,regs);
End
end;

BEGIN
for i:=0 to 6 do
begin SetVideoMode(i);

Writeln(' man hinn dang o mode ',i);
readln
end;
END.

1.4.2 Ví dụ 2.
Xem v thiết lập ngy hệ thống.
Ngắt có số hiệu $21 thực hiện nhiều chức năng khác nhau của DOS.

a) Xem ngy hệ thống.
Chức năng: $2A.
Giá trị trả về:
DL = ngy;
DH = tháng;
CX = Năm;

program LayNgayHeThong;
uses dos;
var regs: registers;
date,year,month,day: string;

Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 248
Lập trình bằng Turbo Pascal
BEGIN
regs.AH:=$2A; { $2A= chức năng lấy ngy hệ thống}
MsDos(regs);
With regs do
begin
str(CX,year); {chuyển gía trị số thnh string}
str(DH,month);

str(DL,day);
end;
date:=day+'/'+month+'/'+year;
Writeln('hom nay la ngay:',date);
readln;
END.

b) Đặt ngy tháng hệ thống
Chức năng AH:=$2B;
Tham số đầu vo:
DL:= ngy (0 31);
DH:= tháng (0 12);
CX:= Năm (1980 2099);
Giá trị trả về:
AL=0 nếu thnh công.
AL=255 nếu có lỗi.

program DatNgayThang;
uses dos;
var regs: registers;
BEGIN
with regs do
begin
AH:=$2B; { $2A= chức năng đặt ngy hệ thống}
DL:=15;
DH:=1;
CX:=1999;
MsDos(Regs);
if AL=0 then Writeln('da dat ngay xong !')
else writeln('co loi');

end;
END.

1.4.3 Ví dụ 3.
Thông báo vùng trống trên đĩa.
Đây cũng l một chức năng của ngắt số $21.
Chức năng AH:= $36;
Tham số đầu vo:
DL:= số hiệu của ổ đĩa (0,1,2 );
Giá trị trả về
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 249
Lập trình bằng Turbo Pascal
AX= số sector cho mỗi cluster ( = $FFFF nếu ổ đĩa không hợp
lệ).
BX= số cluster không sử dung.
CX= số byte trên mỗi sector.
DX= tổng số sector.

Số vùng trống trên đĩa = LongInt (AX)*BX*CX;

program TinhVungTrongTrenDia;
uses dos;
var TenODia: char;
Ketqua: LongInt;
function SoByteConRoi(Drv: char): LongInt;
var regs: registers;
begin
with regs do
begin AH:=$36;
DL:= Ord(Upcase(drv)) - 64;

MsDos(Regs);
if AX=$FFFF then SoByteConRoi:=-1
else SoByteConRoi:= LongInt(AX)*BX*CX;
end;
end;

BEGIN write(' Cho ten o dia:'); readln(TenODia);
Ketqua:= SoByteConRoi(TenODia);
if Ketqua =-1 then writeln(' Co loi!')
else writeln(' con ', Ketqua:0, 'byte trong ');
readln;
END.

1.5 Sử dụng các hàm, thủ tục của unit DOS

Trong phần trớc ta đã thấy việc thâm nhập hệ thống v thực hiện một
số chức năng hệ điều hnh DOS một cách trực tiếp sử dụng các ngắt l khá tỉ
mỉ v mất nhiều công sức.
Unit DOS của Turbo Pascal cung cấp một th viện các chơng trình
con lm sẵn, hỗ trợ ngời sử dụng trong các giao tiếp với hệ thống v hệ điều
hnh. Có thể sử dụng các hm, thủ tục ấy để thâm nhập hệ thống dễ dng hơn.
Ví dụ, có thể xem v đặt ngy tháng hệ thống bằng các thủ tục GetDate,
SetDate.

program XemVaDatNgay_UnitDos;
uses dos;
var
y,m,d,dow: word;
BEGIN
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 250

Lập trình bằng Turbo Pascal
GetDate(y,m,d,dow);
Writeln('GetDate cho ket qua la:',d:2,'/',m:2,'/',y:4,'
thu:', dow:1);
readln;
SetDate(2000,01,01); {Đặt lại ngy hệ thống}

END.

Có thể lm lại chơng trình trong ví dụ 3, xem số byte còn rỗi trên đĩa
bằng thủ tục DiskFree.

program TinhVungTrongTrenDia_UnitDos;
uses dos;
var
TenODia: char;
SoODia: byte;
Ketqua: longInt;
BEGIN
write(' Cho ten o dia:'); readln(TenODia);
SoHieu:= Ord(Upcase(TenODia)) - 64;
Ketqua:= DiskFree(SoHieu);
if Ketqua =-1 then writeln(' Co loi!')
else
writeln('O dia ', TenODia,' con ', Ketqua DIV 1024
,'Kbyte trong !');
readln;
END.

Rõ rng l với sự hỗ trợ của unit Dos, việc thâm nhập hệ thống, thực hiện

các chức năng thông dụng của hệ điều hnh DOS trở nên dễ dng hơn nhiều.
Cần tìm hiểu kĩ về unit Dos để nắm đợc cách sử dụng rất nhiều hm v thủ
tục đã đợc lm sẵn trong đó.

2.
Điều khiển chuột
2.1 Toạ độ chuột

Mn hình văn bản chuẩn gồm 80 cột, 25 dòng tức l 2000 ô chữ nhật,
mỗi ô hiển thị một kí tự. Trong khi đó, mn hình đồ hoạ lại chia lm nhiều
điểm ảnh, thấp nhất cũng l 640 x 480 đối vơí mn hình VGA. Để con chuột
có thể dùng trong cả hai chế độ mn hình, ngời ta quy định toạ độ micki của
chuột biến đổi trong khoảng sau đây, không phụ thuộc chế độ văn bản hay đồ
hoạ.
0

x_mouse

632.
0

y_mouse

192.
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 251
Lập trình bằng Turbo Pascal
Để chuyển sang toạ độ x_text, y_text trên mn hình văn bản, lu ý
rằng 1

x_text


80,

y_text

25 ta sẽ có công thức đổi toạ độ chuột
sang toạ độ trên mn hình văn bản l
x_text = x_mouse/8 + 1
y_text = y_mouse/8 + 1
Theo công thức trên, ta sẽ đợc 8 x 8 = 64 điểm chuột trong một ô kí tự trên
mn hình văn bản. Khi nhấn chuột vo các điểm ny đều chỉ ứng với một ô
vị trí kí tự trên mn hình văn bản m thôi.

Công thức chuyển toạ độ chuột sang toạ độ trên mn hình đồ hoạ l
x_graphics = (x_mouse / 632) * getmaxx;
y_graphics = (y_mouse / 192) * getmaxy;
ở đây getmaxx, getmaxy l toạ độ x, toạ độ y lớn nhất của chế độ đồ hoạ
đang xét.

2.2 Ngắt điều khiển chuột $33

Con chuột đợc điều khiển thông qua sử dụng ngắt số hiệu $33. Ngắt
ny có nhiều chức năng khác nhau. Cần gán các số hiệu chức năng (sẽ liệt kê
dới đây) cho thanh ghi AX (chứ không phải AH nh các ngắt khác)

Các chức năng điều khiển chuột của ngắt $33.
1- Chức năng 0: khởi tạo chuột
Đầu ra
AX:= 0 cha ci đặt chuột
AX:= -1 đã cI đặt chuột

BX: = số phím chuột (thờng l 2)
2- Chức năng 1: Lm hiện con trỏ chuột
3- Chức năng 2: Lm ẩn con trỏ chuột
4- Chức năng 3: Lấy vị trí v kiểm tra nhấn phím
Đầu ra
CX:= toạ độ x_mouse của chuột.
DX:= toạ độ y_mouse của chuột.
BX sẽ cho trạng thái của các phím chuột
Bit 0 = 0 phím trái nhả.
Bit 0 = 1 phím trái đợc nhấn.
Bit 1 = 0 phím phảI nhả.
Bit 1 = 1 phím phảI đợc nhấn.

5 - Chức năng 4: Di chuyển con trỏ chuột.
Đầu vo CX = toạ độ x mới
DX = toạ độ y mới

Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 252
Lập trình bằng Turbo Pascal
6- Hỏi trạng thái v số lần nhấn phím kể từ khi gọi chức năng ny.
Đầu vo BX = phím cần hỏi: 0 = trái, 1 = phải, 2 = giữa.
Đầu ra
AX cho trạng thái phím (0 = nhả, 1 = nhấn)
Bít 0 cho trạng thái phím trái.
Bít 1 cho trạng thái phím phải.
Bít 2 cho trạng thái phím giữa.
BX:= số lần nhấn
CX:= toạ độ x lần nhấn cuối cùng
DX:= toạ độ y lần nhấn cuối cùng


7- Hỏi trạng thái v số lần nhả phím kể từ khi gọi chức năng ny
Đầu vo BX = phím cần hỏi: 0 = tráI, 1 = phảI, 2 = giữa
Đầu ra
AX cho trạng thái phím (0 = nhả, 1 = nhấn)
Bít 0 cho trạng thái phím trái.
Bít 1 cho trạng thái phím phải.
Bít 2 cho trạng thái phím giữa.
BX:= số lần nhấn.
CX:= toạ độ x lần nhấn cuối cùng.
DX:= toạ độ y lần nhấn cuối cùng.

8 - Chức năng 7: Hạn chế phạm vi di chuyển ngang của chuột (phạm vi biến
đổi của x_mouse)
Đầu vo: CX:= cột trái
DX:= cột phải

9 - Chức năng 8: Hạn chế phạm vi di chuyển dọc của chuột (phạm vi biến
đổi của y_mouse)
Đầu vo: CX:= Dòng trên cùng.
DX:= Dòng dới cùng.


2.3 Ví dụ minh hoạ.
2.3.1 Khởi tạo chuột.
Ta xây dựng thủ tục mouseInit thực hiện các chức năng: kiểm tra nếu
đã ci đặt chuột thì thiết lập toạ độ ban đầu của chuột, hạn chế phạm vi tác
dụng của chuột trong một khung hình chữ nhật v cho hiển thị chuột (một
hình chữ nhật) trên mn hình văn bản.
Để dễ nhớ, ta định nghĩa các
hằng số l các chức năng của ngắt $33

nh trình by trên. x,y l toạ độ ban đầu của chuột. Left, Top, Right, Bottom
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 253
Lập trình bằng Turbo Pascal
Trong thủ tục dới đây v các thủ tục khác tiếp theo sau, Regs l biến
kiểu thanh ghi.


var Regs: registers;

const INT_MOUSE = $33; (*ngat chuot*)
MOUSE_INIT = $00;
MOUSE_X_LIMIT = $07;
MOUSE_Y_LIMIT = $08;
MOUSE_SET_POSITION = $04;
MOUSE_SHOW = $01;

procedure mouseInit(x,y,left,top,right,bottom: integer);
var mouse_status: integer;
begin
regs.AX:=MOUSE_INIT;
intr(INT_MOUSE,regs);
mouse_status:= regs.AX;
if (mouse_status <> -1) then
begin
writeln('No mouse driver'); exit;
end;
regs.AX:= MOUSE_X_LIMIT;
regs.CX:= left; regs.DX:= right;
intr(INT_MOUSE,regs);
regs.AX:= MOUSE_Y_LIMIT;

regs.CX:= top; regs.DX:= bottom;
intr(INT_MOUSE,regs);
regs.AX:= MOUSE_SET_POSITION;
regs.CX:= x; regs.DX:= y;
intr(INT_MOUSE,regs);
regs.AX:= MOUSE_SHOW;
intr(INT_MOUSE, regs);
end;

2.3.2 Lấy vị trí chuột.
Số hiệu của chức năng lấy vị trí l 03. Toạ độ chuột sẽ đợc ghi vo
các biến nguyên x, y. Toạ độ ny sẽ đợc dùng để đổi sang toạ độ trên mn
hình văn bản x_text, y_text hay toạ độ trên mn hình đồ hoạ x_graphics,
y_graphics nh đã nói trên.

const MOUSE_GET_POSITION = $03;

procedure getMousePosition(VAR x,y: integer);
begin
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 254
Lập trình bằng Turbo Pascal
regs.AX:= MOUSE_GET_POSITION;
intr(INT_MOUSE,regs);
x:= regs.CX; y:= regs.DX;
end;

2.3.3 Bắt sự kiện nhấn v nhả phím chuột.
Sự kiện thông dụng nhất của chuột cần xử lí l nhấn phím v nhả phím.
Thủ tục getMouseClickLeft cho phép ghi lại sự kiện nhấn phím chuột trái v
toạ độ điểm nhấn. Thủ tục getMouseReleaseLeft cho phép ghi lại sự kiện thả

phím chuột trái v toạ độ điểm thả chuột.
Hon ton tơng tự ta có thể đón bắt các sự kiện chuột phải.

Const MOUSE_CLICK_COUNT = $05;
MOUSE_RELEASE_COUNT = $06;
procedure getMouseClickLeft(VAR numclick, x, y: integer);
begin
regs.AX:= MOUSE_CLICK_COUNT;
regs.BX:= $00;
intr(INT_MOUSE,regs);
numclick:= regs.BX;
x:= regs.CX; y:= regs.DX;
end;

procedure getMouseReleaseLeft(VAR numrelease, x, y:
integer);
begin
regs.AX:= MOUSE_RELEASE_COUNT;
regs.BX:= $00;
intr(INT_MOUSE,regs);
numrelease:= regs.BX;
x:= regs.CX; y:= regs.DX;
end;


2.4 Th viện các thủ tục thao tác chuột.

Tập hợp các thủ tục trên ta có thể xây dựng một th viện các hm tiện
ích thao tác chuột. Ví dụ,


unit mouse;

INTERFACE
const
MOUSE_X_MAX = 632;
MOUSE_Y_MAX = 192;

Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 255
Lập trình bằng Turbo Pascal
INT_MOUSE = $33; (*ngat chuot*)

MOUSE_INIT = $00;
MOUSE_SHOW = $01;
MOUSE_HIDDEN = $02;
MOUSE_GET_POSITION = $03;
MOUSE_SET_POSITION = $04;
MOUSE_CLICK_COUNT = $05;
MOUSE_RELEASE_COUNT = $06;
MOUSE_X_LIMIT = $07;
MOUSE_Y_LIMIT = $08;

procedure mouseInit(x,y,left,top,right,bottom
:integer);
procedure getMousePosition(VAR x,y: integer);
procedure getMouseClickLeft(VAR numclick, x, y
: integer);
procedure getMouseReleaseLeft(VAR numrelease, x, y
: integer);
{ khai báo các thủ tục khác nữa }



IMPLEMENTATION

{ Triển khai chi tiết các thủ tục đã khai báo ở trên }


ứng dụng vào bảng chọn. 2.5
Ta đã xây dựng bảng chọn, xử lí bằng gõ phím mũi tên.
Có thể bổ xung thêm các thủ tục xử lí sự kiện chuột để cho phép chọn
bằng nhấn phím trái chuột.
Thủ tục xử lí gõ phím bây giờ đợc mở rộng thêm thnh thủ tục xử lí
sự kiện, bao gồm cả sự kiện nhấn chuột nh sau.

procedure XuLiSuKien;
var i: integer;
gophim: char;
mouseclick: boolean;
raw_x,raw_y, mouse_x, mouse_y, numclick: integer;
begin
i:=0; {mac dinh ban dau la muc chon thu nhat}
mouseclick:= false;
mouseInit(MOUSE_X_MAX div 2, MOUSE_Y_MAX div 2,
0,0,MOUSE_X_MAX, MOUSE_Y_MAX);
repeat
if keyPressed then
begin
{xu li go phim}
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 256
Lập trình bằng Turbo Pascal



end;

{== phan xu li chuot ==}
getMouseClickLeft(numclick,raw_x, raw_y);
if (numclick > 0) then
begin
mouseclick:= true;
mouse_x:= raw_x DIV 8 + 1;
mouse_y:= raw_y DIV 8 + 1;
{nhan chuot ben ngoai bang}
if ((mouse_x < x) or ( x+rong < mouse_x)
or (mouse_y < y) or ( y+SoMucChon < mouse_y))
then BangChon:= -1
else
{nhan chuot ben trong bang}
for i:=0 to SoMucChon do
begin
LamChimMucChon(i) ;
if (mouse_y = y+i) then
LamNoiMucChon(i);
BangChon:=i;
delay(50); { chỉ để kịp nhận thấy!}
end;
end;
until (gophim=enter) or (gophim=esc) or mouseClick;
end;


3.

Thâm nhập trực tiếp vào bộ nhớ và cổng
3.1 Thâm nhập trực tiếp bộ nhớ.

Turbo Pascal hỗ trợ thâm nhập trực tiếp vo bộ nhớ. Cả bộ nhớ coi nh
một mảng. Kích thớc của các phần tử mảng (ô nhớ) có thể l 1 byte, 1 word
(= 2 byte) hay 1 LongInt (= 4 byte).
Biến mảng Mem dùng để truy cập đến từng byte trong bộ nhớ.
Biến mảng MemW dùng để truy cập theo từng Word.
Biến mảng MemL dùng để truy cập theo từng LongInt.

Cần chỉ rõ địa chỉ của ô nhớ đầu tiên dới dạng Segment: offset. Ví dụ:
Mem[$xxxx: $yyyy]: truy cập một byte bộ nhớ tại địa chỉ $xxxx:
$yyyy.
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 257
Lập trình bằng Turbo Pascal
MemW[$xxxx: $yyyy]: truy cập 2 byte bộ nhớ (1 word) kể từ địa chỉ
$xxxx: $yyyy.
MemL[$xxxx: $yyyy]: truy cập 4 byte bộ nhớ (1 LongInt) kể từ địa chỉ
$xxxx: $yyyy.

Ví dụ 1.
Vùng nhớ mn hình l bắt dầu từ địa chỉ $B800:0000. Chế độ văn bản
cần lu trữ 2000 (80 x 25) kí tự. Mỗi kí tự chiếm 2 byte, một byte l mã
ASCII, byte còn lại l các thuộc tính hiển thị video (xem phần Unit Crt). Ta
có thể thâm nhập trực tiếp vo vùng nhớ ny để tăng tốc độ cho các thao tác
hiển thị ra mn hình.
Chơng trình dới đây thay ton bộ các kí tự trống bằng dấu sao *

var i: word;
begin

for i:=1 to 2000-1 do
if mem[$B800:i*2] = $20 then Mem[$B800:i*2]:= Ord('*');
end.


Ví dụ 2.
Khi ta xây dựng các chơng trình con có chứa các lệnh lm thay đổi
hiển thị mn hình ví dụ nh lm các bảng chọn, hiện các thông báo thì một
yêu cầu l phải chép lu ton bộ hay một vùng mn hình bị thay đổi v cho
hiển thị lại nguyên trạng ban đầu sau khi chơng trình con đã kết thúc. Sử
dụng phép thâm nhập trực tiếp bộ nhớ có thể viết hai thủ tục đơn giản nh
trong chơng trình sau đây.

uses crt;
type one_screen = array[0 4000-1] of word; {80*25*2=2000}
var old_src: one_screen;
origMode: integer; {để lu lại mode mn hình trớc khi
thay đổi}

procedure get_screen(var scr: one_screen);
{truy cập bộ nhớ mn hình v chép lại ton bộ mn hình
hiện tại}
var i: word;
begin
for i:=0 to 4000-1 do
scr[i]:= memL[$B800:i];
end;

procedure put_screen(var scr: one_screen);
{truy cập bộ nhớ mn hình v cho hiển thị mn hình đã lu}

var i: word;
begin
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 258
Lập trình bằng Turbo Pascal
for i:=0 to 4000-1 do
memL[$B800:i]:= scr[i];
end;

BEGIN
origMode:= lastMode;
{thay đổi mode hiển thị}
TextBackGround(Red);
Clrscr;
gotoxy(10,5);
Writeln(' Day la man hinh se luu lai !');
Writeln(' go ENTER de luu man hinh !!!');
readln;
get_screen(old_src);

TextMode(origMode);

Clrscr;
Writeln('Man hinh cu da duoc luu lai');
Writeln('Go ENTER de xem man hinh cu !!!');
Readln;
put_screen(old_src);
Readln;

TextMode(origMode);


END.

3.2 Thâm nhập cổng.

Các thiết bị ngoại vi cắm vo máy tính giao tiếp với bộ xử lí thông qua
trung gian một vùng nhớ gọi l các cổng.
Cách truy cập trực tiếp các cổng xuất nhập (I/O port) cũng giống nh
truy cập trực tiếp bộ nhớ trình by ở phần trên.
Mảng Port để truy cập các cổng theo từng byte,
Mảng PortW để truy cập các cổng theo từng Word.

Port[ i ]: truy cập một byte vùng nhớ của cổng có địa chỉ chứa trong biến i.
PortW[ i ]: truy cập một Word vùng nhớ của cổng có địa chỉ chứa trong biến
i.
Thay đổi địa chỉ ny ta sẽ truy cập cổng của các thiết bị khác nhau.

Chơng trình minh họa dới đây lm mn hình trôi liên tục từ phải
sang trái bằng cách thâm nhập trực tiếp vo cổng CRT.

program CuonManHinh;
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 259
Lập trình bằng Turbo Pascal
uses crt;
const CrtAddr = $3D4;
var i: word;
procedure ScrollLeft(num: byte);
begin
for i:= 0 to num do
begin asm CLI end; {CLI = Clear Interrupt flag}
PortW[CrtAddr]:= Hi(i) SHL 8 + $0C;

PortW[CrtAddr]:= Lo(i) SHL 8 + $0D;
asm STI end;
Delay(50); {STI = Set Interrupt flag}
end;
end;

BEGIN
ScrollLeft(80);
END.

4.
Chơng trình thờng trú
4.1 Khái niệm
Khi thi hnh một chơng trình, hệ điều hnh dnh riêng một khối nhớ
để chứa mã lệnh v các dữ liệu của chơng trình. Lúc chơng trình kết thúc,
DOS sẽ gọi hm $4C - kết thúc tiến trình v giải phóng vùng nhớ dnh riêng
cho chơng trình, chép Command.com đè lên vùng ny.
Một cách thứ hai để kết thúc chơng trình l dùng hm Keep. Sự khác
biệt giữa Keep v kết thúc bình thờng l DOS không giải phóng vùng nhớ
đã dnh cho chơng trình, nghĩa l không chép Command.com đè lên vùng
ny m chép vo phần còn trống khác. Nh vậy, chơng trình vẫn còn nằm
lại trong bộ nhớ.

Chơng trình thờng trú hay TSR- Terminate and Stay Resident - l
chơng trình m khi kết thúc không bị xoá khỏi bộ nhớ, nó vẫn còn lu lại
chờ v thực hiện chức năng công việc của mình phục vụ cho chơng trình
khác khi cần đến.
Ví dụ các phần mềm để chuyển bn phím từ gõ tiếng Anh sang gõ
đợc tiếng Việt đều l các chơng trình thờng trú. Nó chặn ngắt bn phím
v thay vec tơ ngắt thông thờng bằng một thủ tục xử lí khác để khi gõ aa

thì sẽ chuyển thnh mã của â, gõ dd thì chuyển thnh mã của đ, v.v
Kĩ thuật lập trình thờng trú đòi hỏi phải kết hợp chặt chẽ với hệ thống,
chặn v thay đổi một số ngắt để phục vụ cho mục đích của mình.
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 260
Lập trình bằng Turbo Pascal
4.2 Cách xây dựng một chơng trình thờng trú
4.2.1
4.2.2
Các bớc.
Các bớc cơ bản để xây dựng một chơng trình thờng trú l:
- Xây dựng thủ tục xử lí ngắt mới.
- Lu lại các véc tơ ngắt m chơng trình sẽ chiếm dụng.
- Thiết đặt lại các véc tơ ngắt để ứng với các thủ tục ngắt mới.
- Phục hồi lại chúng khi kết thúc.
- Tạo phím nóng để kích hoạt, gỡ bỏ chơng trình.
Các thủ tục hỗ trợ.
Có thể sử dụng ngắt $27 để kết thúc một chơng trình v cho thờng
trú.
Tuy nhiên Unit Dos của Turbo Pascal cung cấp cho chúng ta một số
thủ tục phục vụ xây dựng chơng trình thờng trú. Trớc hết đó l hm Keep
đã nói đến ở trên .

Keep(exitCode: word) ;
Cho kết thúc chơng trình v ở lại thờng trú.
Mã thoát exitCode = 0 l kết thúc bình thờng, exitCode = 1 l thoát
bằng Ctrl+C, exitCode = 2 l thoát khi chia cho 0.
Vậy một chơng trình thờng trú phải có Keep(0) trớc từ khoá END.

Ngoi ra còn một số thủ tục hỗ trợ lm việc vói các véc tơ ngắt.


GetIntVec (IntNo: Byte; Vector: Pointer) ;
Lu lại véc tơ ngắt số hiệu IntNo vo địa chỉ xác dịnh trong biến
Vector.
SetIntVec (IntNo: Byte; Vector: Pointer);
Đặt lại véc tơ ngắt số hiệu IntNo ứng với địa chỉ xác định trong biến
Vector.

Toán tử lấy địa chỉ @ hay Addr() thờng đợc sử dụng để lấy địa chỉ
của một thủ tục xử lí ngắt. thủ tục xử lí ngắt phải có kiểu Interrupt.

Một điểm cần lu ý l chơng trình thờng trú không thể biết chắc các
ngắt hiện tại có phải l bản gốc của BIOS , DOS hay không vì có thể một
chơng trình thờng trú khác đã thay thế nó rồi !

4.3 Ví dụ minh hoạ.

Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 261
Lập trình bằng Turbo Pascal
4.3.1 Ví dụ 1
Đầu tiên ta xét một ví dụ đơn giản, lm một chơng trình thờng trú
chiếm ngắt số $05 l ngắt có chức năng in nội dung của mn hình ra máy in.
Ngắt ny ứng với phím Print-screen. Ta sẽ thay nó bằng một thủ tục ngắt
mới, thực hiện việc in ra kí tự #1 (khuôn mặt cời) tại góc mn hình.

program Smile1;
{$M 1024,0,0}
uses dos;
procedure VietRaGocManHinh; interrupt;
begin
MemW[$B800:0]:=$0701;

{07 l byte thuộc tính, 01 l mã kí tự #1}
end;

BEGIN
SetIntVec($00,saveInt00);
{khôi phục ngắt khi chia cho 0}
SetIntVec($05,@VietRaGocManHinh);
keep(0);
END.

Trong chơng trình trên cần lu ý mấy điểm sau.
1- Phải có chỉ thị biên dịch {$M 1024,0,0} để hạn chế vùng Heap.
Nhắc lại rằng chỉ thị cho trình biên dịch phân bố bộ nhớ {$M StackSize,
HeapMin, HeapMax} có các giá trị mặc định l {$M 16384, 0, 655360},
nghĩa l HeapMax có thể đạt đến địa chỉ cao nhất của bộ nhớ RAM. Nếu
HeapMax không đợc giơí hạn bởi 0 v dùng hm Keep để kết thúc chơng
trình thì bộ nhớ sẽ bị chiếm dụng hết đến byte cuối cùng, không còn chỗ để
tái nạp lại Command.com khi kết thúc thi hnh chơng trình.
2- Khai báo Interrupt l bắt buộc đối với một thủ tục ngắt, tức l thủ
tục đợc gọi khi có một ngắt xảy ra. Trong chơng trình ny l thủ tục
VietRaGocManHinh. Lúc ny trình biên dịch sẽ sinh mã để cất giữ tạm thời
nội dung tất cả các thanh ghi trớc khi gọi thủ tục ngắt v hon nguyên lại
các giá trị thanh ghi sau khi thủ tục ngắt kết thúc. Nếu thiếu khai báo ny,
trình biên dịch coi thủ tục ngắt l một thủ tục thông thờng nh mọi thủ tục
khác, không báo lỗi vo thời điểm dịch v chơng trình sẽ đổ vỡ ngay.
3- Lệnh SetIntVec($00, SaveInt00) để khôi phục lại ngắt $00 - "chia
cho không".

Chơng trình trên mới chỉ quan tâm lm thế no chiếm chỗ của một
ngắt khác. Nó đã viết đè lên giá trị gốc ban đầu của ngắt $05. Không có cách

no để khôi phục lại chức năng gốc của phím Print-Screen ngoi việc tắt máy
khởi động lại.

Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 262
Lập trình bằng Turbo Pascal
Chơng trình dới đây khắc phục nhợc điểm ny. Nó lu lại địa chỉ
chứa trong vec tơ ngắt $08 (l ngắt đồng hồ hệ thống). Sau đó mới thay thế
bằng địa chỉ của thủ tục ngắt VietRaGocManHinh_2 . Trong thủ tục ny, véc
tơ ngắt $08 đợc đặt lại để trỏ đến thủ tục xử lí cũ đã lu.

program Smile2;
{$M 1024,0,0}
uses dos;
var SaveInt08: pointer;

procedure VietRaGocManHinh_2; interrupt;
begin
Inline($9C/ {PUSHF }
$FF/$1E/SaveInt08); {CALL FAR [SaveInt08]}
MemW[$B800:0]:=$0701; {07 l byte thuộc tính, 01 l mã
kí tự #1}
end;
BEGIN
GetIntVec($08, SaveInt08);
SetIntVec($08,@VietRaGocManHinh_2);
keep(0);
END.

4.3.2 Ví dụ 2
Một chơng trình hẹn giờ. Ta thử xây dựng một chơng trình thờng

trú phức tạp hơn, có xử lí dòng lệnh. Dòng lệnh Alarm n sẽ khởi động
chơng trình v chờ đúng n phút máy sẽ phát tiếng còi v dòng thông báo
trên mn hình để nhắc l đã đến giờ hẹn. Trong thời gian chờ máy vẫn hoạt
động bình thờng.
Chơng trình sử dụng ngắt $1C l nhịp đồng hồ cho ngời sử dụng.
Cứ đều đặn một Tic thì ngắt ny đợc gọi một lần. Một giây ngắt ny đợc
gọi khoảng 18.2 lần. Ta sẽ chặn ngắt ny v thay thế nó bằng thủ tục xử lí
mới, tăng biến đếm CurrentTime lên một đơn vị v so sánh với khoảng thời
gian chờ đã ấn định. Nếu vợt quá sẽ phát lệnh báo động.
Các bớc công việc m thủ tục xử lí phải lm l
- Phục hồi vectơ ngắt $1C trả lại cho hệ thống.
- Lu vectơ ngắt bn phím, thay bằng thủ tục xử lí mới l: phát còi liên
tục, hiện thông báo v đợi gõ phím F12 để kết thúc thật sự chơng
trình.

program Alarm;

{$M $1024,0,0} {cap bo nho $M Stacksize,heapmin,heapmax}
uses crt,dos;
const DefaultTime = 1; {Tham số mặc định, báo thức sau 1
phút}
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 263
Lập trình bằng Turbo Pascal
Var CurrentTime: longint;
Worktime: word; {khoảng thời gian đợi báo thức}
KbdIntVec: procedure;
{để lu địa chỉ của chơng trình xử lí ngắt bn phím
của hệ thống}

{$F+} { force Far call}

procedure KeyClick; interrupt;
Begin
gotoXY(1,1);
TextColor(Yellow);
TextBackGround(Red);
Write(' Over time ! - Press F12 to continue.');
While Port[$60] <> 88 do Write(#7);
{ phát còi khi cha gõ F12(có mã 88)để chấm dứt}
{ Port[i] truy cập cổng i=$60 l cổng bn phím }
SetIntVec($9,Addr(KbdIntVec));
Inline($9C);
KbdIntVec;
{gọi lại chơng trình xử lí ngắt bn phím cũ}
end;
{$F-}

{$F+}
procedure TestTime; interrupt;
begin
Inc(CurrentTime);
if (CurrentTime >= Round(workTime*60*18.2)) then
begin
SetIntVec($1C,Addr(KbdIntVec));
{phục hồi vecto ngắt $1C cũ}
GetIntVec($9,Addr(KbdIntVec));
{lu vecto ngắt bn phím}
SetIntVec($9,Addr(KeyClick));
{ngắt bn phím bây giờ nối đến KeyClick}
end;
Inline($9C);

KbdIntVec; {gọi lại chơng trình xử lí ngắt bn phím
cũ }
end;
{$F-}

procedure TestParam; {xử lý tham số dòng lệnh = số phút đợi
báo thức}
var WorkTimeStr: String;
i: byte;
code: integer;
begin WorktimeStr:='';
For i:=1 to ParamCount do
WorkTimeStr:= WorkTimeStr + ParamStr(i);
Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 264
Lập trình bằng Turbo Pascal
Val(WorkTimeStr,WorkTime, code);
if code <> 0 then
workTime:=DefaultTime;
end;

BEGIN
TestParam;
CurrentTime:=0;
GetIntVec($1C,Addr(KbdIntVec));
{lu vecto ngắt $1C }
SetIntVec($1C,Addr(TestTime));
{trỏ đến chơng trình xử lí testTime của ta}
keep(0);
END.




Câu hỏi v bi tập

1. Véc tơ ngắt l gì. Chúng thực hiện những chức năng gì.
2. Để gọi trực tiếp thực hiện ngắt từ trong một chơng tình Pascal cần
phải theo các bớc nh thế no. Tham số đầu vo đặt ở đâu, kết quả
trả vể lấy ở đâu.
3. Vai trò của kiểu thanh ghi Registers trong Unit DOS l gì. Tại sao nó
lại đợc khai báo l kiểu bản ghi có cấu trúc thay đổi.
4. Thâm nhập hệ thống qua hỗ trợ của Unit DOS có u nhợc điểm gì.
5. Nguyên lí chung để xây dựng một chơng trình thờng trú l gì.
6. Khi no cần thâm nhập trực tiêp bộ nhớ, các cổng thiết bị. Cách lm.

Thực hnh.

1. Lập hai thủ tục lu lại mn hình (ở chế độ văn bản) v khôi phục mn
hình đã lu. ứng dụng để hon thiện chơng trình bảng chọn: sau khi
chọn thì bảng chọn biến mất v mn hình cũ đợc khôi phục lại.
2. Lập hai thủ tục lu lại một phần mn hình (ở chế độ văn bản) nằm
trong một khung cửa sổ hình chữ nhật v khôi phục lại phần cửa sổ đó.
ứng dụng để hon thiện chơng trình bảng chọn: sau khi chọn thì bảng
chọn biến mất v mn hình cũ đợc khôi phục lại.





Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 265
Lập trình bằng Turbo Pascal



TI liệu tham khảo


1. Hong Kiếm (chủ biên), Tin học đại cơng nâng cao, Nxb Giáo dục,
1998.
2. Quách Tuấn Ngọc, Ngôn ngữ lập trình Pascal, Nxb Giáo dục, 2000.
3. Quách Tuấn Ngọc, Bi tập ngôn ngữ lập trình Pascal, Nxb Giáo dục,
2000.
4. Schapers A., Mẹo v thủ thuật lập trình bằng Turbo Pascal 5.5, Nxb
KHKT H nội, 1996.
5. Bùi Thế Tâm, Võ Văn Tuấn Dũng, Giáo trình tin học đại cơng,
Nxb GTVT, H nội, 1995.
6. Koffman E. B., Pascal- Problem Solving and Problem Design,
Addison-Wesley Pub. Company, 1993.
7. Catambay B., The Pascal Programming Language, Academic Press,
2001,





Nguyễn Đình Hoá, Viện CNTT - ĐHQG H nội 266

×