Vài điểm lưu ý bộ vi xử lý 8086

Chia sẻ: Hoa Hoa | Ngày: | Loại File: DOC | Số trang:14

3
566
lượt xem
214
download

Vài điểm lưu ý bộ vi xử lý 8086

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Thuật ngữ X86 dùng để chỉ tới kiến trúc tập lệnh của dòng vi xử lý 8086 của Intel. 8086 được Intel đưa ra năm 1978. Intel xem dòng phát triển 8086 là IA-32. Kiến trúc x86 này rất phổ biến cho các thế hệ máy tính cá nhân đang hiện hữu trong nhiều gia đình. Kiến trúc x86 gần như chiếm toàn bộ thị phần máy tính cá nhân,máy workstation và cả server thậm chí siêu máy tính. Vì tính phổ biến của nó và hỗ trợ tài liệu rất tốt từ Intel nên x86 được rất nhiều lập trình...

Chủ đề:
Lưu

Nội dung Text: Vài điểm lưu ý bộ vi xử lý 8086

  1. Bộ thanh ghi trong 8086 Các thanh ghi trong bộ vi xử lí 8086 đều là các thanh ghi 16 bit và đ ược chia thành các nhóm như sau: - Các thanh ghi công dụng chung AX (accumulator), BX (base), CX (counter), DX (data): có thể được truy xuất độc lập như 2 thanh ghi 8 bit : AH và AL, BH và BL, CH và CL, DH và DL. 15 8 7 0 15 8 7 0 AX CX AH AL CH CL 15 8 7 0 15 8 7 0 BX DX BH BL DH DL - Các thanh ghi con trỏ và chỉ mục (xem chi tiết ở các phần sau) SP (Stack Pointer), BP (Base Pointer): con trỏ dùng khi làm việc với stack SI (Source Index), DI (Destination Index): chỉ số mảng khi xử lí mảng (chuỗi) - Các thanh ghi phân đoạn CS (Code Segment), DS (Data Segment), ES (Extra data Segment), SS (Stack Segment): tương ứng lưu địa chỉ phân đoạn mã lệnh, phân đoạn dữ liệu, phân đo ạn dữ liệu bổ sung, phân đoạn ngăn xếp. Địa chỉ phân đoạn này sẽ được kết hợp với địa chỉ offset để truy xuất ô nhớ. (xem chi tiết ở các phần sau) - Các thanh ghi con trỏ lệnh và trạng thái IP (Intruction Pointer): thanh ghi chứa địa chỉ offset của lệnh k ế ti ếp c ần th ực hiện. Thanh ghi này không thể được truy xuất trực tiếp. FLAGS: thanh ghi cờ trạng thái, dùng để chứa các bit mô tả trạng thái c ủa lệnh vừa được thực hiện, hoặc chứa các bit điều khiển cần thiết lập trước khi gọi lệnh. Bao gồm các bit cờ sau đây: (xem chi tiết ở các phần sau) 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 O D I T S Z A P C CF (Carry Flag): bật khi phép tính vừa thực hiện có sử dụng bit nhớ PF (Parrity Flag): bật khi kết quả của phép tính vừa thực hiện có chẵn bit 1 AF (Auxilary Flag): bật khi phép tính vừa thực hiện có sử dụng bit nhớ phụ ZF (Zero Flag): bật khi kết quả của phép tính vừa thực hiện là 0 SF (Sign Flag): bật khi kết quả của phép tính vừa thực hiện có bit dấu bật TF (Trace Flag): bật để chuyển sang chế độ chạy từng bước IF (Interrupt Flag): bật để cho phép các ngắt xảy ra DF (Direction Flag): bật để chọn chế độ giảm chỉ số tự động khi làm việc với mảng OF (Overflow Flag): bật khi phép tính vừa thực hiện gây ra tràn số 1
  2. Tổ chức bộ nhớ trong 8086 Địa chỉ vật lí. Bus địa chỉ có độ rộng 20 bit, nếu đánh địa chỉ tuần tự tăng dần cho các ô nhớ:  số lượng tối đa các ô nhớ có thể được đánh địa chỉ là 2^20 ô nh ớ. Mỗi ô nhớ có kích thước 1 byte  kích thước bộ nhớ tối đa có thể truy cập là 2^20 byte = 1 MB. Khi đó, địa chỉ của một ô nhớ là một con số 20 bit (ho ặc 5 ch ữ số hex), g ọi là đ ịa ch ỉ v ật lí. Địa chỉ logic Các thanh ghi trong 8086 đều là 16 bit. Nếu dùng các thanh ghi này đ ể l ưu tr ữ đ ịa ch ỉ 20bit thì không tiện lợi. Người ta đã tìm cách gi ảm số bit dùng đ ể đánh đ ịa ch ỉ xu ống còn 16bit. Ý tưởng được sử dụng là: thông thường, các dữ liệu mà m ột ch ương trình c ần truy c ập nằm gần nhau và tạo thành một khối không lớn lắm; như vậy, n ếu ta đánh địa ch ỉ tương đ ối (gọi là offset) trong một khối thì số lượng bit dùng để đánh địa chỉ sẽ giảm xuống. Bộ nhớ được chia thành các khối 64KB, gọi là segment, các khối này không xếp tuần tự cạnh nhau mà xếp gối đầu, với khoảng cách 16byte. Nghĩa là, cứ 16 byte thì lại bắt đầu một segment mới. Như vậy, số lượng segment trong 1MB bộ nhớ là 1MB / 16byte = 65536 = 2^16. Do đó, để đánh địa chỉ segment ta cũng cần 16 bit. Trong phạm vi một segment 64KB (=65536 byte), chỉ cần dùng 16 bit làm địa chỉ offset để xác định một ô nhớ. (Xem Hình 1. Tổ chức bộ nhớ kiểu segment - offset) Tóm lại, trong cách đánh địa chỉ logic, m ỗi ô nh ớ có địa chỉ là một cặp (segment:offset), tổng cộng 32bit. Tuy nhiên, như đã nói ở trên, các dữ liệu có liên quan trong b ộ nh ớ th ường ở gần nhau trong một khối segment nên ta có thể không cần xác định tường minh địa chỉ segment mà chỉ cần ngầm hiểu. Khi bắt đầu làm việc với m ột segment nào thì ta s ẽ dùng một thanh ghi để ghi lại địa chỉ segment đó. Thanh ghi này sẽ được dùng chung cho tất cả các phép truy xuất bộ nhớ tiếp theo. Và như thế, trong các phép truy xuất b ộ nh ớ ti ếp theo, ch ỉ cần dùng thêm 16 bit địa chỉ offset là đủ để xác định vị trí một ô nhớ. Nếu địa chỉ gồm cả hai phần segment:offset, người ta gọi đó là địa chỉ xa. Nếu địa chỉ chỉ có offset còn segment ngầm định thì người ta gọi là địa chỉ gần, hàm ý là nó xác định một ô nhớ chỉ ở trong phạm vi của segment ngầm định mà thôi. Chuyển đổi giữa hai kiểu địa chỉ Việc đổi từ địa chỉ logic sang địa chỉ vật lí rất đơn giản. Phy_address = segment * 16 + offset Vd: địa chỉ logic 1234h:0005h sẽ ứng với địa chỉ vật lí 1234h * 16 + 0005h = 12340h + 0005h = 12345h Việc đổi từ địa chỉ vật lí sang địa chỉ logic cũng không có gì phức tạp. Tuy nhiên, do các segment gối đầu nhau nên mỗi ô nhớ có thể thuộc một vài segment khác nhau. Vì vậy, một địa chỉ vật lí có thể ứng với nhiều địa chỉ logic khác nhau. Vd: địa chỉ vật lí 12345h có thể ứng với các địa chỉ logic sau: 1234h:0005h 1230h:0045h 1200h:0345h 1000h:2345h 1232h:0025h 2
  3. … Thông thường khi nói “địa chỉ”, ngầm hiểu là địa chỉ logic, mà thường là địa chỉ gần. 3
  4. Physical RAM address 00000h 00001h 00002h 16 bytes Logical ….. address 0000Fh Segment : Offset Segment 0000 00010h 0000h:0020h 00011h 0001h:0010h ….. 16 bytes 0002h:0000h 0001Fh Segment 0001 00020h Vì mỗi ô nhớ có thể thuộc ….. nhiều segment khác nhau, một địa chỉ vật lí 00020h có 0FFFFh thể ứ ng với nhi ều địa chỉ Segment 0002 10000h logic khác nhau, tùy vào vi ệc ….. lự a chọn segment. 1000Fh 10010h ….. 1001Fh 10020h ….. Một số địa chỉ logic có F0000h thể trở nên không hợp l ệ . ….. Ví dụ, ở segment FFFFh , chỉ có các offset từ Segment F000h F000Fh 0000h đến 000Fh mới F0010h tạo thành một địa chỉ hợp ….. lệ, bởi vì địa chỉ vật lí chỉ Segment F001h FFFF0h có đến FFFFFh là hết ….. Segment FFFFh FFFFFh Hình 1. Tổ chức bộ nhớ kiểu segment - offset Một chương trình khi đã được nạp vào bộ nhớ để thực hiện thông thường chiếm 3 phân đoạn. Một phân đoạn dành cho mã lệnh ( code segment), một phân đoạn dành cho dữ liệu (data segment), và một phân đoạn ngăn xếp (stack segment) dành để lưu các giá trị trung gian hoặc các địa chỉ trở về dùng khi gọi hàm. Địa chỉ của 3 phân đo ạn t ương ứng đ ược n ạp vào 3 thanh ghi CS, DS, SS. Thanh ghi IP chứa địa chỉ offset của lệnh sắp đ ược th ực hi ện. Nh ư vậy, cặp CS:IP sẽ cho biết địa chỉ logic của lệnh. Thanh ghi SP chứa địa chỉ offset của ô nh ớ cu ối cùng được lưu vào stack. Các phép truy xuất đến phân đoạn ngăn xếp sẽ dùng cặp SS:SP. Các lệnh truy xuất dữ liệu trong phân đoạn dữ liệu sẽ dùng đến DS để kết hợp với offset được tính toán từ mã lệnh để có được địa chỉ logic của ô nhớ cần truy xuất. 4
  5. Cấu trúc mã lệnh – Các kiểu định vị dữ liệu Cấu trúc mã lệnh (Instruction format) trong 8086 Một lệnh (instruction) mà bộ vi xử lí có thể hiểu được thường rất đơn giản. Ví dụ như di chuyển dữ liệu từ một ô nhớ vào thanh ghi, cộng thanh ghi thứ hai vào thanh ghi th ứ nh ất,… Trong 8086 mỗi lệnh thường tác động đến 0,1 ho ặc 2 đ ối t ượng ( operand, tạm gọi là toán hạng). Toán hạng có thể là một thanh ghi, một hằng số hoặc một ô nhớ. Thông thường, một lệnh (instruction) có cấu trúc như sau: Prefix Opcode D W MOD REG R/M Displacement Immediate 0-2 bytes 8 bits 8 bits 0-2 bytes 0-2 bytes Hình 2. Cấu trúc mã lệnh Trường Prefix dùng để thay đổi thanh ghi phân đoạn mặc định ho ặc chỉ định sự lặp lại của lệnh trong thao tác xử lí chuỗi. Trường Opcode là mã của thao tác, cho biết lệnh này làm gì. Bit D (direction) cho biết hướng tác động của lệnh. Bit W (width) cho biết kích thước của toán hạng. Hai trường REG và R/M mô tả hai toán hạng chịu tác động của lệnh. Trường REG chứa mã số của một thanh ghi. Trường R/M có thể là mã số của một thanh ghi (trường hợp toán hạng là thanh ghi) ho ặc là mô tả cách tính địa chỉ của một ô nhớ trong bộ nhớ (trường hợp toán hạng là ô nhớ). Trường MOD cho biết trường R/M mô tả thanh ghi hay ô nhớ, cũng như cho bi ết có trường Displacement phía sau hay không. Trường Displacement được sử dụng khi toán hạng là ô nhớ. Trường này được sử dụng kết hơp với các thông tin lưu trong trường R/M để tính địa chỉ của toán hạng. Trường Immediate được sử dụng khi toán hạng là một hằng số. Không phải lệnh nào cũng có đủ các trường được miêu tả ở trên. Chi tiết về các trường như sau: REG W=1 W=0 Segment D = 1 REG là đích 000 AX AL ES đến 001 CX CL CS D = 0 REG là nguồn 010 DX DL SS 011 BX BL DS W = 1 Toán hạng là 100 SP AH word 101 BP CH W = 0 Toán hạng là byte 110 SI DH 111 DI BH Bảng 1 Mã trường REG và các bit D, W 5
  6. MOD Ý nghĩa của R/M và Displacement 00 Nếu R/M = 110 thì EA = disp-high _ disp-low Nếu R/M ≠ 110 thì phần displacement không có. 01 DISP = disp-low, sẽ được signed extended 10 DISP = disp-high _ disp-low 11 R/M mô tả thanh ghi Bảng 2. Mã trường MOD R/M Cách tính địa chỉ (Effective Address) 000 EA = BX + SI + DISP 001 EA = BX + DI + DISP 010 EA = BP + SI + DISP 011 EA = BP + DI + DISP 100 EA = SI + DISP 101 EA = DI + DISP 110 EA = BP + DISP (trừ trường hợp MOD = 00, xem ở trên) 111 EA = BX + DISP Bảng 3. Mã trường R/M Ví dụ: Lệnh chép nội dung thanh ghi DX vào CX có mã 8BCAh, lệnh chép nội dung thanh ghi CX vào DX có mã 8BD1h. R/M là Mã thao tác di chuy ển dữ liệu thanh ghi CX DX Opcode D W MOD REG R/M 100010 1 1 11 001 010 MOV CX, DX 8Bh CAh Opcode D W MOD REG R/M 100010 1 1 11 010 001 MOV DX, CX 8Bh D1h Hình 3. Mã lệnh MOV giữa hai thanh ghi Ví dụ: Lệnh chép nội dung biến var (có địa chỉ DS:1234h) vào thanh ghi CX có mã 8B0E1234h. (Xem Hình 4). Lệnh chép ngược lại có mã 890E1234h. Hai mã này chỉ khác nhau ở bit D. Trường hợp thứ nhất, D = 1 vì thanh ghi CX là đích đ ến. Tr ường h ợp th ứ hai, D = 0 vì thanh ghi CX là nguồn. Trong c ả hai trường h ợp, đ ịa ch ỉ (EA) c ủa ô nh ớ (bi ến var) được lấy trực tiếp từ 2 bytes của vùng Displacement (MOD = 00 và R/M = 110, xem ở b ảng phía trên). Hai byte disp-high và disp-low của vùng Displacement ch ứa offset c ủa bi ến var trong phân đoạn dữ liệu. 6
  7. Opcode D W MOD REG R/M disp-high disp-low 100010 1 1 00 001 110 0001 0010 0011 0100 MOV CX, var 8Bh 0Eh 12h 34h Opcode D W MOD REG R/M disp-high disp-low 100010 0 1 00 001 110 0001 0010 0011 0100 MOV var, CX 89h 0Eh 12h 34h Hình 4. Mã lệnh MOV giữa thanh ghi và bộ nhớ Một số lệnh có cấu trúc đặc biệt, ví dụ, ghép chung trường opcode v ới tr ường reg, nh ằm giúp rút ngắn độ dài lệnh. Ví dụ: lệnh gán AX bằng 4567h có mã B84567h. Opcode W REG Immediate 8 bits 1-2 bytes a) Cấu trúc chung Opcode W REG Immediate 1011 1 000 0100 0101 0110 0111 MOV AX, 4567h B8h 4567 h b) Ví dụ: MOV AX, 4567h Hình 5. Mã lệnh MOV giữa Accumulator và hằng số Khi toán hạng là một thanh ghi phân đoạn, chỉ cần 2 bit để chỉ đ ịnh m ột trong b ốn thanh ghi phân đoạn. Nghĩa là trường REG luôn có dạng 0xx. Bit xx được định nghĩa như trong Bảng 1. Ví dụ, lệnh chép nội dung thanh ghi AX vào thanh ghi phân đoạn DS có mã là 8ED8h. Opcode D 0 MOD 0xx R/M 100011 1 0 11 011 000 MOV DS, AX 8Eh D8h R/M là DS AX thanh ghi Hình 6. Mã lệnh MOV giữa thanh ghi thường và thanh ghi phân đoạn Khi có chỉ định thanh ghi phân đoạn dùng để truy xuất bộ nhớ khác với thanh ghi phân đoạn mặc định thì trong mã lệnh xuất hiện thêm Prefix. Ví dụ: lệnh chép n ội dung ô nh ớ ES:2345h vào thanh ghi DS có mã 268E1E2345h, trong đó 26h là prefix. 001 xx 110 Opcode D 0 MOD REG R/M disp-high disp-low 001 00 110 100011 1 0 00 011 110 0010 0011 0100 0101 MOV DS,ES:2345h 26h 8Eh 1Eh 2345 h ES Hình 7. Mã lệnh MOV giữa thanh ghi phân đoạn và ô nhớ, có sử dụng segment override prefix 7
  8. Stack và ứng dụng trong việc gọi chương trình con, gọi ngắt Khái niệm stack Stack là một vùng bộ nhớ mà ở đó, ngoài việc truy xuất trực tiếp các ô nh ớ bằng đ ịa ch ỉ, người ta định nghĩa thêm hai thao tác là PUSH (bỏ vào) và POP (lấy ra). Stack thường được dùng làm nơi lưu trữ tạm thời các giá trị trung gian hoặc dùng trong vi ệc gọi ch ương trình con. Nếu chỉ sử dụng hai thao tác PUSH & POP để truy xuất dữ liệu trong stack thì stack giống như một cái thùng đựng tài liệu. Những gì bỏ vào sau sẽ n ằm trên nh ững gì b ỏ vào trước, do đó khi lấy ra thì bao giờ cũng phải lấy cái bỏ vào sau cùng. Người ta gọi cấu trúc như vậy là LIFO (last in first out). Để ghi lại địa chỉ offset nơi bỏ dữ liệu vào sau cùng, người ta dùng thanh ghi SP, còn địa chỉ segment thì được lưu trong thanh ghi SS. Ví dụ: (Xem Hình 8, Hình 9) Thao tác PUSH AX bao gồm: giảm SP đi 2, đưa giá trị của AX vào ô nhớ có địa chỉ SS:SP. Thao tác POP AX bao gồm: đưa giá trị từ ô nhớ có địa chỉ SS:SP vào AX, tăng SP lên 2. Như thế, stack được sử dụng (còn gọi là “nở ra”) theo chi ều gi ảm của đ ịa ch ỉ, khác v ới các vùng nhớ thông thường được sử dụng theo chiều tăng của địa chỉ. Nếu không khai báo phân đoạn stack, theo mặc định, đo ạn stack và code s ẽ chung nhau, và khi chương trình bắt đầu thực hiện, SP được khởi đ ộng giá tr ị 0. Vì v ậy, thao tác PUSH xx lần đầu tiên sẽ đặt giá trị xx tại địa chỉ SS:FFFEh (vì 0 – 2 = FFFEh), thao tác PUSH yy liền tiếp theo sẽ đặt giá trị yy tại địa chỉ SS:FFFCh. (Xem Hình 10) Nếu có khai báo phân đoạn stack, ví dụ: “.stack 200h”, đoạn stack sẽ được cấp phát riêng, và khi chương trình bắt đầu thực hiện, SP được khởi động giá trị 200h. Vì vậy, thao tác PUSH xx lần đầu tiên sẽ đặt giá trị xx tại địa chỉ SS:01FEh, thao tác PUSH yy liền tiếp theo sẽ đặt giá trị yy tại địa chỉ SS:01FCh. (Xem Hình 11) SS:0000h SS:0000h ….. ….. SS:00F6h SS:00F6h 12h SP = 00F6h SS:00F7h SS:00F7h 34h SS:00F8h 20h SP = 00F8h SS:00F8h 20h A0h A0h 01h 01h ….. ….. a) AX có giá trị 3412h, trước thao tác PUSH AX b) Sau thao tác PUSH AX Hình 8. Thực hiện PUSH AX SS:0000h SS:0000h ….. ….. SS:00F6h SS:00F6h SS:00F7h SS:00F7h SS:00F8h 20h SP = 00F8h SS:00F8h 20h A0h SS:00F9h A0h 01h SS:00FAh 01h SP = 00FAh ….. ….. a) Trước thao tác POP AX b) Sau thao tác POP AX, thanh ghi AX có giá trị A020h 8
  9. Hình 9. Thực hiện POP AX SS = CS CS:0000h B8h SP = 0000h CS:0000h B8h 25h 25h 00h 00h .code 50h 50h MOV AX, 25h ; mã: B82500h PUSH AX ….. ; mã: 50h ….. ... 25h SP = FFFEh CS:FFFFh CS:FFFFh 00h AX AX 00h 25h a) Trước khi thực hiện PUSH b) Sau khi thực hiện PUSH Hình 10. Trường hợp không khai báo stack segment SS ≠ CS .stack 200h CS:0000h B8h .code CS:0000h B8h 25h MOV AX, 25h 25h ; mã: B82500h 00h 00h PUSH AX 50h ; mã: 50h 50h ... ….. ….. CS:FFFFh CS:FFFFh SS:0000h SS:0000h ….. ….. SS:01FEh 25h SP = 01FEh SS:01FFh SS:01FFh 00h SS:0200h SP = 0200h SS:0200h AX AX 00h 25h a) Trước khi thực hiện PUSH b) Sau khi thực hiện PUSH Hình 11. Trường hợp có khai báo stack segment 9
  10. Chương trình con Chương trình con là một nhóm các lệnh thực hiện một công việc nhất định, có thể cần được làm lặp đi lặp lại nhiều lần ở nhiều thời điểm khác nhau. Mỗi khi cần thực hiện công việc đó, người ta nói rằng cần phải “gọi chương trình con” tương ứng. Nếu nhìn một chương trình máy tính như là một dãy liên tiếp các lệnh, thì vi ệc “gọi m ột chương trình con” chỉ là việc thay đổi trật tự thực hiện lệnh , hay nói khác đi là thay vì thực hiện lệnh tiếp theo liền sau lệnh vừa thực hiện, CPU “nhảy” đ ến m ột ch ỗ khác đ ể th ực hi ện các lệnh ở đó, sau đó quay lại chỗ cũ và thực hiện tiếp các lệnh đang bỏ dở. Nh ư v ậy, đ ể gọi một chương trình con, ta cần hai thao tác là CALL (gọi) và RET (trở về). Đây chính là hai lệnh thuộc nhóm các lệnh chuyển điều khiển. Lệnh CALL dùng trong chương trình chính đ ể gọi một chương trình con, lệnh RET dùng ở cuối chương trình con đ ể quay tr ở v ề ch ương trình chính. Thao tác CALL: • sử dụng stack để lưu trữ (PUSH) địa chỉ của lệnh tiếp ngay sau lệnh CALL (nơi cần quay lại) • ghi vào thanh ghi con trỏ lệnh IP địa chỉ của lệnh đầu tiên của chương trình con. Thao tác RET lấy (POP) giá trị từ stack và ghi vào thanh ghi con tr ỏ l ệnh IP, làm cho l ệnh tiếp theo được thực hiện chính là lệnh ngay sau lệnh CALL. Như đã biết, địa chỉ có thể là địa chỉ gần hoặc địa chỉ xa. Nếu chương trình con và chương trình chính nằm cùng một segment thì lệnh CALL chỉ c ần PUSH vào stack 2 byte (đ ịa chỉ gần). Trong trường hợp chương trình con và ch ương trình chính n ằm ở hai segment khác nhau lệnh CALL phải PUSH 4 byte vào stack (địa ch ỉ xa). T ương ứng, l ệnh RET s ẽ l ấy ra 2 hoặc 4 byte tùy trường hợp. Xem ví dụ đoạn chương trình và mã lệnh tương ứng như sau: Code segment .stack 200h .code 0000 h ... …... MOV AX,’a’ 0005 h B8 00 61 h CALL ToUpper 0008 h E8 00 3A h MOV BX,AX 000 Bh 8B D8 h MOV AX,’z’ 000Dh B8 00 7A h CALL ToUpper 0010 h E8 00 32 h MOV CX,AX 0013 h 8B C8 h ... …... MOV AX,4C00h 0037 h B8 4C 00 h INT 21h 003A CD 21 h ... …... ... …... ToUpper: SUB AX,20h 0045 h 2D 00 20 h RET 0048 h C3 h ... …... 10
  11. Hình 12. Ví dụ sử dụng CALL Trong ví dụ, hàm (chương trình con) ToUpper được gọi 2 lần. (Lưu ý r ằng nhãn ToUpper không tạo ra mã lệnh nên không chiếm chỗ trong code segment). Lần th ứ nh ất khi th ực hi ện lệnh CALL ở địa chỉ CS:0008h (Xem Hình 13) • Tại thời điểm trước khi thực hiện lệnh CALL này, thanh ghi IP có giá trị 0008h, còn thanh ghi SP có giá trị 0200h (trỏ xuống đáy stack). • Khi lệnh CALL được nạp từ bộ nhớ vào CPU để thực hiện, thanh ghi IP được tự động tăng lên một lượng bằng kích thước mã lệnh CALL, do đó s ẽ có giá tr ị 000Bh và trỏ đến lệnh tiếp theo là lệnh MOV. • Khi lệnh CALL được thực hiện, giá trị của thanh ghi IP (chính là địa chỉ của lệnh MOV) được push vào stack (SP giảm xuống còn 01FEh), sau đó thanh ghi IP đ ược cộng thêm một lượng bằng displacement lưu trong mã c ủa lệnh CALL ( 003Ah). Kết quả là IP có giá trị 0045h (=000Bh+003Ah), chính là địa chỉ của ToUpper. • Lệnh tiếp theo được nạp vào CPU để thực hiện sẽ là lệnh SUB ở địa chỉ 0045h. • Sau đó, lệnh RET được thực hiện, làm cho giá trị trong stack đ ược POP ra thanh ghi IP. Kết quả là IP có giá trị 000Bh, trỏ đến lệnh MOV, còn SP tăng lên 0200h. • Tiếp theo, lệnh MOV ở địa chỉ 000Bh được thực hiện. Stack Stack Stack segment segment segment ……. ……. ……. 01FCh 01FCh 01FCh 01FDh 01FDh 01FDh 01FEh 01FEh 0Bh 01FEh 0Bh 01FFh 01FFh 00h 01FFh 00h 0200 h 0200h 0200 h IP 0008h IP 0045 h IP 000Bh SP 0200h SP 01FEh SP 0020h a) Trước CALL b) Sau CALL c) Sau RET Hình 13. Quá trình gọi chương trình con Stack Stack Stack segment segment segment ……. ……. ……. 01FCh 01FCh 01FCh 01FDh 01FDh 01FDh 01FEh 01FEh 13h 01FEh 13h 01FFh 01FFh 00h 01FFh 00h 0200 h 0200h 0200 h IP 0010h IP 0045 h IP 0013h SP 0200h SP 01FEh SP 0020h a) Trước CALL b) Sau CALL c) Sau RET 11
  12. Hình 14. Gọi lần 2 Lần thứ 2, hàm ToUpper được gọi tại địa chỉ 0010h, quá trình diễn ra t ương t ự. (Xem Hình 14). Lưu ý rằng hai lời gọi CALL ToUpper được dịch thành 2 mã l ệnh khác nhau (E8003Ah và E80032h), bởi vì hai lời gọi n ằm ở hai vị trí khác nhau, có displacement khác nhau. Nhưng vì sao cần dùng stack để lưu địa chỉ trở về ? Nguyên nhân là do các lời gọi có th ể lồng nhau, nghĩa là trong chương trình con này có thể xuất hiện lời gọi chương trình con khác, hoặc là gọi chính nó. Khi đó, thao tác RET đầu tiên cần lấy địa chỉ trở về được cất bởi CALL sau cùng. Điều này phù hợp với nguyên tắc LIFO của stack. (Xem Hình 15) Code segment .stack 200h .code 0000 h ... …... MOV AX,’V’ 0005 h B8 00 56 h CALL Upcase 0008 h E8 00 3E h MOV BX,AX 000 Bh 8B D8 h MOV AX,’n’ 000Dh B8 00 6E h CALL Upcase 0010 h E8 00 36 h MOV CX,AX 0013 h 8B C8 h ... …... MOV AX,4C00h 0037 h B8 4C 00 h INT 21h 003A CD 21 h ... …... ... …... ToUpper: SUB AX,20h 0045 h 2D 00 20 h RET 0048 h C3 h Upcase: CMP AX,’a’ 0049 h 3D 00 61 h JB Notaz 004Ch 72 08 h CMP AX,’z’ 004 Eh 3D 00 7A h JA Notaz 0051 h 77 03 h CALL ToUpper 0053 h E8 FF EF h Notaz: RET 0056 h C3 h ... Hình 15. CALL lồng nhau Quá trình PUSH và POP các địa chỉ trở về vào stack diễn ra như sau. Xem Hình 16. 12
  13. Stack Stack Stack Stack Stack segment segment segment segment segment ……. ……. ……. ……. ……. SP 01FCh 01FCh 01FCh 56h 01FCh 56h 01FCh 56h 01FDh 01FDh 01FDh 00h 01FDh 00h 01FDh 00h SP SP 01FEh 01FEh 0Bh 01FEh 0Bh 01FEh 0Bh 01FEh 0Bh 01FFh 01FFh 00h 01FFh 00h 01FFh 00h 01FFh 00h SP SP 0200 h 0200h 0200h 0200 h 0200 h IP 0008 h IP 0049h IP 0045h IP 0056 h IP 000 Bh SP 0200 h SP 01FEh SP 01FCh SP 01FEh SP 0200 h a) Trước b) Sau c) Sau d) Sau e) Sau CALL Upcase CALL Upcase CALL ToUpper RET của ToUpper RET của Upcase Hình 16. Sự thay đổi của stack khi CALL lồng nhau Interrupt (ngắt) Ngắt là một cơ chế cho phép CPU nhận biết về những sự kiện xảy ra bên ngoài (hardware interrupt, ngắt cứng) cũng như bên trong CPU (software interrupt, ngắt mềm) và có một đáp ứng thích hợp. Ngắt cứng được dùng để tránh việc CPU phải chờ đợi những thiết bị ngoại vi khác có tốc độ xử lí chậm hơn. Trong thời gian các thiết bị này còn đang xử lí, thì CPU có th ể làm những công việc khác. Sau khi hoàn thành công việc của mình, thi ết bị sẽ chủ động gây ra một sự thay đổi tín hiệu trên một dây dẫn nhằm mục đích báo cho CPU biết về tình trạng của mình. Khi CPU nhận được sự thay đổi tín hiệu này (xem như một sự kiện), CPU sẽ ngưng công việc hiện tại để thực hiện một đoạn chương trình con ( interrupt handler, trình xử lí ngắt) làm những thao tác cần thiết (đáp ứng), sau đó quay trở lại tiếp tục công việc. Ngắt mềm được dùng khi chương trình chủ động gọi một đoạn chương trình con hệ thống (interrupt handler, trình xử lí ngắt). Các chương trình con hệ thống là m ột phần của h ệ điều hành hoặc của BIOS. Việc gọi này thực hiện không phải bằng lệnh CALL mà b ằng lệnh INT. Có thể xem như lệnh INT đã tạo ra một sự kiện đòi hỏi đáp ứng của CPU. Như vậy, cả hai loại ngắt cứng và ngắt mềm đều có liên quan đến vi ệc gọi một chương trình con khi có một sự kiện xảy ra , chỉ khác nhau ở cách hình thành sự kiện này. Đối với ngắt cứng, một tín hiệu trên phần cứng thay đổi gây ra sự kiện. Đối với ngắt m ềm, lệnh INT gây ra sự kiện. Địa chỉ bắt đầu của các chương trình con (trình xử lí ngắt) này đ ược l ưu trong m ột b ảng, gọi là bảng vector ngắt (interrupt vector table). Mỗi ngắt có một số hiệu để phân biệt. Với mỗi số hiệu ngắt, trong bảng lưu giữ địa chỉ xa trỏ đến lệnh đầu tiên của trình xử lí ngắt tương ứng. Nhưng không giống như việc gọi chương trình con bằng CALL thông th ường, ở đây giá trị của 3 thanh ghi được PUSH vào stack. Đầu tiên là thanh ghi cờ, sau đó là CS và cuối cùng là IP. Tiếp theo, dựa vào số hiệu của ngắt (được cung cấp bởi phần cứng ho ặc b ởi đ ối s ố của lệnh INT), địa chỉ xa của trình xử lí ngắt được lấy từ bảng vector ngắt và đặt vào CS, IP. Để có thể trở về chương trình chính, ở cuối trình xử lí ngắt có lệnh IRET. Lệnh này theo thứ tự ngược lại, POP giá trị từ stack vào IP, CS và thanh ghi cờ. Ngoài ra, còn một loại ngắt nữa, đó là ngắt nội bộ bên trong CPU (internal interrupt), xảy ra khi có một lỗi đặc biệt, ví dụ, thực hiện phép chia cho 0, ho ặc th ực hi ện m ột mã l ệnh không tồn tại. Cơ chế gọi và trở về từ ngắt hoàn toàn giống như hai loại trên. 13
  14. Một số ngắt có thể bị che, nghĩa là không cho phép chúng xảy ra, b ằng cách thay đ ổi m ột số bit trong một thanh ghi điều khiển. Các ngắt như v ậy gọi là maskable. Các ngắt không cho phép che gọi là non-maskable. Mối liên hệ giữa các file .asm, .obj, .lst, .exe và .map 14
Đồng bộ tài khoản