PDA

View Full Version : Tạo Screen saver bằng Pascal..


Mapcon
11-12-2002, 08:30 PM
Các Screen Saver trong thực tế thường bắt đầu kích hoạt chương trình bảo vệ màn hình sau một khoảng thời gian bàn phím hay chuột không hoạt động và trả lại trạng thái cũ sau khi bàn phím hay chuột hoạt động. Nói chung là xử lý chuột và bàn phím thì gần như nhau trong Screen Saver, vì vậy mà tôi sẽ chỉ trình bày với bạn về một Screen Saver được kích hoạt nhờ bàn phím.
Khi viết một Screen Saver chúng ta nhất thiết phải có các thủ tục sau đây:
1. Lưu lại trạng thái trước :
Bạn cần lưu lại màn hình trước khi bảo vệ. Trong mode văn bản thông thường của DOS là 80*25, vùng nhớ màn hình có địa chỉ từ $B800, thật may Turbo Pascal có sẵn mảng MEM để truy nhập đến các vùng nhớ. Chúng ta khai báo 1 mảng screen: array[1..80*25*2] of byte, trước khi bảo vệ màn hình ta lưu vùng nhớ màn hình vào mảng này bằng lệnh: move(MEM[$B800:0], screen[1],80*25*2), và sau này khôi phục bằng lệnh: move(screen[1], MEM[$B800:0],80*25*2). Sở dĩ mảng screen có kích thước 80*25*2 là vì mỗi vị trí trong 80*25 vị trí của màn hình cần 2 byte 1 cho ký tự ở đó và 1 cho thuộc tính (màu sắc) của ký tự đó.
Bạn tham khảo 2 cách bảo vệ màn hình trong chương trình ở dướị

2. Xây dựng các ngắt mới

Chương trình thường trú phải biết được bàn phím đã không được chạm đến trong một khoảng thời gian nhất định vì vậy mà ngắt thời gian và ngắt bàn phím cũ của BIOS cần phải được thay thế bằng ngắt mới của chúng ta, tuy nhiên ngắt mới phải có đoạn gọi ngắt cũ vì chúng ta không thể nào làm lại hoàn toàn một ngắt(!). Cũng vì lý do đó, khi một chương trình đang thường trú chúng ta không nên chạy nó nữa vì ngắt cũ đang không phải là ngắt gốc của BIOS!
Ngắt thời gian mới có nhiệm vụ tăng biến Bộ đếm thời gian và kiểm tra nếu nó vượt qua giới hạn thì bảo vệ màn hình. Ngắt thời gian mới có dạng:

Procedure NewTimer; Interrupt;
Begin
CallOldInterrupt(OldTimer); { Gọi ngắt cũ }
if TimeCounter <= TimeLimit then inc(TimeCounter) else GuardScreen;
End;

Khi có phím ấn thì ngắt bàn phím được gọi, ngắt(Interrupt) cũng là một loại chương trình con nên những điều chúng ta muốn thực hiện khi có phím ấn - chúng ta cho vào ngắt bàn phím. Ngắt bàn phím mới sẽ kiểm tra xem nếu màn hình đang trong tình trạng bảo vệ thì nó sẽ khôi phục màn hình. Khi ngắt bàn phím mới này được gọi thì ta luôn cho biến Bộ đếm thời gian về 0 vì đó là thời điểm gần nhất bàn phím được ấn. Ngắt bàn phím mới có dạng:

Procedure NewKeyboard; Interrupt;
Begin
CallOldInterrupt(OldKeyboard); { Gọi ngắt cũ }
{ Nhận xét: Màn hình đang bảo vệ thì TimeCounter >= Timelimit }
if TimeCounter >= TimeLimit then RestoreScreen;
TimeCounter:=0;
End;

3. Thường trú:

Để thường trú chương trình với các ngắt mới này trong Assembler là một chuỗi công việc dài còn trong Turbo Pascal (và cả C) thì rất đơn giản. Chỉ cần gọi thủ tục Keep(0) của unit DOS là xong.
Cũng cần lưu ý về kích thước các vùng nhớ cho chương trình. Turbo Pascal rất tốt bụng cho ta định hướng biên dịch $M để đặt kích thước Stack và Heap, cú pháp của $M là: {$M StackSize, HeapMin, HeapMax}.

4. Nối kết các thủ tục với nhau:
Sau khi xây dựng các thủ tục này và nắm được tổng thể chương trình, bạn có viết chương trình và chỉnh sửa, thêm thắt đôi chỗ để được chương trình hoàn hảo nhất. Chương trình screensv dưới đây minh hoạ cho những điều nói trên:

{$A+,B-,D+,E+,F+,G-,I-,L+,N-,O-,P-,Q-,R-,S-,T-,V-,X+}
{$M 1024,0,0}
Program ScreenSaver;
Uses CRT, DOS;
Const TimeLimit=91;(* 5s *)
str: string= 'I Love you ! ';
Var TimeCounter: longint;
OldTimer, OldKeyboard: pointer;
screen: array[1..80*25*2] of byte;
cx,cy,i: byte;

Procedure GuardScreen;
Begin
clrscr;
gotoxy(25,10);
for i:=1 to length(str) do
begin textcolor(random(16)); write(str[i]); end;
End;

Procedure GuardScreen2;{Brick Out Characters like NC}
Begin
inc(i); if i>3 then i:=0;
if i mod 3<>0 then exit;
textcolor(black);
gotoxy(random(80)+1,random(25)+1); write(#219);
gotoxy(random(80)+1,random(25)+1); write(#219);
gotoxy(random(80)+1,random(25)+1); write(#219);
End;

Procedure GetScreen;
Begin
move(mem[$B800:0],screen[1],80*25*2);
cx:=wherex; cy:=wherey;
asm mov ch,$01; mov cl,$00;
mov ah,$01; int $10
end;{Hide cursor}
End;

Procedure PutScreen;
Begin
move(screen[1],mem[$B800:0],80*25*2);
gotoxy(cx,cy);
asm mov ch,$06; mov cl,$07;
mov ah,$01; int $10
end;{Show cursor}
End;

Procedure CallOldInterrupt(p: pointer);
Begin
inline($9C);{Push flags}
inline($FF/$5E/$06);{Call p}
End;

Procedure NewTimer; Interrupt;
Begin
CallOldInterrupt(OldTimer);
if TimeCounter <= TimeLimit then inc(TimeCounter) else
begin
if TimeCounter = TimeLimit+1 then
begin
GetScreen;
TimeCounter:=TimeLimit+2;
end;
GuardScreen;
end;
inline($FB);{STI Bat ngat cung}
End;

Procedure NewKeyboard; Interrupt;
Begin
CallOldInterrupt(OldKeyboard);
if TimeCounter >= TimeLimit then PutScreen;
TimeCounter:=0;
inline($FB);{STI Bat ngat cung}
End;

Procedure Init;
Begin
TimeCounter:=0;
(* Saving old interrupts: *)
GetIntVec($08,OldTimer);{Timer Interrupt}
GetIntVec($09,OldKeyboard);{Keyboard Interrupt}
(* Set new interrupts: *)
SetIntVec($08,@NewTimer);
SetIntVec($09,@NewKeyboard);
(* Note: @ - ađress of procedurẹ*)
End;

BEGIN
Init;
Keep(0);
END.

--------------------------------------
Với một Screen Saver cũng cần quan tâm tới các vấn đề phức tạp khác như: chương trình đã thường trú chưa, bỏ thường trú như thế nào,… Những cái này thì tìm ở các hàm 35hex và 25hex trong ngắt 21 hex của DOS.

bimbo
18-12-2002, 08:16 PM
:blink: kin! hehhe! cho học hỏi thêm 1 vài chiêu nữa đi!
hoá ra về Dos em vẫn còn bít ít quá!
ếch ngồi đáy giếng coi trời bằng vung! hehheh!