TẬP LỆNH
Mục tiêu của phần này là dùng các ví dụ trích từ các kiến trúc phần mềm được dùng nhiều nhất, để cho thấy các kỹ thuật ở mức ngôn ngữ máy dùng để thi hành các cấu trúc trong các ngôn ngữ cấp cao. Để minh hoạ bằng thí dụ, ta dùng cú ...
Mục tiêu của phần này là dùng các ví dụ trích từ các kiến trúc phần mềm được dùng nhiều nhất, để cho thấy các kỹ thuật ở mức ngôn ngữ máy dùng để thi hành các cấu trúc trong các ngôn ngữ cấp cao.
Để minh hoạ bằng thí dụ, ta dùng cú pháp lệnh trong hợp ngữ sau đây :
Từ gợi nhớ mã lệnh, thanh ghi đích, thanh ghi nguồn 1, thanh ghi nguồn 2.
Từ gợi nhớ mã lệnh mô tả ngắn gọn tác vụ phải thi hành trên các thanh ghi nguồn, kết quả được lưu giữ trong thanh ghi đích.
Mỗi lệnh của ngôn ngữ cấp cao được xây dựng bằng một lệnh mã máy hoặc một chuỗi nhiều lệnh mã máy. Lệnh nhảy (GOTO) được thực hiện bằng các lệnh hợp ngữ về nhảy (JUMP) hoặc lệnh hợp ngữ về vòng. Chúng ta phân biệt lệnh nhảy làm cho bộ đếm chương trình được nạp vào địa chỉ tuyệt đối nơi phải nhảy đến (PC <- địa chỉ tuyệt đối nơi phải nhảy tới), với lệnh vòng theo đó ta chỉ cần cộng thêm một độ dời vào bộ đếm chương trình (PC <- PC + độ dời). Ta lưu ý là trong trường hợp sau, PC chứa địa chỉ tương đối so với địa chỉ của lệnh sau lệnh vòng.
Gán trị
Việc gán trị, gồm cả gán trị cho biểu thức số học và logic, được thực hiện nhờ một số lệnh mã máy. Cho các kiến trúc RISC, ta có thể nêu lên các lệnh sau :
- Lệnh bộ nhớ
LOAD Ri, M (địa chỉ) M[địa chỉ] <- Ri
STORE Ri, M(địa chỉ) ; Ri <- M[địa chỉ]
Địa chỉ được tính tuỳ theo kiểu định vị được dùng.
- Lệnh tính toán số học: tính toán số nguyên trên nội dung của hai thanh ghi Ri, Rj và xếp kết quả vào trong Rk:
ADD (cộng)
ADDD (cộng số có dấu chấm động, chính xác kép)
SUB (trừ)
SUBD (trừ số có dấu chấm động, chính xác kép)
MUL (nhân)
DIV (chia)
- Lệnh logic: thực hiện phép tính logic cho từng bit một.
AND (lệnh VÀ)
OR (lệnh HOẶC)
XOR (lệnh HOẶC LOẠI)
NEG (lệnh lấy số bù 1 )
Hình II.7: Minh hoạ lệnh dịch chuyển và quay vòng
- Các lệnh dịch chuyển số học hoặc logic (SHIFT ), quay vòng (ROTATE) có hoặc không có số giữ ở ngã vào, sang phải hoặc sang trái. Các lệnh này được thực hiện trên một thanh ghi và kết quả lưu giữ trong thanh ghi khác. Số lần dịch chuyển (mỗi lần dịch sang phải hoặc sang trái một bit) thường được xác định trong thanh ghi thứ ba. Hình II.7 minh hoạ cho các lệnh này
Cho các kiến trúc kiểu RISC, ta có :
SLL (shift left logical : dịch trái logic)
SRL (shift right logical : dịch phải logic)
SRA (shift right arithemtic : dịch phải số học)
Lệnh có điều kiện
Lệnh có điều kiện có dạng :
Nếu <điều kiện> thì <chuỗi lệnh 1> nếu không <chuỗi lệnh 2>
(IF <condition> THEN <instructions1> ELSE <instructions2>)
Lệnh này buộc phải ghi nhớ điều kiện và nhảy vòng nếu điều kiện được thoả.
Ghi nhớ điều kiện .
Bộ làm tính ALU cung cấp kết quả ở ngã ra tuỳ theo các ngã vào và phép tính cần làm. Nó cũng cho một số thông tin khác về kết quả dưới dạng các bit trạng thái. Các bit này là những đại lượng logic ĐÚNG hoặc SAI (hình II.8).
Trong các bit trạng thái ta có bit dấu S (Sign - Đúng nếu kết quả âm), bit trắc nghiệm zero Z (Zero - Đúng nếu kết quả bằng không), bit tràn OVF (Overflow) ĐÚNG nếu phép tính số học làm thanh ghi không đủ khả năng lưu trữ kết quả, bit số giữ C (carry) ĐÚNG nếu số giữ ở ngã ra là 1 .... Các bit trên thường được gọi là bit mã điều kiện.
Hình II.8 : Bit trạng thái mà ALU tạo ra
Có hai kỹ thuật cơ bản để ghi nhớ các bit trạng thái
Cách thứ nhất, ghi các trạng thái trong một thanh ghi đa dụng.
Ví dụ lệnh CMP Rk, Ri, Rj
Lệnh trên sẽ làm phép tính trừ Ri - Rj mà không ghi kết quả phép trừ, mà lại ghi các bit trạng thái vào thanh ghi Rk. Thanh ghi này được dùng cho một lệnh nhảy có điều kiện. Điểm lợi của kỹ thuật này là giúp lưu trữ nhiều trạng thái sau nhiều phép tính để dùng về sau. Điểm bất lợi là phải dùng một thanh ghi đa dụng để ghi lại trạng thái sau mỗi phép tính mà số thanh ghi này lại bị giới hạn ở 32 trong các bộ xử lý hiện đại.
Cách thứ hai, là để các bit trạng thái vào một thanh ghi đặc biệt gọi là thanh ghi trạng thái. Vấn đề lưu giữ nội dung thanh ghi này được giải quyết bằng nhiều cách. Trong kiến trúc SPARC, chỉ có một số giới hạn lệnh được phép thay đổi thanh ghi trạng thái ví dụ như lệnh ADDCC, SUBCC (các lệnh này thực hiện các phép tính cộng ADD và phép tính trừ SUB và còn làm thay đổi thanh ghi trạng thái). Trong kiến trúc PowerPC, thanh ghi trạng thái được phân thành 8 trường, mỗi trường 4 bit, vậy là thanh ghi đã phân thành 8 thanh ghi trạng thái con.
Nhảy vòng
Các lệnh nhảy hoặc nhảy vòng có điều kiện, chỉ thực hiện lệnh nhảy khi điều kiện được thoả. Trong trường hợp ngược lại, việc thực hiện chương trình được tiếp tục với lệnh sau đó. Lệnh nhảy xem xét thanh ghi trạng thái và chỉ nhảy nếu điều kiện nêu lên trong lệnh là đúng.
Chúng ta xem một ví dụ thực hiện lệnh nhảy có điều kiện.
Giả sử trạng thái sau khi bộ xử lý thi hành một tác vụ, được lưu trữ trong thanh ghi, và bộ xử lý thi hành các lệnh sau :
1. CMP R4, R1, R2 : So sánh R1 và R2 bằng cách trừ R1 cho R2 và
lưu giữ trạng thái trong R4
2. BGT R4, +2 : Nhảy bỏ 2 lệnh nếu R1 > R2
3. ADD R3, R0, R2 : R0 có giá trị 0. Chuyển nội dung của R2 vào R3
4. BRA +1 : nhảy bỏ 1 lệnh
5. ADD R3, R0, R1 : chuyển nội dung R1 vào R3
6. Lệnh kế
Nếu R1 > R2 thì chuỗi lệnh được thi hành là 1, 2, 5, 6 được thi hành,
nếu không thì chuỗi lệnh 1, 2, 3, 4, 6 được thi hành.
Chuỗi các lệnh trên , trong đó có 2 lệnh nhảy, thực hiện công việc sau đây :
Nếu R1 > R2 thì R3 = R1 nếu không R3 = R2
Các lệnh nhảy làm tốc độ thi hành lệnh chậm lại, trong các CPU hiện đại dùng kỹ thuật ống dẫn. Trong một vài bộ xử lý người ta dùng lệnh di chuyển có điều kiện để tránh dùng lệnh nhảy trong một vài trường hợp. Thí dụ trên đây có thể được viết lại :
1. CMP R4, R1, R2 : So sánh R1 và R2 và để các bit trạng thái trong R4.
2. ADD R3, R0, R2 : Di chuyển R2 vào R3
3. MGT R4, R3, R1: (MGT : Move if greater than). Nếu R1 > R2 thì
di chuyển R1 vào R3
Vòng lặp
Các lệnh vòng lặp có thể được thực hiện nhờ lệnh nhảy có điều kiện mà ta đã nói ở trên. Trong trường hợp này, ta quản lý số lần lặp lại bằng một bộ đếm vòng lặp, và người ta kiểm tra bộ đếm này sau mỗi vòng lặp để xem đã đủ số vòng cần thực hiện hay chưa.
Bộ xử lý PowerPC có một lệnh quản lý vòng lặp
BNCT Ri, độ dời
Với thanh ghi Ri chứa số lần lặp lại.
Lệnh này làm các công việc sau:
Ri := Ri -1
Nếu Ri <> 0, PC := PC + độ dời. Nếu không thì tiếp tục thi hành lệnh kế.
Thâm nhập bộ nhớ ngăn xếp
Ngăn xếp là một tổ chức bộ nhớ sao cho ta chỉ có thể đọc một từ ở đỉnh ngăn xếp hoặc viết một từ vào đỉnh ngăn xếp. Địa chỉ của đỉnh ngăn xếp được chứa trong một thanh ghi đặc biệt gọi là con trỏ ngăn xếp SP (Stack Pointer).
Ứng với cấu trúc ngăn xếp, người ta có lệnh viết vào ngăn xếp PUSH và lệnh lấy ra khỏi ngăn xếp POP. Các lệnh này vận hành như sau:
- Cho lệnh PUSH
SP := SP +1
M (SP) := Ri (Ri là thanh ghi cần viết vào ngăn xếp)
- Cho lệnh POP
Ri := M(SP) (Ri là thanh ghi, nhận từ lấy ra khỏi ngăn xếp)
SP := SP -1
Trong các bộ xử lý RISC, việc viết vào hoặc lấp ra khỏi ngăn xếp dùng các lệnh bình thường. Ví dụ thanh ghi R30 là con trỏ ngăn xếp thì việc viết vào ngăn xếp được thực hiện bằng các lệnh:
ADDI R30, R30, 4 ; tăng con trỏ ngăn xếp lên 4 vì từ dài 32 bit
STORE Ri, (R30) ; Viết Ri vào đỉnh ngăn xếp
Việc lấy ra khỏi ngăn xếp được thực hiện bằng các lệnh :
LOAD Ri, (R30) ; lấy số liệu ở đỉnh ngăn xếp và nạp vào Ri
SUBI R30, R30,4 ; giảm con trỏ ngăn xếp bớt 4
Các thủ tục
Các thủ tục được gọi từ bất cứ nơi nào của chương trình nhờ lệnh gọi thủ tục CALL. Để khi chấm dứt việc thi hành thủ tục thì chương trình gọi được tiếp tục bình thường, ta cần lưu giữ địa chỉ trở về tức địa chỉ của lệnh sau lệnh gọi thủ tục CALL. Khi chấm dứt thi hành thủ tục, lệnh trở về RETURN nạp địa chỉ trở về vào PC.
Trong các kiến trúc CISC (VAX 11, 80x86, 680x0), địa chỉ trở về được giữ ở ngăn xếp. Trong các kiến trúc RISC, một thanh ghi đặc biệt (thường là thanh ghi R31) được dùng để lưu giữ địa chỉ trở về.
Lệnh gọi thủ tục là một lệnh loại JMPL Ri, lệnh này làm các tác vụ :
R31 := PC ; để địa chỉ trở về trong R31
PC := Ri ; nhảy tới địa chỉ của thủ tục nằm trong thanh ghi Ri
Lệnh trở về khi chấm dứt thủ tục là JMP R31, vì thanh ghi R31 chứa địa chỉ trở về.
Việc dùng một thanh ghi đặc biệt để lưu trữ địa chỉ trở về là một giải pháp chỉ áp dụng cho các thủ tục cuối cùng, nghĩa là cho thủ tục không gọi thủ tục nào cả. Để có thể cho các thủ tục có thể gọi một thủ tục khác, ta có hai giải pháp:
Giải pháp 1: có nhiều thanh ghi để lưu trữ địa chỉ trở về
Giải pháp 2: lưu giữ địa chỉ trở về ở ngăn xếp.
Việc gọi thủ tục có thể được thực hiện bằng chuỗi lệnh sau đây :
ADDI R30, R30,4 ; R30 là con trỏ ngăn xếp
STORE R31, (R30) ; lưu giữ địa chỉ trở về
JMPL Ri ; gọi thủ tục
Người ta dùng chuỗi lệnh sau đây để trở về chương trình gọi :
LOAD R31, (R30) ; phục hồi địa chỉ trở về
SUBI R30, R30,4 ; cập nhật con trỏ ngăn xếp
JMP R31 ; trở về chương trình gọi
Hình II.9 : Gọi thủ tục và trở về khi thực hiện xong thủ tục
Việc truyền tham số từ thủ tục gọi đến thủ tục bị gọi có thể thực hiện bằng cách dùng các thanh ghi của bộ xử lý hoặc dùng ngăn xếp. Nếu số tham số cần truyền ít, ta dùng các thanh ghi.