Chương 8 8086 Interrupts and Interrupt Application

Màu nền
Font chữ
Font size
Chiều cao dòng

Chương 8

8086 Interrupts and Interrupt Application

Chúng ta khai báo 1 segment tên DATA cho dữ liệu chương trình sử dụng. Từ 'WORD' trong câu khai báo bảo cho vị trí liên kết tới vị trí của segment này trên địa chỉ chẵn đầu tiên có thể dùng được. 'PUBLIC' trong câu khai báo cho linker biết rằng segment này có thể là được kết nối cùng nhau với segment của tên tương tự từ module khác. Giá trị đầu vào là Word, vì vậy chúng ta sử dụng chỉ thị DW để khai báo 4 giá trị. Giá trị Scale sẽ là kiểu byte vì vậy sử dụng chỉ thị DB để đặt riêng r 4 vị trí cho chúng. Như chương trình thực hiện kết quả sẽ được viết vào các vị trí này. SCALE_FACTOR DB 09H đặt riêng 1 vị trí byte cho con số mà chúng ta muốn chia.

Phần của phản ứng (đáp ứng, phục vụ) interrupt 8086 là cần thiết gọi xa tới thủ tục phục vụ ngắt. Trong tất cả các chương trình mà gọi 1 thủ tục, chúng ta phải đặt 1 STACK để nắm giữ địa chỉ trả về và tham số truyền tới và từ thủ tục. Phần tiếp theo của chương trình khai báo 1 STACK SEGMENT gọi là STACK_SEGMENT. Nó cũng thiếp lập 1 con trỏ trỏ tới vị tiếp theo bên trên stack với câu lệnh TOP_STACK LABEL WORD.

Hai phần tiếp theo của chương trình là cần thiết bởi vì chúng ta viết 1 chương trình chính và thủ tục phục vụ ngắt như là 2 module riêng biết. Khi ASSEMBLER đọc qua 1 chương trình nguồn. Nó làm 1 bảng ký hiệu mà chứa đựng SEGMENT và OFFSET của mỗi ký tự của tên và nhãn sử dụng trong chương trình. Câu lệnh PUBLIC BAD_DIV_FLAG bảo cho ASSEMBLER nhận biết tên BAD_DIV_FLAG như dùng chung. Nghĩa ở đây là khi module đối tượng của chương trình này được két nối với 1 vài module khác mà câu lệnh khai báo BAD_DIV_FLAG kahi báo là EXTRN, chương trình linker sẽ cho phép để làm kết nối. Câu lệnh INT_PROC SEGMENT WORD PUBLIC và INT_PROC ENDS bảo cho ASSEMBLER biết BAD_DIV được định nghĩa trong 1 SEGMENT tên INT_PROC khi ASSEMBLER đọc những câu lệnh này, nó sẽ làm 1 lối vào trong như bảng ký hiệu cho BAD_DIV và nhận diện 1 nó như bên ngoài. Khi module đối tượng cho chương trình được link với module đối tượng cho chương trình nới có BAD_DIV được định nghĩa, linker sẽ lấp đầy giá trị cho CS và IP của BAD_DIV.

Lệnh thực tế của chương trình chính : Chúng ta khai báo 1 CODE SEGMENT WORD PUBLIC.

Tại phần đầu của CODE SEGMENT chúng ta sử dụng câu khai báo ASSUME để bảo cho ASSEMBLER phân đoạn logic gì (What logical segment) để sử dụng cho mã, cho dữ liệu, và cho stack. Sau đó khởi tạo giá trị cho thanh ghi SS, SP, DS.

4 lệnh tiếp theo nạp địa chỉ của thủ tục phục vụ ngắt BAD_DIV trong vị trí kiểu 0 của bảng vecter ngắt. Chúng ta nạp ES với 0000 vì vậy chúng ta có thể sử dụng nó như 1 segment tưởng tượng tại địa chỉ 00000H. Sau đó chúng ta sử dụng câu lệnh MOV WORD PTR ES:0000 OFFSET BAD_DIV để nạp OFFSET của thủ tục phục vụ ngắt trong bộ nhớ tại 00000H và 00001H. Câu lệnh MOV WORD PTR ES:0002H SEG BAD_DIV sử dụng để nạp địa chỉ cơ sở SEGMENT của BAD_DIV vào bộ nhớ tại 00002H và 00003H. Nó cần thiết lập để nạp địa chỉ thủ tục ngắt trong trường hợp này.

MOV SI, OFFSET INPUTỴVALUES : Chúng ta khởi tạo SE như là 1 con trỏ trỏ tới giá trị vào đầu tiên và MOV BX, OFFSET SCALESỴVALUES khởi tạo BX như 1 con trỏ tới đầu tiên của vị trí. Chúng ta đặt riêng ra cho kết quả SCALE 8 bit. CX khởi tạo biến đếm.

Cuối cùng câu lệnh MOV AX, ỬSIỨ copy giá trị đầu vào từ bộ nhớ tới thanh ghi AX nơi nó thực hiện phép chia. DIV SCALEỴFACTOR sẽ thực hiện phép chia số trong AX bởi 09H. 8bit thương của phép chia này được đặt trong AL và 8 bit còn lại được đặt trong AH. Nếu tương này quá lớn để thích hợp trong AL thì 8086 sẽ tự động thực hiện 1 ngắt 0. Chương trình chúng ta ở đây 8086 push cờ vào stack, đặt lại IF và TF và đẩy địa chỉ trở về trên stack. Nó sẽ nhảy tới địa chỉ 00000H và 00002H để nhận giá trị IP và CS cho bắt đầu của BADỴDIV. Thủ tục của chúng ta sẽ viết để phục vụ ngắt 0. Nó sẽ thực hiện thủ tục BADỴDIV.

Thủ tục BADỴDIV làm như thế nào.

Thủ tục BADỴDIV đắt đàu bằng cho phép MASM biết rằng tên BADỴDIVỴFLAG mô tả biến của kiểu byte và biến là được định nghĩa trong SEGMENT gọi là DATA trong module ASM khác.

Tiếp theo chúng ta khai báo phân đoạn logic gọi là INTỴPROC. Chúng ta có thể đặt thủ tục này trong phân đoạn CODE với chương trình chính. Tuy nhiên trong chương trình chính hệ thống nơi có nhiều thủ tục phục vụ ngắt, một phân đoạn tách biệt thường đặt roc ràng cho chúng. Câu lệnh BADỴDIV PROC FAR nhận biết bắt đầu thực tế của thủ tục và bảo ASM cả 2 giá trị CS và IP cho thủ tục này cất giữ.

Bây giờ bước thực hiện quan trọng để làm tại điểm bắt đầu của bất kỳ thủ tục phục vụ ngắt nào là đẩy vào stack tất cả các thanh ghi được sử dụng trong thủ tục. Bạn có thể hồi phục các thanh ghi này bằng các POP chúng ra khỏi stack trước khi chúng quay về chương trình gọi ngắt. Chương trình gọi ngắt sẽ lấy lại các thanh ghi. Chúng ta lưu AX và DS, khi đó chúng ta sử dụng tương tự phân đoạn dữ liệu DATA. Trong chương trình chính và trong thủ tục ta có thể hỏi tại sao chúng tại lại ghi DS. Con trỏ mà 1 thủ tục phục vụ ngắt nên được viết viết vì vậy nó có thể sử dụng tại bất kỳ điểm nào trong 1 chương trình. Bằng cách lưu giá trị DS cho chương trình ngắt, thủ tục phục vụ ngắt này có thể được sử udngj trong phần chương trình mà không sử dụng DATA như phân đoạn DATA.

Câu lệnh ASSUME bảo cho ASM tên của phân đoạn để sử dụng như phân đoạn dữ liệu, nhưng nhớ rằng nó không nạp thanh ghi DS với 1 giá trị cho sự bắt đầu của phân đoạn. Lệnh MOV AX, DATA và MOV DS, AX làm việc này trong thủ của chúng ta.

Cuối cùng chúng ta nhận đầy đủ con trỏ của thủ tục này với lệnh MOV BADỴDIVỴFLAG, 01. Trong lệnh đơn giản này bit cuổi cùng quan trọng của vị trí bộ nhớ, chúng ta đặt rõ ràng với chỉ thị DB tại đầu chương trình chính. Chú ý rằng trong thủ tục để truy nhập biến này bằng tên ta phải cho ASM biết rằng nó là ở bên ngoài và bạn phải làm cho chắc chắn rằng thanh ghi DS chứa đựng phân đoạn cơ sở cho segment trong vị trí BADỴDIVỴFLAG được đặt.

Ðể hoàn thành thủ tục chúng ta lấy các thanh ghi được lưu trong STACK và trở về chương trình gọi ngắt. Câu lệnh IRET lấy ra các thanh ghi cờ và quay về địa chỉ của stack. Chú ý trong chương trình mà chỉ thị phải được đóng lại với ENDP và phân đoạn phải như được đóng thường xuyên với 1 chỉ thị ENDS.

Bây giờ xem lại trong chương trình chính để. Ngay sau lệnh DIV, chương trình sẽ kiểm tra xem nếu BAGỴDIVỴFLAG là đặt bởi so sánh với 01. Nếu BADỴDIVỴFLAG không được đặt bởi thủ tục (tức là không xảy ra ngắt 0), sau đó 1 lệnh nhảy tới lệnh MOV ỬBXỨ, AL. Lệnh này copy kết quả của phép chia trong AL tới vị trí bộ nhớ trong SCALEDỴVALUES được trỏ bởi BX. Nếu BADỴDIVỴFLAG được đặt bởi ngắt 0 thì 0 sẽ được đặt vào vị trí bộ nhớ trong SCALEỴVALUES và 1 lệnh nhảy tới lệnh MOV BADỴDIVỴFLAG, 00 sẽ đặt lại BADỴDIVỴFLAG.

Ngắt 8086 có thể tới bằng 3 nguồn.

- Thứ nhất : Là một tín hiệu bên ngoài tới chân Nonmaskable Interrupt (NMI) hoặc Interrupt (INTR) chúng được gọi là ngắt cứng.

- Thứ 2 : Sự thực hiện của một lệnh Interrupt như INT. Ðược gọi là ngắt mềm.

- Thứ 3 : Một vài lỗi điều kiện sản sinh bởi 8086 bằng việc thực thi một lệnh. Ví dụ lỗi chia không divide-by-zero. Nếu bạn thứ chia 1 số cho 0 8086 sẽ tự động ngắt sự thực thi chương trình hiện thời.

Khi một ngắt xảy ra 8086 sẽ thực hiện các bước sau :

1- Nó giảm con trỏ stack đi 2 và push thanh ghi cờ vào stack

2- Nó vô hiệu hoá đầu vào INTR của 8086 bằng cách xoá cờ ngắt (IF) trong thanh ghi cờ.

3- Nó đặt lại cờ bẫy (TF) trong thanh ghi cờ

4- Nó giảm con trỏ stack đi 2 và đẩy nội dung thanh ghi mã (CS) hiện thời vào vào stack.

5- Nó giảm con trỏ stack đi 2 và đẩy nội dung thanh ghi con trỏ lệnh (IP) hiện thời vào stack.

6- Nó thực hiện nảy tới bắt đầu của thủ tục phục vụ ngắt.

Bạn đang đọc truyện trên: Truyen2U.Pro