Một ngắt lại có thể lμm vμi 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.. DOS chấm dứt chương trình .COM Thực hiện rất nhiều chức năn
Trang 11.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ỉ Nhưng 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 nμy, bộ nhớ được chia lμm 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 thμnh 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ể lμm vμi 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ụ mμn hình Ngắt nμy lại có nhiều chức năng khác nhau
như
$00: Thiết lập chế độ mμn hình
$03: Lấy toạ độ con chạy trên mμn hình văn bản
$0F: Lấy chế độ hiện tại của mμn hình
Trang 2
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
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);
Trang 3MsDos( Var Regs: Registers);
Biến thanh ghi Regs đóng vai trò cung cấp dữ liệu đầu vμo cho ngắt
được gọi vμ cũng lμ nơi để nhận kết quả trả về nếu có
MsDos dμnh 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 tμi liệu tra cứu về hệ thống, hệ điều hμnh DOS
1.4 Các ví dụ minh hoạ
begin
Trang 4writeln(' mode man hinh la:',AL);
writeln(' so cot :',AH);
writeln(' active page :',BH);
procedure SetVideoMode(mode: integer);
var regs: registers;
begin with regs do
Xem vμ thiÕt lËp ngμy 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
Trang 5if AL=0 then Writeln('da dat ngay xong !')
else writeln('co loi');
Tham số đầu vμo:
DL:= số hiệu của ổ đĩa (0,1,2 );
Giá trị trả về
Trang 6AX= số sector cho mỗi cluster ( = $FFFF nếu ổ đĩa không hợp
function SoByteConRoi(Drv: char): LongInt;
var regs: registers;
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 hμnh 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 lμm sẵn, hỗ trợ người sử dụng trong các giao tiếp với hệ thống vμ hệ điều hμnh Có thể sử dụng các hμm, thủ tục ấy để thâm nhập hệ thống dễ dμng hơn
Ví dụ, có thể xem vμ đặt ngμy tháng hệ thống bằng các thủ tục GetDate,
Trang 7Rõ rμng 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 hμnh DOS trở nên dễ dμng 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 hμm vμ thủ
tục đã được lμm sẵn trong đó
2 Điều khiển chuột
2.1 Toạ độ chuột
Mμn 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 đó, mμn hình đồ hoạ lại chia lμm nhiều
điểm ảnh, thấp nhất cũng lμ 640 x 480 đối vơí mμn hình VGA Để con chuột
có thể dùng trong cả hai chế độ mμn 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
Trang 8Để chuyển sang toạ độ x_text, y_text trên mμn hình văn bản, lưu ý rằng 1 ≤ x_text ≤ 80, ≤ y_text ≤ 25 ta sẽ có công thức đổi toạ độ chuột
sang toạ độ trên mμn 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
mμn hình văn bản Khi nhấn chuột vμo các điểm nμy đều chỉ ứng với một ô
vị trí kí tự trên mμn hình văn bản mμ thôi
Công thức chuyển toạ độ chuột sang toạ độ trên mμn 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 nμy 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
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 vμo CX = toạ độ x mới
DX = toạ độ y mới
Trang 96- Hỏi trạng thái vμ số lần nhấn phím kể từ khi gọi chức năng nμy
Đầu vμo 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 nμy
Đầu vμo 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 vμo: CX:= cột trá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 vμo: 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
đã cμi đặ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 mμn 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 bμy trên x,y lμ toạ độ ban đầu của chuột Left, Top, Right, Bottom
Trang 10Trong 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;
y_graphics như đã nói trên
const MOUSE_GET_POSITION = $03;
procedure getMousePosition(VAR x,y: integer);
begin
Trang 11regs.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
Hoμn toμn tương tự ta có thể đón bắt các sự kiện chuột phải
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 hμm tiện ích thao tác chuột Ví dụ,
Trang 12INT_MOUSE = $33; (*ngat chuot*)
procedure getMousePosition(VAR x,y: integer);
procedure getMouseClickLeft(VAR numclick, x, y
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 thμnh thủ tục xử lí
sự kiện, bao gồm cả sự kiện nhấn chuột nh− sau
Trang 13mouse_x:= raw_x 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
Turbo Pascal hỗ trợ thâm nhập trực tiếp vμo 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
Trang 14MemW[$xxxx: $yyyy]: truy cập 2 byte bộ nhớ (1 word) kể từ địa chỉ
có thể thâm nhập trực tiếp vμo vùng nhớ nμy để tăng tốc độ cho các thao tác hiển thị ra mμn hình
Chương trình dưới đây thay toμn bộ các kí tự trống bằng dấu sao “*”
procedure get_screen(var scr: one_screen);
{truy cập bộ nhớ mμn hình vμ chép lại toμn bộ mμn hình hiện tại}
procedure put_screen(var scr: one_screen);
{truy cập bộ nhớ mμn hình vμ cho hiển thị mμn hình đã lưu} var i: word;
begin
Trang 15Writeln(' 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 !!!');
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 bμy ở 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ỉ nμy 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 lμm mμn 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 vμo cổng CRT
program CuonManHinh;
Trang 16for i:= 0 to num do
PortW[CrtAddr]:= Hi(i) SHL 8 + $0C;
PortW[CrtAddr]:= Lo(i) SHL 8 + $0D;
asm STI end;
Khi thi hμnh một chương trình, hệ điều hμnh dμnh 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 hμm $4C - kết thúc tiến trình vμ giải phóng vùng nhớ dμnh riêng cho chương trình, chép Command.com đè lên vùng nμy
Một cách thứ hai để kết thúc chương trình lμ dùng hμm 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ớ
đã dμnh cho chương trình, nghĩa lμ không chép Command.com đè lên vùng
nμy mμ chép vμo 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 lưu 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 bμn 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 bμn 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 thμnh mã của â, gõ dd thì chuyển thμnh 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
Trang 17- Xây dựng thủ tục xử lí ngắt mới
- Lưu 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
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
Ngoμi ra còn một số thủ tục hỗ trợ lμm việc vói các véc tơ ngắt
GetIntVec (IntNo: Byte; Vector: Pointer) ;
Lưu lại véc tơ ngắt số hiệu IntNo vμo đị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
4.3 Ví dụ minh hoạ
Trang 184.3.1 Ví dụ 1
Đầu tiên ta xét một ví dụ đơn giản, lμm 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 mμn hình ra máy in Ngắt nμy ứ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 mμn hình
Trong chương trình trên cần lưu ý 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 hμm 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 hμnh 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 nμy lμ thủ tục
VietRaGocManHinh Lúc nμy 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μ hoμn 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 nμy, 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 vμo 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 lμm thế nμo 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 nμo để khôi phục lại chức năng gốc của phím Print-Screen ngoμi việc tắt máy
khởi động lại