1. Trang chủ
  2. » Luận Văn - Báo Cáo

Đề tài Ngắt và xử lý ngắt trong C – Chương trình thường trú

17 1,8K 4

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 17
Dung lượng 210 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Thực chất DOS là một hệ điều hành đơn nhiệm tại một thời điểm chỉ chạy được một chương trình ứng dụng, nhưng chúng ta vẫn có phương pháp làm cho các ứng dụng nhỏ chạy song song với chươn

Trang 1

Từ trước tới nay, chúng ta luôn luôn làm việc với các chương trình tuần tự Điều đó có nghĩa là lệnh nọ nối sau lệnh kia theo một trật tự cố định Để thực sự sử dụng sức mạnh của máy tính, tạo ra những ứng dụng có khả năng chạy thường trú và xử lý đa tuyến, nhiều công việc đan xen nhau, chúng ta cần tìm hiểu lập trình ở mức độ ngắt.

Nhóm 12 tham gia nghiên cứu Đề tài “Ngắt và xử lý ngắt trong C – Chương trình thường trú” nhằm nắm được phần

nào dạng lập trình phức tạp này và xây dựng chương trình thường trú hiển thị Đồng hồ thời gian thực trong DOS.

Hiện nay, tài liệu về Lập trình hệ thống không nhiều, tài liệu

từ Internet đa phần viết bằng tiếng Anh nên nhóm cũng gặp một vài khó khăn nhất định Trong quá trình tham gia nghiên

thầy Nguyễn Trần Thi Văn đã nhiệt tình hướng dẫn, tạo điều

kiện thuận lợi về tài liệu và giúp đỡ nhóm hoàn thành đề tài.

Dù đã rất cố gắng nhưng chắc chắn Nhóm vẫn còn nhiều thiếu sót Rất mong nhận được ý kiến đóng góp của Thầy và các bạn.

Tp Hồ Chí Minh, tháng 12/2008

Nhóm 12

Trang 2

Mục lục: Trang

A.Tổng quan 3

B.Nội dung 3

1 NGẮT VÀ XỬ LÝ NGẮT 3

1.1 Ngắt là gì? 3

1.2 Thủ tục ngắt 3

1.3 Bảng vector ngắt (chủ yếu ngắt chính): 4

1.4 Các hàm hỗ trợ gọi ngắt của dos.h: 6

2 CHƯƠNG TRÌNH THƯỜNG TRÚ 10

2.1 Cấu trúc, phân loại và cách xây dựng 10

2.2 Chế độ biên dịch bằng dòng lệnh 13

2.3 Hàm keep() 15

2.4 Tóm tắt các lệnh cần dùng để thường trú chương trình 16

2.5 Ví dụ về chương trình thường trú 16

C.Chương trình 17

D.Kết luận 17

Trang 3

A TỔNG QUAN

DOS là một hệ điều hành phổ biến trong quá khứ và hiện nay nó vẫn còn được ứng dụng rộng rãi Thực chất DOS là một hệ điều hành đơn nhiệm (tại một thời điểm chỉ chạy được một chương trình ứng dụng), nhưng chúng ta vẫn có phương pháp làm cho các ứng dụng nhỏ chạy song song với chương trình chính, ứng dụng nhỏ đó gọi là chương trình xử lý ngắt thường trú (hay còn gọi là chương trình thường trú) Do đó, để viết chương trình thường trú chúng ta cần phải tìm hiểu về ngắt và xử lý ngắt trong DOS

1.1 Ngắt là gì?

Mỗi khi một thiết bị phần cứng hay một chương trình cần sự giúp đỡ của CPU, nó gởi một tín hiệu hoặc lệnh đến CPU, gọi là ngắt, chỉ định một công việc cụ thể mà nó cần CPU thực hiện Khi CPU nhận được tín hiệu ngắt, nó tạm ngưng tất cả các hoạt động khác và kích hoạt một chương trình con đang có trong bộ nhớ, gọi là chương trình

xử lí ngắt, tương ứng với số hiệu ngắt cụ thể Sau khi chương trình xử lí ngắt làm xong nhiệm vụ, các hoạt động của máy tính sẽ tiếp tục lại từ nơi đã bị tạm dừng lúc xảy ra ngắt

• Có hai loại ngắt: ngắt cứng và ngắt mềm Chúng có cấu trúc tương tự nhau, chia nhau bảng vectơ ngắt nhưng lại có những khác nhau cơ bản

• Ngắt cứng xuất hiện khi các thiết bị ngoại vi cần được phục vụ Ví dụ: cắm USB, ấn phím, …

• Ngắt mềm xuất hiện khi chương trình thực hiện một lệnh ngắt

1.2 Thủ tục ngắt

1.2.1 Thủ tục ngắt là gì?

Khi xuất hiện ngắt cứng có số hiệu N thì chương trình tạm thời gác lại, CPU sẽ tìm địa chỉ thủ tục ngắt N trong bảng vector ngắt để thực hiện Nếu ta thay địa chỉ thủ tục ngắt N bằng địa chỉ của một thủ tục do chúng ta lập ra, thì thủ tục của chúng ta sẽ được thực hiện mỗi khi xảy ra ngắt N Thủ tục được xây theo cách này gọi là thủ tục

xử lí ngắt

1.2.2 Cấu trúc chung và cú pháp.

1.2.2.1 Cú pháp:

void interrupt <tên hàm>([Danh sách các tham số]) { các_lệnh}

Trang 4

Từ khóa interrupt dùng để khai báo hàm xử lý ngắt Khi gặp từ khai báo interrupt thì một mã đặc biệt được sinh ra để cất tất cả các thanh ghi và cờ

1.2.2.2 Tham số:

Trong hàm xử lý ngắt có thể dựa vào các đối Việc trao đổi thông thông tin giữa chương trình và hàm được thực hiện thông qua các đối Các tham số phải tuân theo các qui tắc sau:

- Các đối sẽ nhận giá trị của các thanh ghi ở thời điểm xảy ra ngắt ( khi ngắt bắt đầu thực hiện)

- Giá trị của các đối có thể thay đổi bằng câu lệnh trong hàm(nên thận trọng)

- Khi thoát khỏi hàm, giá trị của các đối sẽ gán trả lại cho các thanh ghi

Đối số của hàm xử lý ngắt cần được khai báo theo mẫu sau: void interrupt <tên_hàm>(int bp, int di, int si, int ds, int es, int dx, int cx, int bx, int

ax, int ip, int cs, int flags);

Lưu ý: để tránh các cảnh báo khó chịu do không sử dụng hết đối số, ta dùng các lệnh tiền xử lý sau:

#pragma warn-par //tắt kiểm tra tham số

#pragma warn.par //bật kiểm tra tham số

1.2.2.3 Thân hàm:

i Không được sử dụng các dịch vụ của DOS (như các hàm vào ra)

ii Không được sử dụng các phép tính dấu chấm động

iii Chỉ nên dùng các phép gán, phép tính trên số nguyên, kí tự và các phép logic

1.3 Bảng vector ngắt (chủ yếu ngắt chính):

Số hiệu ngắt(hex) Địa chỉ(hex) Công dụng

1 0004 cho phép thực hiện chương trình từng lệnh một

3 000C dùng để đặt điểm dừng (break-point) trong chương

trình

5 0014 gọi chương trình phục vụ BIOS để in trang màn hình

9 0024 được tạo ra khi có tác động vào phím bất kì

Trang 5

D 0034 được tạo ra khi có hồi dọc trên màn hình, dùng để điều

khiển video

E 0038 được tạo ra bởi bộ điều khiển đĩa mềm để báo đang tìm

kiềm trên đĩa

10 0040 gọi các chương trình phục vụ BIOS về màn hình

12 0048 gọi phục vụ BIOS thông báo kích thước bộ nhớ

13 004C gọi các chương trình phục vụ BIOS về vào/ra đĩa (HDD

và FDD)

14 0050 gọi các chương trình phục vụ BIOS cho truyền tin qua

cổng nối tiếp

15 0054 gọi các chương trình phục vụ BIOS cho cassette và các

phục vụ mở rộng

16 0058 gọi các chương trình phục vụ BIOS cho bàn phím

chuẩn

17 005C gọi các chương trình phục vụ BIOS cho máy in

18 0060 kích hoạt ngôn ngữ ROM-BASIC, hoặc cái thay thế

ngôn ngữ này

19 0064 gọi các chương trình khởi động boot-strap

1A 0068 gọi các chương trình phục vụ BIOS về giờ và ngày

tháng 1B 006C được tạo ra (dưới BIOS) bởi các tổ hợp phím thoát

Ctrl-Break, Ctrl-C) 1C 0070 được tạo ra bởi mổi nhịp đồng hồ, do chính ngắt 8 gọi

22 0088 trỏ đến địa chỉ cần chuyển điều khiển đến khi chương

trình kết thúc dưới DOS

Trang 6

23 008C trỏ đến địa chỉ cần chuyển điều khiển đến khi gặp tổ

hợp phím thoát (Ctrl-Break hay Ctrl-C) dưới DOS

24 0090 trỏ đến địa chỉ cần chuyển điều khiển đến khi gặp lỗi

nghiêm trọng dưới DOS

27 009C kết thúc chương trình và cho thường trú trong bộ nhớ

dưới DOS

44 0110 trỏ đến các kí tự đồ thị thấp (chỉ dùng cho EAG)

49 0124 trỏ đến bảng chuyển đổi cho thiết bị phụ của bàn phím

1.4 Các hàm hỗ trợ gọi ngắt của dos.h:

Gồm: geninterrupt(), int86(), segread(), int86x(), setvect(),

getvect(), disable(), enable(), …

1.4.1 Tìm hiểu về cách gọi một thủ tục ngắt mềm:

Để gọi một ngắt mềm ta phải tiến hành các bước sau:

• Đặt ngắt vào thanh ghi AH

• Xác định tham số đầu vào và đưa chúng vào các thanh ghi theo yêu cầu của mỗi ngắt

• Thực hiện câu lệnh gọi ngắt theo số hiệu ngắt

• Lấy kết quả do ngắt sinh ra từ thanh ghi(tham số đầu ra)

1.4.2 Các giả thanh ghi, union và struct dùng trong các hàm của

dos.h:

1.4.2.1 Giả thanh ghi:

_AX , _AH, _AL _BX , _BH, _BL

_CX , _CH, _CL

_DX , _DH, _AL

_CS, _DS, _ES, _SS

_SP, _BP, _SI, _DI

_FLAGS

Các giả thanh ghi tự động biến đổi theo các thanh ghi, vì vậy không thể dùng chúng với mục đích lưu trữ

Trang 7

1.4.2.2 union và struct:

struct WORDREGS /*thanh ghi chung 16 bit*/

{

unsigned int ax, bx, cx, dx, si, di, cflag, flags;

};

struct WORDREGS /*thanh ghi chung 8 bit*/

{

unsigned int al, ah, bl, bh, cl, ch, dl, dh;

};

struct SREGS /*Các thanh ghi đoạn*/

{

unsigned int es, cs, ss, ds;

};

union REGS /*hợp giữa các thanh ghi 16bit và 8bit*/

{

struct WORDREGS x;

struct BYTEREGS h;

};

1.4.2.3 Chuyển đổi địa chỉ (cần thiết cho các ngắt):

Để chuyển từ địa chỉ thực sang địa chỉ phân đoạn ta dùng các macro sau:

unsigned FP_SEG(địa_chỉ_thực); /*trả về địa chỉ segment*/

unsigned FP_OFF(địa_địa_thực); /*trả về địa chỉ offset*/

Để chuyển ngược lại ta dùng macro: void far *MK_FP(segment, offset);

1.4.2 Chức năng các hàm ngắt mềm:

1.4.2.1 Hàm: geninterrupt(int sh_ngat):

Hàm thực hiện ngắt mềm, sh_ngat là số hiệu ngắt Hàm này có sử dụng các giả thanh

ghi ở 1.4.2.1.

Ví dụ: Để thực hiện ngắt (0x21, 6) (đưa một kí tự lên màn hình) ta thực hiện như sau: _AH=6; /*đưa số hiệu chức năng vào thanh ghi AH*/

_AL=65;/*đưa mã kí tự (chữ A) vào thanh ghi AL*/

geninterrupt(0x21); /*gọi ngắt 0x21*/

Trước khi gán giá trị vào giả thanh ghi, ta nên lưu trữ tình trạng hiện thời của nó vào một biến để sau này khôi phục lại Nếu không làm như vậy có thể dẫn tới treo máy

1.4.2.2 Hàm: int int86(int sh_ngat, union REGS *v, union REGS *r):

Hàm thực hiện ngắt mềm sh_ngat Các tham số đầu vào được nạp vào các thanh ghi thông qua “union v” Tham số đầu ra nhận được từ “union r”

Ví dụ: ở ví dụ 1.4.4.1 ta sử dụng hàm geninterrupt() để thực hiện ngắt (0x21, 6), sau đây là ví dụ minh họa cho hàm int86() thực hiện ngắt tương tự union REGS v, r; /*biến v dùng đề chứa tham số đầu vào, r dùng để chứa tham số đầu ra*/

Trang 8

v.h.ah=6; /*đưa số hiệu chức năng vào thanh ghi AH*/ v.h.al=65; /*đưa mã kí tự (chữ A) vào thanh ghi AL*/ int86(0x21, &v, &r); /*gọi ngắt 0x21*/

Hàm: void segread(struct SREGS *s):

Hàm đặt nội dung các thanh ghi đoạn vào cấu trúc s Hàm này được dùng để chuẩn

bị cho hàm int86x()

1.4.2.3 Hàm: int int86x(int sh_ngat, union REGS *v, union REGS

*r, struct SREGS *s):

Hàm có tác dụng như hàm int86() nhưng tham số đầu vào được xác định thông qua

“union v” và “struct s”, kết quả nhận từ “union r” và “struct s”

1.4.3 Các hàm hỗ trợ xử lý ngắt cứng:

1.4.3.1 Hàm disable() và enable():

Có những đoạn mã (đoạn chương trình) đòi hỏi điều kiện nghiêm ngặt sau: trong thời gian thực hiện đoạn mã không cho phép xuất hiện ngắt cứng Ta gọi là các đoạn mã tới hạn Việc xảy ra ngắt trong đoạn mã tới hạn có thể làm chương trình hoạt động rối loạn hay thiếu chính xác Để viết các đoạn mã tới hạn ta cần dùng hai hàm sau:

disable(); //dùng để tắt hệ thống ngắt cứng.

//các câu lệnh enable(); //dùng để bật trở lại hệ thống ngắt cứng.

Các ngắt chỉ nên tắt trong khoảng thời gian rất ngắn Mọi ngắt xuất hiện khi hệ thống ngắt bị tắt, nó sẽ phải chờ cho đến khi được bật trở lại

1.4.3.2 Hàm getvect() và setvect():

Để chương trình gọi tới một hàm xử lý ngắt, trước đó cần đặt địa chỉ của nó đè lên địa chỉ của thủ tục dịch vụ ngắt chuẩn tương ứng Điều này được thực hiện nhờ hai hàm sau:

void interrupt (*getvect(int sh_ngat))();

Trang 9

void setvect(int sh_ngat, void interrupt (*con_tro_ham)()); Hàm getvect() dùng để nhận địa chỉ của một thủ tục dịch vụ ngắt chuẩn với

mục đích khôi phục sau này

Hàm setvect() dùng để đặt địa chỉ của hàm xử lý ngắt đè lên địa chỉ của thủ tục

dịch vụ ngắt chuẩn có số hiệu sh_ngat

1.4.3.3 Cấu trúc của một chương trình xử lý ngắt:

/* khai báo con trỏ hàm để lưu địa chỉ thủ tục ngắt chuẩn*/ void interrupt (*dos_int)();

/* khai báo hàm xử lý ngắt*/

void interrupt (*user_int)();

main()

{

/*lưu địa chỉ củ thủ tục ngắt chuẩn*/

dos_int=getvect(8);

/*cài đặt địa chỉ mới*/

disable();

setvect(8, user_int);

enable();

/*thực hiện hàm xử lý ngắt*/

/*khôi phục địa chỉ*/

disable()

setvect(8, dos_int);

enable();

}

Trang 10

2 CHƯƠNG TRÌNH THƯỜNG TRÚ

2.1 Cấu trúc, phân loại và cách xây dựng

2.1.1 Khái niệm:

Chương trình xử lý ngắt thường trú hay gọi gọn là Chương trình thường trú (kết thúc và thường trú – Terminate and stay Resident- TSR) là chương trình mà các

ngắt chuẩn bị sẽ chiếm dụng vĩnh viễn(chỉ khi khởi động lại máy mới khôi phục

được)

Khi thực hiện chương trình thường trú thì chương trình thường trú sẽ được thực hiện (trong bất kỳ môi trường nào) mỗi khi xuất hiện ngắt chuẩn (đang bị chiếm dụng bởi chương trình)

Ví dụ: Nếu dùng ngắt 8 (đồng hồ) thì cứ sau mỗi tích tắc (khoảng 1/18 giây) chương

trình lại được kích hoạt Nếu dung ngắt 0x15(bàn phím) thì chương trình được kích hoạt mỗi khi gõ phím

Trong chương trình không thường trú, hàm ngắt thường dùng để lấy thông từ từ các thanh ghi chứa vào các biến ngoài để cho hàm main xử lý

trong hàm TSR, hàm ngắt phải làm tất cả mọi việc Hàm main chỉ làm nhiệm vụ chiếm dụng ngắt và thường trú

2.1.2 Cấu trúc:

/* khai báo con trỏ hàm để lưu địa chỉ thủ tục ngắt

void interrupt(*dos_int)(void);

/*khai báo hàm xử lý ngắt*/

void interrupt(*user_int)(void);

/*xây dựng hàm xử lý ngắt*/

unsigned int_stklen=1024,_heaplen=2;

main()

{

unsigned int size=_SS-_psp+_SP/16+50;

/*lưu địa chỉ của thủ tục ngắt chuẩn*/ dos_int=getvect(8);

/*cài đặt địa chỉ mới*/

disable();

setvect(8,user_int);

enable();

/*thực hiện hàm xử lý ngắt*/

/*khôi phục địa chỉ*/

disable();

setvect(8,dos_int);

enable();

/*kết thúc chương trình thường trú*

Trang 11

keep(0,size);

}

Câu lệnh:

unsigned int _stklen=1024,_heaplen=2;

Chương trình thường trú chiếm dụng lâu dài vùng nhớ vì vậy trước khi đặt vào bộ nhớ ta cần làm giảm độ lớn cho nó, biến _stklen dùng để quy đình kích thước vùng ngăn xếp Còn biến _heaplen dùng để quy định kích thước vùng heap Độ lớn mặc định của ngăn xếp là 4K byte của heap là toàn bộ vùng nhớ có thể cung cấp (ứng với heap

=0) Ta nên đặt vùng nhớ tối thiểu cho ngăn xếp là 1K byte và heap là 1 word (2 byte) câu lệnh:

unsigned int size=_SS-_psp+_SP/16+50;

Xem thêm phần hàm Keep() (Mục 2.4)

2.2.3 Phân loại:

2.2.4 Cách xây dựng:

Chúng ta hãy tham khảo ví dụ sau:

Chương trình thay đổi chức năng các phím :

Ta nêu trường hợp cần thay đổi chức năng các phím Giả sử phím 1( mã quét 0x02) bị hỏng trong khi đó ta thường xuyên phải dùng đến nó, ta hãy chọn một phím như phím ~ ( mã quét 0x29, ở bên trái phím 1) và đổi chức năng để nó trở thành 1 Nói cách khác khi bấm phím ~ thì trên màn hình xuất hiện số 1 Muốn vậy ta lập hàm xử lý ngắt 15 để nhận giá trị các thanh ghi AH, AL và biến đổi theo quy tắc sau: nếu AH = 0x29 ( bấm phím ~) thì đổi AL =0x02 (phím 1)

- Hàm này được viết đơn giản như sau:

#pragma warn – par

void interrupt new_keyboard(INTERRUPT_REGS)

{

if((ax>>8)==KBD_ FUNCTION&&(ax&0xFF)==MA_NGA)

ax=ax&0xFF00|MA_1;

}

#pragma warn.par

// dưới đây là chương trình:

#include”dos.h”

#include”conio.h”

#include”stdio.h”

#include”stdlib.h”

#define MA_NGA 0x29

#define MA_1 0x02

#define KBD_INT 0x15

#define KBD_FUNCTION 0x4F

#define u_int unsigned int

Trang 12

#define INTERRUP_REGS u_int bp, u_int di, u_int si, u_int ds,\

u_int es, u_int dx, u_int cx, u_int bx, u_int ax, u_int ip,\

u_int cs, u_int flags

#define INTERRUP_PARAM bp, di, si, ds, es, dx, cx, bx, ax, ip, cs, flags

void interrupt new_keyboard(INTERRUPT_REGS);

void interrupt (*old_keyboard)(INTERRUPT_REGS);

unsigned int_stklen=1024, _heaplen=2;

#pragma warn – par /*tắt kiểm tra tham số*/

void interrupt new_keyboard(INTERRUPT_REGS)

{

if((ax>>8)==KBD_ FUNCTION&&(ax&0xFF)==MA_NGA)

ax=ax&0xFF00|MA_1;

}

#pragma warn.par

/*bật kiểm tra tham số*/

void main(void)

{

unsigned int size = _SS-_psp+_SP/16+50;

disable();

/*tắt hệ thống ngắt cứng*/

setvect(KBD_INT, new_keyboard);

enable();

/*bật hệ thống ngắt cứng*/

keep(0,size);

/*kết thúc chương trình thường trú*/

}

Cách xây dựng chương trình thường trú về cơ bản giống như một chương trình

xử lý ngắt, ta chỉ sử dụng thêm các câu lệnh sau:

unsigned int_stklen=1024,_heaplen=2;

unsigned int size=_SS-_psp+_SP/16+50;

keep(0,size);

trong đó:

• Lệnh thứ nhất đặt trên( bên ngoài) hàm main();

• Lệnh thứ hai đặt ở đầu( bên trong) hàm main();

• Lệnh thứ ba đặt ở cuối( bên trong hàm main();

2.2 CHẾ ĐỘ BIÊN DỊCH BẰNG DÒNG LỆNH TCC

2.2.1 Khái niệm:

Trình biên dịch TCC thực hiện ở mức DOS cho phép dịch các chương trình lớn gồm nhiều module trên nhiều tệp dạng nguồn (*.C) và dạng đích (*.OBJ) TCC làm việc theo hai bước:

• Dịch một cách độc lập các module nguồn

• Liên kết các module vừa dịch , các module dạng đích (đã dịch từ trước) và các module thư viện (*.LIB) để tạo thành tệp module thực thi (*.EXE)

Ngày đăng: 06/05/2015, 14:58

TỪ KHÓA LIÊN QUAN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w