Menu English Ukrainian Tiếng Nga Trang Chủ

Thư viện kỹ thuật miễn phí cho những người có sở thích và chuyên gia Thư viện kỹ thuật miễn phí


Ghi chú bài giảng, phiếu đánh giá
Thư viện miễn phí / Cẩm nang / Ghi chú bài giảng, phiếu đánh giá

Tin học và công nghệ thông tin. Ghi chú bài giảng: ngắn gọn, quan trọng nhất

Ghi chú bài giảng, phiếu đánh giá

Cẩm nang / Ghi chú bài giảng, phiếu đánh giá

Bình luận bài viết Bình luận bài viết

Mục lục

  1. Giới thiệu về Khoa học Máy tính (Tin học. Thông tin. Trình bày và xử lý thông tin. Hệ thống số. Biểu diễn số trong máy tính. Khái niệm chính thức về thuật toán)
  2. ngôn ngữ Pascal (Giới thiệu về Pascal. Các thủ tục và hàm tiêu chuẩn. Toán tử Pascal)
  3. Thủ tục và chức năng (Khái niệm về thuật toán phụ trợ. Các thủ tục trong Pascal. Các hàm trong Pascal. Mô tả trước và kết nối các chương trình con. Chỉ thị)
  4. chương trình con (Tham số thường trình. Các loại tham số chương trình con. Kiểu chuỗi trong Pascal. Các thủ tục và hàm cho các biến kiểu chuỗi. Bản ghi. Bộ)
  5. Tập tin (Tệp. Thao tác với tệp. Mô-đun. Các loại mô-đun)
  6. Bộ nhớ động (Loại dữ liệu tham chiếu. Bộ nhớ động. Biến động. Làm việc với bộ nhớ động. Con trỏ chưa được gõ)
  7. Cấu trúc dữ liệu trừu tượng (Cấu trúc dữ liệu trừu tượng. Ngăn xếp. Hàng đợi)
  8. Cấu trúc dữ liệu cây (Cấu trúc dữ liệu cây. Các thao tác trên cây. Ví dụ về cách thực hiện các thao tác)
  9. Số lượng (Khái niệm về đồ thị. Các phương pháp biểu diễn đồ thị. Biểu diễn đồ thị bằng danh sách các trường hợp. Thuật toán duyệt theo chiều sâu cho đồ thị. Biểu diễn đồ thị dưới dạng danh sách các danh sách. Thuật toán duyệt theo chiều rộng cho đồ thị )
  10. Kiểu dữ liệu đối tượng (Loại đối tượng trong Pascal. Khái niệm về một đối tượng, mô tả và cách sử dụng của nó. Kế thừa. Tạo các thể hiện của đối tượng. Thành phần và phạm vi)
  11. Phương pháp (Phương thức. Hàm tạo và hàm hủy. Hàm hủy. Phương thức ảo. Trường dữ liệu đối tượng và tham số phương thức hình thức)
  12. Tương thích loại đối tượng (Đóng gói. Đối tượng có thể mở rộng. Khả năng tương thích loại đối tượng)
  13. thợ lắp ráp (Giới thiệu về trình biên dịch mã. Mẫu phần mềm vi xử lý. Thanh ghi người dùng. Thanh ghi mục đích chung. Thanh ghi phân đoạn. Thanh ghi trạng thái và điều khiển)
  14. Đăng ký (Thanh ghi hệ thống vi xử lý. Thanh ghi điều khiển. Thanh ghi địa chỉ hệ thống. Thanh ghi gỡ lỗi)
  15. Chương trình lắp ráp (Cấu trúc chương trình trình biên dịch. Cú pháp của trình biên dịch. Toán tử so sánh. Toán tử và mức độ ưu tiên của chúng. Chỉ thị định nghĩa phân đoạn được đơn giản hóa. Mã định danh được tạo bởi lệnh MODEL. Mô hình bộ nhớ. Bộ sửa đổi mô hình bộ nhớ)
  16. Cấu trúc hướng dẫn lắp ráp (Cấu trúc của lệnh máy. Các phương pháp xác định toán hạng lệnh. Các phương pháp đánh địa chỉ)
  17. Đội (Lệnh truyền dữ liệu. Lệnh số học)
  18. Điều khiển các lệnh chuyển (Các lệnh logic. Bảng chân lý cho sự phủ định logic. Bảng chân lý cho hàm bao hàm logic OR. Bảng chân lý cho hàm logic AND. Bảng chân trị cho hàm logic loại trừ OR. Ý nghĩa của các chữ viết tắt trong tên lệnh jcc. Danh sách các lệnh nhảy có điều kiện cho lệnh. Nhảy có điều kiện lệnh và cờ)

BÀI GIẢNG SỐ 1. Giới thiệu về khoa học máy tính

1. Khoa học máy tính. Thông tin. Trình bày và xử lý thông tin

Tin học tham gia vào việc biểu diễn chính thức các đối tượng và cấu trúc các mối quan hệ của chúng trong các lĩnh vực khoa học, công nghệ và sản xuất khác nhau. Các công cụ chính thức khác nhau được sử dụng để mô hình hóa các đối tượng và hiện tượng, chẳng hạn như công thức logic, cấu trúc dữ liệu, ngôn ngữ lập trình, v.v.

Trong khoa học máy tính, một khái niệm cơ bản như thông tin có nhiều nghĩa khác nhau:

1) trình bày chính thức các dạng thông tin bên ngoài;

2) ý nghĩa trừu tượng của thông tin, nội dung bên trong, ngữ nghĩa của nó;

3) mối quan hệ của thông tin với thế giới thực.

Tuy nhiên, như một quy luật, thông tin được hiểu theo nghĩa trừu tượng của nó - ngữ nghĩa. Giải thích sự biểu diễn của thông tin, chúng ta có được ý nghĩa, ngữ nghĩa của nó. Vì vậy, nếu muốn trao đổi thông tin, chúng ta cần có quan điểm nhất quán để tính đúng đắn của việc diễn giải không bị vi phạm. Để làm được điều này, việc giải thích biểu diễn thông tin được xác định bằng một số cấu trúc toán học. Trong trường hợp này, việc xử lý thông tin có thể được thực hiện bằng các phương pháp toán học chặt chẽ.

Một trong những mô tả toán học của thông tin là biểu diễn nó dưới dạng hàm y =f(x, t), trong đó t là thời gian, x là một điểm trong một trường nhất định mà tại đó giá trị của y được đo. Tùy thuộc vào các tham số của hàm chi (thông tin có thể được phân loại.

Nếu các tham số là đại lượng vô hướng nhận một chuỗi giá trị liên tục, thì thông tin thu được theo cách này được gọi là liên tục (hoặc tương tự). Nếu các tham số được đưa ra một bước thay đổi nhất định, thì thông tin được gọi là rời rạc. Thông tin rời rạc được coi là phổ biến, vì đối với mỗi tham số cụ thể, có thể nhận được một giá trị hàm với mức độ chính xác nhất định.

Thông tin rời rạc thường được xác định với thông tin kỹ thuật số, đây là một trường hợp đặc biệt của thông tin ký hiệu của biểu diễn chữ cái. Bảng chữ cái là một tập hợp hữu hạn các ký hiệu thuộc bất kỳ bản chất nào. Rất thường xuyên trong khoa học máy tính, một tình huống phát sinh khi các ký tự của một bảng chữ cái này phải được biểu diễn bằng các ký tự của bảng chữ cái khác, tức là phải thực hiện một thao tác mã hóa. Nếu số lượng ký tự của bảng chữ cái mã hóa nhỏ hơn số ký tự của bảng chữ cái mã hóa thì bản thân hoạt động mã hóa không phức tạp, ngược lại cần sử dụng một bộ ký tự cố định của bảng chữ cái mã hóa để mã hóa chính xác rõ ràng.

Như thực tế đã chỉ ra, bảng chữ cái đơn giản nhất cho phép bạn mã hóa các bảng chữ cái khác là bảng chữ cái nhị phân, bao gồm hai ký tự, thường được ký hiệu là 0 và 1. Sử dụng n ký tự của bảng chữ cái nhị phân, bạn có thể mã hóa 2n ký tự và điều này là đủ để mã hóa bất kỳ bảng chữ cái nào.

Giá trị có thể được biểu diễn bằng ký hiệu của bảng chữ cái nhị phân được gọi là đơn vị thông tin hoặc bit tối thiểu. Dãy 8 bit - byte. Một bảng chữ cái chứa 256 chuỗi 8 bit khác nhau được gọi là bảng chữ cái byte.

Như một tiêu chuẩn ngày nay trong khoa học máy tính, một mã được sử dụng trong đó mỗi ký tự được mã hóa bằng 1 byte. Ngoài ra còn có các bảng chữ cái khác.

2. Hệ thống số

Hệ thống số là một tập hợp các quy tắc đặt tên và viết số. Có hệ thống số vị trí và không vị trí.

Hệ thống số được gọi là có vị trí nếu giá trị của chữ số của số phụ thuộc vào vị trí của chữ số trong số. Nếu không, nó được gọi là nonpositional. Giá trị của một số được xác định bởi vị trí của các chữ số này trong số.

3. Biểu diễn các số trong máy tính

Bộ xử lý 32-bit có thể hoạt động với RAM lên đến 232-1 và địa chỉ có thể được ghi trong dải 00000000 - FFFFFFFF. Tuy nhiên, ở chế độ thực, bộ xử lý hoạt động với bộ nhớ lên đến 220-1 và các địa chỉ nằm trong khoảng 00000 - FFFFF. Các byte bộ nhớ có thể được kết hợp thành các trường có độ dài cố định và thay đổi. Một từ là một trường độ dài cố định bao gồm 2 byte, một từ kép là một trường 4 byte. Địa chỉ trường có thể chẵn hoặc lẻ, với các địa chỉ chẵn thực hiện các thao tác nhanh hơn.

Số điểm cố định được biểu diễn trong máy tính dưới dạng số nhị phân nguyên và kích thước của chúng có thể là 1, 2 hoặc 4 byte.

Các số nguyên nhị phân được biểu diễn dưới dạng phần bù hai và các số điểm cố định được biểu diễn dưới dạng phần bù hai. Hơn nữa, nếu một số chiếm 2 byte, thì cấu trúc của số được viết theo quy tắc sau: chữ số có nghĩa nhất được gán cho dấu của số đó và phần còn lại - cho các chữ số nhị phân của số đó. Mã bổ sung của một số dương bằng chính số đó và mã bổ sung của số âm có thể được lấy bằng công thức sau: x = 10i - \x\, trong đó n là dung lượng chữ số của số đó.

Trong hệ thống số nhị phân, một mã bổ sung có được bằng cách đảo các bit, tức là thay thế các đơn vị bằng các số không và ngược lại, và thêm một mã vào bit ít quan trọng nhất.

Số bit của phần định trị xác định độ chính xác của việc biểu diễn số, số bit thứ tự máy xác định phạm vi biểu diễn của số dấu phẩy động.

4. Khái niệm chính thức về một thuật toán

Thuật toán chỉ có thể tồn tại nếu đồng thời tồn tại một số đối tượng toán học. Khái niệm chính thức hóa của một thuật toán được kết nối với khái niệm hàm đệ quy, thuật toán Markov thông thường, máy Turing.

Trong toán học, một hàm được gọi là có giá trị đơn nếu, đối với bất kỳ tập đối số nào, có một luật mà giá trị duy nhất của hàm được xác định. Một thuật toán có thể hoạt động như một luật như vậy; trong trường hợp này, hàm được cho là có thể tính toán được.

Các hàm đệ quy là một lớp con của các hàm có thể tính toán được và các thuật toán xác định tính toán được gọi là các thuật toán hàm đệ quy đồng hành. Đầu tiên, các hàm đệ quy cơ bản được cố định, trong đó thuật toán đi kèm là nhỏ, không rõ ràng; sau đó ba quy tắc được giới thiệu - các toán tử thay thế, đệ quy và tối thiểu hóa, với sự trợ giúp của các hàm đệ quy phức tạp hơn trên cơ sở các hàm cơ bản.

Các chức năng cơ bản và các thuật toán đi kèm của chúng có thể là:

1) một hàm gồm n biến độc lập, giống hệt nhau bằng không. Khi đó, nếu dấu của hàm là φn, thì bất kể số đối số là bao nhiêu, giá trị của hàm phải được đặt bằng XNUMX;

2) hàm đồng nhất của n biến độc lập có dạng ψni. Sau đó, nếu dấu của hàm là ψni, thì giá trị của hàm phải được lấy làm giá trị của đối số thứ i, đếm từ trái sang phải;

3) Λ là một hàm của một đối số độc lập. Sau đó, nếu dấu của hàm là λ, thì giá trị của hàm phải được coi là giá trị theo sau giá trị của đối số. Nhiều học giả khác nhau đã đề xuất các cách tiếp cận của riêng họ đối với

biểu diễn của thuật toán. Ví dụ, nhà khoa học người Mỹ Church đề xuất rằng lớp các hàm tính toán được sử dụng hết bởi các hàm đệ quy và kết quả là, bất kỳ thuật toán nào xử lý một tập hợp các số nguyên không âm thành một tập hợp các số nguyên không âm khác, có một thuật toán đi kèm với hàm đệ quy đó là tương đương với cái đã cho. Do đó, nếu không thể xây dựng một hàm đệ quy để giải một bài toán đã cho thì không có thuật toán nào để giải nó. Một nhà khoa học khác, Turing, đã phát triển một máy tính ảo xử lý chuỗi ký tự đầu vào thành đầu ra. Về vấn đề này, ông đưa ra luận điểm rằng bất kỳ hàm tính toán nào cũng có thể tính toán được.

BÀI GIẢNG SỐ 2. Ngôn ngữ Pascal

1. Giới thiệu về ngôn ngữ Pascal

Các ký hiệu cơ bản của ngôn ngữ - chữ cái, số và các ký tự đặc biệt - tạo nên bảng chữ cái của nó. Ngôn ngữ Pascal bao gồm tập hợp các ký hiệu cơ bản sau:

1) 26 chữ thường Latinh và 26 chữ hoa Latinh:

ABCDEFGHIJKLMNOPQRSTUVWXYZ

abcdefghijklmnopqrstuvwxyz;

2) _ (gạch dưới);

3) 10 chữ số: 0123456789;

4) dấu hiệu hoạt động:

+ - x / = <> = := @;

5) giới hạn:

., ' ( ) [ ] (..) { } (* *).. : ;

6) các thông số: ^ # $;

7) từ dịch vụ (dành riêng):

ABSOLUTE, ASSEMBLER, AND, ARRAY, ASM, BEGIN, CASE, CONST, CONADOR, DESTRUCTOR, DIV, DO, DOWNTO, ELSE, END, EXPORT, EXTERNAL, FAR, FILE, FOR, FORWARD, FUNCTION, GOTO, IF, IMPLEMENTATION, IN, INDEX, INHERITED, INLINE, INTERFACE, INTERRUPT, LABEL, LIBRARY, MOD, NAME, NIL, GẦN, NOT, OBJECT, OF, OR, PACKED, PRIVATE, PROCEDURE, PROGRAM, PUBLIC, RECORD, REPEAT, RESIDENT, SET, SHL, SHR, STRING, THEN, TO, TYPE, UNIT, UNTIL, USES, VAR, VIRTUAL, WHILE, WITH, XOR.

Ngoài những ký tự được liệt kê, tập hợp các ký tự cơ bản bao gồm một khoảng trắng. Không thể sử dụng dấu cách bên trong các ký tự kép và các từ dành riêng.

Nhập khái niệm cho dữ liệu

Trong toán học, người ta thường phân loại các biến theo một số đặc điểm quan trọng. Một sự phân biệt chặt chẽ được thực hiện giữa các biến thực, phức và logic, giữa các biến đại diện cho các giá trị riêng lẻ và một tập giá trị, v.v ... Khi xử lý dữ liệu trên máy tính, việc phân loại như vậy càng quan trọng hơn. Trong bất kỳ ngôn ngữ thuật toán nào, mọi hằng, biến, biểu thức hoặc hàm đều thuộc một kiểu cụ thể.

Có một quy tắc trong Pascal: kiểu được chỉ định rõ ràng trong khai báo của một biến hoặc hàm trước khi sử dụng nó. Khái niệm kiểu Pascal có các thuộc tính chính sau:

1) bất kỳ kiểu dữ liệu nào xác định một tập hợp các giá trị mà một hằng số thuộc về, mà một biến hoặc biểu thức có thể nhận, hoặc một phép toán hoặc hàm có thể tạo ra;

2) loại giá trị được cung cấp bởi một hằng số, biến hoặc biểu thức có thể được xác định bằng hình thức hoặc mô tả của chúng;

3) mỗi hoạt động hoặc hàm yêu cầu đối số kiểu cố định và tạo ra kết quả kiểu cố định.

Sau đó, trình biên dịch có thể sử dụng thông tin kiểu để kiểm tra khả năng tính toán và tính đúng đắn của các cấu trúc khác nhau.

Kiểu xác định:

1) các giá trị có thể có của các biến, hằng số, hàm, biểu thức thuộc một kiểu nhất định;

2) hình thức trình bày dữ liệu nội bộ trong máy tính;

3) các hoạt động và chức năng có thể được thực hiện trên các giá trị thuộc một kiểu nhất định.

Cần lưu ý rằng việc mô tả kiểu bắt buộc dẫn đến sự dư thừa trong văn bản của chương trình, nhưng sự dư thừa đó là một công cụ phụ trợ quan trọng để phát triển chương trình và được coi như một thuộc tính cần thiết của các ngôn ngữ thuật toán bậc cao hiện đại.

Có các kiểu dữ liệu vô hướng và có cấu trúc trong Pascal. Kiểu vô hướng bao gồm kiểu chuẩn và kiểu do người dùng xác định. Các kiểu tiêu chuẩn bao gồm các kiểu số nguyên, thực, ký tự, boolean và địa chỉ.

Kiểu số nguyên xác định các hằng số, biến và hàm có giá trị được nhận ra bởi tập hợp các số nguyên được phép trong một máy tính nhất định.

Các kiểu thực xác định những dữ liệu được thực hiện bởi một tập hợp con các số thực được phép trong một máy tính nhất định.

Các kiểu do người dùng xác định là enum và range. Các kiểu có cấu trúc có bốn loại: mảng, tập hợp, bản ghi và tệp.

Ngoài những thứ được liệt kê, Pascal còn bao gồm hai kiểu nữa - thủ tục và đối tượng.

Một biểu thức ngôn ngữ bao gồm hằng số, biến, con trỏ hàm, dấu toán tử và dấu ngoặc. Một biểu thức xác định một quy tắc để tính một số giá trị. Thứ tự tính toán được xác định bởi mức độ ưu tiên (ưu tiên) của các phép toán chứa trong nó. Pascal có ưu tiên toán tử sau:

1) các phép tính trong ngoặc đơn;

2) tính toán các giá trị của hàm;

3) các phép toán một ngôi;

4) các phép toán *, /, div, mod và;

5) các phép toán +, -, hoặc, xor;

6) các phép toán quan hệ =, <>, <,>, <=,> =.

Biểu thức là một phần của nhiều toán tử ngôn ngữ Pascal và cũng có thể là đối số của các hàm dựng sẵn.

2. Các thủ tục và chức năng tiêu chuẩn

Các hàm số học

1. chức năng Abs (X);

Trả về giá trị tuyệt đối của tham số.

X là một biểu thức của kiểu số thực hoặc số nguyên.

2. Hàm ArcTan (X: Extended): Extended;

Trả về tiếp tuyến cung của đối số.

X là một biểu thức của kiểu số thực hoặc số nguyên.

3. Hàm Exp (X: Real): Real;

Trả về số mũ.

X là một biểu thức của kiểu số thực hoặc số nguyên.

4.Frac (X: Real): Thực;

Trả về phần phân số của đối số.

X là một biểu thức kiểu thực. Kết quả là phần phân số của X, tức là

Frac(X) = X-Int(X).

5. Hàm Int (X: Real): Thực;

Trả về phần nguyên của đối số.

X là một biểu thức kiểu thực. Kết quả là phần nguyên của X, tức là X được làm tròn về XNUMX.

6. Hàm Ln (X: Real): Real;

Trả về lôgarit tự nhiên (Ln e = 1) của biểu thức kiểu thực X.

7. Chức năng Pi: Mở rộng;

Trả về giá trị Pi, được định nghĩa là 3.1415926535.

8.Function Sin (X: Extended): Mở rộng;

Trả về sin của đối số.

X là một biểu thức kiểu thực. Sin trả về sin của góc X tính bằng radian.

9.Function Sqr (X: Extended): Mở rộng;

Trả về bình phương của đối số.

X là một biểu thức dấu phẩy động. Kết quả cùng loại với X.

10.Function Sqrt (X: Extended): Extended;

Trả về căn bậc hai của đối số.

X là một biểu thức dấu phẩy động. Kết quả là căn bậc hai của X.

Các thủ tục và chức năng chuyển đổi giá trị

1. Thủ tục Str (X [: Width [: Decimals]]; var S);

Chuyển đổi số X thành biểu diễn chuỗi theo

Các tùy chọn định dạng Width và Decimals. X là một biểu thức kiểu số thực hoặc số nguyên. Width và Decimals là các biểu thức kiểu số nguyên. S là một biến kiểu String hoặc một mảng ký tự kết thúc bằng rỗng nếu cú ​​pháp mở rộng được cho phép.

2. Hàm Chr (X: Byte): Char;

Trả về ký tự có thứ tự X trong bảng ASCII.

3. hàm cao (X);

Trả về giá trị lớn nhất trong phạm vi của tham số.

4.FunctionLow (X);

Trả về giá trị nhỏ nhất trong phạm vi tham số.

5 FunctionOrd (X): Longint;

Trả về giá trị thứ tự của một biểu thức kiểu liệt kê. X là một biểu thức kiểu liệt kê.

6. Vòng chức năng (X: Extended): Longint;

Làm tròn một giá trị thực thành một số nguyên. X là một biểu thức kiểu thực. Round trả về giá trị Longint, là giá trị của X được làm tròn thành số nguyên gần nhất. Nếu X nằm đúng một nửa giữa hai số nguyên, thì số có giá trị tuyệt đối lớn nhất sẽ được trả về. Nếu giá trị làm tròn của X nằm ngoài phạm vi Longint, lỗi thời gian chạy sẽ được tạo ra mà bạn có thể xử lý bằng cách sử dụng ngoại lệ EInvalidOp.

7. Hàm Trunc (X: Extended): Longint;

Cắt bớt một giá trị kiểu thực thành một số nguyên. Nếu giá trị làm tròn của X nằm ngoài phạm vi Longint, lỗi thời gian chạy sẽ được tạo ra mà bạn có thể xử lý bằng cách sử dụng ngoại lệ EInvalidOp.

8. Thủ tục Val (S; var V; var Code: Integer);

Chuyển đổi một số từ giá trị chuỗi S thành một số

biểu diễn V. S - biểu thức kiểu chuỗi - một chuỗi các ký tự tạo thành một số nguyên hoặc số thực. Nếu biểu thức S không hợp lệ, chỉ mục của ký tự không hợp lệ được lưu trữ trong biến Mã. Nếu không, Mã được đặt thành không.

Các thủ tục và chức năng giá trị thông thường

1. Thủ tục Dec (varX [; N: LongInt]);

Trừ một hoặc N khỏi biến X. Dec (X) tương ứng với X: = X - 1 và Dec (X, N) tương ứng với X: = X - N. X là biến thuộc kiểu liệt kê, hoặc kiểu PChar nếu cú ​​pháp mở rộng được cho phép và N là biểu thức của kiểu số nguyên. Thủ tục Dec tạo ra mã tối ưu và đặc biệt hữu ích trong các vòng lặp dài.

2. Thủ tục Inc (varX [; N: LongInt]);

Thêm một hoặc N vào biến X. X là một biến kiểu liệt kê hoặc kiểu PChar nếu cú ​​pháp mở rộng được cho phép và N là một biểu thức của kiểu tích phân. Inc (X) khớp lệnh X: = X + 1 và Inc (X, N) khớp lệnh X: = X + N. Thủ tục Inc tạo ra mã tối ưu và đặc biệt hữu ích trong các vòng lặp dài.

3. FunctionOdd (X: LongInt): Boolean;

Trả về True nếu X là số lẻ, trả về False nếu không.

4.FunctionPred (X);

Trả về giá trị trước đó của tham số. X là một biểu thức kiểu liệt kê. Kết quả là cùng một loại.

5 Hàm Succ (X);

Trả về giá trị tham số tiếp theo. X là một biểu thức kiểu liệt kê. Kết quả là cùng một loại.

3. Các toán tử ngôn ngữ Pascal

Điều hành có điều kiện

Định dạng của câu lệnh điều kiện đầy đủ được định nghĩa như sau: If B then SI else S2; trong đó B là một điều kiện phân nhánh (ra quyết định), một biểu thức logic hoặc một quan hệ; SI, S2 - một câu lệnh thực thi, đơn giản hoặc phức hợp.

Khi thực hiện một câu lệnh điều kiện, trước tiên biểu thức B được đánh giá, sau đó kết quả của nó được phân tích: nếu B đúng, thì câu lệnh S1 được thực hiện - nhánh của then, và câu lệnh S2 bị bỏ qua; nếu B sai, thì câu lệnh S2 - nhánh khác được thực thi và câu lệnh S1 bị bỏ qua.

Ngoài ra còn có một dạng viết tắt của toán tử điều kiện. Nó được viết là: Nếu B thì S.

Chọn câu lệnh

Cấu trúc toán tử như sau:

trường hợp S của

c1: hướng dẫn1;

c2: hướng dẫn2;

hữu ích. Cảm ơn !

cn: hướng dẫnN;

hướng dẫn khác

kết thúc;

trong đó S là một biểu thức kiểu thứ tự có giá trị đang được tính toán;

с1, с2..., сп - các hằng số thuộc loại thứ tự để so sánh các biểu thức

S; lệnh1,..., lệnhN - toán tử trong đó toán tử có hằng số khớp với giá trị của biểu thức S được thực thi;

lệnh - một câu lệnh được thực thi nếu giá trị của biểu thức Sylq không khớp với hằng số c1, c2.... cn.

Toán tử này là tổng quát của toán tử If có điều kiện cho một số tùy chọn thay thế tùy ý. Có một dạng viết tắt của câu lệnh mà không có nhánh nào khác.

Câu lệnh lặp với tham số

Các câu lệnh vòng lặp tham số bắt đầu bằng câu lệnh từ nguyên nhân, có thể là một câu lệnh ghép, được thực thi nhiều lần trong khi biến điều khiển được gán một chuỗi giá trị tăng dần.

Quan điểm chung của toán tử for:

for <bộ đếm vòng lặp>: = <giá trị bắt đầu> to <giá trị kết thúc> do <câu lệnh>;

Khi câu lệnh for bắt đầu thực thi, giá trị bắt đầu và kết thúc được xác định một lần và những giá trị này được giữ lại trong suốt quá trình thực thi câu lệnh for. Câu lệnh chứa trong phần thân của câu lệnh for được thực thi một lần cho mỗi giá trị trong phạm vi giữa giá trị bắt đầu và giá trị kết thúc. Bộ đếm vòng lặp luôn được khởi tạo với một giá trị ban đầu. Khi câu lệnh for đang chạy, giá trị của bộ đếm vòng lặp được tăng lên với mỗi lần lặp. Nếu giá trị bắt đầu lớn hơn giá trị kết thúc, thì câu lệnh chứa trong phần thân của câu lệnh for sẽ không được thực thi. Khi từ khóa downto được sử dụng trong câu lệnh lặp, giá trị của biến điều khiển sẽ giảm đi một trong mỗi lần lặp. Nếu giá trị bắt đầu trong một câu lệnh như vậy nhỏ hơn giá trị kết thúc, thì câu lệnh chứa trong phần thân của câu lệnh lặp sẽ không được thực hiện.

Nếu câu lệnh chứa trong phần thân của câu lệnh for thay đổi giá trị của bộ đếm vòng lặp, thì đây là một lỗi. Sau khi thực hiện câu lệnh for, giá trị của biến điều khiển trở nên không xác định, trừ khi việc thực thi câu lệnh for bị gián đoạn bởi một câu lệnh nhảy.

Câu lệnh lặp với điều kiện tiên quyết

Câu lệnh lặp điều kiện trước (bắt đầu bằng từ khóa while) chứa một biểu thức điều khiển việc thực hiện lặp lại câu lệnh (có thể là một câu lệnh ghép). Hình dạng chu kỳ:

Trong khi B do S;

trong đó B là một điều kiện logic, sự thật của nó được kiểm tra (nó là điều kiện để kết thúc vòng lặp);

S - phần thân của vòng lặp - một câu lệnh.

Biểu thức điều khiển việc lặp lại một câu lệnh phải thuộc kiểu Boolean. Nó được đánh giá trước khi câu lệnh bên trong được thực thi. Câu lệnh bên trong được thực thi lặp đi lặp lại miễn là biểu thức đánh giá là True. Nếu biểu thức đánh giá là Sai ngay từ đầu, thì câu lệnh chứa trong câu lệnh lặp điều kiện trước sẽ không được thực thi.

Câu lệnh lặp với điều kiện sau

Trong một câu lệnh lặp với điều kiện sau (bắt đầu bằng từ lặp lại), biểu thức điều khiển việc thực hiện lặp lại một chuỗi các câu lệnh được chứa trong câu lệnh lặp. Hình dạng chu kỳ:

lặp lại S cho đến khi B;

trong đó B là một điều kiện logic, sự thật của nó được kiểm tra (nó là điều kiện để kết thúc vòng lặp);

S - một hoặc nhiều câu lệnh nội dung vòng lặp.

Kết quả của biểu thức phải thuộc kiểu boolean. Các câu lệnh được bao gồm giữa các từ khóa lặp lại và cho đến khi được thực thi tuần tự cho đến khi kết quả của biểu thức đánh giá là Đúng. Chuỗi câu lệnh sẽ được thực hiện ít nhất một lần vì biểu thức được đánh giá sau mỗi lần thực hiện chuỗi câu lệnh.

LECTURE № 3. Các thủ tục và chức năng

1. Khái niệm về thuật toán bổ trợ

Một thuật toán để giải quyết một vấn đề được thiết kế bằng cách phân tách toàn bộ vấn đề thành các nhiệm vụ con riêng biệt. Thông thường, các nhiệm vụ con được thực hiện dưới dạng chương trình con.

Chương trình con là một số thuật toán bổ trợ được sử dụng lặp đi lặp lại trong thuật toán chính với các giá trị khác nhau của một số đại lượng đến, được gọi là tham số.

Chương trình con trong ngôn ngữ lập trình là một chuỗi các câu lệnh chỉ được định nghĩa và viết ở một nơi trong chương trình, nhưng chúng có thể được gọi để thực thi từ một hoặc nhiều điểm trong chương trình. Mỗi chương trình con được xác định bằng một tên duy nhất.

Có hai loại chương trình con trong Pascal là thủ tục và hàm. Một thủ tục và một hàm là một chuỗi các khai báo và câu lệnh được đặt tên. Khi sử dụng các thủ tục hoặc hàm, chương trình phải chứa văn bản của thủ tục hoặc hàm và một lệnh gọi đến thủ tục hoặc hàm. Các tham số được chỉ định trong mô tả được gọi là hình thức, những tham số được chỉ định trong lệnh gọi chương trình con được gọi là thực tế. Tất cả các tham số chính thức có thể được chia thành các loại sau:

1) tham số-biến;

2) các tham số không đổi;

3) tham số-giá trị;

4) tham số thủ tục và tham số chức năng, tức là tham số kiểu thủ tục;

5) tham số biến không định kiểu.

Các văn bản của các thủ tục và hàm được đặt trong phần mô tả các thủ tục và hàm.

Truyền tên thủ tục và hàm làm tham số

Trong nhiều bài toán, đặc biệt là trong toán học tính toán, cần phải chuyển tên của các thủ tục và hàm làm tham số. Để làm điều này, TURBO PASCAL đã giới thiệu một kiểu dữ liệu mới - thủ tục hoặc chức năng, tùy thuộc vào những gì được mô tả. (Các kiểu thủ tục và hàm được mô tả trong phần khai báo kiểu.)

Một hàm và kiểu thủ tục được định nghĩa là tiêu đề của một thủ tục và một hàm có danh sách các tham số chính thức nhưng không có tên. Có thể xác định một hàm hoặc kiểu thủ tục mà không có tham số, ví dụ:

kiểu

Proc = thủ tục;

Sau khi khai báo một kiểu thủ tục hoặc chức năng, nó có thể được sử dụng để mô tả các tham số chính thức - tên của các thủ tục và hàm. Ngoài ra, cần phải viết các thủ tục hoặc hàm thực đó mà tên sẽ được truyền dưới dạng các tham số thực tế.

2. Các thủ tục trong Pascal

Mỗi mô tả thủ tục chứa một tiêu đề theo sau là một khối chương trình. Hình thức chung của tiêu đề thủ tục như sau:

Thủ tục <tên> [(<danh sách các tham số hình thức>)];

Một thủ tục được kích hoạt với một câu lệnh thủ tục có chứa tên của thủ tục và các tham số cần thiết. Các câu lệnh được thực hiện khi thủ tục được chạy được chứa trong phần câu lệnh của mô-đun thủ tục. Nếu một câu lệnh chứa trong một thủ tục sử dụng một mã định danh thủ tục bên trong một mô-đun thủ tục, thì thủ tục sẽ được thực thi một cách đệ quy, tức là nó sẽ tham chiếu đến chính nó khi được thực thi.

3. Các hàm trong Pascal

Một khai báo hàm xác định một phần của chương trình trong đó giá trị được tính toán và trả về. Hình thức chung của tiêu đề hàm như sau:

Hàm <tên> [(<danh sách các tham số hình thức>)]: <kiểu trả về>;

Chức năng được kích hoạt khi nó được gọi. Khi một hàm được gọi, mã định danh hàm và bất kỳ tham số nào cần thiết để đánh giá nó được chỉ định. Một lời gọi hàm có thể được đưa vào biểu thức như một toán hạng. Khi biểu thức được đánh giá, hàm được thực thi và giá trị của toán hạng trở thành giá trị được trả về bởi hàm.

Phần toán tử của khối chức năng chỉ định các câu lệnh phải được thực hiện khi chức năng được kích hoạt. Mô-đun phải chứa ít nhất một câu lệnh gán giá trị cho một định danh hàm. Kết quả của hàm là giá trị cuối cùng được gán. Nếu không có câu lệnh gán như vậy hoặc nếu nó chưa được thực thi, thì giá trị trả về của hàm là không xác định.

Nếu một định danh hàm được sử dụng khi gọi một hàm trong một mô-đun, thì hàm được thực thi một cách đệ quy.

4. Chuyển tiếp mô tả và kết nối các chương trình con. Chỉ thị

Một chương trình có thể chứa một số chương trình con, tức là cấu trúc của chương trình có thể phức tạp. Tuy nhiên, các chương trình con này có thể ở cùng một mức lồng nhau, vì vậy khai báo chương trình con phải đến trước, sau đó mới đến lệnh gọi nó, trừ khi sử dụng một khai báo chuyển tiếp đặc biệt.

Một khai báo thủ tục có chứa một chỉ thị chuyển tiếp thay vì một khối câu lệnh được gọi là một khai báo chuyển tiếp. Tại một số thời điểm sau khai báo này, một thủ tục phải được định nghĩa bằng một khai báo xác định. Một khai báo xác định là một khai báo sử dụng cùng một mã định danh thủ tục nhưng bỏ qua danh sách các tham số chính thức và bao gồm một khối câu lệnh. Khai báo chuyển tiếp và khai báo xác định phải xuất hiện trong cùng một phần của khai báo thủ tục và chức năng. Giữa chúng, các thủ tục và hàm khác có thể được khai báo có thể tham chiếu đến thủ tục khai báo chuyển tiếp. Do đó, có thể đệ quy lẫn nhau.

Mô tả chuyển tiếp và mô tả xác định là mô tả hoàn chỉnh của quy trình. Thủ tục được coi là được mô tả bằng cách sử dụng mô tả chuyển tiếp.

Nếu chương trình chứa khá nhiều chương trình con thì chương trình sẽ không còn trực quan, khó điều hướng trong đó. Để tránh điều này, một số quy trình được lưu trữ dưới dạng tệp nguồn trên đĩa và nếu cần, chúng được kết nối với chương trình chính ở giai đoạn biên dịch bằng cách sử dụng chỉ thị biên dịch.

Chỉ thị là một nhận xét đặc biệt có thể được đặt ở bất kỳ đâu trong chương trình, nơi có thể là một nhận xét bình thường. Tuy nhiên, chúng khác nhau ở chỗ chỉ thị có một ký hiệu đặc biệt: ngay sau dấu ngoặc đóng mà không có khoảng trắng, ký hiệu S được viết, và sau đó, lại không có khoảng trắng, chỉ thị được chỉ ra.

Ví dụ

1) {SE +} - mô phỏng bộ đồng xử lý toán học;

2) {SF +} - tạo một kiểu gọi hàm và thủ tục ở xa;

3) {SN +} - sử dụng bộ đồng xử lý toán học;

4) {SR +} - kiểm tra xem các phạm vi có nằm ngoài giới hạn không.

Một số công tắc biên dịch có thể chứa một tham số, ví dụ:

{$ 1 tên tệp} - bao gồm tệp đã đặt tên trong văn bản của chương trình đã biên dịch.

LECTURE số 4. Các chương trình con

1. Các tham số của chương trình con

Mô tả của một thủ tục hoặc hàm chỉ định một danh sách các tham số chính thức. Mỗi tham số được khai báo trong danh sách tham số chính thức là cục bộ của thủ tục hoặc chức năng được mô tả và có thể được tham chiếu trong mô-đun được liên kết với thủ tục hoặc chức năng đó bằng mã định danh của nó.

Có ba loại tham số: giá trị, biến và biến không định kiểu. Chúng được đặc trưng như sau.

1. Nhóm tham số không có từ khóa đứng trước là danh sách các tham số giá trị.

2. Một nhóm tham số đứng trước từ khóa const và theo sau là một kiểu là danh sách các tham số không đổi.

3. Một nhóm tham số đứng trước từ khóa var và theo sau bởi một kiểu là danh sách các tham số biến không có kiểu.

4. Một nhóm tham số đứng trước từ khóa var hoặc const và không theo sau bởi một kiểu là danh sách các tham số biến không có kiểu.

2. Các loại tham số của chương trình con

Tham số giá trị

Tham số giá trị chính thức được coi như một biến cục bộ của thủ tục hoặc hàm, ngoại trừ việc nó lấy giá trị ban đầu từ tham số thực tương ứng khi thủ tục hoặc hàm được gọi. Những thay đổi mà tham số giá trị chính thức trải qua không ảnh hưởng đến giá trị của tham số thực. Giá trị thực tương ứng của tham số giá trị phải là một biểu thức và giá trị của nó không được là một loại tệp hoặc bất kỳ kiểu cấu trúc nào có chứa một loại tệp.

Tham số thực tế phải có kiểu được gán tương thích với kiểu của tham số giá trị chính thức. Nếu tham số là kiểu chuỗi, thì tham số hình thức sẽ có thuộc tính kích thước là 255.

Tham số không đổi

Các tham số hằng số chính thức hoạt động tương tự như một biến cục bộ chỉ đọc nhận giá trị của nó khi một thủ tục hoặc hàm được gọi từ tham số thực tương ứng. Không được phép gán cho một tham số hằng chính thức. Một tham số hằng chính thức cũng không thể được truyền như một tham số thực tế cho một thủ tục hoặc hàm khác. Tham số hằng tương ứng với tham số thực trong câu lệnh thủ tục hoặc hàm phải tuân theo các quy tắc giống như giá trị tham số thực.

Trong trường hợp một tham số hình thức không thay đổi giá trị của nó trong quá trình thực thi một thủ tục hoặc một hàm, một tham số hằng nên được sử dụng thay cho một tham số giá trị. Các tham số không đổi cho phép thực hiện một thủ tục hoặc hàm để bảo vệ khỏi việc gán ngẫu nhiên cho một tham số chính thức. Ngoài ra, đối với các tham số kiểu cấu trúc và chuỗi, trình biên dịch có thể tạo mã hiệu quả hơn khi được sử dụng thay vì các tham số giá trị cho các tham số không đổi.

Tham số biến

Một tham số biến được sử dụng khi một giá trị phải được truyền từ một thủ tục hoặc hàm đến chương trình gọi. Tham số thực tế tương ứng trong câu lệnh gọi thủ tục hoặc hàm phải là một tham chiếu biến. Khi một thủ tục hoặc hàm được gọi, biến tham số chính thức được thay thế bằng biến thực tế, bất kỳ thay đổi nào trong giá trị của biến tham số chính thức đều được phản ánh trong tham số thực tế.

Trong một thủ tục hoặc hàm, bất kỳ tham chiếu nào đến tham số biến hình thức đều dẫn đến quyền truy cập vào chính tham số thực. Kiểu của tham số thực tế phải khớp với kiểu của tham số biến chính thức, nhưng hạn chế này có thể được phá vỡ bằng cách sử dụng tham số biến không định kiểu).

Tham số không định dạng

Khi tham số hình thức là một tham số biến không định kiểu, thì tham số thực tế tương ứng có thể là bất kỳ tham chiếu nào đến một biến hoặc hằng số, bất kể kiểu của nó là gì. Một tham số không định kiểu được khai báo với từ khóa var có thể được sửa đổi, trong khi một tham số không định kiểu được khai báo với từ khóa const là chỉ đọc.

Trong một thủ tục hoặc hàm, một tham số biến không định kiểu không có kiểu, tức là nó không tương thích với các biến thuộc mọi kiểu cho đến khi nó được gán một kiểu cụ thể bằng cách gán kiểu biến.

Mặc dù các tham số không được định kiểu cung cấp tính linh hoạt hơn, nhưng có một số rủi ro liên quan đến việc sử dụng chúng. Trình biên dịch không thể kiểm tra tính hợp lệ của các hoạt động trên các biến không định kiểu.

Các biến thủ tục

Sau khi xác định một kiểu thủ tục, có thể mô tả các biến của kiểu này. Các biến như vậy được gọi là các biến thủ tục. Giống như biến số nguyên có thể được gán giá trị kiểu số nguyên, biến thủ tục có thể được gán giá trị kiểu thủ tục. Tất nhiên, giá trị có thể là một biến thủ tục khác, nhưng nó cũng có thể là một định danh thủ tục hoặc hàm. Trong ngữ cảnh này, việc khai báo một thủ tục hoặc hàm có thể được xem như một mô tả của một loại hằng số đặc biệt có giá trị là thủ tục hoặc hàm.

Như với bất kỳ phép gán nào khác, các giá trị của biến ở phía bên trái và phía bên phải phải tương thích với phép gán. Các kiểu thủ tục, để tương thích với phép gán, phải có cùng số lượng tham số và các tham số ở các vị trí tương ứng phải cùng kiểu. Tên tham số trong khai báo kiểu thủ tục không có hiệu lực.

Ngoài ra, để đảm bảo tính tương thích của phép gán, một thủ tục hoặc hàm, nếu nó được gán cho một biến thủ tục, phải đáp ứng các yêu cầu sau:

1) nó không phải là một thủ tục hoặc chức năng tiêu chuẩn;

2) không thể lồng vào nhau một thủ tục hoặc hàm như vậy;

3) thủ tục như vậy không được là thủ tục nội tuyến;

4) nó không được là một thủ tục ngắt.

Các thủ tục và chức năng tiêu chuẩn là các thủ tục và chức năng được mô tả trong mô-đun Hệ thống, chẳng hạn như Writeln, Readln, Chr, Ord. Không thể sử dụng các thủ tục và hàm lồng nhau với các biến thủ tục. Một thủ tục hoặc hàm được coi là lồng nhau khi nó được khai báo trong một thủ tục hoặc hàm khác.

Việc sử dụng các kiểu thủ tục không chỉ giới hạn ở các biến thủ tục. Giống như bất kỳ kiểu nào khác, kiểu thủ tục có thể tham gia vào việc khai báo kiểu cấu trúc.

Khi một biến thủ tục được gán giá trị của một thủ tục, điều xảy ra ở lớp vật lý là địa chỉ của thủ tục được lưu trong biến. Trên thực tế, một biến thủ tục rất giống với một biến con trỏ, chỉ thay vì tham chiếu đến dữ liệu, nó trỏ đến một thủ tục hoặc hàm. Giống như một con trỏ, một biến thủ tục chiếm 4 byte (hai từ) chứa địa chỉ bộ nhớ. Từ đầu tiên lưu trữ phần bù, từ thứ hai lưu trữ phân đoạn.

Các tham số kiểu thủ tục

Vì các kiểu thủ tục có thể được sử dụng trong bất kỳ ngữ cảnh nào, nên có thể mô tả các thủ tục hoặc hàm lấy các thủ tục và hàm làm tham số. Các tham số kiểu thủ tục đặc biệt hữu ích khi bạn cần thực hiện một số hành động phổ biến trên nhiều thủ tục hoặc hàm.

Nếu một thủ tục hoặc hàm được truyền dưới dạng tham số, thì nó phải tuân theo các quy tắc tương thích cùng kiểu với phép gán. Có nghĩa là, các thủ tục hoặc hàm như vậy phải được biên dịch với chỉ thị xa, chúng không thể là hàm dựng sẵn, chúng không thể lồng vào nhau và không thể mô tả chúng bằng các thuộc tính nội tuyến hoặc ngắt.

LECTURE # 5. Kiểu dữ liệu chuỗi

1. Kiểu chuỗi trong Pascal

Một chuỗi các ký tự có độ dài nhất định được gọi là một chuỗi. Các biến kiểu chuỗi được xác định bằng cách chỉ định tên của biến, chuỗi từ dành riêng và tùy chọn, nhưng không nhất thiết, chỉ định kích thước tối đa, tức là độ dài của chuỗi, trong dấu ngoặc vuông. Nếu bạn không đặt kích thước chuỗi tối đa, thì theo mặc định, nó sẽ là 255, tức là chuỗi sẽ bao gồm 255 ký tự.

Mỗi phần tử của một chuỗi có thể được tham chiếu bằng số của nó. Tuy nhiên, đầu vào và đầu ra của chuỗi được thực hiện như một tổng thể chứ không phải từng phần tử, như trường hợp của mảng. Số lượng ký tự được nhập không được vượt quá số ký tự được chỉ định trong kích thước chuỗi tối đa, vì vậy nếu sự vượt quá đó xảy ra, thì các ký tự "thừa" sẽ bị bỏ qua.

2. Thủ tục và hàm cho biến kiểu chuỗi

1. Chức năng Copy (S: String; Index, Count: Integer): String;

Trả về một chuỗi con của một chuỗi. S là một biểu thức của kiểu String.

Index và Count là các biểu thức kiểu số nguyên. Hàm trả về một chuỗi chứa các ký tự Đếm bắt đầu từ vị trí Chỉ mục. Nếu Index lớn hơn độ dài của S, hàm trả về một chuỗi rỗng.

2. Thủ tục Delete (var S: String; Index, Count: Integer);

Loại bỏ một chuỗi ký tự con có độ dài Đếm khỏi chuỗi S, bắt đầu từ vị trí Chỉ mục. S là một biến kiểu String. Index và Count là các biểu thức kiểu số nguyên. Nếu Index lớn hơn độ dài của S, không có ký tự nào bị xóa.

3. Thủ tục Insert (Nguồn: String; var S: String; Index: Integer);

Nối một chuỗi con thành một chuỗi, bắt đầu từ một vị trí được chỉ định. Nguồn là một biểu thức của kiểu Chuỗi. S là một biến Chuỗi có độ dài bất kỳ. Chỉ mục là một biểu thức của kiểu số nguyên. Chèn chèn Nguồn vào S, bắt đầu từ vị trí S [Chỉ mục].

4. Hàm Length (S: String): Integer;

Trả về số ký tự thực sự được sử dụng trong chuỗi S. Lưu ý rằng khi sử dụng chuỗi kết thúc bằng null, số ký tự không nhất thiết phải bằng số byte.

5. Hàm Pos (Substr: String; S: String): Integer;

Tìm kiếm một chuỗi con trong một chuỗi. Pos tìm kiếm Substr trong S và trả về một giá trị nguyên là chỉ số của ký tự đầu tiên của Substr trong S. Nếu không tìm thấy Substr, Pos trả về null.

3. Bản ghi

Một bản ghi là một tập hợp của một số lượng hạn chế các thành phần có liên quan về mặt logic thuộc các kiểu khác nhau. Các thành phần của một bản ghi được gọi là các trường, mỗi trường được xác định bằng một tên. Trường bản ghi chứa tên của trường, theo sau là dấu hai chấm để chỉ ra loại trường. Các trường bản ghi có thể thuộc bất kỳ kiểu nào được phép trong Pascal, ngoại trừ kiểu tệp.

Mô tả một bản ghi trong ngôn ngữ Pascal được thực hiện bằng cách sử dụng từ dịch vụ RECORD, tiếp theo là mô tả các thành phần của bản ghi. Mô tả của mục nhập kết thúc bằng từ dịch vụ END.

Ví dụ: một sổ ghi chép chứa họ, tên viết tắt và số điện thoại, do đó, rất thuận tiện để thể hiện một dòng riêng biệt trong sổ ghi chép dưới dạng mục nhập sau:

gõ Hàng = Bản ghi

FIO: Chuỗi [20];

ĐT: Chuỗi [7];

kết thúc;

var str: Hàng;

Cũng có thể ghi lại các mô tả mà không cần sử dụng tên loại, ví dụ:

var str: Ghi lại

FIO: Chuỗi [20];

ĐT: Chuỗi [7];

kết thúc;

Tham chiếu toàn bộ một bản ghi chỉ được phép trong các câu lệnh gán trong đó các tên bản ghi cùng loại được sử dụng ở bên trái và bên phải của dấu chỉ định. Trong tất cả các trường hợp khác, các trường bản ghi riêng biệt được vận hành. Để tham chiếu đến một thành phần bản ghi riêng lẻ, bạn phải chỉ định tên của bản ghi và được phân tách bằng dấu chấm, chỉ định tên của trường mong muốn. Tên như vậy được gọi là tên ghép. Một thành phần bản ghi cũng có thể là một bản ghi, trong trường hợp này, tên phân biệt sẽ không chứa hai mà là nhiều tên hơn.

Các thành phần bản ghi tham chiếu có thể được đơn giản hóa bằng cách sử dụng toán tử with append. Nó cho phép bạn thay thế các tên ghép đặc trưng cho từng trường bằng các tên trường và xác định tên bản ghi trong câu lệnh nối.

Đôi khi nội dung của một bản ghi riêng lẻ phụ thuộc vào giá trị của một trong các trường của nó. Trong ngôn ngữ Pascal, cho phép mô tả bản ghi, bao gồm phần chung và phần biến thể. Phần biến thể được chỉ định bằng cách sử dụng trường hợp P của cấu trúc, trong đó P là tên của trường từ phần chung của bản ghi. Các giá trị có thể được trường này chấp nhận được liệt kê theo cách tương tự như trong câu lệnh biến thể. Tuy nhiên, thay vì chỉ định hành động cần thực hiện, như được thực hiện trong một câu lệnh biến thể, các trường biến thể được chỉ định trong dấu ngoặc đơn. Mô tả của phần biến thể kết thúc bằng kết thúc từ dịch vụ. Loại trường P có thể được chỉ định trong tiêu đề của phần biến thể. Các bản ghi được khởi tạo bằng các hằng số đã nhập.

4. Bộ

Khái niệm tập hợp trong ngôn ngữ Pascal dựa trên khái niệm toán học về tập hợp: nó là một tập hợp giới hạn của các phần tử khác nhau. Một kiểu dữ liệu được liệt kê hoặc khoảng thời gian được sử dụng để xây dựng một kiểu tập hợp cụ thể. Kiểu của các phần tử tạo nên một tập hợp được gọi là kiểu cơ sở.

Nhiều kiểu được mô tả bằng cách sử dụng Tập hợp các từ chức năng, ví dụ:

loại M = Tập hợp B;

Ở đây M là kiểu số nhiều, B là kiểu cơ sở.

Sự thuộc về các biến đối với kiểu số nhiều có thể được xác định trực tiếp trong phần khai báo biến.

Hằng số kiểu tập hợp được viết dưới dạng một chuỗi các phần tử hoặc phạm vi trong dấu ngoặc vuông của kiểu cơ sở, được phân tách bằng dấu phẩy. Hằng số có dạng [] có nghĩa là một tập hợp con trống.

Một tập hợp bao gồm một tập hợp các phần tử của kiểu cơ sở, tất cả các tập hợp con của tập hợp đã cho và tập hợp con rỗng. Nếu kiểu cơ sở mà tập được xây dựng có K phần tử thì số tập con có trong tập này bằng 2 với lũy thừa của K. Thứ tự liệt kê các phần tử của kiểu cơ sở trong hằng số là không quan trọng. Giá trị của một biến có nhiều kiểu có thể được cho bởi một cấu trúc có dạng [T], trong đó T là một biến của kiểu cơ sở.

Các phép toán gán (: =), union (+), giao (*) và trừ (-) có thể áp dụng cho các biến và hằng số của một kiểu tập hợp. Kết quả của các phép toán này là một giá trị thuộc kiểu số nhiều:

1) ['A', 'B'] + ['A', 'D'] sẽ cho ['A', 'B', 'D'];

2) ['A'] * ['A', 'B', 'C'] sẽ cho ['A'];

3) ['A', 'B', 'C'] - ['A', 'B'] sẽ cho ['C'].

Các phép toán có thể áp dụng cho nhiều giá trị: nhận dạng (=), không nhận dạng (<>), chứa trong (<=), chứa (> =). Kết quả của các phép toán này có kiểu boolean:

1) ['A', 'B'] = ['A', 'C'] sẽ cho FALSE;

2) ['A', 'B'] <> ['A', 'C'] sẽ cho TRUE;

3) ['B'] <= ['B', 'C'] sẽ cho TRUE;

4) ['C', 'D']> = ['A'] sẽ cho kết quả FALSE.

Ngoài các phép toán này, để làm việc với các giá trị của một kiểu đã đặt, phép toán đang sử dụng được sử dụng để kiểm tra xem phần tử của kiểu cơ sở ở bên trái của dấu phép toán có thuộc tập hợp ở bên phải của dấu phép toán hay không. . Kết quả của phép toán này là một boolean. Phép toán kiểm tra xem một phần tử có thuộc một tập hợp hay không thường được sử dụng thay cho các phép toán quan hệ.

Khi nhiều kiểu dữ liệu được sử dụng trong các chương trình, các hoạt động được thực hiện trên các chuỗi bit của dữ liệu. Mỗi giá trị của kiểu bội trong bộ nhớ máy tính tương ứng với một chữ số nhị phân.

Giá trị của nhiều loại không thể là phần tử của danh sách I / O. Trong mỗi lần triển khai cụ thể của trình biên dịch từ ngôn ngữ Pascal, số lượng phần tử của kiểu cơ sở mà tập hợp được xây dựng trên đó bị giới hạn.

Việc khởi tạo nhiều giá trị kiểu được thực hiện bằng cách sử dụng các hằng được định kiểu.

Dưới đây là một số thủ tục để làm việc với bộ.

1. Thủ tục Loại trừ (var S: Tập hợp T; I: T);

Loại bỏ phần tử I khỏi tập S. S là một biến kiểu "set" và I là một biểu thức của kiểu tương thích với kiểu ban đầu của S. Loại trừ (S, I) giống như S: = S - [I] , nhưng tạo ra mã hiệu quả hơn.

2. Thủ tục Bao gồm (var S: Tập hợp T; I: T);

Thêm một phần tử I vào tập S. S là một biến kiểu "set" và I là một biểu thức của một kiểu tương thích với kiểu S. Cấu trúc Bao gồm (S, I) giống như S: = S + [ I], nhưng tạo ra mã hiệu quả hơn.

LECTURE số 6. Các tập tin

1. Tập tin. Hoạt động tệp

Việc đưa loại tệp vào ngôn ngữ Pascal là do nhu cầu cung cấp khả năng làm việc với các thiết bị máy tính ngoại vi (bên ngoài) được thiết kế để nhập, xuất và lưu trữ dữ liệu.

Kiểu dữ liệu tệp (hoặc tệp) xác định một tập hợp có thứ tự của một số thành phần tùy ý của cùng một kiểu. Thuộc tính chung của một mảng, tập hợp và bản ghi là số lượng thành phần của chúng được xác định ở giai đoạn viết chương trình, trong khi số lượng thành phần tệp trong văn bản chương trình không được xác định và có thể là tùy ý.

Khi làm việc với tệp, các hoạt động I / O được thực hiện. Thao tác nhập có nghĩa là truyền dữ liệu từ thiết bị ngoài (từ tệp đầu vào) vào bộ nhớ chính của máy tính, thao tác xuất là chuyển dữ liệu từ bộ nhớ chính ra thiết bị ngoài (sang tệp đầu ra). Các tệp trên thiết bị bên ngoài thường được gọi là tệp vật lý. Tên của chúng được xác định bởi hệ điều hành.

Trong chương trình Pascal, tên tệp được chỉ định bằng cách sử dụng chuỗi. Để làm việc với các tệp trong chương trình, bạn phải xác định một biến tệp. Pascal hỗ trợ ba loại tệp: tệp văn bản, tệp thành phần, tệp không định kiểu.

Các biến tệp được khai báo trong một chương trình được gọi là tệp logic. Tất cả các thủ tục và chức năng cơ bản cung cấp dữ liệu I / O chỉ hoạt động với các tệp logic. Tệp vật lý phải được liên kết với tệp logic trước khi có thể thực hiện các thủ tục mở tệp.

Tệp văn bản

Một vị trí đặc biệt trong ngôn ngữ Pascal bị chiếm bởi các tệp văn bản, các thành phần của chúng có kiểu ký tự. Để mô tả tệp văn bản, ngôn ngữ xác định loại tiêu chuẩn Văn bản:

var TF1, TF2: Văn bản;

Tệp văn bản là một chuỗi các dòng và các dòng là một chuỗi các ký tự. Các dòng có độ dài thay đổi, mỗi dòng kết thúc bằng một dấu chấm cuối dòng.

Tệp thành phần

Một thành phần hoặc tệp được đánh là một tệp có kiểu được khai báo của các thành phần của nó. Các tệp thành phần bao gồm các biểu diễn máy của các giá trị biến; chúng lưu trữ dữ liệu ở dạng giống như bộ nhớ máy tính.

Mô tả của các giá trị loại tệp là:

gõ M = File Of T;

trong đó M là tên của loại tệp;

T - loại thành phần.

Các thành phần tệp có thể là tất cả các kiểu vô hướng và từ các kiểu có cấu trúc - mảng, tập hợp, bản ghi. Trong hầu hết các triển khai cụ thể của ngôn ngữ Pascal, cấu trúc "tệp của tệp" không được phép.

Tất cả các hoạt động trên các tệp thành phần được thực hiện bằng cách sử dụng các thủ tục tiêu chuẩn.

Viết (f, X1, X2, ... XK)

Tệp không định kiểu

Tệp không định kiểu cho phép bạn ghi các phần tùy ý của bộ nhớ máy tính vào đĩa và đọc chúng từ đĩa sang bộ nhớ. Tệp không định kiểu được mô tả như sau:

var f: Tập tin;

Bây giờ chúng tôi liệt kê các thủ tục và chức năng để làm việc với các loại tệp khác nhau.

1. Thủ tục Assign (var F; FileName: String);

Thủ tục AssignFile ánh xạ tên tệp bên ngoài với một biến tệp.

F là biến tệp của bất kỳ loại tệp nào, Tên tệp là biểu thức Chuỗi hoặc biểu thức PChar nếu cú ​​pháp mở rộng được cho phép. Tất cả các hoạt động tiếp theo với F được thực hiện với một tệp bên ngoài.

Bạn không thể sử dụng một thủ tục với một biến tệp đã mở.

2. Thủ tục Đóng (var F);

Thủ tục phá vỡ liên kết giữa biến tệp và tệp đĩa ngoài và đóng tệp.

F là một biến tệp của bất kỳ loại tệp nào, được mở bằng quy trình Đặt lại, Viết lại hoặc Nối. Tệp bên ngoài được liên kết với F được sửa đổi hoàn toàn và sau đó được đóng lại, giải phóng bộ mô tả tệp để sử dụng lại.

Lệnh {SI +} cho phép bạn xử lý các lỗi trong quá trình thực thi chương trình bằng cách sử dụng xử lý ngoại lệ. Khi lệnh {$ 1-} bị tắt, bạn phải sử dụng IOResult để kiểm tra lỗi I / O.

3. Hàm Eof (var F): Boolean;

{Tệp đã nhập hoặc chưa định kiểu}

Hàm Eof [(var F: Text)]: Boolean;

{tệp văn bản}

Kiểm tra xem vị trí tệp hiện tại có phải là phần cuối của tệp hay không.

Eof (F) trả về True nếu vị trí tệp hiện tại nằm sau ký tự cuối cùng của tệp hoặc nếu tệp trống; nếu không thì Eof (F) trả về False.

Lệnh {SI +} cho phép bạn xử lý các lỗi trong quá trình thực thi chương trình bằng cách sử dụng xử lý ngoại lệ. Khi lệnh {SI-} bị tắt, bạn phải sử dụng IOResult để kiểm tra lỗi I / O.

4. Thủ tục Erase (var F);

Xóa tệp bên ngoài được liên kết với F.

F là một biến tệp của bất kỳ loại tệp nào.

Trước khi gọi thủ tục Xóa, tệp phải được đóng.

Lệnh {SI +} cho phép bạn xử lý các lỗi trong quá trình thực thi chương trình bằng cách sử dụng xử lý ngoại lệ. Khi lệnh {SI-} bị tắt, bạn phải sử dụng IOResult để kiểm tra lỗi I / O.

5. Hàm FileSize (var F): Integer;

Trả về kích thước tính bằng byte của tệp F Tuy nhiên, nếu F là tệp đã nhập, FileSize sẽ trả về số lượng bản ghi trong tệp. Tệp phải được mở trước khi sử dụng chức năng FileSize. Nếu tệp trống, FileSize (F) trả về số không. F là một biến của bất kỳ loại tệp nào.

6. chức năng FilePos (varF): LongInt;

Trả về vị trí hiện tại của tệp trong tệp.

Trước khi sử dụng chức năng FilePos, tệp phải được mở. Chức năng FilePos không được sử dụng với các tệp văn bản. F là một biến của bất kỳ loại tệp nào, ngoại trừ kiểu Văn bản.

7. Thủ tục Đặt lại (var F [: File; RecSize: Word]);

Mở tệp hiện có.

F là một biến của bất kỳ loại tệp nào được liên kết với tệp bên ngoài bằng AssignFile. RecSize là một biểu thức tùy chọn được sử dụng nếu F là một tệp không định kiểu. Nếu F là tệp không định kiểu, RecSize xác định kích thước bản ghi được sử dụng khi truyền dữ liệu. Nếu RecSize bị bỏ qua, kích thước bản ghi mặc định là 128 byte.

Thủ tục Đặt lại mở tệp bên ngoài hiện có được liên kết với biến tệp F. Nếu không có tệp bên ngoài nào có tên đó, lỗi thời gian chạy sẽ xảy ra. Nếu tệp được liên kết với F đã được mở, trước tiên nó sẽ được đóng và sau đó được mở lại. Vị trí tệp hiện tại được đặt ở đầu tệp.

8. Thủ tục Viết lại (var F: File [; Recsize: Word]);

Tạo và mở một tệp mới.

F là một biến của bất kỳ loại tệp nào được liên kết với tệp bên ngoài bằng AssignFile. RecSize là một biểu thức tùy chọn được sử dụng nếu F là một tệp không định kiểu. Nếu F là tệp không định kiểu, RecSize xác định kích thước bản ghi được sử dụng khi truyền dữ liệu. Nếu RecSize bị bỏ qua, kích thước bản ghi mặc định là 128 byte.

Thủ tục Ghi lại tạo ra một tệp bên ngoài mới có tên được liên kết với F. Nếu một tệp bên ngoài có cùng tên đã tồn tại, nó sẽ bị xóa và một tệp trống mới được tạo.

9. Thủ tục Seek (var F; N: LongInt);

Di chuyển vị trí tệp hiện tại đến thành phần được chỉ định. Bạn chỉ có thể sử dụng quy trình với các tệp đã nhập hoặc chưa định kiểu đang mở.

Vị trí hiện tại của tệp F được chuyển đến số N. Số thành phần đầu tiên của tệp là 0.

Lệnh Seek (F, FileSize (F)) di chuyển vị trí tệp hiện tại đến cuối tệp.

10. Thủ tục Append (var F: Text);

Mở tệp văn bản hiện có để nối thông tin vào cuối tệp (nối thêm).

Nếu tệp bên ngoài có tên đã cho không tồn tại, lỗi thời gian chạy sẽ xảy ra. Nếu tệp F đã được mở, nó sẽ đóng và mở lại. Vị trí tệp hiện tại được đặt ở cuối tệp.

11.Function Eoln [(var F: Text)]: Boolean;

Kiểm tra xem vị trí tệp hiện tại có phải là cuối dòng trong tệp văn bản hay không.

Eoln (F) trả về True nếu vị trí tệp hiện tại nằm ở cuối dòng hoặc tệp; nếu không thì Eoln (F) trả về False.

12. Thủ tục đọc(F, V1 [, V2,..., Vn]);

{Tệp đã nhập và chưa định kiểu}

Thủ tục Read([var F: Text;] V1 [, V2,..., Vn]);

{tệp văn bản}

Đối với tệp đã nhập, thủ tục đọc thành phần tệp thành một biến. Trên mỗi lần đọc, vị trí hiện tại trong tệp sẽ chuyển sang phần tử tiếp theo.

Đối với tệp văn bản, một hoặc nhiều giá trị được đọc thành một hoặc nhiều biến.

Với các biến kiểu String Read đọc tất cả các ký tự lên đến (nhưng không bao gồm) điểm đánh dấu cuối dòng tiếp theo hoặc cho đến khi Eof (F) đánh giá là True. Chuỗi ký tự kết quả được gán cho biến.

Trong trường hợp một biến kiểu số nguyên hoặc thực, thủ tục chờ một dãy ký tự tạo thành một số theo quy tắc cú pháp Pascal. Việc đọc dừng lại khi gặp phải khoảng trắng, tab hoặc cuối dòng đầu tiên hoặc khi Eof (F) đánh giá là True. Nếu chuỗi số không khớp với định dạng mong đợi, thì lỗi I / O sẽ xảy ra.

13. Thủ tục Readln([var F: Text;] V1 [, V2..., Vn]);

Nó là một phần mở rộng của thủ tục Đọc và được định nghĩa cho các tệp văn bản. Đọc một chuỗi ký tự trong tệp, bao gồm cả điểm đánh dấu cuối dòng và di chuyển đến đầu dòng tiếp theo. Việc gọi hàm Readln (F) không có tham số sẽ di chuyển vị trí tệp hiện tại xuống đầu dòng tiếp theo, nếu có, nếu có, nếu không nó sẽ nhảy xuống cuối tệp.

14. Hàm SeekEof [(var F: Text)]: Boolean;

Trả về phần cuối của tệp và chỉ có thể được sử dụng cho các tệp văn bản đang mở. Thường được sử dụng để đọc các giá trị số từ các tệp văn bản.

15. Hàm SeekEoln [(var F: Text)]: Boolean;

Trả về dấu chấm dứt dòng trong một tệp và chỉ có thể được sử dụng cho các tệp văn bản đang mở. Thường được sử dụng để đọc các giá trị số từ các tệp văn bản.

16. Thủ tục Write([var F: Text;] P1 [, P2,..., Pn]);

{tệp văn bản}

Ghi một hoặc nhiều giá trị vào tệp văn bản.

Mỗi tham số mục nhập phải có kiểu Char, một trong các kiểu số nguyên (Byte, ShortInt, Word, Longint, Cardinal), một trong các kiểu dấu phẩy động (Single, Real, Double, Extended, Currency), một trong các kiểu chuỗi ( PChar, AisiString, ShortString) hoặc một trong các kiểu boolean (Boolean, Bool).

Thủ tục Viết(F,V1,...,Vn);

{Tệp đã nhập}

Ghi một biến vào một thành phần tập tin. Các biến VI...., Vn phải cùng loại với các thành phần tệp. Mỗi khi một biến được ghi, vị trí hiện tại trong tệp sẽ được chuyển sang phần tử tiếp theo.

17. Thủ tục Writeln([var F: Text;] [P1, P2,..., Pn]);

{tệp văn bản}

Thực hiện thao tác Ghi, sau đó đặt điểm đánh dấu cuối dòng trong tệp.

Gọi Writeln (F) không có tham số ghi một điểm đánh dấu cuối dòng vào tệp. Tệp phải được mở để xuất.

2. Mô đun. Các loại mô-đun

Một mô-đun (1Ж1Т) trong Pascal là một thư viện các chương trình con được thiết kế đặc biệt. Một mô-đun, không giống như một chương trình, không thể tự khởi chạy để thực thi, nó chỉ có thể tham gia vào việc xây dựng chương trình và các mô-đun khác. Các mô-đun cho phép bạn tạo thư viện cá nhân về các thủ tục và chức năng cũng như xây dựng các chương trình ở hầu hết mọi kích thước.

Một mô-đun trong Pascal là một đơn vị chương trình được lưu trữ riêng biệt và được biên dịch độc lập. Nói chung, một mô-đun là một tập hợp các tài nguyên phần mềm nhằm mục đích sử dụng cho các chương trình khác. Tài nguyên chương trình được hiểu là bất kỳ phần tử nào của ngôn ngữ Pascal: hằng, kiểu, biến, chương trình con. Bản thân mô-đun không phải là một chương trình thực thi, các phần tử của nó được sử dụng bởi các đơn vị chương trình khác.

Tất cả các phần tử chương trình của mô-đun có thể được chia thành hai phần:

1) các phần tử chương trình được dự định để sử dụng bởi các chương trình hoặc mô-đun khác, các phần tử đó được gọi là có thể nhìn thấy bên ngoài mô-đun;

2) các phần tử phần mềm chỉ cần thiết cho hoạt động của chính mô-đun, chúng được gọi là vô hình (hoặc ẩn).

Phù hợp với điều này, mô-đun, ngoài tiêu đề, chứa ba phần chính, được gọi là giao diện, thực thi và khởi tạo.

Nói chung, một mô-đun có cấu trúc sau:

đơn vị <tên mô-đun>; {Tiêu đề Mô-đun}

giao diện

{mô tả các phần tử chương trình hiển thị của mô-đun}

thực hiện

{mô tả các phần tử lập trình ẩn của mô-đun}

bắt đầu

{câu lệnh khởi tạo phần tử mô-đun}

cuối.

Trong một trường hợp cụ thể, mô-đun có thể không chứa một phần triển khai và một phần khởi tạo, khi đó cấu trúc mô-đun sẽ như sau:

đơn vị <tên mô-đun>; {Tiêu đề Mô-đun}

giao diện

{mô tả các phần tử chương trình hiển thị của mô-đun}

thực hiện

cuối.

Việc sử dụng các thủ tục và chức năng trong các mô-đun có những đặc thù riêng của nó. Phần đầu chương trình con chứa tất cả các thông tin cần thiết để gọi nó: tên, danh sách và kiểu tham số, kiểu kết quả cho các hàm. Thông tin này phải có sẵn cho các chương trình và mô-đun khác. Mặt khác, văn bản của chương trình con thực hiện thuật toán của nó không thể được sử dụng bởi các chương trình và mô-đun khác. Do đó, các tiêu đề của các thủ tục và chức năng được đặt trong phần giao diện của mô-đun, và văn bản được đặt trong phần thực hiện.

Phần giao diện của mô-đun chỉ chứa các tiêu đề hiển thị (có thể truy cập được đối với các chương trình và mô-đun khác) của các thủ tục và chức năng (không có từ dịch vụ chuyển tiếp). Văn bản đầy đủ của thủ tục hoặc hàm được đặt trong phần triển khai và tiêu đề có thể không chứa danh sách các tham số chính thức.

Mã nguồn của mô-đun phải được biên dịch bằng lệnh Make của menu con Biên dịch và được ghi vào đĩa. Kết quả của quá trình biên dịch mô-đun là một tệp có phần mở rộng. TPU (Đơn vị Turbo Pascal). Tên cơ sở của mô-đun được lấy từ tiêu đề của mô-đun.

Để kết nối một mô-đun với chương trình, bạn phải chỉ định tên của nó trong phần mô tả mô-đun, ví dụ:

sử dụng Crt, Graph;

Trong trường hợp tên của các biến trong phần giao diện của mô-đun và trong chương trình sử dụng mô-đun này giống nhau, thì tham chiếu sẽ là biến được mô tả trong chương trình. Để tham chiếu đến một biến được khai báo trong một mô-đun, bạn phải sử dụng một tên ghép bao gồm tên mô-đun và tên biến, được phân tách bằng dấu chấm. Việc sử dụng tên ghép không chỉ áp dụng cho các tên biến mà còn cho tất cả các tên được khai báo trong phần giao diện của mô-đun.

Sử dụng đệ quy các mô-đun bị cấm.

Nếu một mô-đun có một phần khởi tạo, thì các câu lệnh trong phần đó sẽ được thực thi trước khi chương trình sử dụng mô-đun đó bắt đầu thực thi.

Hãy liệt kê các loại mô-đun.

1. Môđun HỆ THỐNG.

Mô-đun HỆ THỐNG thực hiện các quy trình hỗ trợ cấp thấp hơn cho tất cả các tính năng tích hợp sẵn như I / O, thao tác chuỗi, hoạt động dấu phẩy động và cấp phát bộ nhớ động.

Mô-đun HỆ THỐNG chứa tất cả các quy trình và chức năng Pascal tiêu chuẩn và tích hợp sẵn. Bất kỳ chương trình con Pascal nào không phải là một phần của Pascal chuẩn và không được tìm thấy trong bất kỳ mô-đun nào khác đều được chứa trong mô-đun Hệ thống. Mô-đun này được sử dụng tự động trong tất cả các chương trình và không cần phải chỉ định trong câu lệnh use.

2. Mô-đun DOS.

Mô-đun Dos thực hiện nhiều quy trình và chức năng Pascal tương đương với các lệnh gọi DOS thông dụng nhất, chẳng hạn như GetTime, SetTime, DiskSize, v.v.

3. Mô-đun CRT.

Mô-đun CRT triển khai một số chương trình mạnh mẽ cung cấp toàn quyền kiểm soát các tính năng của PC như điều khiển chế độ màn hình, mã bàn phím mở rộng, màu sắc, cửa sổ và âm thanh. Mô-đun CRT chỉ có thể được sử dụng trong các chương trình chạy trên máy tính cá nhân IBM PC, PC AT, PS / 2 của IBM và hoàn toàn tương thích với chúng.

Một trong những lợi thế chính của việc sử dụng mô-đun CRT là tốc độ và tính linh hoạt cao hơn trong các hoạt động trên màn hình. Các chương trình không hoạt động với mô-đun CRT hiển thị thông tin trên màn hình bằng hệ điều hành DOS, được liên kết với chi phí bổ sung. Khi sử dụng mô-đun CRT, thông tin đầu ra được gửi trực tiếp đến hệ thống đầu vào / đầu ra cơ bản (BIOS) hoặc, đối với các hoạt động thậm chí nhanh hơn, trực tiếp đến bộ nhớ video.

4. Mô-đun ĐỒ HỌA.

Sử dụng các thủ tục và chức năng có trong mô-đun này, bạn có thể tạo các đồ họa khác nhau trên màn hình.

5. Mô-đun OVERLAY.

Mô-đun OVERLAY cho phép bạn giảm yêu cầu bộ nhớ của chương trình DOS chế độ thực. Trên thực tế, có thể viết chương trình vượt quá tổng dung lượng bộ nhớ khả dụng, vì chỉ một phần chương trình sẽ ở trong bộ nhớ tại bất kỳ thời điểm nào.

LECTURE № 7. Bộ nhớ động

1. Kiểu dữ liệu tham chiếu. bộ nhớ động. Biến động

Biến static (cấp phát tĩnh) là một biến được khai báo rõ ràng trong chương trình, nó được gọi bằng tên. Vị trí trong bộ nhớ để đặt các biến tĩnh được xác định khi chương trình được biên dịch. Không giống như các biến tĩnh như vậy, các chương trình Pascal có thể tạo ra các biến động. Thuộc tính chính của các biến động là chúng được tạo ra và bộ nhớ được cấp cho chúng trong quá trình thực thi chương trình.

Các biến động được đặt trong một vùng nhớ động (heap-area). Một biến động không được chỉ định rõ ràng trong khai báo biến và không thể được gọi bằng tên. Các biến như vậy được truy cập bằng cách sử dụng con trỏ và tham chiếu.

Một kiểu tham chiếu (con trỏ) xác định một tập hợp các giá trị trỏ đến các biến động của một kiểu cụ thể, được gọi là kiểu cơ sở. Một biến kiểu tham chiếu chứa địa chỉ của một biến động trong bộ nhớ. Nếu kiểu cơ sở là một định danh chưa được khai báo, thì nó phải được khai báo trong cùng một phần của khai báo kiểu với kiểu con trỏ.

Từ dành riêng nil biểu thị một hằng số có giá trị con trỏ không trỏ đến bất kỳ thứ gì.

Hãy để chúng tôi đưa ra một ví dụ về mô tả của các biến động.

var p1, p2: ^ real;

p3, p4: ^ số nguyên;

2. Làm việc với bộ nhớ động. Con trỏ không định dạng

Các thủ tục và chức năng của bộ nhớ động

1. Thủ tục Mới (var p: Pointer).

Phân bổ không gian trong vùng bộ nhớ động để chứa biến động pЛ, và gán địa chỉ của nó cho con trỏ p.

2. Thủ tục Dispose (varp: Pointer).

Giải phóng bộ nhớ được cấp phát cho việc cấp phát biến động bằng thủ tục Mới và giá trị của con trỏ p trở nên không xác định.

3. Thủ tục GetMem (varp: Pointer; size: Word).

Phân bổ một phần bộ nhớ trong vùng heap, gán địa chỉ phần đầu của nó cho con trỏ p, kích thước của phần tính bằng byte được chỉ định bởi tham số kích thước.

4. Thủ tục FreeMem (var p: Pointer; size: Word).

Giải phóng vùng bộ nhớ, địa chỉ đầu của vùng này được chỉ định bởi con trỏ p và kích thước được chỉ định bởi tham số size. Giá trị của con trỏ p trở nên không xác định.

5. Dấu thủ tục (var p: Pointer)

Ghi tới con trỏ p địa chỉ của phần đầu của phần bộ nhớ động miễn phí tại thời điểm gọi của nó.

6. Giải phóng thủ tục (var p: Pointer)

Giải phóng một phần của bộ nhớ động, bắt đầu từ địa chỉ được ghi vào con trỏ p bởi thủ tục Mark, tức là xóa bộ nhớ động đã bị chiếm sau lệnh gọi đến thủ tục Mark.

7. Hàm MaxAvaikLongint

Trả về độ dài, tính bằng byte, của heap miễn phí dài nhất.

8. Hàm MemAvaikLongint

Trả về tổng dung lượng bộ nhớ động miễn phí tính bằng byte.

9. Chức năng trợ giúp SizeOf (X): Word

Trả về số lượng byte bị chiếm bởi X, trong đó X có thể là tên biến của bất kỳ kiểu nào hoặc tên kiểu.

Con trỏ kiểu tích hợp biểu thị một con trỏ không có kiểu, có nghĩa là, một con trỏ không trỏ đến bất kỳ kiểu cụ thể nào. Các biến kiểu Con trỏ có thể được tham chiếu: chỉ định ký tự ^ sau khi một biến như vậy gây ra lỗi.

Giống như giá trị được biểu thị bằng nil, các giá trị của con trỏ tương thích với tất cả các loại con trỏ khác.

LECTURE № 8. Cấu trúc dữ liệu trừu tượng

1. Cấu trúc dữ liệu trừu tượng

Các kiểu dữ liệu có cấu trúc, chẳng hạn như mảng, tập hợp và bản ghi, là cấu trúc tĩnh vì kích thước của chúng không thay đổi trong toàn bộ quá trình thực thi chương trình.

Các cấu trúc dữ liệu thường được yêu cầu thay đổi kích thước của chúng trong quá trình giải quyết một vấn đề. Cấu trúc dữ liệu như vậy được gọi là động. Chúng bao gồm ngăn xếp, hàng đợi, danh sách, cây, v.v.

Mô tả cấu trúc động sử dụng mảng, bản ghi và tệp dẫn đến việc sử dụng lãng phí bộ nhớ máy tính và làm tăng thời gian giải quyết vấn đề.

Mỗi thành phần của bất kỳ cấu trúc động nào đều là một bản ghi chứa ít nhất hai trường: một trường thuộc loại "con trỏ" và trường thứ hai - dành cho vị trí dữ liệu. Nói chung, một bản ghi có thể không chứa một mà là một số con trỏ và một số trường dữ liệu. Trường dữ liệu có thể là một biến, một mảng, một tập hợp hoặc một bản ghi.

Nếu phần trỏ chứa địa chỉ của một phần tử của danh sách, thì danh sách được gọi là một hướng (hoặc liên kết đơn). Nếu nó chứa hai thành phần, thì nó được kết nối kép. Bạn có thể thực hiện các thao tác khác nhau trên danh sách, ví dụ:

1) thêm một phần tử vào danh sách;

2) xóa một phần tử khỏi danh sách bằng một khóa cho trước;

3) tìm kiếm một phần tử có giá trị cho trước của trường khóa;

4) sắp xếp các phần tử của danh sách;

5) chia danh sách thành hai hoặc nhiều danh sách;

6) kết hợp hai hoặc nhiều danh sách thành một;

7) các hoạt động khác.

Tuy nhiên, như một quy luật, không phát sinh nhu cầu về tất cả các thao tác trong việc giải quyết các vấn đề khác nhau. Vì vậy, tùy theo các thao tác cơ bản cần áp dụng mà có các loại danh sách khác nhau. Phổ biến nhất trong số này là ngăn xếp và hàng đợi.

2. Ngăn xếp

Ngăn xếp là một cấu trúc dữ liệu động, việc bổ sung một thành phần vào đó và loại bỏ một thành phần được tạo ra từ một đầu, được gọi là đỉnh của ngăn xếp. Ngăn xếp hoạt động theo nguyên tắc LIFO (Last-In, First-Out) - "Nhập trước, xuất trước".

Thường có ba hoạt động được thực hiện trên ngăn xếp:

1) sự hình thành ban đầu của ngăn xếp (bản ghi của thành phần đầu tiên);

2) thêm một thành phần vào ngăn xếp;

3) lựa chọn thành phần (xóa).

Để tạo một ngăn xếp và làm việc với nó, bạn phải có hai biến kiểu "con trỏ", biến thứ nhất xác định đỉnh của ngăn xếp và biến thứ hai là phụ trợ.

Thí dụ. Viết một chương trình tạo thành một ngăn xếp, thêm một số thành phần tùy ý vào nó, sau đó đọc tất cả các thành phần và hiển thị chúng trên màn hình hiển thị. Lấy một chuỗi ký tự làm dữ liệu. Nhập dữ liệu - từ bàn phím, một dấu hiệu kết thúc nhập - một chuỗi ký tự KẾT THÚC.

Chương trình STACK;

sử dụng Crt;

kiểu

Alpha = Chuỗi [10];

PComp = ^ Comp;

Comp = bản ghi

SD: Alfa

pNext: PComp

kết thúc;

pTop: PComp;

sc: Alfa;

Tạo ProcedureStack (var pTop: PComp; var sC: Alfa);

bắt đầu

Mới (pTop);

pTop ^ .pNext: = NIL;

pTop ^ .sD: = sC;

kết thúc;

Thêm ProcedureComp (var pTop: PComp; var sC: Alfa);

var pAux: PComp;

bắt đầu

MỚI (pAux);

pAux ^ .pNext: = pTop;

pTop: = pAux;

pTop ^ .sD: = sC;

kết thúc;

Thủ tục DelComp (var pTop: PComp; var sC: ALFA);

bắt đầu

sC: = pTop ^ .sD;

pTop: = pTop ^ .pNext;

kết thúc;

bắt đầu

Clrscr;

writeln ('NHẬP STRING');

readln (sC);

CreateStack (pTop, sc);

lặp lại

writeln ('NHẬP STRING');

readln (sC);

AddComp (pTop, sc);

cho đến khi sC = 'END';

writeln ('****** ĐẦU RA ******');

lặp lại

DelComp (pTop, sc);

writeln (sC);

cho đến khi pTop = NIL;

cuối.

3. Hàng đợi

Hàng đợi là một cấu trúc dữ liệu động, nơi một thành phần được thêm vào ở một đầu và được truy xuất ở đầu kia. Hàng đợi hoạt động theo nguyên tắc FIFO (First-In, First-Out) - "Nhập trước, phục vụ trước".

Để hình thành một hàng đợi và làm việc với nó, cần có ba biến kiểu con trỏ, biến thứ nhất xác định điểm bắt đầu của hàng đợi, biến thứ hai - cuối hàng đợi, biến thứ ba - phụ trợ.

Thí dụ. Viết một chương trình tạo thành một hàng đợi, thêm một số thành phần tùy ý vào nó, sau đó đọc tất cả các thành phần và hiển thị chúng trên màn hình hiển thị. Lấy một chuỗi ký tự làm dữ liệu. Nhập dữ liệu - từ bàn phím, một dấu hiệu kết thúc nhập - một chuỗi ký tự KẾT THÚC.

ProgramQUEUE;

sử dụng Crt;

kiểu

Alpha = Chuỗi [10];

PComp = ^ Comp;

Comp = bản ghi

SD: Alfa

pNext: PComp;

kết thúc;

pBegin, pEnd: PComp;

sc: Alfa;

Tạo Thủ tụcQueue (var pBegin, pEnd: PComp; var sC: Alfa);

bắt đầu

Mới (pBegin);

pBegin ^ .pNext: = NIL;

pBegin ^ .sD: = sC;

pEnd: = pBegin;

kết thúc;

Thủ tục Thêm Thủ tụcQueue (var pEnd: PComp; var sC: Alfa);

var pAux: PComp;

bắt đầu

Mới (pAux);

pAux ^ .pNext: = NIL;

pEnd ^ .pNext: = pAux;

pEnd: = pAux;

pEnd ^ .sD: = sC;

kết thúc;

Thủ tục DelQueue (var pBegin: PComp; var sC: Alfa);

bắt đầu

sC: = pBegin ^ .sD;

pBegin: = pBegin ^ .pNext;

kết thúc;

bắt đầu

Clrscr;

writeln ('NHẬP STRING');

readln (sC);

CreateQueue (pBegin, pEnd, sc);

lặp lại

writeln ('NHẬP STRING');

readln (sC);

AddQueue (pEnd, sc);

cho đến khi sC = 'END';

writeln ('***** HIỂN THỊ KẾT QUẢ *****');

lặp lại

DelQueue (pBegin, sc);

writeln (sC);

cho đến khi pBegin = NIL;

cuối.

KIẾN TRÚC SỐ 9. Cấu trúc dữ liệu dạng cây

1. Cấu trúc dữ liệu cây

Cấu trúc dữ liệu dạng cây là một tập hợp hữu hạn các phần tử-nút giữa chúng có các mối quan hệ - kết nối giữa nguồn và phần tử được tạo ra.

Nếu chúng ta sử dụng định nghĩa đệ quy do N. Wirth đề xuất, thì một cấu trúc dữ liệu cây với kiểu cơ sở t là một cấu trúc rỗng hoặc một nút của kiểu t, trong đó một tập hữu hạn cấu trúc cây với kiểu cơ sở t, được gọi là cây con, là có liên quan.

Tiếp theo, chúng tôi đưa ra các định nghĩa được sử dụng khi hoạt động với cấu trúc cây.

Nếu nút y nằm ngay bên dưới nút x thì nút y được gọi là hậu duệ trực tiếp của nút x và x là tổ tiên trực tiếp của nút y, tức là nếu nút x ở cấp thứ i thì nút y tương ứng tại (i + 1) - cấp thứ.

Mức tối đa của một nút cây được gọi là chiều cao hoặc chiều sâu của cây. Tổ tiên không chỉ có một nút của cây - gốc của nó.

Các nút cây không có con gọi là nút lá (hay lá cây). Tất cả các nút khác được gọi là nút bên trong. Số lượng nút con ngay lập tức xác định cấp độ của nút đó và cấp độ tối đa có thể có của một nút trong một cây nhất định xác định cấp độ của cây.

Tổ tiên và con cháu không thể thay thế cho nhau, tức là mối liên hệ giữa nguyên bản và tác phẩm được tạo ra chỉ hoạt động theo một hướng.

Nếu bạn đi từ gốc của cây đến một nút cụ thể nào đó, thì số nhánh của cây sẽ được đi ngang trong trường hợp này được gọi là độ dài của đường dẫn cho nút này. Nếu tất cả các nhánh (nút) của một cây được đặt hàng, thì cây đó được cho là đã được sắp xếp theo thứ tự.

Cây nhị phân là một trường hợp đặc biệt của cấu trúc cây. Đây là những cây mà mỗi cây có nhiều nhất hai con, gọi là cây con trái và cây con phải. Do đó, cây nhị phân là một cấu trúc cây có bậc là hai.

Thứ tự của cây nhị phân được xác định theo quy tắc sau: mỗi nút có trường khóa riêng và đối với mỗi nút, giá trị khóa lớn hơn tất cả các khóa trong cây con bên trái của nó và nhỏ hơn tất cả các khóa trong cây con bên phải của nó.

Cây có độ lớn hơn hai được gọi là phân cành mạnh.

2. Hoạt động trên cây

Hơn nữa, chúng ta sẽ xem xét tất cả các phép toán liên quan đến cây nhị phân.

I. Cấu tạo cây

Chúng tôi trình bày một thuật toán để xây dựng một cây có thứ tự.

1. Nếu cây trống, thì dữ liệu được chuyển đến gốc của cây. Nếu cây không trống, thì một trong các nhánh của nó bị hạ xuống sao cho trật tự của cây không bị vi phạm. Kết quả là, nút mới trở thành lá tiếp theo của cây.

2. Để thêm một nút vào cây đã có, bạn có thể sử dụng thuật toán trên.

3. Khi xóa một nút khỏi cây, bạn nên cẩn thận. Nếu nút cần loại bỏ là một lá hoặc chỉ có một nút con, thì thao tác rất đơn giản. Nếu nút bị xóa có hai nút con, thì cần phải tìm một nút trong số các nút con có thể được đặt vào vị trí của nó. Điều này là cần thiết do yêu cầu của cây được đặt hàng.

Bạn có thể thực hiện điều này: hoán đổi nút cần xóa với nút có giá trị khóa lớn nhất trong cây con bên trái hoặc với nút có giá trị khóa nhỏ nhất trong cây con bên phải, rồi xóa nút mong muốn dưới dạng lá.

II. Tìm một nút có giá trị trường khóa nhất định

Khi thực hiện thao tác này, cần phải đi ngang cây. Cần phải tính đến các hình thức viết cây khác nhau: tiền tố, tiền tố và hậu tố.

Câu hỏi đặt ra: làm thế nào để biểu diễn các nút của cây sao cho thuận tiện nhất khi làm việc với chúng? Có thể biểu diễn cây bằng cách sử dụng một mảng, trong đó mỗi nút được mô tả bằng một giá trị của kiểu kết hợp, có một trường thông tin kiểu ký tự và hai trường kiểu tham chiếu. Nhưng điều này không thuận tiện lắm, vì cây có một số lượng lớn các nút không được xác định trước. Vì vậy, tốt nhất là sử dụng các biến động khi mô tả một cây. Sau đó, mỗi nút được biểu diễn bằng một giá trị cùng kiểu, chứa mô tả về một số trường thông tin nhất định, và số trường tương ứng phải bằng mức độ của cây. Nó là hợp lý để xác định sự vắng mặt của con cháu bằng nil. Sau đó, trong Pascal, mô tả của một cây nhị phân có thể trông như thế này:

TYPE TreeLink = ^ Cây;

cây = bản ghi;

Inf: <datatype>;

Trái, phải: TreeLink;

Kết thúc

3. Ví dụ về việc thực hiện các hoạt động

1. Xây dựng một cây gồm n nút có chiều cao tối thiểu, hoặc một cây hoàn toàn cân bằng (số nút của cây con bên trái và bên phải của một cây như vậy không được chênh lệch nhau quá một).

Thuật toán xây dựng đệ quy:

1) nút đầu tiên được lấy làm gốc của cây.

2) cây con bên trái của nl nút được xây dựng theo cách tương tự.

3) cây con bên phải của nr nút được xây dựng theo cách tương tự;

nr = n - nl - 1. Là một trường thông tin, chúng ta sẽ lấy các số nút được nhập từ bàn phím. Hàm đệ quy thực hiện cấu trúc này sẽ giống như sau:

Cây hàm (n: Byte): TreeLink;

Var t: TreeLink; nl, nr, x: Byte;

Bắt đầu

Nếu n = 0 thì Tree: = nil

Khác

Bắt đầu

nl: = n div 2;

nr = n - nl - 1;

writeln ('Nhập số đỉnh');

readln (x);

mới (t);

t ^ .inf: = x;

t ^ .left: = Tree (nl);

t ^ .right: = Tree (nr);

Cây: = t;

Kết thúc;

{Cây}

Kết thúc

2. Trong cây có thứ tự nhị phân, tìm nút có giá trị cho trước của trường khóa. Nếu không có yếu tố này trong cây, thì hãy thêm nó vào cây.

Thủ tục Tìm kiếm (x: Byte; var t: TreeLink);

Bắt đầu

Nếu t = nil thì

Bắt đầu

Mới (t);

t ^ inf: = x;

t ^ .left: = nil;

t ^ .right: = nil;

Kết thúc

Khác nếu x <t ^ .inf thì

Tìm kiếm (x, t ^ .left)

Khác nếu x> t ^ .inf thì

Tìm kiếm (x, t ^ .right)

Khác

Bắt đầu

{phần tử tìm thấy quá trình}

hữu ích. Cảm ơn !

Kết thúc;

Kết thúc

3. Viết các quy trình duyệt cây theo thứ tự thuận, đối xứng và ngược lại.

3.1. Thủ tục đặt hàng trước (t: TreeLink);

Bắt đầu

Nếu t <> nil thì

Bắt đầu

WriteIn (t ^ .inf);

Đặt hàng trước (t ^ .left);

Đặt hàng trước (t ^ .right);

Kết thúc;

Kết thúc;

3.2. Thủ tục Inorder (t: TreeLink);

Bắt đầu

Nếu t <> nil thì

Bắt đầu

Inorder (t ^ .left);

WriteIn (t ^ .inf);

Inorder (t ^ .right);

Kết thúc;

Kết thúc

3.3. Thủ tục Postorder (t: TreeLink);

Bắt đầu

Nếu t <> nil thì

Bắt đầu

postorder (t ^ .left);

postorder (t ^ .right);

WriteIn (t ^ .inf);

Kết thúc;

Kết thúc

4. Trong cây có thứ tự nhị phân, xóa nút có giá trị cho trước của trường khóa.

Hãy để chúng tôi mô tả một thủ tục đệ quy sẽ tính đến sự hiện diện của phần tử bắt buộc trong cây và số lượng con của nút này. Nếu nút bị xóa có hai nút con, thì nút đó sẽ được thay thế bằng giá trị khóa lớn nhất trong cây con bên trái của nó, và chỉ khi đó nó mới bị xóa vĩnh viễn.

Thủ tục Delete1 (x: Byte; var t: TreeLink);

Var p: TreeLink;

Thủ tục Delete2 (var q: TreeLink);

Bắt đầu

Nếu q ^ .right <> nil thì Delete2 (q ^ .right)

Khác

Bắt đầu

p ^ .inf: = q ^ .inf;

p: = q;

q: = q ^ .left;

Kết thúc;

Kết thúc;

Bắt đầu

Nếu t = nil thì

Writeln ('không tìm thấy phần tử')

Khác nếu x <t ^ .inf thì

Delete1 (x, t ^ .left)

Khác nếu x> t ^ .inf thì

Delete1 (x, t ^ .right)

Khác

Bắt đầu

P: = t;

Nếu p ^ .left = nil thì

t: = p ^ .right

Khác

Nếu p ^ .right = nil thì

t: = p ^ .left

Khác

Delete2 (p ^ .left);

Kết thúc;

Kết thúc

LECTURE số 10. Số đếm

1. Khái niệm về đồ thị. Các cách biểu diễn đồ thị

Đồ thị là một cặp G = (V, E), trong đó V là tập các đối tượng có tính chất tùy ý, được gọi là các đỉnh, và E là một họ các cặp ei = (vil, vi2), vijOV, được gọi là các cạnh. Trong trường hợp tổng quát, tập V và / hoặc họ E có thể chứa vô số phần tử, nhưng chúng ta sẽ chỉ xem xét các đồ thị hữu hạn, tức là các đồ thị mà cả V và E đều hữu hạn. Nếu thứ tự của các phần tử có trong ei quan trọng, thì đồ thị được gọi là có hướng, viết tắt là đồ thị, nếu không nó được gọi là vô hướng. Các cạnh của một đồ thị được gọi là cung tròn. Theo những gì sau đây, chúng tôi giả định rằng thuật ngữ "đồ thị", được sử dụng mà không có đặc điểm kỹ thuật (có hướng hoặc vô hướng), biểu thị một đồ thị vô hướng.

Nếu e = , khi đó các đỉnh v và u được gọi là các điểm cuối của cạnh. Ở đây chúng ta nói rằng cạnh e là kề (sự cố) với mỗi đỉnh v và u. Đỉnh v và và còn được gọi là kề (sự cố). Trong trường hợp tổng quát, các cạnh có dạng e = ; các cạnh như vậy được gọi là vòng lặp.

Bậc của một đỉnh trong đồ thị là số cạnh gắn với đỉnh đó, với các vòng lặp được tính hai lần. Vì mỗi cạnh liên tiếp với hai đỉnh nên tổng bậc của tất cả các đỉnh trong đồ thị bằng hai lần số cạnh: Sum(deg(vi), i=1...|V|) = 2 * | E|.

Trọng số của nút là một số (thực, số nguyên hoặc số hữu tỷ) được gán cho một nút nhất định (được hiểu là chi phí, thông lượng, v.v.). Trọng lượng, chiều dài cạnh - một số hoặc một số con số được hiểu là chiều dài, băng thông, v.v.

Đường đi trong đồ thị (hoặc đường đi trong đồ thị) là một dãy xen kẽ các đỉnh và cạnh (hoặc cung trong đồ thị) có dạng v0, (v0,v1), v1..., (vn - 1,vn ), vn. Số n được gọi là độ dài đường đi. Một đường đi không có các cạnh lặp lại được gọi là một chuỗi; một đường đi không có các đỉnh lặp lại được gọi là một chuỗi đơn. Đường dẫn có thể đóng lại (v0=vn). Một đường đi khép kín không có các cạnh lặp lại được gọi là một chu trình (hoặc đường viền trong sơ đồ); không lặp lại các đỉnh (ngoại trừ đỉnh đầu tiên và đỉnh cuối cùng) - một vòng lặp đơn giản.

Một đồ thị được gọi là liên thông nếu có một đường đi giữa hai đỉnh bất kỳ của nó và bị ngắt kết nối nếu không. Một đồ thị bị ngắt kết nối bao gồm một số thành phần được kết nối (đồ thị con được kết nối).

Có nhiều cách khác nhau để biểu diễn đồ thị. Chúng ta hãy xem xét từng người trong số họ một cách riêng biệt.

1. Ma trận tỷ lệ.

Đây là ma trận hình chữ nhật có kích thước n x n, trong đó n là số đỉnh, am là số cạnh. Giá trị của các phần tử ma trận được xác định như sau: nếu cạnh xi và đỉnh vj liên tiếp nhau thì giá trị của phần tử ma trận tương ứng bằng 1, ngược lại giá trị bằng 1. Đối với đồ thị có hướng, ma trận tỷ lệ được xây dựng theo nguyên tắc sau: giá trị của phần tử bằng -XNUMX nếu cạnh xi xuất phát từ đỉnh vj, bằng XNUMX nếu cạnh xi đi vào đỉnh vj, và bằng XNUMX nếu ngược lại .

2. Ma trận kề.

Đây là ma trận vuông có kích thước n x n, trong đó n là số đỉnh. Nếu các đỉnh vi và vj liền kề nhau, nghĩa là nếu có một cạnh nối chúng thì phần tử ma trận tương ứng bằng 1, nếu không thì bằng 0. Các quy tắc xây dựng ma trận này cho đồ thị có hướng và đồ thị vô hướng không khác nhau. Ma trận kề nhỏ gọn hơn ma trận tỷ lệ. Cần lưu ý rằng ma trận này cũng rất thưa thớt, nhưng trong trường hợp đồ thị vô hướng, nó đối xứng với đường chéo chính, do đó bạn có thể lưu trữ không phải toàn bộ ma trận mà chỉ một nửa của nó (ma trận tam giác ).

3. Danh sách các adjacencies (sự cố).

Nó là một cấu trúc dữ liệu lưu trữ danh sách các đỉnh liền kề cho mỗi đỉnh đồ thị. Danh sách là một mảng các con trỏ, phần tử thứ i của nó chứa một con trỏ đến danh sách các đỉnh liền kề với đỉnh thứ i.

Một danh sách kề hiệu quả hơn ma trận kề vì nó loại bỏ việc lưu trữ các phần tử rỗng.

4. Danh sách các danh sách.

Nó là một cấu trúc dữ liệu dạng cây, trong đó một nhánh chứa danh sách các đỉnh kề với mỗi đỉnh đồ thị và nhánh thứ hai trỏ đến đỉnh đồ thị tiếp theo. Cách biểu diễn đồ thị này là tối ưu nhất.

2. Biểu diễn đồ thị bằng danh sách tỷ lệ. Thuật toán truyền tải độ sâu của đồ thị

Để triển khai biểu đồ dưới dạng danh sách tỷ lệ, bạn có thể sử dụng loại sau:

TypeList = ^ S;

S = bản ghi;

inf: Byte;

tiếp theo: Danh sách;

kết thúc;

Sau đó, đồ thị được xác định như sau:

Var Gr: array [1..n] of List;

Bây giờ chúng ta hãy chuyển sang thủ tục duyệt đồ thị. Đây là một thuật toán bổ trợ cho phép bạn xem tất cả các đỉnh của đồ thị, phân tích tất cả các trường thông tin. Nếu chúng ta xem xét chiều sâu của một đường truyền đồ thị, thì có hai loại thuật toán: đệ quy và không đệ quy.

Với thuật toán duyệt theo độ sâu đệ quy đệ quy, chúng ta lấy một đỉnh tùy ý và tìm một đỉnh v (mới) không nhìn thấy tùy ý liền kề với nó. Sau đó, chúng tôi lấy đỉnh v không phải là mới và tìm bất kỳ đỉnh mới nào liền kề với nó. Nếu một số đỉnh không có các đỉnh mới hơn chưa được nhìn thấy, thì chúng ta coi đỉnh này đã được sử dụng và trả về một mức cao hơn cho đỉnh mà từ đó chúng ta đã đến đỉnh đã sử dụng của chúng ta. Việc di chuyển tiếp tục theo cách này cho đến khi không có đỉnh mới chưa quét nào trong đồ thị.

Trong Pascal, thủ tục duyệt theo chiều sâu sẽ giống như sau:

Thủ tục Obhod (gr: Graph; k: Byte);

Var g: Đồ thị; l: Danh sách;

Bắt đầu

nov [k]: = false;

g: = gr;

Trong khi g ^ .inf <> k làm

g: = g ^ .next;

l: = g ^ .smeg;

While l <> nil do begin

If nov [l ^ .inf] then Obhod (gr, l ^ .inf);

l: = l ^ .next;

Kết thúc;

Kết thúc;

Ghi

Trong quy trình này, khi mô tả kiểu Biểu đồ, chúng tôi muốn nói đến việc mô tả một biểu đồ bằng một danh sách các danh sách. Mảng nov [i] là một mảng đặc biệt có phần tử thứ i là True nếu đỉnh thứ i không được thăm và nếu không thì là False.

Thuật toán duyệt không đệ quy cũng thường được sử dụng. Trong trường hợp này, đệ quy được thay thế bằng một ngăn xếp. Khi một đỉnh đã được xem, nó được đẩy lên ngăn xếp và nó sẽ được sử dụng khi không còn đỉnh mới nào liền kề với nó.

3. Biểu diễn đồ thị bằng bảng liệt kê. Thuật toán duyệt đồ thị bề rộng

Một biểu đồ có thể được xác định bằng cách sử dụng một danh sách các danh sách như sau:

TypeList = ^ Danh sách;

tlist = bản ghi

inf: Byte;

tiếp theo: Danh sách;

kết thúc;

Đồ thị = ^ TGpaph;

TGpaph = bản ghi

inf: Byte;

smeg: Danh sách;

tiếp theo: Đồ thị;

kết thúc;

Khi duyệt đồ thị theo chiều rộng, chúng ta chọn một đỉnh tùy ý và xem xét tất cả các đỉnh kề với nó cùng một lúc. Một hàng đợi được sử dụng thay vì một ngăn xếp. Thuật toán tìm kiếm theo chiều rộng rất tiện dụng để tìm đường đi ngắn nhất trong biểu đồ.

Đây là một quy trình để duyệt qua một biểu đồ theo chiều rộng trong mã giả:

Thủ tục Obhod2 (v);

{giá trị spisok, nov - toàn cầu}

Bắt đầu

hàng đợi = O;

hàng đợi <= v;

nov [v] = Sai;

Trong khi hàng đợi <> O do

Bắt đầu

p <= hàng đợi;

For u in spisok (p) do

Nếu mới [u] thì

Bắt đầu

nov [u]: = Sai;

hàng đợi <= u;

Kết thúc;

Kết thúc;

Kết thúc;

LECTURE # 11. Kiểu dữ liệu đối tượng

1. Kiểu đối tượng trong Pascal. Khái niệm về một đối tượng, mô tả và sử dụng nó

Trong lịch sử, cách tiếp cận đầu tiên để lập trình là lập trình thủ tục, hay còn được gọi là lập trình từ dưới lên. Ban đầu, các thư viện chung của các chương trình tiêu chuẩn được sử dụng trong các lĩnh vực ứng dụng máy tính khác nhau đã được tạo ra. Sau đó, dựa trên các chương trình này, các chương trình phức tạp hơn đã được tạo ra để giải quyết các vấn đề cụ thể.

Tuy nhiên, công nghệ máy tính không ngừng phát triển, nó bắt đầu được sử dụng để giải quyết các vấn đề khác nhau của sản xuất, nền kinh tế, và do đó nó trở nên cần thiết để xử lý dữ liệu ở nhiều định dạng khác nhau và giải quyết các vấn đề phi tiêu chuẩn (ví dụ, các bài toán phi số). Vì vậy, khi phát triển các ngôn ngữ lập trình, họ bắt đầu chú ý đến việc tạo ra nhiều loại dữ liệu khác nhau. Điều này góp phần vào sự xuất hiện của các kiểu dữ liệu phức tạp như kết hợp, nhiều, chuỗi, tệp, v.v. Trước khi giải quyết vấn đề, lập trình viên tiến hành phân rã, tức là chia nhiệm vụ thành nhiều nhiệm vụ con, cho mỗi người trong số đó một mô-đun riêng biệt được viết . Công nghệ lập trình chính bao gồm ba giai đoạn:

1) thiết kế từ trên xuống;

2) lập trình mô-đun;

3) mã hóa cấu trúc.

Nhưng bắt đầu từ giữa những năm 60 của thế kỷ XX, các khái niệm và cách tiếp cận mới bắt đầu hình thành, là cơ sở hình thành nên công nghệ lập trình hướng đối tượng. Theo cách tiếp cận này, mô hình hóa và mô tả thế giới thực được thực hiện ở cấp độ các khái niệm của một lĩnh vực chủ đề cụ thể mà vấn đề đang được giải quyết thuộc về.

Lập trình hướng đối tượng là một kỹ thuật lập trình gần giống với hành vi của chúng ta. Đó là một sự tiến hóa tự nhiên của những đổi mới trước đó trong thiết kế ngôn ngữ lập trình. Lập trình hướng đối tượng có cấu trúc hơn tất cả những phát triển trước đây liên quan đến lập trình có cấu trúc. Nó cũng mang tính mô-đun hơn và trừu tượng hơn so với những nỗ lực trước đây trong việc trừu tượng hóa dữ liệu và lập trình chi tiết bên trong. Một ngôn ngữ lập trình hướng đối tượng được đặc trưng bởi ba thuộc tính chính:

1) Đóng gói. Kết hợp các bản ghi với các thủ tục và chức năng thao tác với các trường của các bản ghi này tạo thành một kiểu dữ liệu mới - một đối tượng;

2) Tính kế thừa. Định nghĩa một đối tượng và việc sử dụng nó để xây dựng một hệ thống phân cấp các đối tượng con với khả năng cho mỗi đối tượng con liên quan đến hệ thống phân cấp truy cập mã và dữ liệu của tất cả các đối tượng mẹ;

3) Tính đa hình. Đặt cho một hành động một tên duy nhất, sau đó được chia sẻ lên và xuống hệ thống phân cấp của các đối tượng, với mỗi đối tượng trong hệ thống phân cấp thực hiện hành động đó theo cách phù hợp với nó.

Nói về đối tượng, chúng tôi giới thiệu một kiểu dữ liệu mới - đối tượng. Một kiểu đối tượng là một cấu trúc bao gồm một số thành phần cố định. Mỗi thành phần là một trường chứa dữ liệu của một kiểu được xác định nghiêm ngặt hoặc một phương thức thực hiện các hoạt động trên một đối tượng. Tương tự với khai báo các biến, khai báo một trường chỉ định kiểu dữ liệu của trường này và định danh đặt tên cho trường: tương tự với khai báo một thủ tục hoặc hàm, khai báo một phương thức chỉ định tiêu đề của một thủ tục, hàm, hàm tạo hoặc hàm hủy.

Một kiểu đối tượng có thể kế thừa các thành phần của một kiểu đối tượng khác. Nếu kiểu T2 kế thừa từ kiểu T1, thì kiểu T2 là con của kiểu T1, và bản thân kiểu T1 là cha của kiểu T2. Kế thừa có tính bắc cầu, tức là nếu TK kế thừa từ T2 và T2 kế thừa từ T1, thì TK kế thừa từ T1. Phạm vi (miền) của một kiểu đối tượng bao gồm chính nó và tất cả các con của nó.

Mã nguồn sau đây là một ví dụ về khai báo kiểu đối tượng, kiểu

kiểu

point = object

X, Y: số nguyên;

kết thúc;

Rect = đối tượng

A, B: Điểm TP;

thủ tục Init (XA, YA, XB, YB: Integer);

thủ tục Copy (var R: TRectangle);

thủ tục Move (DX, DY: Integer);

thủ tục Grow (DX, DY: Integer);

thủ tục Intersect (var R: TRectangle);

thủ tục Union (var R: TRectangle);

function Chứa (P: Point): Boolean;

kết thúc;

StringPtr = ^ Chuỗi;

FieldPtr = ^ TField;

TField = đối tượng

X, Y, Len: Số nguyên;

Tên: StringPtr;

phương thức khởi tạo Copy (var F: TField);

constructor Init (FX, FY, FLen: Integer; FName: String);

trình hủy Đã xong; ảo;

thủ tục Hiển thị; ảo;

thủ tục Chỉnh sửa; ảo;

hàm GetStr: String; ảo;

function PutStr (S: String): Boolean; ảo;

kết thúc;

StrFieldPtr = ^ TStrField;

StrField = đối tượng (TField)

Giá trị: PString;

constructor Init (FX, FY, FLen: Integer; FName: String);

trình hủy Đã xong; ảo;

hàm GetStr: String; ảo;

function PutStr (S: String): Boolean;

ảo;

hàm Get: string;

thủ tục Put (S: String);

kết thúc;

NumFieldPtr = ^ TNumField;

TNumField = đối tượng (TField)

riêng

Giá trị, Min, Max: Longint;

công khai

phương thức khởi tạo Init (FX, FY, FLen: Integer; FName: String;

FMin, FMax: Longint);

hàm GetStr: String; ảo;

function PutStr (S: String): Boolean; ảo;

hàm Get: Longint;

hàm Put (N: Longint);

kết thúc;

ZipFieldPtr = ^ TZipField;

ZipField = đối tượng (TNumField)

hàm GetStr: String; ảo;

function PutStr (S: String): Boolean;

ảo;

cuối.

Không giống như các kiểu khác, kiểu đối tượng chỉ có thể được khai báo trong phần khai báo kiểu ở cấp ngoài cùng của phạm vi chương trình hoặc mô-đun. Do đó, các kiểu đối tượng không thể được khai báo trong phần khai báo biến hoặc bên trong một khối thủ tục, hàm hoặc phương thức.

Kiểu thành phần kiểu tệp không thể có kiểu đối tượng hoặc bất kỳ kiểu cấu trúc nào chứa các thành phần kiểu đối tượng.

2. Thừa kế

Quá trình mà một kiểu kế thừa các đặc tính của kiểu khác được gọi là kế thừa. Kiểu con được gọi là kiểu dẫn xuất (con) và kiểu mà kiểu con kế thừa được gọi là kiểu cha (mẹ).

Các kiểu bản ghi Pascal đã biết trước đây không thể kế thừa. Tuy nhiên, Borland Pascal mở rộng ngôn ngữ Pascal để hỗ trợ tính kế thừa. Một trong những phần mở rộng này là một danh mục cấu trúc dữ liệu mới liên quan đến các bản ghi, nhưng mạnh hơn nhiều. Các kiểu dữ liệu trong danh mục mới này được xác định bằng cách sử dụng từ dành riêng mới "đối tượng". Một kiểu đối tượng có thể được định nghĩa là một kiểu hoàn chỉnh, độc lập theo cách mô tả các mục nhập trong Pascal, nhưng nó cũng có thể được định nghĩa là con của một kiểu đối tượng hiện có bằng cách đặt kiểu mẹ trong dấu ngoặc đơn sau từ dành riêng "object".

3. Instantiate Objects

Một cá thể đối tượng được tạo bằng cách khai báo một biến hoặc hằng số của một kiểu đối tượng, hoặc bằng cách áp dụng thủ tục chuẩn Mới cho một biến kiểu "con trỏ đến kiểu đối tượng". Đối tượng kết quả được gọi là một thể hiện của kiểu đối tượng;

F: Trường;

Z: TZipField;

FP: Trường P;

ZP: PZipField;

Với các khai báo biến này, F là một thể hiện của TField và Z là một thể hiện của TZipField. Tương tự, sau khi áp dụng New cho FP và ZP, FP sẽ trỏ đến một cá thể TField và ZP sẽ trỏ đến một cá thể TZipField.

Nếu một kiểu đối tượng chứa các phương thức ảo, thì các thể hiện của kiểu đối tượng đó phải được khởi tạo bằng cách gọi một phương thức khởi tạo trước khi gọi bất kỳ phương thức ảo nào.

Dưới đây là một ví dụ:

S: StrField;

đầu

S.Init (1, 1, 25, 'Tên');

S.Put ('Vladimir');

S. Màn hình;

hữu ích. Cảm ơn !

S Đã xong;

cuối.

Nếu S.Init không được gọi, thì việc gọi S.Display sẽ khiến ví dụ này không thành công.

Việc gán một thể hiện của một kiểu đối tượng không có nghĩa là khởi tạo thể hiện đó. Một đối tượng được khởi tạo bằng mã do trình biên dịch tạo chạy giữa lệnh gọi của hàm tạo và thời điểm mà việc thực thi thực sự đạt đến câu lệnh đầu tiên trong khối mã của hàm tạo.

Nếu cá thể đối tượng không được khởi tạo và kiểm tra phạm vi được bật (bằng chỉ thị {SR +}), thì lệnh gọi đầu tiên đến phương thức ảo của cá thể đối tượng sẽ xảy ra lỗi thời gian chạy. Nếu tính năng kiểm tra phạm vi bị tắt (bởi lệnh {SR-}), thì lệnh gọi đầu tiên đến một phương thức ảo của một đối tượng chưa được khởi tạo có thể dẫn đến hành vi không thể đoán trước.

Quy tắc khởi tạo bắt buộc cũng áp dụng cho các phiên bản là thành phần của kiểu cấu trúc. Ví dụ:

Nhận xét: array [1..5] of TStrField;

I: số nguyên

bắt đầu

for I: = 1 to 5 do

Comment [I] .Init (1, I + 10, 40, 'first_name');

.

.

.

for I: = 1 to 5 do Comment [I] .Done;

kết thúc;

Đối với các trường hợp động, việc khởi tạo thường là vị trí và dọn dẹp là loại bỏ, điều này đạt được thông qua cú pháp mở rộng của các thủ tục tiêu chuẩn Mới và Loại bỏ. Ví dụ:

SP: StrFieldPtr;

bắt đầu

Mới (SP, Init (1, 1, 25, 'first_name');

SP ^ .Put ('Vladimir');

SP ^ .Hiển thị;

.

.

.

Vứt bỏ (SP, Xong);

cuối.

Một con trỏ tới một kiểu đối tượng là phép gán tương thích với một con trỏ tới bất kỳ kiểu đối tượng mẹ nào, vì vậy trong thời gian chạy, một con trỏ tới một kiểu đối tượng có thể trỏ đến một thể hiện của kiểu đó hoặc đến một thể hiện của bất kỳ kiểu con nào.

Ví dụ: một con trỏ kiểu ZipFieldPtr có thể được gán cho các con trỏ kiểu PZipField, PNumField và PField và trong thời gian chạy, một con trỏ kiểu PField có thể là nil hoặc trỏ đến một phiên bản của TField, TNumField hoặc TZipField, hoặc bất kỳ ví dụ về kiểu con của TField..

Các quy tắc tương thích con trỏ gán này cũng áp dụng cho các tham số kiểu đối tượng. Ví dụ: phương thức TField.Cop có thể được chuyển qua các phiên bản của TField, TStrField, TNumField, TZipField hoặc bất kỳ loại con nào khác của TField.

4. Thành phần và Phạm vi

Phạm vi của một mã định danh bean mở rộng ra ngoài kiểu đối tượng. Hơn nữa, phạm vi của một mã định danh bean mở rộng qua các khối thủ tục, hàm, hàm tạo và hàm hủy thực thi các phương thức của kiểu đối tượng và con cháu của nó. Dựa trên những cân nhắc này, cách viết của mã định danh thành phần phải là duy nhất trong kiểu đối tượng và trong tất cả các con của nó, cũng như trong tất cả các phương thức của nó.

Phạm vi của định danh thành phần được mô tả trong phần riêng của khai báo kiểu được giới hạn trong mô-đun (chương trình) có chứa khai báo kiểu đối tượng. Nói cách khác, các bean số nhận dạng riêng hoạt động giống như các mã định danh công khai thông thường bên trong mô-đun có chứa khai báo kiểu đối tượng và bên ngoài mô-đun, bất kỳ mã nhận dạng và đậu riêng nào đều không xác định và không thể truy cập được. Bằng cách đặt các loại đối tượng liên quan trong cùng một mô-đun, bạn có thể đảm bảo rằng các đối tượng này có thể truy cập các thành phần riêng tư của nhau và các thành phần riêng tư này sẽ không được biết đối với các mô-đun khác.

Trong khai báo kiểu đối tượng, tiêu đề phương thức có thể chỉ định các tham số của kiểu đối tượng đang được mô tả, ngay cả khi mô tả chưa hoàn chỉnh.

LECTURE số 12. Phương pháp

1. Phương pháp

Một khai báo phương thức bên trong một kiểu đối tượng tương ứng với một khai báo phương thức chuyển tiếp (forward). Vì vậy, ở đâu đó sau một khai báo kiểu đối tượng, nhưng trong cùng phạm vi với phạm vi của khai báo kiểu đối tượng, một phương thức phải được thực hiện bằng cách xác định khai báo của nó.

Đối với các phương thức thủ tục và hàm, khai báo xác định có dạng một thủ tục hoặc khai báo hàm thông thường, ngoại trừ trường hợp này, mã định danh thủ tục hoặc hàm được coi như một định danh phương thức.

Đối với các phương thức hàm tạo và hàm hủy, khai báo định nghĩa có dạng một khai báo phương thức thủ tục, ngoại trừ thủ tục từ dành riêng được thay thế bằng hàm tạo hoặc hủy từ dành riêng.

Khai báo phương thức xác định có thể, nhưng không cần, lặp lại danh sách các tham số chính thức của tiêu đề phương thức trong kiểu đối tượng. Trong trường hợp này, tiêu đề phương thức phải khớp chính xác với tiêu đề trong kiểu đối tượng theo thứ tự, kiểu và tên tham số và trong kiểu trả về của kết quả hàm nếu phương thức là một hàm.

Mô tả xác định của một phương thức luôn chứa một tham số ngầm định với định danh Self, tương ứng với một tham số biến hình thức có kiểu đối tượng. Trong một khối phương thức, Self đại diện cho cá thể có thành phần phương thức được chỉ định để gọi phương thức. Do đó, bất kỳ thay đổi nào đối với giá trị của các trường Bản thân đều được phản ánh trong trường hợp.

Phạm vi của mã nhận dạng bean kiểu đối tượng mở rộng đến các khối thủ tục, hàm, hàm tạo và hủy thực thi các phương thức của kiểu đối tượng đó. Hiệu ứng tương tự như nếu một câu lệnh with có dạng sau được chèn vào đầu khối phương thức:

tự làm

bắt đầu

hữu ích. Cảm ơn !

kết thúc;

Dựa trên những cân nhắc này, cách viết của số nhận dạng thành phần, tham số phương thức chính thức, Bản thân và bất kỳ số nhận dạng nào được đưa vào phần thực thi của phương thức phải là duy nhất.

Nếu cần có mã định danh phương thức duy nhất, thì mã định danh phương thức đủ điều kiện sẽ được sử dụng. Nó bao gồm một mã định danh kiểu đối tượng theo sau là một dấu chấm và một mã định danh phương thức. Như với bất kỳ số nhận dạng nào khác, số nhận dạng phương thức đủ điều kiện có thể bắt đầu bằng số nhận dạng gói và dấu chấm.

Phương pháp ảo

Các phương thức là tĩnh theo mặc định, nhưng ngoại trừ các hàm tạo, chúng có thể là ảo (bằng cách bao gồm chỉ thị ảo trong khai báo phương thức). Trình biên dịch giải quyết các tham chiếu đến các cuộc gọi phương thức tĩnh trong quá trình biên dịch, trong khi các cuộc gọi phương thức ảo được giải quyết tại thời điểm chạy. Điều này đôi khi được gọi là ràng buộc muộn.

Nếu một kiểu đối tượng khai báo hoặc kế thừa bất kỳ phương thức ảo nào, thì các biến của kiểu đó phải được khởi tạo bằng cách gọi một phương thức khởi tạo trước khi gọi bất kỳ phương thức ảo nào. Do đó, một kiểu đối tượng mô tả hoặc kế thừa một phương thức ảo cũng phải mô tả hoặc kế thừa ít nhất một phương thức khởi tạo.

Một kiểu đối tượng có thể ghi đè lên bất kỳ phương thức nào mà nó kế thừa từ cha mẹ của nó. Nếu một khai báo phương thức trong phần con chỉ định cùng một định danh phương thức như một khai báo phương thức trong phần cha, thì phần khai báo trong phần con sẽ ghi đè phần khai báo trong phần cha. Phạm vi của một phương thức ghi đè mở rộng đến phạm vi con mà phương thức được giới thiệu và sẽ vẫn như vậy cho đến khi định danh phương thức được ghi đè một lần nữa.

Ghi đè một phương thức tĩnh không phụ thuộc vào việc thay đổi tiêu đề phương thức. Ngược lại, ghi đè phương thức ảo phải bảo toàn thứ tự, kiểu tham số và tên cũng như kiểu kết quả của hàm, nếu có. Hơn nữa, định nghĩa lại một lần nữa phải bao gồm chỉ thị ảo.

Phương pháp động

Borland Pascal hỗ trợ các phương thức giới hạn cuối bổ sung được gọi là phương thức động. Các phương thức động chỉ khác với các phương thức ảo ở cách chúng được gửi đi trong thời gian chạy. Trong tất cả các khía cạnh khác, các phương thức động được coi là tương đương với các phương thức ảo.

Khai báo phương thức động tương đương với khai báo phương thức ảo, nhưng khai báo phương thức động phải bao gồm chỉ mục phương thức động, chỉ mục này được chỉ định ngay sau từ khóa virtual. Chỉ mục của một phương thức động phải là một hằng số nguyên từ 1 đến 656535 và phải là duy nhất trong số các chỉ mục của các phương thức động khác có trong kiểu đối tượng hoặc tổ tiên của nó. Ví dụ:

thủ tục FileOpen (var Msg: TMessage); ảo 100;

Ghi đè một phương thức động phải khớp với thứ tự, kiểu và tên của các tham số và khớp chính xác với kiểu kết quả của hàm của phương thức mẹ. Việc ghi đè cũng phải bao gồm một chỉ thị ảo theo sau cùng một chỉ mục phương thức động đã được chỉ định trong kiểu đối tượng tổ tiên.

2. Bộ tạo và bộ hủy

Bộ tạo và bộ hủy là các dạng phương pháp chuyên biệt. Được sử dụng liên quan đến cú pháp mở rộng của các thủ tục tiêu chuẩn Mới và Loại bỏ, các hàm tạo và hàm hủy có khả năng đặt và loại bỏ các đối tượng động. Ngoài ra, các hàm tạo có khả năng thực hiện việc khởi tạo các đối tượng chứa các phương thức ảo theo yêu cầu. Giống như tất cả các phương thức, hàm tạo và hàm hủy có thể được kế thừa và các đối tượng có thể chứa bất kỳ số lượng hàm tạo và hàm hủy nào.

Constructors được sử dụng để khởi tạo các đối tượng mới được tạo. Thông thường, việc khởi tạo dựa trên các giá trị được truyền cho hàm tạo dưới dạng các tham số. Một phương thức khởi tạo không thể là ảo bởi vì cơ chế điều phối của một phương thức ảo phụ thuộc vào phương thức khởi tạo đã khởi tạo đối tượng trước.

Dưới đây là một số ví dụ về các hàm tạo:

constructor Field.Copy (var F: Field);

bắt đầu

Tự: = F;

kết thúc;

constructor Field.Init (FX, FY, FLen: integer; FName: string);

bắt đầu

X: = FX;

Y: = FY;

GetMem (Tên, Độ dài (Tên) + 1);

Tên ^: = FName;

kết thúc;

constructor TStrField.Init (FX, FY, FLen: integer; FName: string);

bắt đầu

kế thừa Init (FX, FY, FLen, FName);

Field.Init (FX, FY, FLen, FName);

GetMem (Giá trị, Len);

Giá trị ^: = '';

kết thúc;

Hành động chính của một phương thức khởi tạo kiểu dẫn xuất (con), chẳng hạn như Trường TStr ở trên. Init hầu như luôn luôn là một lời gọi đến phương thức khởi tạo thích hợp của cha mẹ trực tiếp của nó để khởi tạo các trường kế thừa của đối tượng. Sau khi thực hiện thủ tục này, hàm khởi tạo khởi tạo các trường của đối tượng chỉ thuộc về kiểu dẫn xuất.

Bộ hủy đối lập với hàm tạo và được sử dụng để dọn dẹp các đối tượng sau khi chúng đã được sử dụng. Thông thường, dọn dẹp bao gồm loại bỏ tất cả các trường con trỏ trong đối tượng.

Ghi

Một hàm hủy có thể là ảo, và thường là như vậy. Một hàm hủy hiếm khi có các tham số.

Dưới đây là một số ví dụ về hàm hủy:

Trường hủy kết thúc Đã xong;

bắt đầu

FreeMem (Tên, Độ dài (Tên ^) + 1);

kết thúc;

hàm hủy StrField.Done;

bắt đầu

FreeMem (Giá trị, Len);

Thực hiện xong;

kết thúc;

Hàm hủy của một kiểu con, chẳng hạn như TStrField ở trên. Xong, thường trước tiên xóa các trường con trỏ được giới thiệu trong kiểu dẫn xuất và sau đó, bước cuối cùng, gọi bộ thu-hủy thích hợp của cha mẹ trực tiếp để xóa các trường con trỏ kế thừa của đối tượng.

3. Kẻ hủy diệt

Borland Pascal cung cấp một loại phương thức đặc biệt được gọi là bộ thu gom rác (hoặc bộ hủy) để dọn dẹp và xóa một đối tượng được cấp phát động. Bộ hủy kết hợp bước xóa một đối tượng với bất kỳ hành động hoặc tác vụ nào khác được yêu cầu cho loại đối tượng đó. Bạn có thể xác định nhiều hàm hủy cho một loại đối tượng.

Hàm hủy được định nghĩa cùng với tất cả các phương thức đối tượng khác trong định nghĩa kiểu của đối tượng:

loại hình

Temployee = đối tượng

Tên: string [25];

Tiêu đề: string [25];

Tỷ lệ: Real;

phương thức khởi tạo Init (AName, ATitle: String; ARate: Real);

trình hủy Đã xong; ảo;

hàm GetName: String;

hàm GetTitle: String;

chức năng GetRate: Tỷ lệ; ảo;

hàm GetPayAmount: Real; ảo;

kết thúc;

Bộ hủy có thể được kế thừa và chúng có thể là tĩnh hoặc ảo. Vì các trình hoàn thiện khác nhau có xu hướng yêu cầu các loại đối tượng khác nhau, nên nói chung các trình hủy luôn là ảo để trình hủy chính xác được thực thi cho từng loại đối tượng.

Bộ hủy từ dành riêng không cần phải được chỉ định cho mọi phương thức dọn dẹp, ngay cả khi định nghĩa kiểu của đối tượng có chứa các phương thức ảo. Bộ hủy thực sự chỉ hoạt động trên các đối tượng được cấp phát động.

Khi một đối tượng được cấp phát động được dọn dẹp, trình hủy thực hiện một chức năng đặc biệt: nó đảm bảo rằng số byte chính xác luôn được giải phóng trong vùng bộ nhớ được cấp phát động. Không cần lo lắng về việc sử dụng trình hủy với các đối tượng được cấp phát tĩnh; trên thực tế, bằng cách không chuyển kiểu của đối tượng tới trình hủy, lập trình viên sẽ tước đi lợi ích đầy đủ của việc quản lý bộ nhớ động trong Borland Pascal đối với một đối tượng thuộc kiểu đó.

Bộ hủy thực sự trở thành chính nó khi các đối tượng đa hình phải được xóa và khi bộ nhớ mà chúng chiếm phải được phân bổ.

Đối tượng đa hình là những đối tượng đã được gán cho kiểu mẹ do các quy tắc tương thích kiểu mở rộng của Borland Pascal. Một ví dụ của một đối tượng kiểu TH được gán hàng giờ cho một biến kiểu TE Jobee là một ví dụ về một đối tượng đa hình. Các quy tắc này cũng có thể được áp dụng cho các đối tượng; một con trỏ tới THourly có thể tự do được gán cho một con trỏ tới TE Jobee, và đối tượng được trỏ tới bởi con trỏ đó sẽ lại là một đối tượng đa hình. Thuật ngữ "đa hình" là thích hợp bởi vì mã xử lý một đối tượng "không biết" chính xác tại thời điểm biên dịch loại đối tượng mà nó sẽ cần xử lý cuối cùng. Điều duy nhất nó biết là đối tượng này thuộc về một hệ thống phân cấp các đối tượng là hậu duệ của loại đối tượng được chỉ định.

Rõ ràng, kích thước của các loại đối tượng là khác nhau. Vì vậy, khi đến lúc dọn dẹp một đối tượng đa hình được phân bổ theo heap, làm cách nào Dispose biết còn bao nhiêu byte không gian heap để giải phóng? Tại thời điểm biên dịch, không có thông tin nào liên quan đến kích thước của đối tượng có thể được trích xuất từ ​​một đối tượng đa hình.

Bộ hủy giải quyết câu đố này bằng cách tham chiếu đến nơi ghi thông tin này - trong các biến triển khai TCM. Mỗi TBM của một loại đối tượng chứa kích thước tính bằng byte của loại đối tượng đó. Bảng các phương thức ảo của bất kỳ đối tượng nào có sẵn thông qua tham số ẩn Self, được gửi đến phương thức khi phương thức được gọi. Hàm hủy chỉ là một loại phương thức, và vì vậy khi một đối tượng gọi nó, bộ hủy sẽ nhận được một bản sao của Bản thân trên ngăn xếp. Do đó, nếu một đối tượng là đa hình tại thời điểm biên dịch, thì đối tượng đó sẽ không bao giờ đa hình tại thời gian chạy do ràng buộc muộn.

Để thực hiện phân bổ thỏa thuận giới hạn cuối này, hàm hủy phải được gọi như một phần của cú pháp mở rộng của thủ tục Hủy bỏ:

Loại bỏ (P, Xong);

(Một lệnh gọi tới hàm hủy bên ngoài thủ tục Dispose không giải quyết bất kỳ bộ nhớ nào.) Điều thực sự đang xảy ra ở đây là bộ thu gom rác của đối tượng được trỏ tới bởi P được thực thi như một phương thức bình thường. Tuy nhiên, sau khi hành động cuối cùng được hoàn thành, trình hủy sẽ tra cứu kích thước của việc triển khai kiểu của nó trong TCM và chuyển kích thước cho thủ tục Loại bỏ. Thủ tục Dispose kết thúc quá trình bằng cách xóa số byte chính xác của không gian đống mà (không gian) trước đó thuộc về P ^. Số lượng byte được giải phóng sẽ chính xác bất kể P có trỏ đến một thể hiện của kiểu TSalaried hay nó trỏ đến một trong những kiểu con của kiểu TSalaried, chẳng hạn như TCommissoned.

Lưu ý rằng bản thân phương thức hủy có thể trống và chỉ thực hiện chức năng này:

destructorAnObject.Done;

bắt đầu

kết thúc;

Điều hữu ích trong trình hủy này không phải là thuộc tính của phần thân của nó, tuy nhiên, trình biên dịch tạo ra mã phần kết để đáp lại từ dành riêng cho trình hủy. Nó giống như một mô-đun không xuất bất cứ thứ gì, nhưng thực hiện một số hoạt động vô hình bằng cách thực hiện phần khởi tạo của nó trước khi bắt đầu chương trình. Tất cả các hành động diễn ra ở hậu trường.

4. Phương thức ảo

Một phương thức trở thành ảo nếu khai báo kiểu đối tượng của nó được theo sau bởi từ dành riêng mới là virtual. Nếu một phương thức trong kiểu mẹ được khai báo là ảo, thì tất cả các phương thức có cùng tên trong kiểu con cũng phải được khai báo là ảo để tránh lỗi trình biên dịch.

Sau đây là các đối tượng từ bảng lương ví dụ, được ảo hóa đúng cách:

loại hình

PE Jobee = ^ TE Jobee;

Temployee = đối tượng

Tên, Tiêu đề: string [25];

Tỷ lệ: Real;

phương thức khởi tạo Init (AName, ATitle: String; ARate: Real);

hàm GetPayAmount: Real; ảo;

hàm GetName: String;

hàm GetTitle: String;

hàm GetRate: Real;

thủ tục Hiển thị; ảo;

kết thúc;

Hàng giờ = ^ Hàng giờ;

THourly = object (TE Jobee);

Thời gian: Số nguyên;

hàm khởi tạo Init(AName, ATitle: String; ARate: Real; Time: Integer);

hàm GetPayAmount: Real; ảo;

hàm GetTime: Integer;

kết thúc;

PSalaried = ^ TSalaried;

TSalaried = đối tượng (TE Jobee);

hàm GetPayAmount: Real; ảo;

kết thúc;

Đã được ủy quyền = ^ TC đã được ủy quyền;

TCommissoned = đối tượng (Được trả lương);

Hoa hồng: Real;

Doanh số bán hàng: Real;

phương thức khởi tạo Init (AName, ATitle: String; ARate,

Hoa hồng, ASố tiền bán hàng: Thực);

hàm GetPayAmount: Real; ảo;

kết thúc;

Hàm tạo là một loại thủ tục đặc biệt thực hiện một số thiết lập hoạt động cho cơ chế phương thức ảo. Hơn nữa, phương thức khởi tạo phải được gọi trước khi bất kỳ phương thức ảo nào được gọi. Việc gọi một phương thức ảo mà không gọi phương thức khởi tạo trước có thể chặn hệ thống và không có cách nào để trình biên dịch kiểm tra thứ tự các phương thức được gọi.

Mọi kiểu đối tượng có các phương thức ảo đều phải có một phương thức khởi tạo.

Phạt cảnh cáo

Phương thức khởi tạo phải được gọi trước khi bất kỳ phương thức ảo nào khác được gọi. Việc gọi một phương thức ảo mà không có lệnh gọi trước đến phương thức khởi tạo có thể gây ra khóa hệ thống và trình biên dịch không thể kiểm tra thứ tự các phương thức được gọi.

Ghi

Đối với các hàm tạo đối tượng, nên sử dụng Init định danh.

Mỗi cá thể đối tượng riêng biệt phải được khởi tạo bằng một lời gọi phương thức khởi tạo riêng biệt. Không đủ để khởi tạo một phiên bản của một đối tượng và sau đó gán phiên bản đó cho những người khác. Các trường hợp khác, ngay cả khi chúng có thể chứa dữ liệu hợp lệ, sẽ không được khởi tạo bằng toán tử gán và sẽ chặn hệ thống khi có bất kỳ lệnh gọi nào đến các phương thức ảo của chúng. Ví dụ:

FBee, GBee: Bee; {tạo hai phiên bản Bee}

bắt đầu

FBee.Init (5, 9) {lệnh gọi hàm tạo cho FBee}

GBee: = FBee; {Gbee không hợp lệ! }

kết thúc;

Chính xác thì một hàm tạo tạo ra gì? Mỗi kiểu đối tượng chứa một thứ gọi là bảng phương thức ảo (VMT) trong phân đoạn dữ liệu. TVM chứa kích thước của kiểu đối tượng và đối với mỗi phương thức ảo, một con trỏ đến mã thực thi phương thức đó. Một phương thức khởi tạo thiết lập mối quan hệ giữa việc thực hiện cuộc gọi của đối tượng và kiểu TCM của đối tượng.

Điều quan trọng cần nhớ là chỉ có một TBM cho mỗi loại đối tượng. Các trường hợp riêng biệt của một loại đối tượng (tức là các biến thuộc loại này) chỉ chứa kết nối đến TBM, chứ không chứa chính TBM. Hàm khởi tạo đặt giá trị của kết nối này thành TBM. Chính vì điều này mà không nơi nào bạn có thể bắt đầu thực thi trước khi gọi hàm tạo.

5. Các trường dữ liệu đối tượng và các tham số phương thức chính thức

Hàm ý của thực tế là các phương thức và đối tượng của chúng có chung một phạm vi là các tham số chính thức của một phương thức không thể giống với bất kỳ trường dữ liệu nào của đối tượng. Đây không phải là một số giới hạn mới do lập trình hướng đối tượng áp đặt, mà là các quy tắc phạm vi cũ mà Pascal luôn có. Điều này cũng giống như việc ngăn các tham số chính thức của thủ tục giống hệt với các biến cục bộ của thủ tục:

thủ tục CrunchIt (Crunchee: MyDataRec, Crunchby,

ErrorCode: số nguyên);

A, B: ký tự;

ErrorCode: số nguyên;

bắt đầu

.

.

.

Các biến cục bộ của một thủ tục và các tham số chính thức của nó có chung một phạm vi và do đó không thể giống hệt nhau. Bạn sẽ nhận được "Lỗi 4: Mã định danh trùng lặp" nếu bạn cố gắng biên dịch một cái gì đó như thế này, lỗi tương tự xảy ra khi bạn cố gắng đặt một tham số phương thức chính thức cho tên của trường đối tượng mà phương thức đó thuộc về.

Hoàn cảnh có phần khác nhau, vì việc đặt tiêu đề thủ tục bên trong cấu trúc dữ liệu là dấu hiệu cho thấy một sự đổi mới trong Turbo Pascal, nhưng các nguyên tắc cơ bản của phạm vi Pascal không thay đổi.

LECTURE số 13. Khả năng tương thích của các loại đối tượng

1. Đóng gói

Sự kết hợp của mã và dữ liệu trong một đối tượng được gọi là tính đóng gói. Về nguyên tắc, có thể cung cấp đủ các phương thức để người dùng của một đối tượng sẽ không bao giờ truy cập trực tiếp vào các trường của đối tượng. Một số ngôn ngữ hướng đối tượng khác, chẳng hạn như Smalltalk, yêu cầu đóng gói bắt buộc, nhưng Borland Pascal có một sự lựa chọn.

Ví dụ: các đối tượng TE Employee và THourly được viết theo cách mà hoàn toàn không cần truy cập trực tiếp vào các trường dữ liệu nội bộ của chúng:

kiểu

Temployee = đối tượng

Tên, Tiêu đề: string [25];

Tỷ lệ: Real;

thủ tục Init (AName, ATitle: string; ARate: Real);

hàm GetName: String;

hàm GetTitle: String;

hàm GetRate: Real;

hàm GetPayAmount: Real;

kết thúc;

THourly = object (TE Jobee)

Thời gian: Số nguyên;

thủ tục Init (AName, ATitle: string; ARate:

Thực tế, Atime: Số nguyên);

hàm GetPayAmount: Real;

kết thúc;

Ở đây chỉ có bốn trường dữ liệu: Tên, Tiêu đề, Tỷ lệ và Thời gian. Phương thức GetName và GetTitle lần lượt hiển thị họ và chức vụ của nhân viên. Phương thức GetPayAmount sử dụng Tỷ lệ và trong trường hợp Làm việc Hàng giờ và Thời gian để tính toán số tiền thanh toán cho công việc đang làm việc. Không còn cần thiết phải tham chiếu trực tiếp đến các trường dữ liệu này.

Giả sử có sự tồn tại của một phiên bản AnHourly kiểu THourly, chúng ta có thể sử dụng một tập hợp các phương thức để thao tác các trường dữ liệu AnHourly như sau:

với một giờ làm

bắt đầu

Init (Aleksandr Petrov, Nhà điều hành thang máy Fork '12.95, 62);

{Hiển thị họ, chức vụ và số tiền thanh toán}

Chỉ;

kết thúc;

Cần lưu ý rằng việc truy cập vào các trường của một đối tượng chỉ được thực hiện với sự trợ giúp của các phương thức của đối tượng này.

2. Mở rộng Đối tượng

Thật không may, Pascal tiêu chuẩn không cung cấp bất kỳ cơ sở nào để tạo các thủ tục linh hoạt cho phép bạn làm việc với các kiểu dữ liệu hoàn toàn khác nhau. Lập trình hướng đối tượng giải quyết vấn đề này với tính kế thừa: nếu một kiểu dẫn xuất được định nghĩa, thì các phương thức của kiểu mẹ sẽ được kế thừa, nhưng chúng có thể bị ghi đè nếu muốn. Để ghi đè một phương thức được kế thừa, chỉ cần khai báo một phương thức mới có cùng tên với phương thức được kế thừa, nhưng có phần thân khác và (nếu cần) một bộ tham số khác.

Hãy xác định một loại TE Employee đại diện cho một nhân viên được trả lương theo giờ trong ví dụ sau:

const

PayPeriods = 26; {kỳ hạn thanh toán}

Ngưỡng ngoài giờ = 80; {cho khoảng thời gian thanh toán}

OvertimeFactor = 1.5; {tỷ lệ hàng giờ}

kiểu

THourly = object (TE Jobee)

Thời gian: Số nguyên;

thủ tục Init (AName, ATitle: string; ARate:

Thực tế, Atime: Số nguyên);

hàm GetPayAmount: Real;

kết thúc;

thủ tục THourly.Init (AName, ATitle: string;

ARate: Thực, Atime: Số nguyên);

bắt đầu

TE Jobee.Init (AName, ATitle, ARate);

Thời gian: = ATime;

kết thúc;

chức năng THourly.GetPayAmount: Real;

Tăng ca: Số nguyên;

bắt đầu

Overtime: = Time - OvertimeThreshold;

nếu Overtime> 0 thì

GetPayAmount: = RoundPay (OvertimeThreshold * Rate +

Tỷ lệ OverTime * OvertimeFactor * Tỷ lệ)

khác

GetPayAmount: = RoundPay (Thời gian * Tỷ lệ)

kết thúc;

Một người được trả lương theo giờ là một công nhân: anh ta có mọi thứ được dùng để xác định đối tượng Người lao động (tên, chức vụ, tỷ lệ) và chỉ số tiền mà người đó nhận được theo giờ phụ thuộc vào số giờ anh ta làm việc kỳ phải trả. Do đó, THourly cũng yêu cầu trường Thời gian.

Bởi vì THourly xác định trường Thời gian mới, việc khởi tạo nó yêu cầu một phương thức Init mới khởi tạo cả trường thời gian và trường kế thừa. Thay vì chỉ định trực tiếp các giá trị cho các trường kế thừa như Tên, Tiêu đề và Tỷ lệ, tại sao không sử dụng lại phương thức khởi tạo của đối tượng TE Jobee (minh họa bằng câu lệnh THourly Init đầu tiên).

Gọi một phương thức đang bị ghi đè không phải là kiểu tốt nhất. Nói chung, có thể TE Jobee.Init thực hiện một khởi tạo quan trọng nhưng ẩn.

Khi gọi một phương thức được ghi đè, bạn phải chắc chắn rằng kiểu đối tượng dẫn xuất bao gồm chức năng của cha mẹ. Ngoài ra, bất kỳ thay đổi nào trong phương thức cha sẽ tự động ảnh hưởng đến tất cả các phương thức con.

Sau khi gọi TE Jobee.Init, THourly.Init sau đó có thể thực hiện quá trình khởi tạo của chính nó, trong trường hợp này chỉ bao gồm việc gán giá trị được truyền trong ATime.

Một ví dụ khác về phương pháp ghi đè là hàm THourly.GetPayAmount, tính toán số tiền thanh toán cho một nhân viên theo giờ. Trên thực tế, mỗi loại đối tượng TE Employee đều có phương thức GetPayAmount riêng, vì loại worker phụ thuộc vào cách tính toán được thực hiện. Phương pháp THourly.GetPayAmount phải tính đến số giờ nhân viên làm việc, có làm thêm giờ hay không, hệ số tăng của thời gian làm thêm là bao nhiêu, v.v.

Phương pháp TSalaried. GetPayAmount chỉ nên chia tỷ lệ của nhân viên cho số lần thanh toán trong mỗi năm (trong ví dụ của chúng tôi).

công nhân đơn vị;

giao diện

const

PayPeriods = 26; {trong năm}

Ngưỡng ngoài giờ = 80; {cho mỗi kỳ thanh toán}

OvertimeFactor = 1.5; {tăng so với thanh toán thông thường}

kiểu

Temployee = đối tượng

Tên, Tiêu đề: string [25];

Tỷ lệ: Real;

thủ tục Init (AName, ATitle: string; ARate: Real);

hàm GetName: String;

hàm GetTitle: String;

hàm GetRate: Real;

hàm GetPayAmount: Real;

kết thúc;

THourly = object (TE Jobee)

Thời gian: Số nguyên;

thủ tục Init (AName, ATitle: string; ARate:

Thực tế, Atime: Số nguyên);

hàm GetPayAmount: Real;

hàm GetTime: Real;

kết thúc;

TSalaried = đối tượng (TE Jobee)

hàm GetPayAmount: Real;

kết thúc;

TCommissoned = đối tượng (TSalaried)

Hoa hồng: Real;

Doanh số bán hàng: Real;

phương thức khởi tạo Init (AName, ATitle: String; ARate,

Hoa hồng, ASố tiền bán hàng: Thực);

hàm GetPayAmount: Real;

kết thúc;

thực hiện

chức năng RoundPay (Lương: Thực): Thực;

{làm tròn các khoản thanh toán để bỏ qua số tiền nhỏ hơn

Đơn vị tiền tệ}

bắt đầu

RoundPay: = Trunc (Lương * 100) / 100;

.

.

.

TE Jobee là phần trên cùng của hệ thống phân cấp đối tượng của chúng tôi và chứa phương thức GetPayAmount đầu tiên.

function TE Jobee.GetPayAmount: Real;

bắt đầu

RunError (211); {đưa ra lỗi thời gian chạy}

kết thúc;

Có thể ngạc nhiên khi phương pháp này xuất hiện lỗi thời gian chạy. Nếu Employee.GetPayAmount được gọi, chương trình sẽ xảy ra lỗi. Tại sao? Bởi vì TE Jobee là người đứng đầu trong hệ thống phân cấp đối tượng của chúng ta và không xác định một worker thực sự; do đó, không có phương thức nào trong số các phương thức TE Employee được gọi theo một cách cụ thể, mặc dù chúng có thể được kế thừa. Tất cả nhân viên của chúng tôi đều làm việc theo giờ, làm công ăn lương hoặc làm việc theo công việc. Lỗi thời gian chạy chấm dứt thực thi chương trình và xuất ra kết quả 211, tương ứng với một thông báo lỗi được liên kết với một lệnh gọi phương thức trừu tượng (nếu chương trình gọi TE Jobee.GetPayAmount do nhầm lẫn).

Dưới đây là phương pháp THourly.GetPayAmount, sẽ tính đến những thứ như lương làm thêm giờ, giờ làm việc, v.v.

function THourly.GetPayAMount: Real;

OverTime: Số nguyên;

bắt đầu

Overtime: = Time - OvertimeThreshold;

nếu Overtime> 0 thì

GetPayAmount: = RoundPay (OvertimeThreshold * Rate +

Tỷ lệ OverTime * OvertimeFactor * Tỷ lệ)

khác

GetPayAmount: = RoundPay (Thời gian * Tỷ lệ)

kết thúc;

Phương thức TSalaried.GetPayAmount đơn giản hơn nhiều; đặt cược vào nó

chia cho số lần thanh toán:

function TSalaried.GetPayAmount: Real;

bắt đầu

GetPayAmount: = RoundPay (Tỷ lệ / PayPeriods);

kết thúc;

Nếu bạn nhìn vào phương thức TCommissoned.GetPayAmount, bạn sẽ thấy rằng nó gọi TSalaried.GetPayAmount, tính toán hoa hồng và thêm nó vào giá trị được trả về bởi phương thức TSalaried. GetPayAmount.

chức năng TCommissoned.GetPayAmount: Real;

bắt đầu

GetPayAmount: = RoundPay (TSalaried.GetPayAmount +

Hoa hồng * Số tiền bán hàng);

kết thúc;

Lưu ý quan trọng: Mặc dù có thể ghi đè các phương thức, nhưng không thể ghi đè các trường dữ liệu. Khi một trường dữ liệu đã được xác định trong phân cấp đối tượng, không có kiểu con nào có thể xác định một trường dữ liệu có tên giống hệt nhau.

3. Khả năng tương thích của các loại đối tượng

Kế thừa sửa đổi các quy tắc tương thích kiểu của Borland Pascal ở một mức độ nào đó. Trong số những thứ khác, một kiểu dẫn xuất thừa kế khả năng tương thích kiểu của tất cả các kiểu mẹ của nó.

Khả năng tương thích kiểu mở rộng này có ba dạng:

1) giữa các triển khai của các đối tượng;

2) giữa các con trỏ đến các triển khai đối tượng;

3) giữa các tham số chính thức và thực tế.

Tuy nhiên, điều rất quan trọng cần nhớ là trong cả ba hình thức, khả năng tương thích kiểu chỉ mở rộng từ con đến cha. Nói cách khác, các kiểu con có thể được sử dụng tự do thay cho các kiểu mẹ, nhưng không phải ngược lại.

Ví dụ, TSalaried là một phần tử con của TEprisee và TSosh-missoned là con của TSalaried. Với ý nghĩ đó, hãy xem xét các mô tả sau:

loại hình

PE Jobee = ^ TE Jobee;

PSalaried = ^ TSalaried;

Đã được ủy quyền = ^ TC đã được ủy quyền;

AnE Employee: Người lao động;

ASalaried: Phân bổ theo TSalaried;

Đã được ủy thác: TComised;

TE JobeePtr: PE Jobee;

TSalariedPtr: Được phân bổ;

TCommissonedPtr: Đã được ủy quyền;

Theo các mô tả này, các toán tử sau hợp lệ

nhiệm vụ:

AnE Employee: = ASalaried;

ASalaried: = Được ủy quyền;

TCommissonedPtr: = Được ủy quyền;

Ghi

Một đối tượng cha có thể được gán một thể hiện của bất kỳ kiểu dẫn xuất nào của nó. Bài tập quay lại không được phép.

Khái niệm này mới đối với Pascal, và lúc đầu có thể khó nhớ khả năng tương thích của loại thứ tự nào. Bạn cần nghĩ như thế này: nguồn phải có khả năng lấp đầy hoàn toàn đầu thu. Kiểu gốc chứa mọi thứ mà kiểu mẹ của chúng chứa do thuộc tính kế thừa. Do đó, kiểu dẫn xuất có cùng kích thước hoặc (thường là trường hợp này) nó lớn hơn kiểu gốc của nó, nhưng không bao giờ nhỏ hơn. Việc gán đối tượng cha (mẹ) cho con (con) có thể khiến một số trường của đối tượng con không được xác định, điều này nguy hiểm và do đó bất hợp pháp.

Trong câu lệnh gán, chỉ các trường chung cho cả hai kiểu mới được sao chép từ nguồn đến đích. Trong toán tử gán:

AnE Employee: = Được ủy quyền;

Chỉ các trường Tên, Chức danh và Tỷ lệ từ Người được ủy quyền sẽ được sao chép vào AnE Employee, vì đây là những trường duy nhất phổ biến đối với Người được ủy quyền và Người có công. Khả năng tương thích kiểu cũng hoạt động giữa các con trỏ tới các kiểu đối tượng và tuân theo các quy tắc chung giống như đối với việc triển khai đối tượng. Một con trỏ tới con có thể được gán cho một con trỏ tới cha. Với các định nghĩa trước, các phép gán con trỏ sau là hợp lệ:

TSalariedPtr: = TCommissonedPtr;

TE JobeePtr: = TSalariedPtr;

TE JobeePtr: = PCommissonedPtr;

Hãy nhớ rằng không được phép chuyển nhượng ngược lại!

Một tham số hình thức (hoặc một giá trị hoặc một tham số biến) của một kiểu đối tượng nhất định có thể coi tham số thực của nó là một đối tượng thuộc kiểu riêng của nó hoặc các đối tượng thuộc tất cả các kiểu con. Nếu bạn xác định một tiêu đề thủ tục như thế này:

thủ tục CalcFedTax (Nạn nhân: TSalaried);

thì các loại tham số thực tế có thể là TSalaried hoặc TCommissoned, nhưng không phải là TE Jobee. Nạn nhân cũng có thể là một tham số biến. Trong trường hợp này, các quy tắc tương thích tương tự được tuân theo.

Ghi chú:

Có một sự khác biệt cơ bản giữa tham số giá trị và tham số biến. Tham số giá trị là một con trỏ đến đối tượng thực được truyền dưới dạng tham số, trong khi tham số biến chỉ là bản sao của tham số thực. Hơn nữa, bản sao này chỉ bao gồm những trường được bao gồm trong kiểu của tham số giá trị chính thức. Điều này có nghĩa là tham số thực được chuyển đổi theo nghĩa đen thành kiểu của tham số chính thức. Một tham số biến giống như truyền đến một mẫu, theo nghĩa là tham số thực tế không thay đổi.

Tương tự, nếu tham số hình thức là một con trỏ tới một kiểu đối tượng, thì tham số thực có thể là một con trỏ tới kiểu đối tượng đó hoặc đến bất kỳ kiểu con nào. Hãy để tiêu đề của thủ tục được đưa ra:

thủ tục Worker.Add (AWorker: PSalared);

Khi đó, các loại thông số thực tế hợp lệ sẽ là PSalaried hoặc PCom Authorised, nhưng không phải là PEFastee.

LECTURE số 14. Người lắp ráp

1. Giới thiệu về trình lắp ráp

Đã có thời gian, trình hợp ngữ là một ngôn ngữ nếu không biết nó là một ngôn ngữ không thể làm cho máy tính làm được bất cứ điều gì hữu ích. Dần dần tình hình đã thay đổi. Nhiều phương tiện giao tiếp tiện lợi hơn với máy tính đã xuất hiện. Nhưng không giống như các ngôn ngữ khác, trình dịch hợp ngữ không chết; hơn nữa, về nguyên tắc, nó không thể làm điều này. Tại sao? Để tìm kiếm câu trả lời, chúng tôi sẽ cố gắng hiểu ngôn ngữ hợp ngữ nói chung là gì.

Tóm lại, hợp ngữ là một biểu diễn tượng trưng của ngôn ngữ máy. Tất cả các tiến trình trong máy ở mức thấp nhất, mức phần cứng chỉ được điều khiển bởi các lệnh (lệnh) của ngôn ngữ máy. Từ đó rõ ràng rằng, mặc dù có tên chung, nhưng hợp ngữ cho mỗi loại máy tính là khác nhau. Điều này cũng áp dụng cho sự xuất hiện của các chương trình được viết bằng trình dịch hợp ngữ và những ý tưởng mà ngôn ngữ này phản ánh.

Thực sự việc giải quyết các vấn đề liên quan đến phần cứng (hoặc thậm chí nhiều hơn là những vấn đề liên quan đến phần cứng, chẳng hạn như tăng tốc một chương trình chẳng hạn) là không thể nếu không có kiến ​​thức về trình hợp dịch.

Một lập trình viên hoặc bất kỳ người dùng nào khác có thể sử dụng bất kỳ công cụ cấp cao nào cho đến các chương trình để xây dựng thế giới ảo và thậm chí có thể không nghi ngờ rằng máy tính thực sự không thực hiện các lệnh của ngôn ngữ mà chương trình của nó được viết, mà là biểu diễn đã được biến đổi của chúng. dưới dạng một chuỗi lệnh nhàm chán và buồn tẻ của một ngôn ngữ hoàn toàn khác - ngôn ngữ máy. Bây giờ hãy tưởng tượng rằng một người dùng như vậy có một vấn đề không chuẩn. Ví dụ, chương trình của anh ta phải hoạt động với một số thiết bị bất thường hoặc thực hiện các hành động khác đòi hỏi kiến ​​thức về nguyên tắc của phần cứng máy tính. Bất kể ngôn ngữ mà lập trình viên viết chương trình của anh ta có tốt đến đâu, anh ta cũng không thể làm được nếu không biết trình hợp dịch. Và không phải ngẫu nhiên mà hầu hết tất cả các trình biên dịch của các ngôn ngữ bậc cao đều chứa các phương tiện kết nối các mô-đun của chúng với các mô-đun trong trình hợp dịch hoặc hỗ trợ truy cập vào trình độ lập trình trình hợp dịch.

Máy tính được tạo thành từ một số thiết bị vật lý, mỗi thiết bị được kết nối với một đơn vị, được gọi là đơn vị hệ thống. Để hiểu mục đích chức năng của chúng, chúng ta hãy xem sơ đồ khối của một máy tính điển hình (Hình 1). Nó không giả vờ chính xác tuyệt đối và chỉ nhằm mục đích hiển thị mục đích, mối quan hệ và thành phần điển hình của các yếu tố của một máy tính cá nhân hiện đại.

Cơm. 1. Sơ đồ cấu trúc của máy tính cá nhân

2. Mô hình phần mềm của bộ vi xử lý

Trên thị trường máy tính hiện nay, có rất nhiều loại máy tính khác nhau. Do đó, có thể giả định rằng người tiêu dùng sẽ có câu hỏi về cách đánh giá khả năng của một loại (hoặc kiểu máy) cụ thể và các tính năng khác biệt của nó với các máy tính thuộc kiểu (kiểu máy) khác. Để tập hợp tất cả các khái niệm đặc trưng cho một máy tính về các thuộc tính được điều khiển bằng chương trình chức năng của nó, có một thuật ngữ đặc biệt - kiến ​​trúc máy tính. Lần đầu tiên, khái niệm kiến ​​trúc máy tính bắt đầu được đề cập với sự ra đời của các máy thế hệ thứ 3 để đánh giá so sánh chúng.

Chỉ nên bắt đầu học hợp ngữ của bất kỳ máy tính nào sau khi tìm ra phần nào của máy tính có thể nhìn thấy và khả dụng để lập trình bằng ngôn ngữ này. Đây là cái gọi là mô hình chương trình máy tính, một phần của nó là mô hình chương trình vi xử lý, chứa ba mươi hai thanh ghi, ít nhiều có sẵn để lập trình viên sử dụng.

Các thanh ghi này có thể được chia thành hai nhóm lớn:

1) 6 đăng ký người dùng;

2) 16 thanh ghi hệ thống.

3. Đăng ký người dùng

Như tên của nó, thanh ghi người dùng được gọi vì lập trình viên có thể sử dụng chúng khi viết chương trình của mình. Các thanh ghi này bao gồm (Hình 2):

1) tám thanh ghi 32-bit mà người lập trình có thể sử dụng để lưu trữ dữ liệu và địa chỉ (chúng còn được gọi là thanh ghi mục đích chung (RON)):

eax / ax / ah / al;

ebx / bx / bh / bl;

edx / dx / dh / dl;

ecx / cx / ch / cl;

ebp / bp;

esi / si;

edi / di;

esp / sp.

2) sáu thanh ghi phân đoạn: cs, ds, ss, es, fs, gs;

3) thanh ghi trạng thái và điều khiển:

cờ đăng ký eflags / cờ;

thanh ghi con trỏ lệnh eip / ip.

Cơm. 2. Đăng ký người dùng

Nhiều thanh ghi này được đưa ra với một dấu gạch chéo. Đây không phải là các thanh ghi khác nhau - chúng là các phần của một thanh ghi 32 bit lớn. Chúng có thể được sử dụng trong chương trình như các đối tượng riêng biệt.

4. Thanh ghi chung

Tất cả các đăng ký của nhóm này cho phép bạn truy cập các phần "thấp hơn" của chúng. Chỉ các phần 16 và 8 bit thấp hơn của các thanh ghi này có thể được sử dụng để tự định địa chỉ. 16 bit trên của các thanh ghi này không có sẵn như các đối tượng độc lập.

Hãy liệt kê các thanh ghi thuộc nhóm thanh ghi đa dụng. Vì các thanh ghi này được đặt vật lý trong bộ vi xử lý bên trong đơn vị logic số học (AL>), chúng còn được gọi là thanh ghi ALU:

1) eax / ax / ah / al (Thanh ghi bộ tích lũy) - pin. Dùng để lưu trữ dữ liệu trung gian. Trong một số lệnh, việc sử dụng thanh ghi này là bắt buộc;

2) ebx / bx / bh / bl (Thanh ghi cơ sở) - thanh ghi cơ sở. Được sử dụng để lưu trữ địa chỉ cơ sở của một số đối tượng trong bộ nhớ;

3) ecx / cx / ch / cl (Thanh ghi đếm) - thanh ghi bộ đếm. Nó được sử dụng trong các lệnh thực hiện một số hành động lặp đi lặp lại. Việc sử dụng nó thường được ngầm hiểu và ẩn trong thuật toán của lệnh tương ứng.

Ví dụ, lệnh tổ chức vòng lặp lặp, ngoài việc chuyển quyền điều khiển đến một lệnh đặt tại một địa chỉ nhất định, phân tích và giảm giá trị của thanh ghi esx / cx từng cái một;

4) edx / dx / dh / dl (Data register) - thanh ghi dữ liệu.

Cũng giống như thanh ghi eax / ax / ah / al, nó lưu trữ dữ liệu trung gian. Một số lệnh yêu cầu sử dụng nó; đối với một số lệnh, điều này xảy ra ngầm.

Hai thanh ghi sau được sử dụng để hỗ trợ cái gọi là hoạt động chuỗi, tức là các hoạt động xử lý tuần tự các chuỗi phần tử, mỗi thanh ghi có thể dài 32, 16 hoặc 8 bit:

1) esi / si (Thanh ghi chỉ mục nguồn) - chỉ mục nguồn.

Thanh ghi này trong các hoạt động chuỗi chứa địa chỉ hiện tại của phần tử trong chuỗi nguồn;

2) edi / di (Thanh ghi chỉ số đích) - chỉ số của người nhận (người nhận). Thanh ghi này trong chuỗi hoạt động chứa địa chỉ hiện tại trong chuỗi đích.

Trong kiến ​​trúc của bộ vi xử lý ở cấp độ phần cứng và phần mềm, cấu trúc dữ liệu như một ngăn xếp được hỗ trợ. Để làm việc với ngăn xếp trong hệ thống lệnh bộ vi xử lý, có các lệnh đặc biệt và trong mô hình phần mềm bộ vi xử lý có các thanh ghi đặc biệt cho việc này:

1) esp / sp (Stack Pointer register) - thanh ghi con trỏ ngăn xếp. Chứa một con trỏ đến đầu ngăn xếp trong phân đoạn ngăn xếp hiện tại.

2) ebp / bp (Thanh ghi con trỏ cơ sở) - thanh ghi con trỏ cơ sở khung ngăn xếp. Được thiết kế để tổ chức truy cập ngẫu nhiên vào dữ liệu bên trong ngăn xếp.

Việc sử dụng ghim cứng các thanh ghi cho một số lệnh giúp cho việc mã hóa biểu diễn máy của chúng nhỏ gọn hơn. Biết các tính năng này, nếu cần thiết sẽ tiết kiệm ít nhất một vài byte bộ nhớ bị chiếm bởi mã chương trình.

5. Thanh ghi phân đoạn

Có sáu thanh ghi phân đoạn trong mô hình phần mềm vi xử lý: cs, ss, ds, es, gs, fs.

Sự tồn tại của chúng là do các đặc điểm cụ thể của việc tổ chức và sử dụng RAM của bộ vi xử lý Intel. Nó nằm ở chỗ phần cứng của bộ vi xử lý hỗ trợ tổ chức cấu trúc của chương trình dưới dạng ba phần, được gọi là phân đoạn. Theo đó, một tổ chức bộ nhớ như vậy được gọi là phân đoạn.

Để chỉ ra các phân đoạn mà chương trình có quyền truy cập tại một thời điểm cụ thể, các thanh ghi phân đoạn được dự định. Trên thực tế (với một chút chỉnh sửa) các thanh ghi này chứa các địa chỉ bộ nhớ mà từ đó các phân đoạn tương ứng bắt đầu. Logic của việc xử lý một lệnh máy được xây dựng theo cách mà khi tìm nạp một lệnh, truy cập dữ liệu chương trình hoặc truy cập ngăn xếp, các địa chỉ trong các thanh ghi phân đoạn được xác định rõ ràng được sử dụng ngầm.

Bộ vi xử lý hỗ trợ các loại phân đoạn sau.

1. Đoạn mã. Chứa các lệnh chương trình. Để truy cập phân đoạn này, thanh ghi cs (thanh ghi đoạn mã) được sử dụng - thanh ghi mã phân đoạn. Nó chứa địa chỉ của phân đoạn lệnh máy mà bộ vi xử lý có quyền truy cập (tức là các lệnh này được tải vào đường ống của bộ vi xử lý).

2. Phân đoạn dữ liệu. Chứa dữ liệu do chương trình xử lý. Để truy cập phân đoạn này, thanh ghi ds (thanh ghi phân đoạn dữ liệu) được sử dụng - một thanh ghi dữ liệu phân đoạn lưu trữ địa chỉ của phân đoạn dữ liệu của chương trình hiện tại.

3. Phân đoạn ngăn xếp. Phân đoạn này là một vùng bộ nhớ được gọi là ngăn xếp. Bộ vi xử lý tổ chức công việc với ngăn xếp theo nguyên tắc sau: phần tử cuối cùng ghi vào vùng này được chọn trước. Để truy cập phân đoạn này, thanh ghi ss (thanh ghi phân đoạn ngăn xếp) được sử dụng - thanh ghi phân đoạn ngăn xếp chứa địa chỉ của phân đoạn ngăn xếp.

4. Phân đoạn dữ liệu bổ sung. Mặc nhiên, các thuật toán để thực hiện hầu hết các lệnh máy giả định rằng dữ liệu mà chúng xử lý nằm trong phân đoạn dữ liệu, địa chỉ của phân đoạn này nằm trong thanh ghi phân đoạn ds. Nếu một phân đoạn dữ liệu không đủ cho chương trình, thì chương trình có cơ hội sử dụng thêm ba phân đoạn dữ liệu bổ sung. Nhưng không giống như phân đoạn dữ liệu chính, có địa chỉ được chứa trong thanh ghi phân đoạn ds, khi sử dụng phân đoạn dữ liệu bổ sung, địa chỉ của chúng phải được chỉ định rõ ràng bằng cách sử dụng các tiền tố xác định lại phân đoạn đặc biệt trong lệnh. Địa chỉ của các đoạn dữ liệu bổ sung phải được chứa trong các thanh ghi es, gs, fs (thanh ghi đoạn dữ liệu mở rộng).

6. Thanh ghi trạng thái và điều khiển

Bộ vi xử lý bao gồm một số thanh ghi liên tục chứa thông tin về trạng thái của cả bản thân bộ vi xử lý và chương trình có các lệnh hiện được tải vào đường ống. Các thanh ghi này bao gồm:

1) cờ đăng ký eflags / cờ;

2) Thanh ghi con trỏ lệnh eip / ip.

Sử dụng các thanh ghi này, bạn có thể nhận được thông tin về kết quả thực thi lệnh và ảnh hưởng đến trạng thái của chính bộ vi xử lý. Chúng ta hãy xem xét chi tiết hơn mục đích và nội dung của các thanh ghi này.

1. eflags / flags (thanh ghi cờ) - thanh ghi cờ. Độ sâu bit của eflags / cờ là 32/16 bit. Các bit riêng lẻ của thanh ghi này có một mục đích chức năng cụ thể và được gọi là cờ. Phần dưới của thanh ghi này hoàn toàn giống với thanh ghi cờ cho 18086. Hình 3 cho thấy nội dung của thanh ghi eflags.

Cơm. 3. Nội dung của sổ đăng ký eflags

Tùy thuộc vào cách chúng được sử dụng, các cờ của thanh ghi eflags / cờ có thể được chia thành ba nhóm:

1) tám cờ trạng thái.

Các cờ này có thể thay đổi sau khi các lệnh máy được thực thi. Các cờ trạng thái của thanh ghi eflags phản ánh các chi tiết cụ thể của kết quả thực hiện các phép toán số học hoặc logic. Điều này làm cho nó có thể phân tích trạng thái của quá trình tính toán và phản hồi nó bằng cách sử dụng các lệnh nhảy có điều kiện và lệnh gọi chương trình con. Bảng 1 liệt kê các cờ trạng thái và mục đích của chúng.

2) một cờ điều khiển.

Ký hiệu là df (Cờ thư mục). Nó nằm ở bit 10 của thanh ghi eflags và được sử dụng bởi các lệnh chuỗi. Giá trị của cờ df xác định hướng xử lý từng phần tử trong các hoạt động này: từ đầu chuỗi đến cuối (df = 0) hoặc ngược lại, từ cuối chuỗi đến đầu của nó (df = 1). Có các lệnh đặc biệt để làm việc với cờ df: domains (loại bỏ cờ df) và std (đặt cờ df). Việc sử dụng các lệnh này cho phép bạn điều chỉnh cờ df phù hợp với thuật toán và đảm bảo rằng các bộ đếm được tự động tăng hoặc giảm khi thực hiện các thao tác trên chuỗi.

3) năm cờ hệ thống.

Kiểm soát I / O, ngắt có thể che, gỡ lỗi, chuyển đổi tác vụ và chế độ ảo 8086. Các chương trình ứng dụng không nên sửa đổi các cờ này một cách không cần thiết, vì điều này sẽ khiến chương trình kết thúc trong hầu hết các trường hợp. Bảng 2 liệt kê các cờ hệ thống và mục đích của chúng.

Bảng 1. Cờ trạng tháiBảng 2. Cờ hệ thống

2. eip / ip (Instraction Pointer register) - thanh ghi con trỏ lệnh. Thanh ghi eip / ip có chiều rộng 32/16 bit và chứa phần bù của lệnh tiếp theo sẽ được thực hiện so với nội dung của thanh ghi đoạn cs trong đoạn lệnh hiện tại. Thanh ghi này không thể truy cập trực tiếp đối với lập trình viên, nhưng giá trị của nó được tải và thay đổi bởi các lệnh điều khiển khác nhau, bao gồm các lệnh cho các bước nhảy có điều kiện và không điều kiện, các thủ tục gọi và quay trở lại từ các thủ tục. Sự xuất hiện của các ngắt cũng sửa đổi thanh ghi eip / ip.

LECTURE số 15. Sổ đăng ký

1. Thanh ghi hệ thống vi xử lý

Tên của các thanh ghi này gợi ý rằng chúng thực hiện các chức năng cụ thể trong hệ thống. Việc sử dụng các thanh ghi hệ thống được quy định chặt chẽ. Chính họ là người cung cấp chế độ được bảo vệ. Chúng cũng có thể được coi là một phần của kiến ​​trúc bộ vi xử lý, được cố ý để lộ ra ngoài để một lập trình viên hệ thống đủ điều kiện có thể thực hiện các hoạt động cấp thấp nhất.

Thanh ghi hệ thống có thể được chia thành ba nhóm:

1) bốn thanh ghi điều khiển;

2) bốn thanh ghi địa chỉ hệ thống;

3) tám thanh ghi gỡ lỗi.

2. Thanh ghi điều khiển

Nhóm thanh ghi điều khiển bao gồm bốn thanh ghi: cr0, cr1, cr2, cr3. Các thanh ghi này dùng để điều khiển hệ thống chung. Thanh ghi điều khiển chỉ khả dụng cho các chương trình có mức đặc quyền 0.

Mặc dù bộ vi xử lý có bốn thanh ghi điều khiển, nhưng chỉ có ba thanh ghi trong số đó khả dụng - cr1 bị loại trừ, các chức năng của chúng vẫn chưa được xác định (nó được dành để sử dụng trong tương lai).

Thanh ghi cr0 chứa các cờ hệ thống điều khiển các chế độ hoạt động của bộ vi xử lý và phản ánh trạng thái của nó trên toàn cầu, bất kể các tác vụ cụ thể đang được thực hiện.

Mục đích của cờ hệ thống:

1) pe (Protect Enable), bit 0 - bật chế độ hoạt động được bảo vệ. Trạng thái của cờ này hiển thị trong hai chế độ - thực (pe = 0) hoặc được bảo vệ (pe = 1) - bộ vi xử lý đang hoạt động tại một thời điểm nhất định;

2) mp (Math Present), bit 1 - sự hiện diện của bộ đồng xử lý. Luôn luôn là 1;

3) ts (Task Switched), bit 3 - chuyển đổi nhiệm vụ. Bộ xử lý tự động đặt bit này khi nó chuyển sang tác vụ khác;

4) am (Alignment Mask), bit 18 - mặt nạ căn chỉnh. Bit này cho phép (am = 1) hoặc tắt (am = 0) điều khiển căn chỉnh;

5) cd (Cache Disable), bit 30 - tắt bộ nhớ cache.

Sử dụng bit này, bạn có thể tắt (cd = 1) hoặc cho phép (cd = 0) sử dụng bộ đệm bên trong (bộ đệm cấp đầu tiên);

6) pg (PaGing), bit 31 - bật (pg = 1) hoặc vô hiệu hóa (pg = 0) phân trang.

Cờ được sử dụng trong mô hình phân trang của tổ chức bộ nhớ.

Thanh ghi cr2 được sử dụng trong phân trang RAM để đăng ký tình huống khi lệnh hiện tại truy cập địa chỉ chứa trong trang bộ nhớ hiện không có trong bộ nhớ.

Trong trường hợp này, ngoại lệ số 14 xảy ra trong bộ vi xử lý và địa chỉ 32 bit tuyến tính của lệnh gây ra ngoại lệ này được ghi vào thanh ghi cr2. Với thông tin này, trình xử lý ngoại lệ 14 xác định trang mong muốn, hoán đổi trang đó vào bộ nhớ và tiếp tục hoạt động bình thường của chương trình;

Thanh ghi cr3 cũng được sử dụng cho bộ nhớ phân trang. Đây là cái gọi là thanh ghi thư mục trang cấp một. Nó chứa địa chỉ cơ sở vật lý 20 bit của thư mục trang của nhiệm vụ hiện tại. Thư mục này chứa 1024 bộ mô tả 32 bit, mỗi bộ mô tả chứa địa chỉ của bảng trang cấp hai. Đổi lại, mỗi bảng trang cấp hai chứa 1024 bộ mô tả 32-bit đánh địa chỉ các khung trang trong bộ nhớ. Kích thước khung trang là 4 KB.

3. Thanh ghi địa chỉ hệ thống

Các thanh ghi này còn được gọi là thanh ghi quản lý bộ nhớ.

Chúng được thiết kế để bảo vệ các chương trình và dữ liệu trong chế độ đa nhiệm của bộ vi xử lý. Khi hoạt động ở chế độ được bảo vệ bằng bộ vi xử lý, không gian địa chỉ được chia thành:

1) toàn cầu - chung cho tất cả các nhiệm vụ;

2) cục bộ - riêng biệt cho từng nhiệm vụ.

Sự tách biệt này giải thích sự hiện diện của các thanh ghi hệ thống sau trong kiến ​​trúc bộ vi xử lý:

1) thanh ghi của bảng bộ mô tả toàn cục gdtr (Global Descriptor Table Register), có kích thước 48 bit và chứa địa chỉ cơ sở 32 bit (bit 16-47) của bảng bộ mô tả toàn cục GDT và 16 bit (bit 0-15) giá trị giới hạn, có kích thước tính bằng byte của bảng GDT;

2) thanh ghi bảng bộ mô tả cục bộ ldtr (Thanh ghi bảng bộ mô tả cục bộ), có kích thước 16 bit và chứa cái gọi là bộ chọn của bộ mô tả của bảng bộ mô tả cục bộ LDT Bộ chọn này là một con trỏ trong bảng GDT, nó mô tả phân đoạn chứa bảng mô tả cục bộ LDT;

3) thanh ghi của bảng bộ mô tả ngắt idtr (Interrupt Descriptor Table Register), có kích thước 48 bit và chứa địa chỉ cơ sở 32 bit (bit 16-47) của bảng bộ mô tả ngắt IDT và 16 bit (bit 0-15) giá trị giới hạn, có kích thước tính bằng byte của bảng IDT;

4) Thanh ghi tác vụ 16 bit tr (Thanh ghi tác vụ), giống như thanh ghi ldtr, chứa một bộ chọn, tức là một con trỏ tới một bộ mô tả trong bảng GDT. Bộ mô tả này mô tả Trạng thái Phân đoạn Nhiệm vụ (TSS) hiện tại. Phân đoạn này được tạo cho từng tác vụ trong hệ thống, có cấu trúc được quy định chặt chẽ và chứa bối cảnh (trạng thái hiện tại) của tác vụ. Mục đích chính của các phân đoạn TSS là lưu trạng thái hiện tại của một tác vụ tại thời điểm chuyển sang một tác vụ khác.

4. ​​Thanh ghi gỡ lỗi

Đây là một nhóm thanh ghi rất thú vị dùng để gỡ lỗi phần cứng. Các công cụ gỡ lỗi phần cứng lần đầu tiên xuất hiện trong bộ vi xử lý i486. Về phần cứng, bộ vi xử lý chứa tám thanh ghi gỡ lỗi, nhưng chỉ sáu trong số chúng thực sự được sử dụng.

Các thanh ghi dr0, dr1, dr2, dr3 có chiều rộng 32 bit và được thiết kế để đặt địa chỉ tuyến tính của bốn điểm dừng. Cơ chế được sử dụng trong trường hợp này như sau: bất kỳ địa chỉ nào do chương trình hiện tại tạo ra sẽ được so sánh với các địa chỉ trong thanh ghi dr0... dr3 và nếu có sự trùng khớp, một ngoại lệ gỡ lỗi với số 1 sẽ được tạo.

Thanh ghi dr6 được gọi là thanh ghi trạng thái gỡ lỗi. Các bit trong thanh ghi này được đặt theo nguyên nhân khiến số ngoại lệ cuối cùng xảy ra.

Chúng tôi liệt kê các bit này và mục đích của chúng:

1) b0 - nếu bit này được đặt thành 1, thì ngoại lệ cuối cùng (ngắt) xảy ra do đạt được điểm kiểm tra được xác định trong thanh ghi dr0;

2) b1 - tương tự như b0, nhưng đối với một điểm kiểm tra trong thanh ghi dr1;

3) b2 - tương tự như b0, nhưng đối với một điểm kiểm tra trong thanh ghi dr2;

4) bЗ - tương tự như b0, nhưng đối với một điểm kiểm tra trong thanh ghi dr3;

5) bd (bit 13) - dùng để bảo vệ các thanh ghi gỡ lỗi;

6) bs (bit 14) - đặt thành 1 nếu ngoại lệ 1 được gây ra bởi trạng thái của cờ tf = 1 trong thanh ghi eflags;

7) bt (bit 15) được đặt thành 1 nếu ngoại lệ 1 được gây ra bởi việc chuyển sang một nhiệm vụ với bit bẫy được đặt trong TSS t = 1.

Tất cả các bit khác trong thanh ghi này được điền bằng các số không. Trình xử lý ngoại lệ 1, dựa trên nội dung của dr6, phải xác định lý do cho ngoại lệ và thực hiện các hành động cần thiết.

Thanh ghi dr7 được gọi là thanh ghi điều khiển gỡ lỗi. Nó chứa các trường cho từng thanh ghi trong số bốn thanh ghi điểm ngắt gỡ lỗi cho phép bạn chỉ định các điều kiện sau mà theo đó ngắt sẽ được tạo:

1) vị trí đăng ký trạm kiểm soát - chỉ trong nhiệm vụ hiện tại hoặc trong bất kỳ nhiệm vụ nào. Các bit này chiếm 8 bit thấp hơn của thanh ghi dr7 (2 bit cho mỗi điểm ngắt (thực ra là điểm ngắt) được thiết lập bởi các thanh ghi dr0, dr1, dr2, dr3, tương ứng).

Bit đầu tiên của mỗi cặp được gọi là độ phân giải cục bộ; thiết lập nó cho biết điểm ngắt có hiệu lực nếu nó nằm trong không gian địa chỉ của tác vụ hiện tại.

Bit thứ hai trong mỗi cặp xác định quyền toàn cục, cho biết rằng điểm ngắt đã cho hợp lệ trong không gian địa chỉ của tất cả các tác vụ nằm trong hệ thống;

2) kiểu truy cập mà ngắt được bắt đầu: chỉ khi tìm nạp một lệnh, khi viết hoặc khi ghi / đọc dữ liệu. Các bit xác định tính chất này của sự xuất hiện của ngắt nằm ở phần trên của thanh ghi này. Hầu hết các thanh ghi hệ thống đều có thể truy cập theo chương trình.

LECTURE số 16. Các chương trình lắp ráp

1. Cấu trúc của chương trình trong trình hợp dịch

Một chương trình hợp ngữ là một tập hợp các khối bộ nhớ được gọi là các đoạn bộ nhớ. Một chương trình có thể bao gồm một hoặc nhiều khối-phân đoạn này. Mỗi phân đoạn chứa một tập hợp các câu ngôn ngữ, mỗi câu trong số đó chiếm một dòng mã chương trình riêng biệt.

Câu lệnh hợp ngữ có bốn loại:

1) các lệnh hoặc hướng dẫn, là biểu tượng tương tự của các lệnh máy. Trong quá trình dịch, các lệnh hợp ngữ được chuyển thành các lệnh tương ứng của tập lệnh bộ vi xử lý;

2) macro. Đây là những câu của văn bản của chương trình được chính thức hóa theo một cách nhất định và được thay thế bằng các câu khác trong quá trình phát sóng;

3) các chỉ thị, là một chỉ dẫn để trình dịch trình hợp ngữ thực hiện các hành động nhất định. Các chỉ thị không có đối tác trong biểu diễn máy;

4) dòng chú thích có chứa bất kỳ ký tự nào, bao gồm cả các chữ cái trong bảng chữ cái tiếng Nga. Bình luận bị bỏ qua bởi người dịch.

2. Cú pháp hợp ngữ

Các câu tạo nên một chương trình có thể là một cấu trúc cú pháp tương ứng với một lệnh, macro, chỉ thị hoặc chú thích. Để người dịch hợp ngữ có thể nhận ra chúng, chúng phải được hình thành theo những quy tắc cú pháp nhất định. Để làm điều này, cách tốt nhất là sử dụng mô tả chính thức về cú pháp của ngôn ngữ, giống như các quy tắc của ngữ pháp. Các cách phổ biến nhất để mô tả một ngôn ngữ lập trình theo cách này là sơ đồ cú pháp và các dạng Backus-Naur mở rộng. Để sử dụng thực tế, sơ đồ cú pháp thuận tiện hơn. Ví dụ, cú pháp của các câu lệnh hợp ngữ có thể được mô tả bằng cách sử dụng các sơ đồ cú pháp được hiển thị trong các hình sau.

Cơm. 4. Định dạng câu Assembler

Cơm. 5. Định dạng Chỉ thị

Cơm. 6. Định dạng các lệnh và macro

Trên các bản vẽ này:

1) tên nhãn - một định danh, giá trị của nó là địa chỉ của byte đầu tiên của câu mã nguồn của chương trình mà nó biểu thị;

2) tên - một định danh phân biệt chỉ thị này với các chỉ thị khác cùng tên. Là kết quả của quá trình xử lý bởi trình hợp dịch của một chỉ thị nhất định, một số đặc điểm nhất định có thể được gán cho tên này;

3) mã hoạt động (COP) và chỉ thị là các ký hiệu dễ nhớ của lệnh máy, lệnh macro hoặc lệnh phiên dịch tương ứng;

4) Toán hạng - các phần của lệnh, chỉ thị macro hoặc trình hợp dịch, biểu thị các đối tượng mà các hoạt động được thực hiện. Toán hạng Assembler được mô tả bằng các biểu thức với hằng số và văn bản, nhãn biến và mã định danh sử dụng các dấu hiệu toán tử và một số từ dành riêng.

Làm thế nào để sử dụng các sơ đồ cú pháp? Nó rất đơn giản: tất cả những gì bạn cần làm là tìm và sau đó đi theo đường dẫn từ đầu vào của sơ đồ (bên trái) đến đầu ra của nó (bên phải). Nếu một đường dẫn như vậy tồn tại, thì câu hoặc cấu trúc đó đúng về mặt cú pháp. Nếu không có đường dẫn như vậy, thì trình biên dịch sẽ không chấp nhận cấu trúc này. Khi làm việc với các sơ đồ cú pháp, hãy chú ý đến hướng của đường di chuyển được chỉ ra bởi các mũi tên, vì trong số các đường dẫn có thể có những đường có thể đi theo từ phải sang trái. Trên thực tế, sơ đồ cú pháp phản ánh logic của trình dịch khi phân tích cú pháp các câu đầu vào của chương trình.

Các ký tự được phép khi viết văn bản của chương trình là:

1) tất cả các chữ cái Latinh: A - Z, a - z. Trong trường hợp này, chữ hoa và chữ thường được coi là tương đương nhau;

2) các số từ 0 đến 9;

3) các dấu ?, @, S, _, &;

4) dải phân cách.

Các câu lắp ghép được hình thành từ các từ vựng, là các chuỗi ký hiệu ngôn ngữ hợp lệ không thể tách rời về mặt cú pháp có ý nghĩa đối với người dịch.

Các mã thông báo như sau.

1. Định danh là chuỗi các ký tự hợp lệ được sử dụng để chỉ định các đối tượng chương trình như mã hoạt động, tên biến và tên nhãn. Quy tắc viết mã định danh như sau: mã định danh có thể bao gồm một hoặc nhiều ký tự. Dưới dạng ký tự, bạn có thể sử dụng các chữ cái trong bảng chữ cái Latinh, số và một số ký tự đặc biệt - _,?, $, @. Mã định danh không được bắt đầu bằng ký tự chữ số. Độ dài của mã định danh có thể lên đến 255 ký tự, mặc dù trình dịch chỉ chấp nhận 32 ký tự đầu tiên và bỏ qua phần còn lại. Bạn có thể điều chỉnh độ dài của số nhận dạng có thể sử dụng tùy chọn dòng lệnh mv. Ngoài ra, có thể yêu cầu người dịch phân biệt giữa chữ hoa và chữ thường hoặc bỏ qua sự khác biệt của chúng (điều này được thực hiện theo mặc định). Các tùy chọn dòng lệnh / mu, / ml, / mx được sử dụng cho việc này.

2. Chuỗi ký tự - chuỗi ký tự được đặt trong dấu ngoặc kép hoặc đơn.

3. Các số nguyên thuộc một trong các hệ số sau: nhị phân, thập phân, thập lục phân. Việc nhận dạng các số khi viết chúng trong các chương trình hợp ngữ được thực hiện theo các quy tắc nhất định:

1) số thập phân không yêu cầu bất kỳ ký tự bổ sung nào được xác định, ví dụ, 25 hoặc 139;

2) để xác định các số nhị phân trong văn bản nguồn của chương trình, cần phải đặt chữ "b" trong tiếng Latinh sau khi viết các số không và các số tạo nên chúng, ví dụ, 10010101 b;

3) Số thập lục phân có nhiều quy ước hơn khi viết:

a) Thứ nhất, chúng gồm các số 0...9, chữ thường và chữ in hoa của bảng chữ cái Latinh a, b, c, d, e, Gili D B, C, D, E, E

b) thứ hai, người dịch có thể gặp khó khăn khi nhận dạng các số thập lục phân do chúng chỉ có thể bao gồm các chữ số 0...9 (ví dụ: 190845) hoặc bắt đầu bằng một chữ cái trong bảng chữ cái Latinh (ví dụ: efl5 ). Để “giải thích” cho người dịch rằng mã thông báo nhất định không phải là số thập phân hoặc số nhận dạng, lập trình viên phải làm nổi bật số thập lục phân theo cách đặc biệt. Để thực hiện việc này, hãy viết chữ cái Latinh “h” vào cuối chuỗi các chữ số thập lục phân tạo thành số thập lục phân. Đây là điều bắt buộc. Nếu một số thập lục phân bắt đầu bằng một chữ cái thì số 0 đứng đầu sẽ được viết trước nó: 5 eflXNUMX h.

Vì vậy, chúng tôi đã tìm ra cách các câu của một chương trình hợp ngữ được xây dựng. Nhưng đây chỉ là cái nhìn phiến diện nhất.

Hầu hết mọi câu đều có mô tả về đối tượng mà trên đó hoặc với sự trợ giúp của một số hành động được thực hiện. Các đối tượng này được gọi là toán hạng. Chúng có thể được định nghĩa như sau: toán hạng là các đối tượng (một số giá trị, thanh ghi hoặc vị trí bộ nhớ) bị ảnh hưởng bởi lệnh hoặc chỉ thị, hoặc chúng là đối tượng xác định hoặc tinh chỉnh hành động của lệnh hoặc chỉ thị.

Toán hạng có thể được kết hợp với các toán tử số học, logic, bitwise và thuộc tính để tính toán một số giá trị hoặc xác định vị trí bộ nhớ sẽ bị ảnh hưởng bởi một lệnh hoặc chỉ thị nhất định.

Chúng ta hãy xem xét chi tiết hơn các đặc điểm của các toán hạng trong cách phân loại sau:

1) các toán hạng liên tục hoặc ngay lập tức - một số, chuỗi, tên hoặc biểu thức có một số giá trị cố định. Tên không được thay đổi vị trí, nghĩa là nó không được phụ thuộc vào địa chỉ của chương trình được tải vào bộ nhớ. Ví dụ, nó có thể được định nghĩa bằng các toán tử bằng hoặc =;

2) toán hạng địa chỉ, thiết lập vị trí vật lý của toán hạng trong bộ nhớ bằng cách chỉ định hai thành phần của địa chỉ: phân đoạn và độ lệch (Hình 7);

Cơm. 7. Cú pháp mô tả toán hạng địa chỉ

3) các toán hạng có thể định vị lại - bất kỳ tên tượng trưng nào đại diện cho một số địa chỉ bộ nhớ. Các địa chỉ này có thể chỉ ra vị trí trong bộ nhớ của một số lệnh (nếu toán hạng là nhãn) hoặc dữ liệu (nếu toán hạng là tên của vị trí bộ nhớ trong đoạn dữ liệu).

Toán hạng định vị lại khác với toán hạng địa chỉ ở chỗ chúng không gắn với một địa chỉ bộ nhớ vật lý cụ thể. Thành phần phân đoạn của địa chỉ của toán hạng được di chuyển là không xác định và sẽ được xác định sau khi chương trình được tải vào bộ nhớ để thực thi.

Bộ đếm địa chỉ là một loại toán hạng cụ thể. Nó được ký hiệu bằng dấu S. Đặc trưng của toán hạng này là khi trình dịch hợp ngữ gặp ký hiệu này trong chương trình nguồn, nó sẽ thay thế giá trị hiện tại của bộ đếm địa chỉ. Giá trị của bộ đếm địa chỉ, hoặc bộ đếm vị trí như đôi khi nó được gọi, là độ lệch của lệnh máy hiện tại từ đầu đoạn mã. Trong định dạng danh sách, cột thứ hai hoặc thứ ba tương ứng với bộ đếm địa chỉ (tùy thuộc vào việc cột có cấp độ lồng nhau có hiện diện hay không trong danh sách). Nếu chúng ta lấy bất kỳ danh sách nào làm ví dụ, thì rõ ràng là khi trình biên dịch xử lý lệnh trình hợp dịch tiếp theo, bộ đếm địa chỉ sẽ tăng theo độ dài của lệnh máy được tạo. Điều quan trọng là phải hiểu chính xác điểm này. Ví dụ, xử lý chỉ thị trình hợp dịch không thay đổi bộ đếm. Các lệnh, không giống như các lệnh của trình hợp dịch, chỉ là các lệnh để trình biên dịch thực hiện một số hành động nhất định để tạo thành biểu diễn máy của chương trình và đối với chúng, trình biên dịch không tạo ra bất kỳ cấu trúc nào trong bộ nhớ.

Khi sử dụng một biểu thức như vậy để nhảy, hãy lưu ý độ dài của chính lệnh mà biểu thức này được sử dụng, vì giá trị của bộ đếm địa chỉ tương ứng với độ lệch trong phân đoạn lệnh của lệnh này, chứ không phải của lệnh theo sau nó. . Trong ví dụ của chúng tôi, lệnh jmp có 2 byte. Nhưng hãy cẩn thận, độ dài của một lệnh phụ thuộc vào toán hạng mà nó sử dụng. Một lệnh có toán hạng thanh ghi sẽ ngắn hơn lệnh có một trong các toán hạng của nó nằm trong bộ nhớ. Trong hầu hết các trường hợp, thông tin này có thể thu được bằng cách biết định dạng của lệnh máy và bằng cách phân tích cột liệt kê với mã đối tượng của lệnh;

4) Toán hạng thanh ghi chỉ là một tên thanh ghi. Trong một chương trình hợp ngữ, bạn có thể sử dụng tên của tất cả các thanh ghi mục đích chung và hầu hết các thanh ghi hệ thống;

5) toán hạng cơ sở và chỉ số. Loại toán hạng này được sử dụng để triển khai cơ sở gián tiếp, địa chỉ chỉ mục gián tiếp, hoặc các kết hợp và phần mở rộng của chúng;

6) Toán hạng cấu trúc được sử dụng để truy cập một phần tử cụ thể của kiểu dữ liệu phức tạp được gọi là cấu trúc.

Bản ghi (tương tự như kiểu cấu trúc) được sử dụng để truy cập vào một trường bit của bản ghi nào đó.

Toán hạng là các thành phần cơ bản tạo thành một phần của lệnh máy, biểu thị các đối tượng mà thao tác được thực hiện. Trong trường hợp tổng quát hơn, các toán hạng có thể được bao gồm như các thành phần trong các dạng phức tạp hơn được gọi là biểu thức. Biểu thức là sự kết hợp của toán hạng và toán tử, được coi là một tổng thể. Kết quả của đánh giá biểu thức có thể là địa chỉ của một số ô nhớ hoặc một giá trị không đổi (tuyệt đối) nào đó.

Chúng tôi đã xem xét các loại toán hạng có thể có. Bây giờ chúng ta liệt kê các loại toán tử hợp ngữ có thể có và các quy tắc cú pháp để hình thành biểu thức trình hợp dịch, và đưa ra một mô tả ngắn gọn về các toán tử.

1. Các toán tử số học. Bao gồm các:

1) một ngôi "+" và "-";

2) nhị phân "+" và "-";

3) phép nhân "*";

4) phép chia số nguyên "/";

5) nhận phần còn lại từ phép chia "mod".

Các toán tử này được đặt ở các mức ưu tiên 6,7,8 trong Bảng 4.

Cơm. 8. Cú pháp của các phép toán số học

2. Các toán tử Shift chuyển biểu thức theo số bit được chỉ định (Hình 9).

Cơm. 9. Cú pháp của toán tử shift

3. Các toán tử so sánh (trả về giá trị "true" hoặc "false") nhằm mục đích hình thành các biểu thức logic (Hình 10 và Bảng 3). Giá trị logic "true" tương ứng với một đơn vị kỹ thuật số và "false" - bằng không.

Cơm. 10. Cú pháp của các toán tử so sánh

Bảng 3. Các toán tử so sánh

4. Các toán tử logic thực hiện các phép toán bit trên các biểu thức (Hình 11). Biểu thức phải là tuyệt đối, tức là, giá trị số mà người dịch có thể tính được.

Cơm. 11. Cú pháp của các toán tử logic

5. Toán tử chỉ mục []. Dấu ngoặc đơn cũng là một toán tử và người dịch coi sự hiện diện của chúng như một lệnh để thêm giá trị của biểu thức_1 vào sau các dấu ngoặc này với biểu thức_2 được đặt trong dấu ngoặc (Hình 12).

Cơm. 12. Cú pháp toán tử chỉ mục

Lưu ý rằng ký hiệu sau được chấp nhận trong tài liệu về trình hợp dịch: khi văn bản đề cập đến nội dung của một sổ đăng ký, tên của nó được đặt trong dấu ngoặc đơn. Chúng tôi cũng sẽ tuân theo ký hiệu này.

6. Toán tử định nghĩa lại kiểu ptr được sử dụng để xác định lại hoặc xác định loại nhãn hoặc biến được xác định bởi một biểu thức (Hình 13).

Kiểu có thể nhận một trong các giá trị sau: byte, word, dword, qword, tbyte, near, far.

Cơm. 13. Cú pháp của toán tử xác định lại kiểu

7. Toán tử xác định lại phân đoạn ":" (dấu hai chấm) buộc tính toán địa chỉ vật lý liên quan đến thành phần phân đoạn được chỉ định cụ thể: "tên thanh ghi phân đoạn", "tên phân đoạn" từ chỉ thị SEGMENT tương ứng hoặc "tên nhóm" (Hình . 14). Khi thảo luận về phân đoạn, chúng ta đã nói về thực tế là bộ vi xử lý ở cấp phần cứng hỗ trợ ba loại phân đoạn - mã, ngăn xếp và dữ liệu. Hỗ trợ phần cứng này là gì? Ví dụ, để chọn việc thực hiện lệnh tiếp theo, bộ vi xử lý nhất thiết phải xem nội dung của thanh ghi phân đoạn cs và chỉ nó. Và thanh ghi này, như chúng ta đã biết, chứa địa chỉ vật lý (chưa được dịch chuyển) của phần đầu của đoạn lệnh. Để lấy địa chỉ của một lệnh cụ thể, bộ vi xử lý cần nhân nội dung của cs với 16 (có nghĩa là dịch chuyển với bốn bit) và thêm giá trị 20 bit kết quả vào nội dung 16 bit của thanh ghi ip. Điều tương tự cũng xảy ra khi bộ vi xử lý xử lý các toán hạng trong lệnh máy. Nếu nó thấy rằng toán hạng là một địa chỉ (địa chỉ hiệu quả chỉ là một phần của địa chỉ vật lý), thì nó sẽ biết phân đoạn nào cần tìm kiếm nó - theo mặc định, đó là phân đoạn có địa chỉ bắt đầu được lưu trữ trong thanh ghi phân đoạn ds .

Nhưng những gì về phân đoạn ngăn xếp? Trong bối cảnh chúng tôi đang xem xét, chúng tôi quan tâm đến thanh ghi sp và bp. Nếu bộ vi xử lý coi một trong các thanh ghi này là toán hạng (hoặc một phần của nó, nếu toán hạng là một biểu thức), thì theo mặc định, nó tạo địa chỉ vật lý của toán hạng, sử dụng nội dung của thanh ghi ss làm thành phần phân đoạn của nó. Đây là một tập hợp các vi chương trình trong bộ điều khiển vi chương trình, mỗi vi chương trình thực hiện một trong các lệnh trong hệ thống lệnh của máy vi xử lý. Mỗi vi chương trình hoạt động theo thuật toán riêng của nó. Tất nhiên, bạn không thể thay đổi nó, nhưng bạn có thể sửa lại một chút. Điều này được thực hiện bằng cách sử dụng trường tiền tố lệnh máy tùy chọn. Nếu chúng ta đồng ý về cách hoạt động của lệnh, thì trường này bị thiếu. Nếu chúng ta muốn thực hiện một sửa đổi (tất nhiên, nếu nó được phép đối với một lệnh cụ thể) đối với thuật toán của lệnh, thì cần phải tạo một tiền tố thích hợp.

Tiền tố là một giá trị một byte có giá trị số xác định mục đích của nó. Bộ vi xử lý nhận ra theo giá trị được chỉ định rằng byte này là tiền tố và công việc tiếp theo của chương trình vi xử lý được thực hiện có tính đến lệnh đã nhận để sửa công việc của nó. Bây giờ chúng tôi quan tâm đến một trong số chúng - tiền tố thay thế phân đoạn (định nghĩa lại). Mục đích của nó là để chỉ ra cho bộ vi xử lý (và trên thực tế là phần sụn) rằng chúng tôi không muốn sử dụng phân đoạn mặc định. Tất nhiên, khả năng tái định nghĩa như vậy là có giới hạn. Đoạn lệnh không thể được xác định lại, địa chỉ của lệnh thực thi tiếp theo được xác định duy nhất bởi cặp cs: ip. Và đây là các phân đoạn của một ngăn xếp và dữ liệu - điều đó hoàn toàn có thể xảy ra. Đó là những gì toán tử ":" dành cho. Trình dịch hợp ngữ, xử lý câu lệnh này, tạo tiền tố thay thế phân đoạn một byte tương ứng.

Cơm. 14. Cú pháp của toán tử xác định lại phân đoạn

8. Toán tử đặt tên kiểu cấu trúc "." (Dấu chấm) cũng buộc trình biên dịch thực hiện các phép tính nhất định nếu nó xảy ra trong một biểu thức.

9. Toán tử để lấy thành phần phân đoạn của địa chỉ của biểu thức seg trả về địa chỉ vật lý của phân đoạn cho biểu thức (Hình 15), có thể là nhãn, biến, tên phân đoạn, tên nhóm hoặc một số tên tượng trưng .

Cơm. 15. Cú pháp của toán tử nhận thành phần phân đoạn

10. Toán tử để lấy phần bù của phần bù biểu thức cho phép bạn nhận giá trị phần bù của biểu thức (Hình 16) tính bằng byte liên quan đến phần đầu của đoạn mà trong đó biểu thức được xác định.

Cơm. 16. Cú pháp của toán tử lấy bù

Như trong các ngôn ngữ bậc cao, việc thực thi các toán tử hợp ngữ khi đánh giá các biểu thức được thực hiện theo mức độ ưu tiên của chúng (Bảng 4). Các thao tác có cùng mức độ ưu tiên được thực hiện tuần tự từ trái sang phải. Có thể thay đổi thứ tự thực hiện bằng cách đặt các dấu ngoặc đơn có mức độ ưu tiên cao nhất.

Bảng 4. Các nhà khai thác và mức độ ưu tiên của họ

3. Chỉ thị Phân đoạn

Trong quá trình thảo luận trước, chúng ta đã tìm hiểu tất cả các quy tắc cơ bản để viết lệnh và toán hạng trong một chương trình hợp ngữ. Câu hỏi làm thế nào để định dạng đúng chuỗi lệnh để trình dịch có thể xử lý chúng và bộ vi xử lý có thể thực thi chúng vẫn còn bỏ ngỏ.

Khi xem xét kiến ​​trúc của bộ vi xử lý, chúng tôi biết rằng nó có sáu thanh ghi phân đoạn, qua đó nó có thể hoạt động đồng thời:

1) với một đoạn mã;

2) với một phân đoạn ngăn xếp;

3) với một phân đoạn dữ liệu;

4) với ba phân đoạn dữ liệu bổ sung.

Nhắc lại một lần nữa rằng một phân đoạn là một vùng bộ nhớ bị chiếm bởi các lệnh và (hoặc) dữ liệu có địa chỉ được tính liên quan đến giá trị trong thanh ghi phân đoạn tương ứng.

Mô tả cú pháp của một phân đoạn trong trình hợp dịch là cấu trúc được hiển thị trong Hình 17:

Cơm. 17. Cú pháp mô tả phân đoạn

Điều quan trọng cần lưu ý là chức năng của một phân đoạn có phần rộng hơn so với việc chỉ đơn giản là chia nhỏ chương trình thành các khối mã, dữ liệu và ngăn xếp. Phân đoạn là một phần của cơ chế tổng quát hơn liên quan đến khái niệm lập trình mô-đun. Nó liên quan đến việc thống nhất thiết kế các mô-đun đối tượng do trình biên dịch tạo ra, bao gồm cả những mô-đun từ các ngôn ngữ lập trình khác nhau. Điều này cho phép bạn kết hợp các chương trình được viết bằng các ngôn ngữ khác nhau. Các toán hạng trong chỉ thị SEGMENT nhằm mục đích thực hiện các tùy chọn khác nhau cho sự kết hợp như vậy.

Hãy xem xét chúng chi tiết hơn.

1. Thuộc tính căn chỉnh phân đoạn (kiểu căn chỉnh) cho trình liên kết biết để đảm bảo rằng phần đầu của phân đoạn được đặt trên ranh giới xác định. Điều này rất quan trọng vì căn chỉnh phù hợp giúp truy cập dữ liệu nhanh hơn trên bộ vi xử lý i80x86. Các giá trị hợp lệ cho thuộc tính này như sau:

1) BYTE - căn chỉnh không được thực hiện. Một phân đoạn có thể bắt đầu tại bất kỳ địa chỉ bộ nhớ nào;

2) WORD - phân đoạn bắt đầu từ một địa chỉ là bội số của hai, tức là bit cuối cùng (ít quan trọng nhất) của địa chỉ vật lý là 0 (được căn chỉnh với ranh giới từ);

3) DWORD - phân đoạn bắt đầu từ một địa chỉ là bội số của bốn, tức là hai bit cuối cùng (ít quan trọng nhất) là 0 (căn chỉnh ranh giới từ kép);

4) PARA - đoạn bắt đầu từ địa chỉ là bội số của 16, tức là chữ số thập lục phân cuối cùng của địa chỉ phải là Oh (căn chỉnh với ranh giới đoạn);

5) PAGE - phân đoạn bắt đầu tại một địa chỉ là bội số của 256, tức là hai chữ số thập lục phân cuối cùng phải là 00h (được căn chỉnh theo đường biên của trang 256 byte);

6) MEMPAGE - phân đoạn bắt đầu từ địa chỉ là bội số của 4 KB, tức là ba chữ số thập lục phân cuối cùng phải là OOOh (địa chỉ của trang bộ nhớ 4 KB tiếp theo). Loại căn chỉnh mặc định là PARA.

2. Thuộc tính phân đoạn kết hợp (kiểu tổ hợp) cho trình liên kết biết cách kết hợp các phân đoạn của các mô-đun khác nhau có cùng tên. Các giá trị thuộc tính kết hợp phân đoạn có thể là:

1) RIÊNG TƯ - phân đoạn sẽ không được kết hợp với các phân đoạn khác có cùng tên bên ngoài mô-đun này;

2) CÔNG CỘNG - khiến trình liên kết kết nối tất cả các phân đoạn có cùng tên. Phân đoạn mới được hợp nhất sẽ là toàn bộ và liên tục. Tất cả địa chỉ (hiệu số) của các đối tượng, và điều này có thể phụ thuộc vào loại lệnh và phân đoạn dữ liệu, sẽ được tính toán liên quan đến phần đầu của phân đoạn mới này;

3) COMMON - đặt tất cả các phân đoạn có cùng tên tại cùng một địa chỉ. Tất cả các phân đoạn có tên đã cho sẽ chồng lên nhau và chia sẻ bộ nhớ. Kích thước của phân đoạn kết quả sẽ bằng kích thước của phân đoạn lớn nhất;

4) AT xxxx - định vị đoạn ở địa chỉ tuyệt đối của đoạn (đoạn là dung lượng bộ nhớ, bội số của 16; do đó, chữ số thập lục phân cuối cùng của địa chỉ đoạn là 0). Địa chỉ tuyệt đối của một đoạn văn được đưa ra bởi xxx. Trình liên kết đặt phân đoạn tại một địa chỉ bộ nhớ nhất định (ví dụ: điều này có thể được sử dụng để truy cập bộ nhớ video hoặc vùng ROM>), có tính đến thuộc tính kết hợp. Về mặt vật lý, điều này có nghĩa là phân đoạn, khi được tải vào bộ nhớ, sẽ được định vị bắt đầu từ địa chỉ tuyệt đối này của đoạn văn, nhưng để truy cập nó, giá trị được chỉ định trong thuộc tính phải được tải vào thanh ghi phân đoạn tương ứng. Tất cả các nhãn và địa chỉ trong một phân đoạn được xác định như vậy là liên quan đến địa chỉ tuyệt đối đã cho;

5) STACK - định nghĩa của một đoạn ngăn xếp. Làm cho trình liên kết kết nối tất cả các phân đoạn có cùng tên và tính toán các địa chỉ trong các phân đoạn này liên quan đến thanh ghi ss. Kiểu kết hợp STACK (ngăn xếp) tương tự như kiểu kết hợp PUBLIC, ngoại trừ thanh ghi ss là thanh ghi phân đoạn chuẩn cho các phân đoạn ngăn xếp. Thanh ghi sp được đặt ở cuối đoạn ngăn xếp được nối. Nếu không có phân đoạn ngăn xếp nào được chỉ định, trình liên kết sẽ đưa ra cảnh báo rằng không tìm thấy phân đoạn ngăn xếp nào. Nếu một phân đoạn ngăn xếp đã được tạo và kiểu STACK kết hợp không được sử dụng, lập trình viên phải nạp rõ ràng địa chỉ phân đoạn vào thanh ghi ss (tương tự như thanh ghi ds).

Thuộc tính kết hợp mặc định là RIÊNG TƯ.

3. Thuộc tính lớp phân đoạn (kiểu lớp) là một chuỗi được trích dẫn giúp trình liên kết xác định thứ tự phân đoạn thích hợp khi lắp ráp một chương trình từ nhiều phân đoạn mô-đun. Trình liên kết kết hợp với nhau trong bộ nhớ tất cả các phân đoạn có cùng tên lớp (tên lớp nói chung có thể là bất kỳ thứ gì, nhưng sẽ tốt hơn nếu nó phản ánh chức năng của phân đoạn). Một cách sử dụng điển hình của tên lớp là để nhóm tất cả các đoạn mã của một chương trình lại với nhau (thường là lớp "mã" được sử dụng cho việc này). Sử dụng cơ chế nhập lớp, bạn cũng có thể nhóm các phân đoạn dữ liệu đã khởi tạo và chưa được khởi tạo.

4. Thuộc tính kích thước phân đoạn. Đối với bộ xử lý i80386 trở lên, các phân đoạn có thể là 16-bit hoặc 32-bit. Điều này chủ yếu ảnh hưởng đến kích thước của phân khúc và thứ tự mà địa chỉ vật lý được hình thành bên trong nó. Thuộc tính có thể nhận các giá trị sau:

1) USE16 - điều này có nghĩa là phân đoạn cho phép định địa chỉ 16 bit. Khi tạo địa chỉ vật lý, chỉ có thể sử dụng độ lệch 16 bit. Theo đó, một phân đoạn như vậy có thể chứa tối đa 64 KB mã hoặc dữ liệu;

2) USE32 - phân đoạn sẽ là 32-bit. Khi tạo địa chỉ vật lý, có thể sử dụng độ lệch 32 bit. Do đó, một phân đoạn như vậy có thể chứa tối đa 4 GB mã hoặc dữ liệu.

Bản thân tất cả các phân đoạn đều bằng nhau, vì các lệnh SEGMENT và ENDS không chứa thông tin về mục đích chức năng của các phân đoạn. Để sử dụng chúng làm phân đoạn mã, dữ liệu hoặc ngăn xếp, cần phải thông báo trước cho người dịch về điều này, chỉ thị ASSUME đặc biệt được sử dụng, có định dạng được hiển thị trong Hình. 18. Chỉ thị này cho người dịch biết phân đoạn nào được liên kết với thanh ghi phân đoạn nào. Đổi lại, điều này sẽ cho phép người dịch liên kết chính xác các tên ký hiệu được xác định trong các phân đoạn. Việc liên kết các phân đoạn với các thanh ghi phân đoạn được thực hiện bằng cách sử dụng các toán hạng của chỉ thị này, trong đó tên_mảng phải là tên của phân đoạn, được định nghĩa trong văn bản nguồn của chương trình bằng chỉ thị SEGMENT hoặc từ khoá nothing. Nếu chỉ từ khóa không có gì được sử dụng làm toán hạng, thì các phép gán thanh ghi phân đoạn trước đó sẽ bị hủy bỏ và cho tất cả sáu thanh ghi phân đoạn cùng một lúc. Nhưng từ khóa không có gì có thể được sử dụng thay thế cho đối số tên phân đoạn; trong trường hợp này, kết nối giữa phân đoạn với tên phân đoạn tên và thanh ghi phân đoạn tương ứng sẽ bị phá vỡ có chọn lọc (xem Hình 18).

Cơm. 18. Chỉ thị ASSUME

Đối với các chương trình đơn giản chứa một phân đoạn cho mã, dữ liệu và ngăn xếp, chúng tôi muốn đơn giản hóa mô tả của nó. Để làm được điều này, các trình dịch MASM và TASM đã giới thiệu khả năng sử dụng các chỉ thị phân đoạn đơn giản hóa. Nhưng ở đây một vấn đề nảy sinh liên quan đến thực tế là cần phải bù đắp bằng cách nào đó cho việc không có khả năng kiểm soát trực tiếp việc bố trí và kết hợp các phân đoạn. Để làm điều này, cùng với các chỉ thị phân đoạn đơn giản, họ bắt đầu sử dụng chỉ thị để chỉ định mô hình bộ nhớ MODEL, một phần bắt đầu kiểm soát vị trí của các phân đoạn và thực hiện các chức năng của chỉ thị ASSUME (do đó, khi sử dụng các chỉ thị phân đoạn đơn giản, Chỉ thị ASSUME có thể được bỏ qua). Chỉ thị này liên kết các phân đoạn, trong trường hợp sử dụng các chỉ thị phân đoạn đơn giản, có tên được xác định trước, với các thanh ghi phân đoạn (mặc dù bạn vẫn phải khởi tạo ds một cách rõ ràng).

Cú pháp của chỉ thị MODEL được hiển thị trong Hình 19.

Cơm. 19. Cú pháp của chỉ thị MODEL

Tham số bắt buộc của chỉ thị MODEL là mô hình bộ nhớ. Tham số này xác định mô hình phân đoạn bộ nhớ cho POU. Giả định rằng một mô-đun chương trình chỉ có thể có một số loại phân đoạn nhất định, được xác định bởi các chỉ thị mô tả phân đoạn đơn giản mà chúng tôi đã đề cập trước đó. Các chỉ thị này được thể hiện trong Bảng 5.

Bảng 5. Các chỉ thị định nghĩa phân đoạn được đơn giản hóa

Sự hiện diện của tham số [name] trong một số chỉ thị cho thấy rằng có thể xác định một số phân đoạn của loại này. Mặt khác, sự tồn tại của một số loại phân đoạn dữ liệu là do yêu cầu đảm bảo tính tương thích với một số trình biên dịch của ngôn ngữ cấp cao, tạo ra các phân đoạn dữ liệu khác nhau cho dữ liệu được khởi tạo và chưa được khởi tạo, cũng như các hằng số.

Khi sử dụng chỉ thị MODEL, trình biên dịch cung cấp một số mã định danh có thể được truy cập trong quá trình vận hành chương trình để có được thông tin về các đặc điểm nhất định của một mô hình bộ nhớ nhất định (Bảng 7). Hãy liệt kê các mã định danh này và giá trị của chúng (Bảng 6).

Bảng 6. Các số nhận dạng được tạo bởi chỉ thị MODEL

Bây giờ chúng ta có thể hoàn thành cuộc thảo luận của chúng ta về chỉ thị MODEL. Các toán hạng của chỉ thị MODEL được sử dụng để chỉ định một mô hình bộ nhớ xác định tập hợp các phân đoạn chương trình, kích thước của dữ liệu và phân đoạn mã, và phương pháp liên kết các phân đoạn và thanh ghi phân đoạn. Bảng 7 cho thấy một số giá trị của tham số "mô hình bộ nhớ" của chỉ thị MODEL.

Bảng 7. Các mô hình bộ nhớ

Tham số "sửa đổi" của chỉ thị MODEL có thể làm rõ một số tính năng của việc sử dụng mô hình bộ nhớ đã chọn (Bảng 8).

Bảng 8. Các công cụ sửa đổi mô hình bộ nhớ

Các tham số tùy chọn "language" và "language modifier" xác định một số tính năng của lời gọi thủ tục. Nhu cầu sử dụng các tham số này nảy sinh khi viết và liên kết các chương trình bằng các ngôn ngữ lập trình khác nhau.

Các chỉ thị phân đoạn tiêu chuẩn và đơn giản hóa mà chúng tôi đã mô tả không loại trừ lẫn nhau. Các chỉ thị chuẩn được sử dụng khi lập trình viên muốn có toàn quyền kiểm soát vị trí của các phân đoạn trong bộ nhớ và sự kết hợp của chúng với các phân đoạn từ các mô-đun khác.

Các chỉ thị đơn giản hóa rất hữu ích cho các chương trình đơn giản và các chương trình nhằm mục đích liên kết với các mô-đun chương trình được viết bằng ngôn ngữ cấp cao. Điều này cho phép trình liên kết liên kết hiệu quả các mô-đun từ các ngôn ngữ khác nhau bằng cách chuẩn hóa liên kết và quản lý.

LECTURE số 17. Các cấu trúc lệnh trong Assembler

1. Cấu trúc lệnh máy

Lệnh máy là một lệnh tới bộ vi xử lý được mã hóa theo các quy tắc nhất định để thực hiện một số hoạt động hoặc hành động. Mỗi lệnh chứa các phần tử xác định:

1) làm gì? (Câu trả lời cho câu hỏi này được đưa ra bởi phần tử lệnh được gọi là mã hoạt động (COP).);

2) các đối tượng cần thực hiện điều gì đó (các phần tử này được gọi là toán hạng);

3) làm thế nào để làm gì? (Những phần tử này được gọi là kiểu toán hạng và thường là ngầm định.)

Định dạng lệnh máy thể hiện trong Hình 20 là tổng quát nhất. Độ dài tối đa của một lệnh máy là 15 byte. Một lệnh thực có thể chứa một số lượng trường nhỏ hơn nhiều, tối đa một - chỉ KOP.

Cơm. 20. Định dạng lệnh máy

Hãy để chúng tôi mô tả mục đích của các trường lệnh máy.

1. Các tiền tố.

Các phần tử lệnh máy tùy chọn, mỗi phần tử là 1 byte hoặc có thể bị bỏ qua. Trong bộ nhớ, các tiền tố đứng trước lệnh. Mục đích của tiền tố là sửa đổi hoạt động được thực hiện bởi lệnh. Một ứng dụng có thể sử dụng các loại tiền tố sau:

1) tiền tố thay thế phân đoạn. Chỉ định rõ ràng thanh ghi phân đoạn nào được sử dụng trong lệnh này để giải quyết ngăn xếp hoặc dữ liệu. Tiền tố ghi đè lựa chọn thanh ghi phân đoạn mặc định. Các tiền tố thay thế phân đoạn có ý nghĩa sau:

a) 2eh - thay thế của đoạn cs;

b) 36h - thay thế của phân đoạn ss;

c) 3eh - thay thế của đoạn ds;

d) 26h - thay thế các đoạn es;

e) 64h - thay thế đoạn fs;

e) 65h - thay thế của phân đoạn gs;

2) tiền tố bitness địa chỉ chỉ định bitness của địa chỉ (32 hoặc 16 bit). Mỗi lệnh sử dụng toán hạng địa chỉ được gán độ rộng bit của địa chỉ của toán hạng đó. Địa chỉ này có thể là 16 hoặc 32 bit. Nếu độ dài địa chỉ cho lệnh này là 16 bit, điều này có nghĩa là lệnh chứa phần bù 16 bit (Hình 20), nó tương ứng với phần bù 16 bit của toán hạng địa chỉ so với phần đầu của một số đoạn. Trong bối cảnh của Hình 21, phần bù này được gọi là địa chỉ hiệu dụng. Nếu địa chỉ là 32 bit, điều này có nghĩa là lệnh chứa độ lệch 32 bit (Hình 20), nó tương ứng với độ lệch 32 bit của toán hạng địa chỉ so với đầu đoạn và giá trị của nó tạo thành 32 -bit bù trong đoạn. Tiền tố bitness địa chỉ có thể được sử dụng để thay đổi bitness địa chỉ mặc định. Thay đổi này sẽ chỉ ảnh hưởng đến lệnh được đặt trước tiền tố;

Cơm. 21. Cơ chế hình thành địa chỉ vật lý trong chế độ thực

3) Tiền tố độ rộng bit toán hạng tương tự như tiền tố độ rộng bit địa chỉ, nhưng cho biết độ dài bit toán hạng (32-bit hoặc 16-bit) mà lệnh hoạt động. Các quy tắc để thiết lập các thuộc tính địa chỉ và độ rộng bit toán hạng theo mặc định là gì?

Trong chế độ thực và chế độ 18086 ảo, giá trị của các thuộc tính này là 16 bit. Trong chế độ được bảo vệ, các giá trị thuộc tính phụ thuộc vào trạng thái của bit D trong bộ mô tả phân đoạn thực thi. Nếu D = 0, thì các giá trị thuộc tính mặc định là 16 bit; nếu D = 1, thì 32 bit.

Giá trị tiền tố cho chiều rộng toán hạng 66h và chiều rộng địa chỉ 67h. Với tiền tố bit địa chỉ chế độ thực, bạn có thể sử dụng địa chỉ 32 bit, nhưng lưu ý giới hạn kích thước đoạn 64 KB. Tương tự như tiền tố chiều rộng địa chỉ, bạn có thể sử dụng tiền tố chiều rộng toán hạng chế độ thực để làm việc với các toán hạng 32 bit (ví dụ: trong các lệnh số học);

4) tiền tố lặp lại được sử dụng với các lệnh chuỗi (lệnh xử lý dòng). Tiền tố này "lặp" lệnh để xử lý tất cả các phần tử của chuỗi. Hệ thống lệnh hỗ trợ hai loại tiền tố:

a) không điều kiện (rep - OOh), buộc lệnh chuỗi được lặp lại một số lần nhất định;

b) có điều kiện (lặp lại / repz - OOh, repne / repnz - 0f2h), khi lặp lại, hãy kiểm tra một số cờ và kết quả của việc kiểm tra, có thể thoát sớm khỏi vòng lặp.

2. Mã hoạt động.

Phần tử bắt buộc mô tả hoạt động được thực hiện bởi lệnh. Nhiều lệnh tương ứng với một số mã hoạt động, mỗi mã xác định các sắc thái của hoạt động. Các trường tiếp theo của lệnh máy xác định vị trí của các toán hạng liên quan đến hoạt động và các chi tiết cụ thể của việc sử dụng chúng. Việc xem xét các trường này được kết nối với các cách chỉ định toán hạng trong một lệnh máy và do đó sẽ được thực hiện sau.

3. Chế độ định địa chỉ byte modr / m.

Giá trị của byte này xác định dạng địa chỉ toán hạng được sử dụng. Toán hạng có thể nằm trong bộ nhớ trong một hoặc hai thanh ghi. Nếu toán hạng nằm trong bộ nhớ, thì byte modr / m chỉ định các thành phần (thanh ghi bù, thanh ghi cơ sở và chỉ số) được sử dụng để tính toán địa chỉ hiệu dụng của nó (Hình 21). Trong chế độ được bảo vệ, byte sib (Scale-Index-Base) cũng có thể được sử dụng để xác định vị trí của toán hạng trong bộ nhớ. Byte modr / m bao gồm ba trường (Hình 20):

1) trường mod xác định số byte bị chiếm bởi địa chỉ toán hạng trong lệnh (Hình 20, trường offset trong lệnh). Trường mod được sử dụng cùng với trường r / m, trường này chỉ định cách sửa đổi địa chỉ của toán hạng "lệnh bù". Ví dụ, nếu mod = 00, điều này có nghĩa là không có trường offset trong lệnh và địa chỉ toán hạng được xác định bởi nội dung của thanh ghi cơ sở và (hoặc) chỉ số. Thanh ghi nào sẽ được sử dụng để tính toán địa chỉ hiệu dụng được xác định bởi giá trị của byte này. Nếu mod = 01, điều này có nghĩa là trường offset có trong lệnh, chiếm 1 byte và được sửa đổi bởi nội dung của thanh ghi cơ sở và (hoặc) chỉ mục. Nếu mod = 10, điều này có nghĩa là trường offset có trong lệnh, chiếm 2 hoặc 4 byte (tùy thuộc vào kích thước địa chỉ mặc định hoặc được xác định trước) và được sửa đổi bởi nội dung của thanh ghi cơ sở và / hoặc chỉ mục. Nếu mod = 11, điều này có nghĩa là không có toán hạng nào trong bộ nhớ: chúng nằm trong các thanh ghi. Giá trị tương tự của byte mod được sử dụng khi toán hạng tức thời được sử dụng trong lệnh;

2) trường reg / cop xác định thanh ghi nằm trong lệnh thay cho toán hạng đầu tiên hoặc phần mở rộng có thể có của opcode;

3) trường r / m được sử dụng cùng với trường mod và xác định thanh ghi nằm trong lệnh ở vị trí của toán hạng đầu tiên (nếu mod = 11) hoặc thanh ghi cơ sở và chỉ mục được sử dụng để tính toán địa chỉ hiệu quả (cùng với trường offset trong lệnh).

4. Thang byte - chỉ số - cơ sở (byte sib).

Được sử dụng để mở rộng khả năng giải quyết các toán hạng. Sự hiện diện của byte sib trong một lệnh máy được biểu thị bằng sự kết hợp của một trong các giá trị 01 hoặc 10 của trường mod và giá trị của trường r / m = 100. Byte sib bao gồm ba trường:

1) các trường tỷ lệ ss. Trường này chứa hệ số tỷ lệ cho chỉ mục thành phần chỉ mục, chiếm 3 bit tiếp theo của byte sib. Trường ss có thể chứa một trong các giá trị sau: 1, 2, 4, 8.

Khi tính toán địa chỉ hiệu quả, nội dung của thanh ghi chỉ mục sẽ được nhân với giá trị này;

2) các trường chỉ mục. Được sử dụng để lưu trữ số thanh ghi chỉ mục được sử dụng để tính toán địa chỉ hiệu quả của toán hạng;

3) các trường cơ sở. Được sử dụng để lưu trữ số thanh ghi cơ sở, cũng được sử dụng để tính toán địa chỉ hiệu dụng của toán hạng. Hầu hết tất cả các thanh ghi mục đích chung đều có thể được sử dụng làm thanh ghi cơ sở và chỉ mục.

5. Trường offset trong lệnh.

Một số nguyên có dấu 8, 16 hoặc 32 bit đại diện cho toàn bộ hoặc một phần (tùy thuộc vào các cân nhắc ở trên), giá trị của địa chỉ hiệu dụng của toán hạng.

6. Trường của toán hạng ngay lập tức.

Trường tùy chọn là toán hạng tức thời 8 bit, 16 bit hoặc 32 bit. Tất nhiên, sự hiện diện của trường này được phản ánh trong giá trị của byte modr / m.

2. Các phương pháp xác định toán hạng lệnh

Toán hạng được đặt ngầm định ở cấp phần sụn

Trong trường hợp này, lệnh rõ ràng không chứa toán hạng. Thuật toán thực thi lệnh sử dụng một số đối tượng mặc định (thanh ghi, cờ trong eflags, v.v.).

Ví dụ, các lệnh cli và sti hoạt động ngầm với cờ ngắt if trong thanh ghi eflags và lệnh xlat truy cập ngầm vào thanh ghi al và một dòng trong bộ nhớ tại địa chỉ được chỉ định bởi cặp thanh ghi ds: bx.

Toán hạng được chỉ định trong chính lệnh (toán hạng tức thì)

Toán hạng nằm trong mã lệnh, tức là nó là một phần của nó. Để lưu trữ một toán hạng như vậy, một trường dài tối đa 32 bit được cấp phát trong lệnh (Hình 20). Toán hạng tức thời chỉ có thể là toán hạng thứ hai (nguồn). Toán hạng đích có thể nằm trong bộ nhớ hoặc trong một thanh ghi.

Ví dụ: mov ax, 0ffffti di chuyển hằng số thập lục phân ffff vào thanh ghi ax. Lệnh add sum, 2 thêm nội dung của trường tại địa chỉ tổng với số nguyên 2 và ghi kết quả vào vị trí của toán hạng đầu tiên, tức là vào bộ nhớ.

Toán hạng nằm trong một trong các thanh ghi

Toán hạng thanh ghi được chỉ định bởi tên thanh ghi. Các đăng ký có thể được sử dụng:

1) Thanh ghi 32-bit EAX, EBX, ECX, EDX, ESI, EDI, ESP, EUR;

2) Các thanh ghi 16-bit AX, BX, CX, DX, SI, DI, SP, BP;

3) Các thanh ghi 8-bit AH, AL, BH, BL, CH, CL, DH, DL;

4) thanh ghi phân đoạn CS, DS, SS, ES, FS, GS.

Ví dụ, lệnh add ax, bx thêm nội dung của thanh ghi ax và bx và ghi kết quả vào bx. Lệnh dec si giảm nội dung của si đi 1.

Toán hạng nằm trong bộ nhớ

Đây là cách phức tạp nhất và đồng thời là cách linh hoạt nhất để chỉ định toán hạng. Nó cho phép bạn thực hiện hai kiểu xưng hô chính sau: trực tiếp và gián tiếp.

Đổi lại, cách xưng hô gián tiếp có các loại sau:

1) địa chỉ cơ sở gián tiếp; tên khác của nó là đăng ký địa chỉ gián tiếp;

2) định địa chỉ cơ sở gián tiếp với bù đắp;

3) địa chỉ chỉ mục gián tiếp với bù đắp;

4) địa chỉ chỉ mục cơ sở gián tiếp;

5) định địa chỉ chỉ mục cơ sở gián tiếp với bù đắp.

Toán hạng là một cổng I / O

Ngoài không gian địa chỉ RAM, bộ vi xử lý duy trì một không gian địa chỉ I / O, được sử dụng để truy cập các thiết bị I / O. Không gian địa chỉ I / O là 64 KB. Địa chỉ được cấp cho bất kỳ thiết bị máy tính nào trong không gian này. Một giá trị địa chỉ cụ thể trong không gian này được gọi là cổng I / O. Về mặt vật lý, cổng I / O tương ứng với một thanh ghi phần cứng (không nên nhầm lẫn với một thanh ghi bộ vi xử lý), được truy cập bằng cách sử dụng các lệnh vào và ra của trình hợp dịch đặc biệt.

Ví dụ:

trong al, 60 giờ; nhập một byte từ cổng 60h

Các thanh ghi được đánh địa chỉ bởi một cổng I / O có thể rộng 8,16, 32 hoặc XNUMX bit, nhưng độ rộng bit của thanh ghi được cố định cho một cổng cụ thể. Các lệnh vào và ra hoạt động trên một phạm vi đối tượng cố định. Cái gọi là thanh ghi bộ tích lũy EAX, AX, AL được sử dụng như một nguồn thông tin hoặc một người nhận. Sự lựa chọn của thanh ghi được xác định bởi bitness của cổng. Số cổng có thể được chỉ định như một toán hạng tức thì trong lệnh vào và lệnh ra, hoặc dưới dạng giá trị trong thanh ghi DX. Phương pháp cuối cùng cho phép bạn xác định động số cổng trong chương trình.

Toán hạng nằm trên ngăn xếp

Hướng dẫn có thể không có toán hạng nào cả, có thể có một hoặc hai toán hạng. Hầu hết các lệnh yêu cầu hai toán hạng, một trong số đó là toán hạng nguồn và một là toán hạng đích. Điều quan trọng là một toán hạng có thể được đặt trong một thanh ghi hoặc bộ nhớ, và toán hạng thứ hai phải nằm trong một thanh ghi hoặc trực tiếp trong lệnh. Một toán hạng tức thời chỉ có thể là một toán hạng nguồn. Trong lệnh máy hai toán hạng, có thể kết hợp các toán hạng sau:

1) đăng ký - đăng ký;

2) thanh ghi - bộ nhớ;

3) bộ nhớ - thanh ghi;

4) toán hạng ngay lập tức - đăng ký;

5) toán hạng tức thời - bộ nhớ.

Có những ngoại lệ đối với quy tắc này liên quan đến:

1) chuỗi lệnh có thể di chuyển dữ liệu từ bộ nhớ sang bộ nhớ;

2) các lệnh ngăn xếp có thể chuyển dữ liệu từ bộ nhớ sang ngăn xếp cũng nằm trong bộ nhớ;

3) các lệnh của kiểu nhân, ngoài toán hạng được chỉ định trong lệnh, còn sử dụng toán hạng thứ hai, ẩn.

Trong số các tổ hợp toán hạng được liệt kê, thanh ghi - bộ nhớ và bộ nhớ - thanh ghi thường được sử dụng nhất. Xét về tầm quan trọng của chúng, chúng tôi sẽ xem xét chúng chi tiết hơn. Chúng tôi sẽ đi kèm với cuộc thảo luận với các ví dụ về lệnh trình hợp dịch sẽ cho thấy cách định dạng của lệnh trình hợp dịch thay đổi như thế nào khi một hoặc một kiểu địa chỉ khác được áp dụng. Về vấn đề này, hãy xem lại Hình 21, nó cho thấy nguyên tắc hình thành một địa chỉ vật lý trên bus địa chỉ của bộ vi xử lý. Có thể thấy rằng địa chỉ của toán hạng được hình thành là tổng của hai thành phần - nội dung của thanh ghi phân đoạn dịch chuyển 4 bit và địa chỉ hiệu dụng 16 bit, thường được tính bằng tổng của ba thành phần: cơ sở, bù đắp và lập chỉ mục.

3. Các phương pháp giải quyết

Chúng tôi liệt kê và sau đó xem xét các tính năng của các loại toán hạng địa chỉ chính trong bộ nhớ:

1) địa chỉ trực tiếp;

2) địa chỉ cơ bản (đăng ký) gián tiếp;

3) địa chỉ cơ bản gián tiếp (đăng ký) với bù đắp;

4) địa chỉ chỉ mục gián tiếp với bù đắp;

5) địa chỉ chỉ mục cơ sở gián tiếp;

6) định địa chỉ chỉ mục cơ sở gián tiếp với bù đắp.

Địa chỉ trực tiếp

Đây là hình thức đơn giản nhất để định địa chỉ một toán hạng trong bộ nhớ, vì địa chỉ hiệu quả được chứa trong chính lệnh và không có nguồn hoặc thanh ghi bổ sung nào được sử dụng để tạo thành nó. Địa chỉ hiệu dụng được lấy trực tiếp từ trường bù lệnh máy (xem Hình 20), có thể có kích thước 8, 16, 32 bit. Giá trị này xác định duy nhất byte, từ hoặc từ kép nằm trong phân đoạn dữ liệu.

Địa chỉ trực tiếp có thể có hai loại.

Địa chỉ tương đối trực tiếp

Được sử dụng cho các lệnh nhảy có điều kiện để chỉ ra địa chỉ nhảy tương đối. Tính tương đối của sự chuyển đổi như vậy nằm ở chỗ trường offset của lệnh máy chứa giá trị 8, 16 hoặc 32-bit, do kết quả của hoạt động của lệnh, sẽ được thêm vào nội dung của thanh ghi con trỏ lệnh ip / eip. Kết quả của việc bổ sung này, địa chỉ thu được, nơi quá trình chuyển đổi được thực hiện.

Địa chỉ trực tiếp tuyệt đối

Trong trường hợp này, địa chỉ hiệu dụng là một phần của lệnh máy, nhưng địa chỉ này chỉ được hình thành từ giá trị của trường offset trong lệnh. Để tạo địa chỉ vật lý của toán hạng trong bộ nhớ, bộ vi xử lý thêm trường này với giá trị của thanh ghi phân đoạn được dịch chuyển 4 bit. Một số dạng địa chỉ này có thể được sử dụng trong một lệnh hợp ngữ.

Nhưng địa chỉ như vậy hiếm khi được sử dụng - các ô thường được sử dụng trong chương trình được gán tên tượng trưng. Trong quá trình dịch, trình hợp dịch sẽ tính toán và thay thế các giá trị bù của các tên này vào lệnh máy mà nó tạo ra trong trường "lệnh bù". Kết quả là, lệnh máy giải quyết trực tiếp toán hạng của nó, trên thực tế, trong một trong các trường của nó, giá trị của địa chỉ hiệu dụng.

Các kiểu xưng hô khác là gián tiếp. Từ "gián tiếp" trong tên của các kiểu địa chỉ này có nghĩa là chỉ một phần của địa chỉ hiệu quả có thể nằm trong chính lệnh và các thành phần còn lại của nó nằm trong các thanh ghi, được biểu thị bằng nội dung của chúng bằng byte modr / m và, có thể, bởi byte sib.

Định địa chỉ cơ bản gián tiếp (đăng ký)

Với cách định địa chỉ này, địa chỉ hiệu quả của toán hạng có thể nằm trong bất kỳ thanh ghi mục đích chung nào, ngoại trừ sp / esp và bp / ebp (đây là những thanh ghi cụ thể để làm việc với một phân đoạn ngăn xếp). Về mặt cú pháp trong một lệnh, chế độ định địa chỉ này được thể hiện bằng cách đặt tên thanh ghi trong dấu ngoặc vuông []. Ví dụ, lệnh mov ax, [ecx] đặt trong thanh ghi ax nội dung của từ tại địa chỉ từ đoạn dữ liệu với phần bù được lưu trữ trong thanh ghi esx. Vì nội dung của thanh ghi có thể dễ dàng thay đổi trong suốt quá trình của chương trình, phương pháp định địa chỉ này cho phép bạn gán động địa chỉ của một toán hạng cho một số lệnh máy. Thuộc tính này rất hữu ích, ví dụ, để tổ chức các phép tính theo chu kỳ và để làm việc với các cấu trúc dữ liệu khác nhau như bảng hoặc mảng.

Định địa chỉ cơ sở gián tiếp (đăng ký) với bù đắp

Loại địa chỉ này là một bổ sung cho loại trước đó và được thiết kế để truy cập dữ liệu với một độ lệch đã biết liên quan đến một số địa chỉ cơ sở. Loại địa chỉ này thuận tiện để sử dụng để truy cập các phần tử của cấu trúc dữ liệu, khi độ lệch của các phần tử được biết trước, ở giai đoạn phát triển chương trình và địa chỉ cơ sở (bắt đầu) của cấu trúc phải được tính toán động, tại giai đoạn thực hiện chương trình. Việc sửa đổi nội dung của thanh ghi cơ sở cho phép bạn truy cập các phần tử có cùng tên trong các trường hợp khác nhau của cùng một kiểu cấu trúc dữ liệu.

Ví dụ, lệnh mov ax, [edx + 3h] chuyển các từ từ vùng nhớ đến các thanh ghi ax tại địa chỉ: nội dung của edx + 3h.

Lệnh mov ax, mas [dx] di chuyển một từ vào thanh ghi ax tại địa chỉ: nội dung của dx cộng với giá trị của số nhận dạng mas (hãy nhớ rằng trình biên dịch gán cho mỗi số nhận dạng một giá trị bằng với phần bù của số nhận dạng này từ đầu của phân đoạn dữ liệu).

Định địa chỉ chỉ mục gián tiếp với bù đắp

Loại địa chỉ này rất giống với địa chỉ cơ sở gián tiếp với một phần bù. Ở đây, một trong những thanh ghi mục đích chung được sử dụng để tạo địa chỉ hiệu quả. Nhưng địa chỉ chỉ mục có một tính năng thú vị là rất thuận tiện để làm việc với mảng. Nó được kết nối với khả năng được gọi là mở rộng quy mô nội dung của thanh ghi chỉ mục. Nó là gì?

Nhìn vào Hình 20. Chúng ta quan tâm đến byte sib. Khi thảo luận về cấu trúc của byte này, chúng tôi lưu ý rằng nó bao gồm ba trường. Một trong những trường này là trường tỷ lệ ss, trong đó nội dung của thanh ghi chỉ mục được nhân lên.

Ví dụ, trong lệnh mov ax, mas [si * 2], giá trị của địa chỉ hiệu dụng của toán hạng thứ hai được tính bằng biểu thức mas + (si) * 2. Do trình hợp dịch không có phương tiện để tổ chức lập chỉ mục mảng, lập trình viên phải tự tổ chức nó.

Khả năng chia tỷ lệ giúp giải quyết vấn đề này một cách đáng kể, nhưng với điều kiện là kích thước của các phần tử mảng là 1, 2, 4 hoặc 8 byte.

Định địa chỉ chỉ mục cơ sở gián tiếp

Với kiểu địa chỉ này, địa chỉ hiệu quả được hình thành là tổng nội dung của hai thanh ghi có mục đích chung: cơ sở và chỉ mục. Các thanh ghi này có thể là bất kỳ thanh ghi có mục đích chung nào và việc chia tỷ lệ nội dung của thanh ghi chỉ mục thường được sử dụng.

Định địa chỉ chỉ mục cơ sở gián tiếp với bù đắp

Loại địa chỉ này là sự bổ sung của địa chỉ được lập chỉ mục gián tiếp. Địa chỉ hiệu dụng được hình thành dưới dạng tổng của ba thành phần: nội dung của thanh ghi cơ sở, nội dung của thanh ghi chỉ mục và giá trị của trường offset trong lệnh.

Ví dụ, lệnh mov eax, [esi + 5] [edx] di chuyển một từ kép vào thanh ghi eax tại địa chỉ: (esi) + 5 + (edx).

Lệnh add ax, array [esi] [ebx] thêm nội dung của thanh ghi ax vào nội dung của từ tại địa chỉ: giá trị của mảng định danh + (esi) + (ebx).

BÀI GIẢNG SỐ 18. Các đội

1. Các lệnh truyền dữ liệu

Để thuận tiện cho việc áp dụng thực tế và phản ánh chi tiết cụ thể của chúng, sẽ thuận tiện hơn khi xem xét các lệnh của nhóm này phù hợp với mục đích chức năng của chúng, theo đó chúng có thể được chia thành các nhóm lệnh sau:

1) chuyển dữ liệu mục đích chung;

2) đầu vào-đầu ra của cổng;

3) làm việc với các địa chỉ và con trỏ;

4) các phép biến đổi dữ liệu;

5) làm việc với ngăn xếp.

Các lệnh truyền dữ liệu mục đích chung

Nhóm này bao gồm các lệnh sau:

1) mov là lệnh truyền dữ liệu cơ bản. Nó thực hiện nhiều tùy chọn vận chuyển. Lưu ý các chi tiết cụ thể của lệnh này:

a) Không thể sử dụng lệnh mov để chuyển từ vùng nhớ này sang vùng nhớ khác. Nếu nhu cầu như vậy phát sinh, thì bất kỳ thanh ghi đa năng nào hiện có sẵn nên được sử dụng làm bộ đệm trung gian;

b) không thể tải giá trị trực tiếp từ bộ nhớ vào thanh ghi phân đoạn. Vì vậy, để thực hiện một tải như vậy, bạn cần phải sử dụng một đối tượng trung gian. Đây có thể là một thanh ghi mục đích chung hoặc một ngăn xếp;

c) bạn không thể chuyển nội dung của thanh ghi phân đoạn này sang thanh ghi phân đoạn khác. Điều này là do không có opcode tương ứng trong hệ thống lệnh. Nhưng nhu cầu về hành động như vậy thường phát sinh. Bạn có thể thực hiện việc chuyển giao như vậy bằng cách sử dụng các thanh ghi có mục đích chung giống như các thanh ghi trung gian;

d) bạn không thể sử dụng CS đăng ký phân đoạn làm toán hạng đích. Lý do rất đơn giản. Thực tế là trong kiến ​​trúc của bộ vi xử lý, cặp cs: ip luôn chứa địa chỉ của lệnh sẽ được thực thi tiếp theo. Thay đổi nội dung của thanh ghi CS bằng lệnh mov sẽ thực sự có nghĩa là một hoạt động nhảy, không phải là chuyển giao, điều này không thể chấp nhận được. 2) xchg - được sử dụng để truyền dữ liệu hai chiều. Đối với hoạt động này, tất nhiên, bạn có thể sử dụng một chuỗi các lệnh mov, nhưng do hoạt động trao đổi được sử dụng khá thường xuyên, các nhà phát triển hệ thống lệnh bộ vi xử lý cho rằng cần thiết phải đưa ra một lệnh trao đổi xchg riêng biệt. Đương nhiên, các toán hạng phải cùng loại. Không được phép (đối với tất cả các lệnh của trình hợp dịch) trao đổi nội dung của hai ô nhớ với nhau.

Lệnh cổng I / O

Nhìn vào Hình 22. Nó cho thấy một sơ đồ khái niệm, đơn giản hóa về điều khiển phần cứng máy tính.

Cơm. 22. Sơ đồ khái niệm về điều khiển phần cứng máy tính

Như bạn có thể thấy từ Hình 22, mức thấp nhất là mức BIOS, nơi phần cứng được xử lý trực tiếp thông qua các cổng. Điều này thực hiện khái niệm độc lập thiết bị. Khi thay thế phần cứng, chỉ cần sửa các chức năng tương ứng của BIOS, định hướng lại chúng đến các địa chỉ mới và logic của các cổng.

Về cơ bản, việc quản lý thiết bị trực tiếp thông qua các cổng rất dễ dàng. Thông tin về số cổng, độ sâu bit của chúng, định dạng thông tin điều khiển được đưa ra trong mô tả kỹ thuật của thiết bị. Bạn chỉ cần biết mục tiêu cuối cùng của các hành động của mình, thuật toán phù hợp với cách hoạt động của một thiết bị cụ thể và thứ tự lập trình các cổng của nó, tức là trên thực tế, bạn cần biết bạn cần gửi cái gì và theo trình tự nào cổng (khi ghi vào nó) hoặc đọc từ nó (khi đọc) và thông tin này nên được diễn giải như thế nào. Để làm điều này, chỉ cần hai lệnh có trong hệ thống lệnh của bộ vi xử lý là đủ:

1) trong bộ tích lũy, port_number - đầu vào cho bộ tích lũy từ cổng có số port_number;

2) cổng ra, bộ tích lũy - xuất nội dung của bộ tích lũy đến cổng với số port_number.

Các lệnh để làm việc với địa chỉ và con trỏ bộ nhớ

Khi viết chương trình trong trình hợp dịch, công việc chuyên sâu được thực hiện với địa chỉ của các toán hạng nằm trong bộ nhớ. Để hỗ trợ loại hoạt động này, có một nhóm lệnh đặc biệt, bao gồm các lệnh sau:

1) điểm đến, nguồn - tải địa chỉ hiệu quả;

2) Id đích, nguồn - tải con trỏ vào thanh ghi phân đoạn dữ liệu ds;

3) đích les, nguồn - tải con trỏ vào thanh ghi của phân đoạn dữ liệu bổ sung es;

4) lgs đích, nguồn - tải con trỏ vào thanh ghi của phân đoạn dữ liệu bổ sung gs;

5) lfs đích, nguồn - tải con trỏ vào thanh ghi của đoạn dữ liệu bổ sung fs;

6) lss đích, con trỏ nguồn - tải vào thanh ghi phân đoạn ngăn xếp ss.

Lệnh lea tương tự như lệnh mov ở chỗ nó cũng thực hiện một bước di chuyển. Tuy nhiên, lệnh lea không chuyển dữ liệu, mà là địa chỉ hiệu quả của dữ liệu (nghĩa là, phần bù của dữ liệu từ đầu đoạn dữ liệu) đến thanh ghi được chỉ ra bởi toán hạng đích.

Thông thường, để thực hiện một số hành động trong một chương trình, chỉ cần biết giá trị của địa chỉ dữ liệu hiệu quả là chưa đủ, mà cần phải có một con trỏ đầy đủ đến dữ liệu. Một con trỏ dữ liệu hoàn chỉnh bao gồm một thành phần phân đoạn và một phần bù. Tất cả các lệnh khác của nhóm này cho phép bạn nhận được một con trỏ đầy đủ như vậy đến một toán hạng trong bộ nhớ trong một cặp thanh ghi. Trong trường hợp này, tên của thanh ghi phân đoạn, trong đó thành phần phân đoạn của địa chỉ được đặt, được xác định bởi mã hoạt động. Theo đó, phần bù được đặt trong thanh ghi chung được chỉ ra bởi toán hạng đích.

Nhưng không phải mọi thứ đều đơn giản như vậy với toán hạng nguồn. Trên thực tế, trong lệnh với tư cách là nguồn, bạn không thể chỉ định trực tiếp tên của toán hạng trong bộ nhớ mà chúng ta muốn nhận một con trỏ. Đầu tiên, bạn cần lấy giá trị của con trỏ đầy đủ trong một số vùng bộ nhớ và chỉ định địa chỉ đầy đủ của tên vùng này trong lệnh get. Để thực hiện hành động này, bạn cần nhớ các chỉ thị để lưu trữ và khởi tạo bộ nhớ.

Khi áp dụng các chỉ thị này, một trường hợp đặc biệt có thể xảy ra khi tên của một chỉ thị định nghĩa dữ liệu khác (thực tế là tên của một biến) được chỉ định trong trường toán hạng. Trong trường hợp này, địa chỉ của biến này được hình thành trong bộ nhớ. Địa chỉ nào sẽ được tạo (có hiệu lực hoặc đầy đủ) tùy thuộc vào chỉ thị được áp dụng. Nếu là dw, thì chỉ giá trị 16 bit của địa chỉ hiệu dụng được hình thành trong bộ nhớ; nếu là dd, địa chỉ đầy đủ được ghi vào bộ nhớ. Vị trí của địa chỉ này trong bộ nhớ như sau: từ thấp chứa phần bù, từ cao chứa thành phần đoạn 16 bit của địa chỉ.

Ví dụ, khi tổ chức công việc với một chuỗi ký tự, rất tiện lợi khi đặt địa chỉ bắt đầu của nó vào một thanh ghi nhất định và sau đó sửa đổi giá trị này trong một vòng lặp để truy cập tuần tự vào các phần tử của chuỗi.

Đặc biệt, nhu cầu sử dụng các lệnh để có được một con trỏ dữ liệu đầy đủ trong bộ nhớ, tức là địa chỉ của phân đoạn và giá trị bù trong phân đoạn, khi làm việc với các chuỗi.

Các lệnh chuyển đổi dữ liệu

Nhiều lệnh của bộ vi xử lý có thể được quy cho nhóm này, nhưng hầu hết chúng đều có một số tính năng nhất định yêu cầu chúng phải được gán cho các nhóm chức năng khác. Do đó, trong toàn bộ tập hợp các lệnh của bộ vi xử lý, chỉ có một lệnh có thể được quy trực tiếp cho các lệnh chuyển đổi dữ liệu: xlat [address_of_transcoding_table]

Đây là một đội rất thú vị và hữu ích. Tác dụng của nó là nó thay thế giá trị trong thanh ghi al bằng một byte khác từ bảng bộ nhớ nằm tại địa chỉ được chỉ định bởi toán hạng recoding_table_address.

Từ "bảng" rất có điều kiện, trên thực tế, nó chỉ là một chuỗi byte. Địa chỉ của byte trong chuỗi sẽ thay thế nội dung của thanh ghi al được xác định bởi tổng (bx) + (al), tức là nội dung của al hoạt động như một chỉ số trong mảng byte.

Khi làm việc với lệnh xlat, hãy chú ý đến điểm nhỏ sau đây. Mặc dù lệnh chỉ định địa chỉ của chuỗi byte mà từ đó giá trị mới sẽ được truy xuất, địa chỉ này phải được tải trước (ví dụ: sử dụng lệnh lea) vào thanh ghi bx. Do đó, toán hạng lookup_table_address không thực sự cần thiết (tính tùy chọn của toán hạng được thể hiện bằng cách đặt nó trong dấu ngoặc vuông). Còn đối với chuỗi byte (bảng chuyển mã), nó là một vùng nhớ có kích thước từ 1 đến 255 byte (phạm vi của một số không dấu trong một thanh ghi 8 bit).

Lệnh ngăn xếp

Nhóm này là một tập hợp các lệnh chuyên biệt tập trung vào việc tổ chức công việc linh hoạt và hiệu quả với ngăn xếp.

Ngăn xếp là một vùng bộ nhớ được cấp phát đặc biệt để lưu trữ tạm thời dữ liệu chương trình. Tầm quan trọng của ngăn xếp được xác định bởi thực tế là một phân đoạn riêng biệt được cung cấp cho nó trong cấu trúc của chương trình. Trong trường hợp lập trình viên quên khai báo một đoạn ngăn xếp trong chương trình của mình, trình liên kết tlink sẽ đưa ra thông báo cảnh báo.

Ngăn xếp có ba thanh ghi:

1) ss - thanh ghi phân đoạn ngăn xếp;

2) sp / esp - thanh ghi con trỏ ngăn xếp;

3) bp / ebp - thanh ghi con trỏ cơ sở khung ngăn xếp.

Kích thước ngăn xếp phụ thuộc vào chế độ hoạt động của bộ vi xử lý và được giới hạn ở 64 KB (hoặc 4 GB ở chế độ được bảo vệ).

Chỉ có một ngăn xếp khả dụng tại một thời điểm, địa chỉ phân đoạn được chứa trong thanh ghi SS. Ngăn xếp này được gọi là ngăn xếp hiện tại. Để tham chiếu đến một ngăn xếp khác ("chuyển ngăn xếp"), cần phải tải một địa chỉ khác vào thanh ghi ss. Thanh ghi SS được bộ xử lý tự động sử dụng để thực thi tất cả các lệnh hoạt động trên ngăn xếp.

Chúng tôi liệt kê thêm một số tính năng làm việc với ngăn xếp:

1) việc ghi và đọc dữ liệu trên ngăn xếp được thực hiện theo nguyên tắc LIFO,

2) khi dữ liệu được ghi vào ngăn xếp, ngăn xếp sẽ phát triển về phía các địa chỉ thấp hơn. Tính năng này được nhúng trong thuật toán các lệnh để làm việc với ngăn xếp;

3) khi sử dụng các thanh ghi esp / sp và ebp / bp để định địa chỉ bộ nhớ, trình hợp dịch tự động coi rằng các giá trị chứa trong nó là các hiệu số so với thanh ghi phân đoạn ss.

Nói chung, ngăn xếp được tổ chức như trong Hình 23.

Cơm. 23. Sơ đồ khái niệm về tổ chức ngăn xếp

Các thanh ghi SS, ESP / SP và EUR / BP được thiết kế để hoạt động với ngăn xếp. Các thanh ghi này được sử dụng một cách phức tạp và mỗi thanh ghi đều có mục đích chức năng riêng.

Thanh ghi ESP / SP luôn trỏ đến đỉnh của ngăn xếp, nghĩa là, nó chứa phần bù mà tại đó phần tử cuối cùng được đẩy lên ngăn xếp. Các lệnh ngăn xếp thay đổi ngầm định thanh ghi này để nó luôn trỏ đến phần tử cuối cùng được đẩy lên ngăn xếp. Nếu ngăn xếp trống, thì giá trị của esp bằng địa chỉ của byte cuối cùng của phân đoạn được phân bổ cho ngăn xếp. Khi một phần tử được đẩy lên ngăn xếp, bộ xử lý sẽ giảm giá trị của thanh ghi esp, và sau đó ghi phần tử vào địa chỉ của đỉnh mới. Khi bật dữ liệu từ ngăn xếp, bộ xử lý sao chép phần tử nằm ở địa chỉ đỉnh, sau đó tăng giá trị của thanh ghi con trỏ ngăn xếp đặc biệt. Do đó, nó chỉ ra rằng ngăn xếp phát triển xuống, theo hướng giảm địa chỉ.

Điều gì sẽ xảy ra nếu chúng ta cần truy cập các phần tử không phải ở trên cùng mà ở bên trong ngăn xếp? Để thực hiện việc này, hãy sử dụng thanh ghi EBP Thanh ghi EBP là thanh ghi con trỏ cơ sở khung ngăn xếp.

Ví dụ, một thủ thuật điển hình khi nhập chương trình con là chuyển các tham số mong muốn bằng cách đẩy chúng vào ngăn xếp. Nếu chương trình con cũng đang hoạt động tích cực với ngăn xếp, thì việc truy cập vào các tham số này trở nên có vấn đề. Giải pháp là lưu địa chỉ của đỉnh ngăn xếp trong con trỏ khung (cơ sở) của ngăn xếp, thanh ghi EUR, sau khi ghi dữ liệu cần thiết vào ngăn xếp. Giá trị bằng EUR sau đó có thể được sử dụng để truy cập các tham số đã truyền.

Phần đầu của ngăn xếp được đặt tại các địa chỉ bộ nhớ cao hơn. Trong hình 23, địa chỉ này được ký hiệu bằng cặp ss: fffF. Sự thay đổi của wT được đưa ra ở đây có điều kiện. Trong thực tế, giá trị này được xác định bởi giá trị mà người lập trình chỉ định khi mô tả đoạn ngăn xếp trong chương trình của mình.

Để tổ chức công việc với ngăn xếp, có các lệnh đặc biệt để viết và đọc.

1. nguồn đẩy - ghi giá trị của nguồn lên đầu ngăn xếp.

Điều quan tâm là thuật toán của lệnh này, bao gồm các hành động sau (Hình 24):

1) (sp) = (sp) - 2; giá trị của sp giảm đi 2;

2) giá trị từ nguồn được ghi vào địa chỉ được chỉ định bởi cặp ss: sp.

Cơm. 24. Cách hoạt động của lệnh đẩy

2. phép gán pop - ghi giá trị từ trên cùng của ngăn xếp đến vị trí được chỉ định bởi toán hạng đích. Do đó, giá trị bị "loại bỏ" khỏi đầu ngăn xếp. Thuật toán của lệnh pop là ngược lại với thuật toán của lệnh push (Hình 25):

1) ghi nội dung của phần trên cùng của ngăn xếp tại vị trí được chỉ ra bởi toán hạng đích;

2) (sp) = (sp) + 2; làm tăng giá trị của sp.

Cơm. 25. Cách hoạt động của lệnh pop

3. latexha - một nhóm ghi lệnh vào ngăn xếp. Bằng lệnh này, các thanh ghi ax, cx, dx, bx, sp, bp, si, di được ghi tuần tự vào ngăn xếp. Lưu ý rằng nội dung gốc của sp được viết, tức là nội dung có trước khi lệnh latex được ban hành (Hình 26).

Cơm. 26. Cách hoạt động của lệnh latexha

4. latexhaw gần như đồng nghĩa với lệnh pushha, sự khác biệt là gì? Thuộc tính bitness có thể là use16 hoặc use32. Chúng ta hãy xem cách hoạt động của các lệnh pushha và latexhaw với từng thuộc tính sau:

1) use16 - thuật toán latexhaw tương tự như thuật toán pushha;

2) use32 - latexhaw không thay đổi (tức là nó không nhạy cảm với độ rộng phân đoạn và luôn hoạt động với các thanh ghi có kích thước từ - ax, cx, dx, bx, sp, bp, si, di). Lệnh latexha nhạy cảm với độ rộng phân đoạn đã đặt và khi phân đoạn 32 bit được chỉ định, nó hoạt động với các thanh ghi 32 bit tương ứng, tức là eax, esx, edx, ebx, esp, ebp, esi, edi.

5. pushhad - được thực hiện tương tự như lệnh pushha, nhưng có một số điểm đặc biệt.

Ba lệnh sau thực hiện ngược lại các lệnh trên:

1) rora;

2) cây bìm bịp;

3) bật.

Nhóm hướng dẫn được mô tả bên dưới cho phép bạn lưu sổ đăng ký cờ trên ngăn xếp và viết một từ hoặc từ kép vào ngăn xếp. Lưu ý rằng các hướng dẫn được liệt kê dưới đây là những hướng dẫn duy nhất trong tập lệnh của bộ vi xử lý cho phép (và yêu cầu) truy cập vào toàn bộ nội dung của thanh ghi cờ.

1. pushf - lưu sổ đăng ký cờ trên ngăn xếp.

Hoạt động của lệnh này phụ thuộc vào thuộc tính kích thước phân đoạn:

1) sử dụng 16 - thanh ghi cờ có kích thước 2 byte được ghi vào ngăn xếp;

2) use32 - thanh ghi eflags gồm 4 byte được ghi vào ngăn xếp.

2. pushfw - lưu một đăng ký cờ có kích thước bằng chữ trên ngăn xếp. Luôn hoạt động giống như pushf với thuộc tính use16.

3. pushfd - lưu các cờ hoặc cờ eflags đăng ký trên ngăn xếp tùy thuộc vào thuộc tính độ rộng bit của phân đoạn (tức là giống với pushf).

Tương tự, ba lệnh sau thực hiện ngược lại các hoạt động được thảo luận ở trên:

1) popf;

2) popftv;

3) popfd.

Và kết luận, chúng tôi lưu ý các loại hoạt động chính khi sử dụng ngăn xếp gần như không thể tránh khỏi:

1) gọi các chương trình con;

2) lưu trữ tạm thời các giá trị thanh ghi;

3) định nghĩa các biến cục bộ.

2. Các lệnh số học

Bộ vi xử lý có thể thực hiện các phép toán số nguyên và dấu phẩy động. Để làm điều này, kiến ​​trúc của nó có hai khối riêng biệt:

1) một thiết bị để thực hiện các phép toán số nguyên;

2) một thiết bị để thực hiện các phép toán dấu phẩy động.

Mỗi thiết bị này có hệ thống lệnh riêng. Về nguyên tắc, một vùng nhớ số nguyên có thể đảm nhiệm nhiều chức năng của một vùng nhớ dấu chấm động, nhưng điều này sẽ tốn kém về mặt tính toán. Đối với hầu hết các vấn đề sử dụng hợp ngữ, số học nguyên là đủ.

Tổng quan về một nhóm hướng dẫn và dữ liệu số học

Một thiết bị tính toán số nguyên hỗ trợ hơn một tá lệnh số học. Hình 27 cho thấy sự phân loại các lệnh trong nhóm này.

Cơm. 27. Phân loại các lệnh số học

Nhóm hướng dẫn số học số nguyên hoạt động với hai loại số:

1) số nhị phân số nguyên. Số có thể có hoặc không có chữ số có dấu, tức là số có dấu hoặc không dấu;

2) số thập phân số nguyên.

Xem xét các định dạng máy mà các loại dữ liệu này được lưu trữ.

Số nhị phân số nguyên

Số nguyên nhị phân điểm cố định là một số được mã hóa trong hệ thống số nhị phân.

Kích thước của một số nguyên nhị phân có thể là 8, 16 hoặc 32 bit. Dấu của một số nhị phân được xác định bằng cách diễn giải bit quan trọng nhất trong biểu diễn của số. Đây là bit thứ 7,15 hoặc 31 cho các số có kích thước tương ứng. Đồng thời, điều thú vị là trong số các lệnh số học chỉ có hai lệnh thực sự coi bit quan trọng nhất này là dấu một, đó là các lệnh nhân và chia số nguyên imul và idiv. Trong các trường hợp khác, trách nhiệm đối với các hành động với các số có dấu và theo đó, với một bit dấu là thuộc về lập trình viên. Phạm vi giá trị của một số nhị phân phụ thuộc vào kích thước của nó và cách diễn giải bit quan trọng nhất là bit quan trọng nhất của số hoặc là bit dấu của số (Bảng 9).

Bảng 9. Phạm vi số nhị phân Số thập phân

Số thập phân là một kiểu biểu diễn đặc biệt của thông tin số, dựa trên nguyên tắc mã hóa từng chữ số thập phân của một số theo một nhóm bốn bit. Trong trường hợp này, mỗi byte của số chứa một hoặc hai chữ số thập phân trong cái gọi là mã thập phân được mã hóa nhị phân (BCD - Binary-Coded Decimal). Bộ vi xử lý lưu trữ số BCD ở hai định dạng (Hình 28):

1) định dạng đóng gói. Ở định dạng này, mỗi byte chứa hai chữ số thập phân. Một chữ số thập phân là một giá trị nhị phân 0 bit từ 9 đến 4. Trong trường hợp này, mã của chữ số cao nhất của số chiếm 4 bit cao nhất. Do đó, phạm vi biểu diễn của một số thập phân được đóng gói trong 1 byte là từ 00 đến 99;

2) định dạng không đóng gói. Ở định dạng này, mỗi byte chứa một chữ số thập phân trong bốn bit có ý nghĩa nhỏ nhất. 4 bit trên được đặt thành không. Đây là cái gọi là khu vực. Do đó, phạm vi biểu diễn một số thập phân được giải nén trong 1 byte là từ 0 đến 9.

Cơm. 28. Biểu diễn số BCD

Làm thế nào để mô tả số thập phân nhị phân trong một chương trình? Để làm điều này, bạn chỉ có thể sử dụng hai chỉ thị mô tả và khởi tạo dữ liệu - db và dt. Khả năng chỉ sử dụng các chỉ thị này để mô tả các số BCD là do nguyên tắc "byte thấp ở địa chỉ thấp" cũng có thể áp dụng cho các số như vậy, điều này rất thuận tiện cho việc xử lý chúng. Và nói chung, khi sử dụng kiểu dữ liệu như số BCD, thứ tự mà các số này được mô tả trong chương trình và thuật toán để xử lý chúng là vấn đề sở thích và sở thích cá nhân của người lập trình. Điều này sẽ trở nên rõ ràng sau khi chúng ta xem xét những điều cơ bản về cách làm việc với các số BCD dưới đây.

Các phép toán số học trên số nguyên nhị phân

Phép cộng các số nhị phân không dấu

Bộ vi xử lý thực hiện phép cộng các toán hạng theo quy tắc cộng số nhị phân. Không có vấn đề gì miễn là giá trị của kết quả không vượt quá kích thước của trường toán hạng. Ví dụ, khi thêm toán hạng có kích thước byte, kết quả không được vượt quá số 255. Nếu điều này xảy ra, thì kết quả không chính xác. Hãy xem xét lý do tại sao điều này xảy ra.

Ví dụ, hãy thực hiện phép cộng: 254 + 5 = 259 trong hệ nhị phân. 11111110 + 0000101 = 1 00000011. Kết quả vượt quá 8 bit và giá trị đúng của nó khớp với 9 bit và giá trị 8 vẫn nằm trong trường 3 bit của toán hạng, tất nhiên, giá trị này không đúng. Trong bộ vi xử lý, kết quả của việc bổ sung này được dự đoán và các phương tiện đặc biệt được cung cấp để khắc phục các tình huống như vậy và xử lý chúng. Vì vậy, để khắc phục tình trạng vượt ra ngoài lưới bit của kết quả, như trong trường hợp này, cờ thực hiện cf được dự định. Nó nằm ở bit 0 của thanh ghi cờ EFLAGS / FLAGS. Việc thiết lập cờ này khắc phục thực tế của việc chuyển một từ bậc cao của toán hạng. Đương nhiên, người lập trình phải tính đến khả năng xảy ra kết quả như vậy của hoạt động bổ sung và cung cấp các phương tiện để hiệu chỉnh. Điều này liên quan đến việc bao gồm các phần mã sau hoạt động bổ sung trong đó cờ cf được phân tích cú pháp. Cờ này có thể được phân tích cú pháp theo nhiều cách khác nhau.

Cách dễ nhất và dễ tiếp cận nhất là sử dụng lệnh rẽ nhánh có điều kiện jcc. Lệnh này có tên của nhãn trong đoạn mã hiện tại là toán hạng của nó. Việc chuyển đổi sang nhãn này được thực hiện nếu, do hoạt động của lệnh trước đó, cờ cf được đặt thành 1. Có ba lệnh cộng nhị phân trong hệ thống lệnh của bộ vi xử lý:

1) inc operand - phép toán gia tăng, tức là tăng giá trị của toán hạng lên 1;

2) thêm toán hạng thứ 1, toán hạng thứ 2 - lệnh cộng với nguyên tắc hoạt động: toán hạng thứ 1 = toán hạng thứ 1 + toán hạng thứ 2;

3) toán hạng adc_1, toán hạng_2 - lệnh bổ sung có tính đến cờ thực hiện cf. Nguyên tắc hoạt động của lệnh: toán hạng_1 = toán hạng_1 + toán hạng thứ 2 + giá trị_sG.

Hãy chú ý đến lệnh cuối cùng - đây là lệnh bổ sung, có tính đến việc chuyển một từ lệnh cao. Chúng tôi đã xem xét cơ chế xuất hiện của một đơn vị như vậy. Do đó, lệnh adc là một công cụ vi xử lý để thêm các số nhị phân dài, kích thước của chúng vượt quá độ dài của các trường tiêu chuẩn được hỗ trợ bởi bộ vi xử lý.

Phép cộng nhị phân đã ký

Trên thực tế, bộ vi xử lý "không biết" về sự khác biệt giữa số có dấu và không có dấu. Thay vào đó, anh ta có phương tiện để sửa chữa sự xuất hiện của các tình huống đặc trưng phát triển trong quá trình tính toán. Chúng tôi đã đề cập đến một số trong số chúng khi thảo luận về bổ sung không dấu:

1) cờ mang cf, đặt nó thành 1 cho biết rằng các toán hạng nằm ngoài phạm vi;

2) lệnh adc, có tính đến khả năng thoát như vậy (thực hiện từ bit ít quan trọng nhất).

Một phương tiện khác là đăng ký trạng thái bit (dấu) bậc cao của toán hạng, được thực hiện bằng cách sử dụng cờ tràn trong thanh ghi EFLAGS (bit 11).

Tất nhiên, bạn nhớ cách các số được biểu diễn trong máy tính: dương - ở dạng nhị phân, âm - trong phần bù của hai. Xem xét các tùy chọn khác nhau để thêm số. Các ví dụ nhằm hiển thị hoạt động của hai bit quan trọng nhất của các toán hạng và tính đúng đắn của kết quả của phép toán cộng.

Ví dụ

30566 = 0111011101100110

+

00687 = 00000010

=

31253 = 01111010

Chúng tôi theo dõi các lần chuyển từ số 14 và 15 và tính đúng đắn của kết quả: không có chuyển nào, kết quả là chính xác.

Ví dụ

30566 = 0111011101100110

+

30566 = 0111011101100110

=

1132 = 11101110

Có một sự chuyển giao từ loại thứ 14; không có chuyển từ loại 15. Kết quả là sai, vì có sự tràn - giá trị của số hóa ra lớn hơn giá trị của một số có dấu 16 bit (+32 767) có thể có.

Ví dụ

-30566 = 10001000 10011010

+

-04875 = 11101100 11110101

=

-35441 = 01110101 10001111

Đã có chuyển từ chữ số 15, không có chuyển từ chữ số 14. Kết quả không chính xác, bởi vì thay vì một số âm, nó lại là một số dương (bit quan trọng nhất là 0).

Ví dụ

-4875 = 11101100 11110101

+

-4875 = 11101100 11110101

=

09750 = 11011001

Có chuyển từ bit thứ 14 và 15. Kết quả là chính xác.

Do đó, chúng tôi đã kiểm tra tất cả các trường hợp và phát hiện ra rằng tình huống tràn (đặt cờ OF thành 1) xảy ra trong quá trình chuyển:

1) từ chữ số 14 (đối với các số dương có dấu);

2) từ chữ số thứ 15 (đối với số âm).

Ngược lại, không xảy ra tràn (nghĩa là cờ OF được đặt lại về 0) nếu có chuyển từ cả hai bit hoặc nếu không có chuyển nào trong cả hai bit.

Vì vậy, tràn được đăng ký với cờ tràn của. Ngoài cờ của, khi chuyển từ bit bậc cao, cờ chuyển CF được đặt thành 1. Vì bộ vi xử lý không biết về sự tồn tại của các số có dấu và không dấu, nên người lập trình hoàn toàn chịu trách nhiệm về các hành động chính xác với các số kết quả. Bạn có thể phân tích cú pháp các cờ CF và OF với các lệnh nhảy có điều kiện JC \ JNC và JO \ JNO tương ứng.

Đối với lệnh cộng số có dấu cũng giống như lệnh cộng số không có dấu.

Phép trừ các số nhị phân không dấu

Như trong phần phân tích của phép toán cộng, chúng ta sẽ thảo luận về bản chất của các quá trình xảy ra khi thực hiện phép tính trừ. Nếu minuend lớn hơn subtrahend, thì không có vấn đề gì - sự khác biệt là dương, kết quả là chính xác. Nếu số tối thiểu nhỏ hơn số bị trừ, có một vấn đề: kết quả nhỏ hơn 0 và đây đã là một số có dấu. Trong trường hợp này, kết quả phải được gói lại. Điều đó có nghĩa là gì? Với phép trừ thông thường (trong một cột), họ thực hiện một khoản vay là 1 từ thứ tự cao nhất. Bộ vi xử lý cũng làm như vậy, tức là nó lấy 1 từ chữ số theo sau chữ số cao nhất trong lưới bit của toán hạng. Hãy giải thích bằng một ví dụ.

Ví dụ

05 = 00000000

-10 = 00000000 00001010

Để thực hiện phép trừ, chúng ta hãy làm

khoản vay tưởng tượng cao cấp:

100000000 00000101

-

00000000 00001010

=

11111111 11111011

Do đó, về bản chất, hành động

(65 + 536) - 5 = 10

0 ở đây, như nó vốn có, tương đương với số 65536. Tất nhiên, kết quả không chính xác, nhưng bộ vi xử lý cho rằng mọi thứ đều ổn, mặc dù nó sửa lỗi mượn một đơn vị bằng cách đặt cờ mang cf. Nhưng hãy xem kỹ lại kết quả của phép tính trừ. Nó là -5 trong hai phần bổ sung! Hãy tiến hành một thí nghiệm: biểu diễn sự khác biệt dưới dạng tổng của 5 + (-10).

Ví dụ

5 = 00000000

+

(-10) = 11111111 11110110

=

11111111 11111011

tức là chúng tôi nhận được kết quả tương tự như trong ví dụ trước.

Vì vậy, sau lệnh trừ các số không có dấu, cần phải phân tích trạng thái của cờ CE, nếu nó được đặt thành 1, thì điều này cho thấy rằng đã có sự vay mượn từ lệnh cao và kết quả thu được trong một mã bổ sung. .

Giống như các hướng dẫn cộng, nhóm các lệnh trừ bao gồm tập hợp nhỏ nhất có thể. Các lệnh này thực hiện phép trừ theo các thuật toán mà chúng ta đang xem xét, và các ngoại lệ phải được tính đến bởi chính người lập trình. Các lệnh trừ bao gồm:

1) toán hạng dec - phép toán giảm, tức là giảm giá trị của toán hạng đi 1;

2) toán hạng con thứ 1, toán hạng thứ 2 - lệnh trừ; nguyên tắc hoạt động của nó: operand_1 = operand_1 - operand_2;

3) toán hạng sbb thứ 1, toán hạng thứ 2 - lệnh trừ có tính đến khoản vay (cờ ci): toán hạng_1 = toán hạng thứ_1 - toán hạng thứ 2 - giá trị_sG.

Như bạn có thể thấy, trong số các lệnh trừ có một lệnh sbb có tính đến cờ thực hiện cf. Lệnh này tương tự như adc, nhưng bây giờ cờ cf hoạt động như một chỉ báo mượn 1 từ chữ số có nghĩa nhất khi trừ các số.

Phép trừ nhị phân có dấu

Ở đây mọi thứ có phần phức tạp hơn. Bộ vi xử lý không cần phải có hai thiết bị - cộng và trừ. Chỉ cần có một - thiết bị bổ sung là đủ. Nhưng đối với phép trừ bằng cách cộng các số có dấu trong mã bổ sung, cần phải biểu diễn cả hai toán hạng - cả số bị giảm và số bị trừ. Kết quả cũng nên được coi là giá trị bổ sung của hai. Nhưng ở đây nảy sinh khó khăn. Trước hết, chúng có liên quan đến thực tế là bit quan trọng nhất của toán hạng được coi là bit dấu. Hãy xem xét ví dụ về phép trừ 45 - (-127).

Ví dụ

Phép trừ các số có dấu 1

45 = 0010

-

-127 = 1000 0001

=

-44 = 1010 1100

Đánh giá bằng bit dấu hiệu, kết quả trở thành âm, do đó, chỉ ra rằng số nên được coi là phần bù bằng -44. Kết quả đúng phải là 172. Ở đây, như trong trường hợp cộng có dấu, chúng ta đã gặp lỗi tràn phần định trị, khi bit quan trọng của số thay đổi bit dấu của toán hạng. Bạn có thể theo dõi tình huống này bằng nội dung của cờ tràn của. Đặt nó thành 1 cho biết rằng kết quả nằm ngoài phạm vi số có dấu (nghĩa là bit quan trọng nhất đã thay đổi) cho một toán hạng có kích thước này và lập trình viên phải thực hiện hành động để sửa kết quả.

Ví dụ

Phép trừ các số có dấu 2

-45-45 = -45 + (-45) = -90.

-45 = 11010011

+

-45 = 11010011

=

-90 = 1010 0110

Mọi thứ đều ổn ở đây, cờ tràn của được đặt lại thành 0 và 1 trong bit dấu chỉ ra rằng giá trị kết quả là số bù của hai số.

Phép trừ và cộng các toán hạng lớn

Nếu bạn để ý, các lệnh cộng và trừ hoạt động với các toán hạng có kích thước cố định: 8, 16, 32 bit. Nhưng nếu bạn cần thêm các số có kích thước lớn hơn, ví dụ 48 bit, sử dụng toán hạng 16 bit thì sao? Ví dụ: hãy thêm hai số 48 bit:

Cơm. 29. Thêm toán hạng lớn

Hình 29 cho thấy công nghệ thêm các số dài theo từng bước. Có thể thấy rằng quá trình thêm các số nhiều byte xảy ra giống như khi cộng hai số "trong một cột" - với việc thực hiện, nếu cần, chuyển 1 đến bit cao nhất. Nếu chúng ta quản lý để lập trình quá trình này, thì chúng ta sẽ mở rộng đáng kể phạm vi số nhị phân mà chúng ta có thể thực hiện các phép tính cộng và trừ.

Nguyên tắc trừ các số có phạm vi biểu diễn vượt quá lưới bit tiêu chuẩn của các toán hạng cũng giống như đối với phép cộng, tức là cờ mang cf được sử dụng. Bạn chỉ cần tưởng tượng quá trình trừ trong một cột và kết hợp chính xác các lệnh của bộ vi xử lý với lệnh sbb.

Để kết thúc cuộc thảo luận của chúng ta về các lệnh cộng và trừ, ngoài các cờ cf và của, có một số cờ khác trong thanh ghi eflags có thể được sử dụng với các lệnh số học nhị phân. Đây là các cờ sau:

1) cờ zf - zero, được đặt thành 1 nếu kết quả của phép toán là 0 và thành 1 nếu kết quả không bằng 0;

2) cờ dấu sf, giá trị mà sau các phép toán số học (và không chỉ) trùng với giá trị của bit quan trọng nhất của kết quả, tức là với bit 7, 15 hoặc 31. Do đó, cờ này có thể được sử dụng cho các phép toán trên các số có dấu.

Phép nhân các số không có dấu

Lệnh nhân các số không có dấu là

đa hệ số_1

Như bạn có thể thấy, lệnh chỉ chứa một toán hạng cấp số nhân. Hệ số toán hạng thứ hai_2 được chỉ định ngầm. Vị trí của nó là cố định và phụ thuộc vào kích thước của các yếu tố. Nói chung, kết quả của một phép nhân lớn hơn bất kỳ hệ số nào của nó, kích thước và vị trí của nó cũng phải được xác định duy nhất. Các tùy chọn về kích thước của các thừa số và vị trí của toán hạng thứ hai và kết quả được trình bày trong Bảng 10.

Bảng 10. Sắp xếp các toán hạng và kết quả trong phép nhân

Từ bảng có thể thấy rằng sản phẩm bao gồm hai phần và, tùy thuộc vào kích thước của các toán hạng, được đặt ở hai vị trí - thay cho hệ số 2 (phần dưới) và trong thanh ghi bổ sung ah, dx, edx (cao hơn phần). Sau đó, làm thế nào để tự động (tức là trong quá trình thực thi chương trình) để biết rằng kết quả đủ nhỏ để vừa với một thanh ghi hoặc nó vượt quá kích thước của thanh ghi và phần cao nhất nằm trong một thanh ghi khác? Để làm điều này, chúng tôi sử dụng cf và cờ tràn mà chúng tôi đã biết từ cuộc thảo luận trước:

1) nếu phần đầu của kết quả bằng 0, thì sau khi hoạt động sản phẩm, các cờ cf = 0 và of = XNUMX;

2) nếu các cờ này khác XNUMX, thì điều này có nghĩa là kết quả đã vượt ra ngoài phần nhỏ nhất của sản phẩm và bao gồm hai phần, cần được tính đến trong quá trình làm việc tiếp theo.

Nhân các số có chữ ký

Lệnh nhân các số với một dấu là

[toán hạng imul_1, toán hạng_2, toán hạng_3]

Lệnh này được thực hiện giống như lệnh mul. Một đặc điểm khác biệt của lệnh imul là chỉ sự hình thành của dấu hiệu.

Nếu kết quả nhỏ và phù hợp với một thanh ghi (nghĩa là, nếu cf = of = 0), thì nội dung của thanh ghi kia (phần cao) là phần mở rộng dấu - tất cả các bit của nó đều bằng bit cao (bit dấu ) của phần thấp của kết quả. Ngược lại (nếu cf = of = 1), dấu của kết quả là bit dấu của phần cao của kết quả và bit dấu của phần thấp là bit có nghĩa của mã kết quả nhị phân.

Phân chia các số không dấu

Lệnh chia các số không có dấu là

dải phân cách

Số chia có thể nằm trong bộ nhớ hoặc trong một thanh ghi và có kích thước 8, 16 hoặc 32 bit. Vị trí của số bị chia là cố định và giống như trong lệnh nhân, phụ thuộc vào kích thước của các toán hạng. Kết quả của lệnh chia là các giá trị thương và phần dư.

Các tùy chọn cho vị trí và kích thước của các toán hạng của phép chia được thể hiện trong Bảng 11.

Bảng 11. Sắp xếp các toán hạng và kết quả trong phép chia

Sau khi lệnh chia được thực hiện, nội dung của các cờ không được xác định, nhưng ngắt số 0, được gọi là "chia cho không", có thể xảy ra. Loại gián đoạn này thuộc về cái gọi là ngoại lệ. Loại ngắt này xảy ra bên trong bộ vi xử lý do một số bất thường trong quá trình tính toán. Ngắt O, "chia cho không", trong khi thực hiện lệnh div có thể xảy ra vì một trong những lý do sau:

1) số chia là XNUMX;

2) thương số không được bao gồm trong lưới bit được phân bổ cho nó, điều này có thể xảy ra trong các trường hợp sau:

a) khi chia một số bị chia có giá trị là một từ cho một số bị chia có giá trị là byte và giá trị của số bị chia lớn hơn 256 lần giá trị của số bị chia;

b) Khi chia một số bị chia có giá trị là một từ kép cho một số bị chia có giá trị là một từ và giá trị của số bị chia lớn hơn giá trị của số bị chia là 65 lần;

c) khi chia số bị chia có giá trị từ gấp đôi cho số bị chia có giá trị từ kép và giá trị của số bị chia lớn hơn giá trị của số bị chia là 4 lần.

Phân chia có dấu hiệu

Lệnh chia số có dấu là

dải phân cách idiv

Đối với lệnh này, tất cả các điều khoản được xem xét liên quan đến lệnh và số có dấu đều hợp lệ. Chúng tôi chỉ lưu ý các đặc điểm của sự xuất hiện của ngoại lệ 0, "phép chia cho số không", trong trường hợp các số có dấu. Nó xảy ra khi thực thi lệnh idiv vì một trong những lý do sau:

1) số chia là XNUMX;

2) thương số không được bao gồm trong lưới bit được phân bổ cho nó.

Sau đó lần lượt có thể xảy ra:

1) khi chia một số bị chia có giá trị từ có dấu cho một số bị chia có giá trị byte có dấu và giá trị của số bị chia lớn hơn 128 lần giá trị của số bị chia (do đó, thương số không được nằm ngoài phạm vi từ -128 đến + 127);

2) khi chia số bị chia cho một giá trị từ kép có dấu cho số bị chia cho một giá trị từ có dấu và giá trị của số bị chia lớn hơn 32 lần giá trị của số bị chia (do đó, thương không được nằm ngoài phạm vi từ - 768 đến +32);

3) khi chia số bị chia cho một giá trị từ bốn chữ có dấu cho một số chia từ kép có dấu và giá trị của số bị chia lớn hơn 2 lần giá trị của số bị chia (do đó, thương số không được nằm ngoài phạm vi -147 đến + 483 648 2 147).

Hướng dẫn bổ trợ cho các phép toán số nguyên

Có một số hướng dẫn trong tập lệnh của bộ vi xử lý có thể giúp lập trình các thuật toán thực hiện các phép tính số học dễ dàng hơn. Các vấn đề khác nhau có thể phát sinh trong đó, để giải quyết vấn đề mà các nhà phát triển bộ vi xử lý đã cung cấp một số lệnh.

Nhập lệnh chuyển đổi

Điều gì sẽ xảy ra nếu kích thước của các toán hạng tham gia vào các phép toán số học là khác nhau? Ví dụ, giả sử trong một phép toán cộng, một toán hạng là một từ và toán hạng kia là một từ kép. Ở trên đã nói rằng các toán hạng có cùng định dạng phải tham gia vào phép toán cộng. Nếu các số không có dấu, thì kết quả đầu ra rất dễ tìm. Trong trường hợp này, trên cơ sở toán hạng ban đầu, một toán hạng mới (định dạng từ kép) có thể được hình thành, các bit cao của chúng có thể đơn giản được điền bằng các số không. Tình hình phức tạp hơn đối với các số có dấu: làm thế nào để tính đến dấu của toán hạng một cách động, trong khi thực hiện chương trình? Để giải quyết những vấn đề như vậy, tập lệnh của bộ vi xử lý có cái gọi là hướng dẫn chuyển đổi kiểu. Các hướng dẫn này mở rộng byte thành từ, từ thành từ kép và từ kép thành từ bốn (giá trị 64-bit). Hướng dẫn chuyển đổi kiểu đặc biệt hữu ích khi chuyển đổi số nguyên có dấu, vì chúng tự động điền vào các bit bậc cao của toán hạng mới được xây dựng với các giá trị của bit dấu của đối tượng cũ. Thao tác này dẫn đến các giá trị nguyên có cùng dấu và cùng độ lớn với giá trị ban đầu, nhưng ở định dạng dài hơn. Phép biến đổi như vậy được gọi là phép toán lan truyền dấu hiệu.

Có hai loại lệnh chuyển đổi kiểu.

1. Hướng dẫn không có toán hạng. Các lệnh này hoạt động với các thanh ghi cố định:

1) cbw (Chuyển đổi Byte sang Word) - lệnh chuyển một byte (trong thanh ghi al) thành một từ (trong thanh ghi ah) bằng cách truyền giá trị của bit cao al cho tất cả các bit của thanh ghi ah;

2) cwd (Convert Word to Double) - lệnh chuyển một từ (trong thanh ghi ax) thành một từ kép (trong thanh ghi dx: ax) bằng cách truyền giá trị của bit cao ax cho tất cả các bit của thanh ghi dx;

3) cwde (Chuyển đổi Word thành Double) - lệnh chuyển một từ (trong thanh ghi ax) thành một từ kép (trong thanh ghi eax) bằng cách trải rộng giá trị của bit cao ax cho tất cả các bit của nửa trên của thanh ghi eax ;

4) cdq (Convert Double Word to Quarter Word) - lệnh chuyển một từ kép (trong thanh ghi eax) thành một từ bốn (trong thanh ghi edx: eax) bằng cách chia đều giá trị của bit quan trọng nhất của eax cho tất cả bit của thanh ghi edx.

2. Các lệnh movsx và movzx liên quan đến các lệnh xử lý chuỗi. Các lệnh này có một thuộc tính hữu ích trong bối cảnh vấn đề của chúng ta:

1) movsx toán hạng thứ 1, toán hạng thứ 2 - gửi với sự lan truyền dấu hiệu. Mở rộng giá trị 8 hoặc 16 bit của toán hạng_2, có thể là thanh ghi hoặc toán hạng bộ nhớ, thành giá trị 16 hoặc 32 bit trong một trong các thanh ghi, sử dụng giá trị của bit dấu để lấp đầy các vị trí cao hơn của toán hạng_1. Hướng dẫn này hữu ích để chuẩn bị các toán hạng có dấu cho các phép toán số học;

2) movzx toán hạng_1, toán hạng thứ 2 - gửi không có phần mở rộng. Mở rộng giá trị 8 bit hoặc 16 bit của toán hạng_2 thành 16 bit hoặc 32 bit, xóa (lấp đầy) các vị trí cao của toán hạng_2 bằng các số không. Hướng dẫn này hữu ích để chuẩn bị các toán hạng không dấu cho số học.

Các lệnh hữu ích khác

1. xadd đích, nguồn - trao đổi và bổ sung.

Lệnh cho phép bạn thực hiện hai hành động theo trình tự:

1) trao đổi giá trị đích và nguồn;

2) đặt toán hạng đích thay cho tổng: đích = đích + nguồn.

2. Toán hạng phủ định - phủ định với phần bù của hai.

Lệnh đảo ngược giá trị của toán hạng. Về mặt vật lý, lệnh thực hiện một hành động:

toán hạng = 0 - toán hạng, tức là trừ toán hạng cho số không.

Lệnh neg operand có thể được sử dụng:

1) để thay đổi dấu hiệu;

2) để thực hiện phép trừ với một hằng số.

Các phép toán số học trên các số thập phân-nhị phân

Trong phần này, chúng ta sẽ xem xét các chi tiết cụ thể của từng trong bốn phép tính số học cơ bản đối với các số BCD được đóng gói và chưa đóng gói.

Đúng là có thể nảy sinh câu hỏi: tại sao chúng ta cần số BCD? Câu trả lời có thể là: Số BCD cần thiết trong các ứng dụng kinh doanh, tức là những con số cần phải lớn và chính xác. Như chúng ta đã thấy trên ví dụ về số nhị phân, các phép toán với những số như vậy khá khó khăn đối với hợp ngữ. Những bất lợi của việc sử dụng số nhị phân bao gồm những điều sau:

1) Các giá trị trong định dạng từ và từ kép có một phạm vi giới hạn. Nếu chương trình được thiết kế để hoạt động trong lĩnh vực tài chính, thì việc giới hạn số tiền bằng rúp ở mức 65 (cho một từ) hoặc thậm chí là 536 (cho một từ kép) sẽ thu hẹp đáng kể phạm vi ứng dụng của nó;

2) sự hiện diện của lỗi làm tròn. Bạn có thể tưởng tượng một chương trình chạy ở đâu đó trong một ngân hàng không tính đến giá trị của số dư khi hoạt động với số nguyên nhị phân và hoạt động với hàng tỷ? Tôi không muốn trở thành tác giả của một chương trình như vậy. Việc sử dụng các số dấu phẩy động sẽ không tiết kiệm - vấn đề làm tròn tương tự cũng tồn tại ở đó;

3) trình bày một lượng lớn kết quả dưới dạng ký hiệu (mã ASCII). Các chương trình kinh doanh không chỉ thực hiện các phép tính; một trong những mục đích sử dụng của chúng là cung cấp thông tin nhanh chóng cho người dùng. Để làm được điều này, tất nhiên, thông tin phải được trình bày dưới dạng biểu tượng. Việc chuyển đổi số từ nhị phân sang ASCII đòi hỏi một số nỗ lực tính toán. Một số dấu phẩy động thậm chí còn khó chuyển thành dạng ký hiệu hơn. Nhưng nếu bạn nhìn vào biểu diễn thập lục phân của một chữ số thập phân không được đóng gói và ký tự tương ứng của nó trong bảng ASCII, bạn có thể thấy rằng chúng chênh lệch nhau 30 giờ. Như vậy, việc chuyển đổi sang dạng biểu tượng và ngược lại dễ dàng và nhanh chóng hơn rất nhiều.

Bạn có thể đã thấy tầm quan trọng của việc nắm vững ít nhất những điều cơ bản của các hành động với số thập phân. Tiếp theo, hãy xem xét các tính năng thực hiện các phép tính số học cơ bản với số thập phân. Chúng tôi ngay lập tức lưu ý thực tế rằng không có lệnh riêng biệt nào để cộng, trừ, nhân và chia các số BCD. Điều này được thực hiện vì những lý do khá dễ hiểu: thứ nguyên của những con số như vậy có thể lớn tùy ý. Số BCD có thể được cộng và trừ, cả đóng gói và giải nén, nhưng chỉ số BCD không được đóng gói mới có thể chia và nhân. Tại sao điều này là như vậy sẽ được xem xét từ các cuộc thảo luận thêm.

Số học trên các số BCD đã giải nén

Thêm số BCD đã giải nén

Chúng ta hãy xem xét hai trường hợp của phép cộng.

Ví dụ

Kết quả của phép cộng không quá 9

6 = 0000

+

3 = 0000

=

9 = 0000

Không có sự chuyển giao từ cơ sở đến tetrad cao cấp. Kết quả là chính xác.

Ví dụ

Kết quả của phép cộng lớn hơn 9:

06 = 0000

+

07 = 0000

=

13 = 0000

Chúng tôi đã không nhận được số BCD nữa. Kết quả là sai. Kết quả chính xác ở định dạng BCD được giải nén phải là 0000 0001 0000 0011 ở dạng nhị phân (hoặc 13 ở dạng thập phân).

Sau khi phân tích vấn đề này khi cộng số BCD (và các vấn đề tương tự khi thực hiện các phép tính số học khác) và các cách giải quyết có thể xảy ra, các nhà phát triển hệ thống lệnh của bộ vi xử lý đã quyết định không giới thiệu các lệnh đặc biệt để làm việc với các số BCD, mà giới thiệu một số lệnh điều chỉnh. .

Mục đích của các lệnh này là để sửa chữa kết quả của phép toán các lệnh số học thông thường đối với các trường hợp toán hạng trong chúng là số BCD.

Trong trường hợp phép trừ ở ví dụ 10, có thể thấy rằng kết quả thu được cần phải được sửa lại. Để sửa thao tác cộng hai số BCD chưa được đóng gói có một chữ số trong hệ thống lệnh của bộ vi xử lý, có một lệnh đặc biệt - aaa (ASCII Adjust for Addition) - sửa kết quả của phép cộng để biểu diễn ở dạng ký hiệu.

Lệnh này không có toán hạng. Nó hoạt động ngầm chỉ với thanh ghi al và phân tích cú pháp giá trị của tetrad thấp hơn của nó:

1) nếu giá trị này nhỏ hơn 9, thì cờ cf được đặt lại về XNUMX và quá trình chuyển đổi sang lệnh tiếp theo được thực hiện;

2) nếu giá trị này lớn hơn 9, thì các hành động sau được thực hiện:

a) 6 được thêm vào nội dung của tetrad al thấp hơn (nhưng không phải vào nội dung của toàn bộ thanh ghi!) Vì vậy, giá trị của kết quả thập phân được sửa theo hướng chính xác;

b) cờ cf được đặt thành 1, do đó cố định việc chuyển giao thành bit quan trọng nhất để nó có thể được tính đến trong các hành động tiếp theo.

Vì vậy, trong ví dụ 10, giả sử rằng tổng giá trị 0000 1101 là al, sau lệnh aaa, thanh ghi sẽ có 1101 + 0110 = 0011, tức là nhị phân 0000 0011 hoặc thập phân 3, và cờ cf sẽ được đặt thành 1, tức là quá trình truyền đã được lưu trữ trong bộ vi xử lý. Tiếp theo, lập trình viên sẽ cần sử dụng lệnh bổ sung adc, lệnh này sẽ tính đến giá trị mang từ bit trước đó.

Phép trừ các số BCD chưa giải nén

Tình hình ở đây là khá giống với việc bổ sung. Chúng ta hãy xem xét các trường hợp tương tự.

Ví dụ

Kết quả của phép trừ không lớn hơn 9:

6 = 0000

-

3 = 0000

=

3 = 0000

Như bạn có thể thấy, không có khoản vay nào từ máy tính xách tay cao cấp. Kết quả là chính xác và không cần hiệu chỉnh.

Ví dụ

Kết quả của phép trừ lớn hơn 9:

6 = 0000

-

7 = 0000

=

-1 = 1111 1111

Phép trừ được thực hiện theo quy tắc của số học nhị phân. Do đó, kết quả không phải là số BCD.

Kết quả chính xác ở định dạng BCD được giải nén phải là 9 (0000 1001 ở dạng nhị phân). Trong trường hợp này, một khoản vay từ chữ số có nghĩa nhất được giả định, như với lệnh trừ thông thường, tức là trong trường hợp số BCD, phép trừ 16-7 sẽ thực sự được thực hiện. Do đó, rõ ràng là, như trong trường hợp cộng thì phải sửa lại kết quả trừ. Đối với điều này, có một lệnh đặc biệt - aas (ASCII Điều chỉnh cho phép trừ) - hiệu chỉnh kết quả của phép trừ để biểu diễn ở dạng ký hiệu.

Lệnh aas cũng không có toán hạng và hoạt động trên thanh ghi al, phân tích cú pháp tetrad bậc nhỏ nhất của nó như sau:

1) nếu giá trị của nó nhỏ hơn 9, thì cờ cf được đặt lại về 0 và quyền điều khiển được chuyển sang lệnh tiếp theo;

2) nếu giá trị tetrad trong al lớn hơn 9, thì lệnh aas thực hiện các hành động sau:

a) trừ đi 6 từ nội dung của tứ phân dưới của thanh ghi al (lưu ý - không phải từ nội dung của toàn bộ thanh ghi);

b) đặt lại tứ diện trên của thanh ghi al;

c) đặt cờ cf thành 1, do đó sửa lỗi mượn bậc cao tưởng tượng.

Rõ ràng là lệnh aas được sử dụng cùng với các lệnh trừ con và lệnh sbb cơ bản. Trong trường hợp này, chỉ nên sử dụng lệnh phụ một lần, khi trừ đi các chữ số thấp nhất của các toán hạng, thì nên sử dụng lệnh sbb, lệnh này sẽ tính đến khoản vay có thể có từ lệnh cao nhất.

Phép nhân các số BCD đã giải nén

Bằng cách sử dụng ví dụ về cộng và trừ các số không được đóng gói, rõ ràng là không có thuật toán tiêu chuẩn nào để thực hiện các phép toán này trên số BCD và bản thân người lập trình, dựa trên các yêu cầu đối với chương trình của mình, thực hiện các phép toán này.

Việc thực hiện hai phép tính còn lại - nhân và chia - thậm chí còn phức tạp hơn. Trong tập lệnh của bộ vi xử lý, chỉ có các phương tiện để tạo ra phép nhân và chia các số BCD không đóng gói có một chữ số.

Để nhân các số có thứ nguyên tùy ý, bạn cần tự thực hiện quy trình nhân, dựa trên một số thuật toán nhân, ví dụ: "trong một cột".

Để nhân hai số BCD có một chữ số, bạn phải:

1) đặt một trong các yếu tố trong thanh ghi AL (theo yêu cầu của lệnh mul);

2) đặt toán hạng thứ hai vào thanh ghi hoặc bộ nhớ, cấp phát một byte;

3) nhân các thừa số với lệnh mul (kết quả, như mong đợi, sẽ là ah);

4) kết quả, tất nhiên, sẽ ở dạng mã nhị phân, vì vậy nó cần được sửa chữa.

Để sửa kết quả sau phép nhân, một lệnh đặc biệt được sử dụng - aam (ASCII Điều chỉnh cho phép nhân) - hiệu chỉnh kết quả của phép nhân để biểu diễn ở dạng ký hiệu.

Nó không có toán hạng và hoạt động trên thanh ghi AX như sau:

1) chia al cho 10;

2) kết quả của phép chia được viết như sau: thương bằng al, dư bằng ah. Kết quả là, sau khi thực hiện lệnh aam, thanh ghi AL và ah chứa các chữ số BCD chính xác của tích hai chữ số.

Trước khi kết thúc cuộc thảo luận về lệnh aam, chúng ta cần lưu ý thêm một cách sử dụng lệnh này. Lệnh này có thể được sử dụng để chuyển đổi một số nhị phân trong thanh ghi AL thành số BCD chưa được đóng gói, số này sẽ được đặt trong thanh ghi ah: chữ số có nghĩa nhất của kết quả là ah, chữ số có nghĩa nhỏ nhất là al. Rõ ràng là số nhị phân phải nằm trong khoảng 0... 99.

Phân chia số BCD đã giải nén

Quá trình thực hiện phép toán chia hai số BCD chưa được giải nén có phần khác với các phép toán khác được xem xét trước đó với chúng. Ở đây cũng cần thực hiện các thao tác sửa chữa, nhưng chúng phải được thực hiện trước thao tác chính chia trực tiếp một số BCD cho một số BCD khác. Đầu tiên, trong sổ đăng ký ah, bạn cần lấy hai chữ số BCD chưa giải nén của cổ tức. Điều này làm cho lập trình viên thoải mái cho anh ta theo một cách nào đó. Tiếp theo, bạn cần sử dụng lệnh aad - aad (ASCII Adjust for Division) - hiệu chỉnh phép chia cho biểu diễn tượng trưng.

Lệnh không có toán hạng và chuyển đổi số BCD chưa đóng gói gồm hai chữ số trong thanh ghi ax thành số nhị phân. Số nhị phân này sau đó sẽ đóng vai trò số bị chia trong phép chia. Ngoài việc chuyển đổi, lệnh aad sẽ đặt số nhị phân kết quả vào thanh ghi AL. Cổ tức đương nhiên sẽ là số nhị phân trong phạm vi 0... 99.

Thuật toán mà lệnh aad thực hiện chuyển đổi này như sau:

1) nhân chữ số cao nhất của số BCD ban đầu trong ah (nội dung của AH) với 10;

2) thực hiện phép cộng AH + AL, kết quả của nó (số nhị phân) được nhập vào AL;

3) thiết lập lại nội dung của AN.

Tiếp theo, người lập trình cần đưa ra lệnh chia div thông thường để thực hiện phép chia nội dung của ax cho một chữ số BCD duy nhất nằm trong thanh ghi byte hoặc vị trí bộ nhớ byte.

Tương tự như aash, lệnh aad cũng có thể được sử dụng để chuyển đổi các số BCD đã giải nén từ phạm vi 0... 99 sang số nhị phân tương đương của chúng.

Để chia các số có dung lượng lớn hơn, cũng như trong trường hợp nhân, bạn cần triển khai thuật toán của riêng mình, ví dụ: "trong một cột", hoặc tìm một cách tối ưu hơn.

Số học trên các số BCD đóng gói

Như đã lưu ý ở trên, số BCD được đóng gói chỉ có thể được cộng và trừ. Để thực hiện các hành động khác trên chúng, chúng phải được chuyển đổi bổ sung sang định dạng giải nén hoặc thành biểu diễn nhị phân. Do thực tế là số BCD được đóng gói không được quan tâm nhiều, chúng tôi sẽ xem xét chúng một cách ngắn gọn.

Thêm số BCD được đóng gói

Đầu tiên, hãy đi vào trọng tâm của vấn đề và thử cộng hai số BCD có hai chữ số. Ví dụ thêm số BCD được đóng gói:

67 = 01100111

+

75 = 01110101

=

142 = 1101 1100 = 220

Như bạn có thể thấy, ở dạng nhị phân, kết quả là 1101 1100 (hoặc 220 ở dạng thập phân), điều này không chính xác. Điều này là do bộ vi xử lý không biết về sự tồn tại của số BCD và thêm chúng theo quy tắc cộng số nhị phân. Trên thực tế, kết quả trong BCD phải là 0001 0100 0010 (hoặc 142 ở dạng thập phân).

Có thể thấy rằng, đối với các số BCD không đóng gói, đối với các số BCD đã đóng gói, cần phải hiệu chỉnh bằng cách nào đó kết quả của các phép toán số học.

Bộ vi xử lý cung cấp cho lệnh này daa - daa (Điều chỉnh thập phân cho phép cộng) - hiệu chỉnh kết quả của phép cộng để trình bày ở dạng thập phân.

Lệnh daa chuyển đổi nội dung của thanh ghi al thành hai chữ số thập phân đóng gói theo thuật toán được đưa ra trong mô tả của lệnh daa. Đơn vị kết quả (nếu kết quả của phép cộng lớn hơn 99) được lưu trữ trong cờ cf, do đó tính đến việc chuyển giao cho bit quan trọng nhất.

Phép trừ số BCD được đóng gói

Tương tự như phép cộng, bộ vi xử lý xử lý các số BCD được đóng gói dưới dạng nhị phân và trừ các số BCD dưới dạng nhị phân tương ứng.

Ví dụ

Phép trừ số BCD đã đóng gói.

Hãy trừ 67-75. Vì bộ vi xử lý thực hiện phép trừ theo cách cộng, chúng ta sẽ làm theo như sau:

67 = 01100111

+

-75 = 10110101

=

-8 = 0001 1100 = 28

Như bạn có thể thấy, kết quả là 28 ở dạng thập phân, điều này thật vô lý. Trong BCD, kết quả phải là 0000 1000 (hoặc 8 ở dạng thập phân).

Khi lập trình phép trừ các số BCD được đóng gói, người lập trình cũng như khi thực hiện phép trừ các số BCD chưa được đóng gói, phải tự mình kiểm soát dấu hiệu. Điều này được thực hiện bằng cách sử dụng cờ CF, giúp sửa lỗi vay bậc cao.

Bản thân phép trừ các số BCD được thực hiện bằng lệnh trừ con đơn giản hoặc lệnh trừ sbb. Việc sửa kết quả được thực hiện bằng lệnh das - das (Decimal Adjust for Substraction) - sửa kết quả của phép trừ để biểu diễn ở dạng thập phân.

Lệnh das chuyển đổi nội dung của thanh ghi AL thành hai chữ số thập phân đóng gói theo thuật toán được đưa ra trong mô tả của lệnh das.

LECTURE số 19. Các lệnh chuyển điều khiển

1. Các lệnh logic

Cùng với các phương tiện tính toán số học, hệ thống lệnh của bộ vi xử lý còn có các phương tiện chuyển đổi dữ liệu logic. Theo lôgic có nghĩa là các phép biến đổi dữ liệu như vậy, dựa trên các quy tắc của lôgic hình thức.

Logic hình thức hoạt động ở cấp độ của các câu lệnh đúng và sai. Đối với bộ vi xử lý, điều này thường có nghĩa là 1 và 0 tương ứng. Đối với máy tính, ngôn ngữ của số không và số là bản địa, nhưng đơn vị dữ liệu tối thiểu mà lệnh máy hoạt động là byte. Tuy nhiên, ở cấp độ hệ thống, thường phải có khả năng hoạt động ở mức thấp nhất có thể là mức bit.

Cơm. 29. Phương tiện xử lý dữ liệu logic

Các phương tiện biến đổi dữ liệu lôgic bao gồm các lệnh lôgic và các phép toán lôgic. Toán hạng của một lệnh hợp ngữ nói chung có thể là một biểu thức, đến lượt nó là sự kết hợp của các toán tử và toán hạng. Trong số các toán tử này có thể có các toán tử thực hiện các phép toán logic trên các đối tượng biểu thức.

Trước khi xem xét các công cụ này một cách chi tiết, chúng ta hãy xem xét bản thân dữ liệu logic là gì và những thao tác nào được thực hiện trên chúng.

Dữ liệu boolean

Cơ sở lý thuyết để xử lý dữ liệu lôgic là lôgic hình thức. Có một số hệ thống logic. Một trong những cái nổi tiếng nhất là phép tính mệnh đề. Mệnh đề là bất kỳ phát biểu nào có thể được cho là đúng hoặc sai.

Phép tính mệnh đề là một tập hợp các quy tắc được sử dụng để xác định tính đúng hay sai của một số tổ hợp mệnh đề.

Phép tính mệnh đề được kết hợp rất hài hòa với các nguyên tắc của máy tính và các phương pháp lập trình cơ bản của nó. Tất cả các thành phần phần cứng của máy tính đều được xây dựng trên các chip logic. Hệ thống biểu diễn thông tin trong máy tính ở mức thấp nhất dựa trên khái niệm bit. Một bit, chỉ có hai trạng thái (0 (sai) và 1 (đúng)), phù hợp một cách tự nhiên với phép tính mệnh đề.

Theo lý thuyết, các phép toán logic sau đây có thể được thực hiện trên các câu lệnh (trên bit).

1. Phủ định (logic NOT) - một phép toán logic trên một toán hạng, kết quả của nó là nghịch đảo của giá trị của toán hạng ban đầu.

Phép toán này được đặc trưng duy nhất bởi bảng chân trị sau (Bảng 12).

Bảng 12. Bảng chân lý cho phủ định logic

2. Phép cộng logic (OR bao gồm logic) - một phép toán logic trên hai toán hạng, kết quả của nó là "true" (1) nếu một hoặc cả hai toán hạng là true (1) và "false" (0) nếu cả hai toán hạng là sai (0).

Hoạt động này được mô tả bằng cách sử dụng bảng sự thật sau (Bảng 13).

Bảng 13. Bảng chân trị cho OR bao hàm lôgic

3. Phép nhân logic (logic AND) - một phép toán logic trên hai toán hạng, kết quả của nó là đúng (1) chỉ khi cả hai toán hạng đều đúng (1). Trong tất cả các trường hợp khác, giá trị của phép toán là "false" (0).

Hoạt động này được mô tả bằng cách sử dụng bảng sự thật sau (Bảng 14).

Bảng 14. Bảng logic VÀ chân lý

4. Phép cộng loại trừ logic (OR loại trừ logic) - một phép toán logic trên hai toán hạng, kết quả của nó là "true" (1), nếu chỉ một trong hai toán hạng là true (1) và false (0), nếu cả hai toán hạng đều sai (0) hoặc đúng (1). Hoạt động này được mô tả bằng cách sử dụng bảng sự thật sau (Bảng 15).

Bảng 15. Bảng chân trị cho XOR logic

Tập lệnh của bộ vi xử lý chứa năm lệnh hỗ trợ các hoạt động này. Các lệnh này thực hiện các phép toán logic trên các bit của toán hạng. Tất nhiên, kích thước của các toán hạng phải giống nhau. Ví dụ, nếu thứ nguyên của các toán hạng bằng từ (16 bit), thì phép toán logic được thực hiện đầu tiên trên các bit 0 của toán hạng và kết quả của nó được viết thay cho bit XNUMX của kết quả. Tiếp theo, lệnh lặp lại các thao tác này tuần tự trên tất cả các bit từ bit đầu tiên đến bit thứ mười lăm.

Lệnh logic

Hệ thống lệnh của bộ vi xử lý có bộ lệnh sau hỗ trợ làm việc với dữ liệu logic:

1) và toán hạng thứ 1, toán hạng thứ 2 - phép toán nhân logic. Lệnh thực hiện phép toán AND logic theo chiều bit (kết hợp) trên các bit của toán hạng thứ 1 và toán hạng thứ 2. Kết quả được viết thay cho toán hạng_1;

2) og toán hạng thứ 1, toán hạng thứ 2 - phép toán cộng logic. Lệnh thực hiện phép toán OR logic theo bit (ngắt kết nối) trên các bit của toán hạng thứ 1 và toán hạng thứ 2. Kết quả được viết thay cho toán hạng_1;

3) toán hạng xor_1, toán hạng thứ 2 - hoạt động của phép cộng loại trừ logic. Lệnh thực hiện phép toán XOR logic theo chiều bit trên các bit của toán hạng thứ 1 và toán hạng thứ 2. Kết quả được viết thay cho toán hạng;

4) toán hạng kiểm tra thứ 1, toán hạng thứ 2 - hoạt động "kiểm tra" (sử dụng phương pháp nhân logic). Lệnh thực hiện phép toán AND logic theo chiều bit trên các bit của toán hạng thứ 1 và toán hạng thứ 2. Trạng thái của các toán hạng được giữ nguyên, chỉ có các cờ zf, sf và pf được thay đổi, điều này giúp có thể phân tích trạng thái của các bit riêng lẻ của toán hạng mà không thay đổi trạng thái của chúng;

5) không phải toán hạng - hoạt động của phủ định logic. Lệnh thực hiện đảo ngược bit (thay thế giá trị bằng giá trị ngược lại) của mỗi bit của toán hạng. Kết quả được viết thay cho toán hạng.

Để hiểu vai trò của các lệnh logic trong tập lệnh của bộ vi xử lý, điều rất quan trọng là phải hiểu các lĩnh vực ứng dụng của chúng và các phương pháp điển hình sử dụng chúng trong lập trình.

Với sự trợ giúp của các lệnh logic, có thể chọn các bit riêng lẻ trong toán hạng nhằm mục đích thiết lập chúng, đặt lại chúng, đảo ngược chúng hoặc đơn giản là kiểm tra một giá trị nhất định.

Để tổ chức công việc như vậy với các bit, toán hạng_2 thường đóng vai trò của một mặt nạ. Với sự trợ giúp của các bit của mặt nạ này được đặt trong bit 1, toán hạng_1 bit cần thiết cho một hoạt động cụ thể được xác định. Hãy chỉ ra những lệnh logic nào có thể được sử dụng cho mục đích này:

1) để đặt các chữ số (bit) nhất định thành 1, lệnh og toán hạng_1, toán hạng thứ 2 được sử dụng.

Trong hướng dẫn này, toán hạng_2, hoạt động như một mặt nạ, phải chứa 1 bit thay cho các bit đó sẽ được đặt thành 1 trong toán hạng_XNUMX;

2) để đặt lại các chữ số (bit) nhất định về 0, lệnh và toán hạng_1, toán hạng thứ 2 được sử dụng.

Trong hướng dẫn này, toán hạng_2, hoạt động như một mặt nạ, phải chứa các bit 0 thay cho các bit đó phải được đặt thành 1 trong toán hạng_XNUMX;

3) lệnh xor toán hạng_1, toán hạng_2 được áp dụng:

a) để tìm ra bit nào trong toán hạng_1 và toán hạng khác nhau;

b) đảo ngược trạng thái của các bit được chỉ định trong toán hạng_1.

Các bit mặt nạ mà chúng ta quan tâm (toán hạng 2) khi thực hiện lệnh xor phải là đơn lẻ, phần còn lại phải bằng XNUMX;

Toán hạng kiểm tra lệnh thứ 1, toán hạng thứ 2 (kiểm tra toán hạng thứ 1) được sử dụng để kiểm tra trạng thái của các bit được chỉ định.

Các bit đã kiểm tra của toán hạng_1 trong mặt nạ (toán hạng_2) phải được đặt thành một. Thuật toán của lệnh kiểm tra tương tự như thuật toán của lệnh và, nhưng nó không thay đổi giá trị của toán hạng_1. Kết quả của lệnh là đặt giá trị của cờ XNUMX zf:

1) nếu zf = 0, thì theo kết quả của phép nhân logic, kết quả bằng không sẽ thu được, tức là một bit đơn vị của mặt nạ, không khớp với bit đơn vị tương ứng của toán hạng;

2) nếu zf = 1, thì theo kết quả của phép nhân logic, sẽ thu được kết quả khác 1, tức là ít nhất một bit đơn vị của mặt nạ trùng với bit đơn vị tương ứng của toán hạng_XNUMX.

Để phản ứng với kết quả của lệnh kiểm tra, bạn nên sử dụng lệnh nhảy nhãn jnz (Nhảy nếu không bằng 0) - nhảy nếu cờ XNUMX zf khác XNUMX, hoặc lệnh hành động ngược lại - nhãn jz (Nhảy nếu XNUMX ) - nhảy nếu cờ không zf = XNUMX.

Hai lệnh sau tìm kiếm bit toán hạng đầu tiên được đặt thành 1. Tìm kiếm có thể được thực hiện cả từ đầu và từ cuối toán hạng:

1) Toán hạng bsf_1, toán hạng thứ 2 (Chuyển tiếp quét bit) - quét các bit về phía trước. Lệnh tìm kiếm (quét) các bit của toán hạng_2 từ ít quan trọng nhất đến quan trọng nhất (từ bit 0 đến bit quan trọng nhất) để tìm kiếm bit đầu tiên được đặt thành 1. Nếu tìm thấy một, toán hạng_1 được lấp đầy bằng số bit này như một giá trị số nguyên. Nếu tất cả các bit của toán hạng_2 là 0, thì cờ 1 zf được đặt thành 0, ngược lại cờ zf được đặt lại thành XNUMX;

2) Toán hạng bsr thứ 1, toán hạng thứ 2 (Đặt lại quét bit) - quét các bit theo thứ tự ngược lại. Lệnh tìm kiếm (quét) các bit của toán hạng_2 từ quan trọng nhất đến ít quan trọng nhất (từ bit quan trọng nhất đến bit 0) để tìm kiếm bit đầu tiên được đặt thành 1. Nếu tìm thấy một, toán hạng_1 được lấp đầy bằng số bit này như một giá trị số nguyên. Điều quan trọng là vị trí của bit đơn vị đầu tiên ở bên trái vẫn được đếm so với bit 0. Nếu tất cả các bit của toán hạng 2 là 0, thì cờ 1 zf được đặt thành 0, ngược lại cờ zf được đặt lại thành XNUMX.

Trong các mô hình mới nhất của bộ vi xử lý Intel, một vài lệnh khác đã xuất hiện trong nhóm lệnh logic cho phép bạn truy cập vào một bit cụ thể của toán hạng. Toán hạng có thể nằm trong bộ nhớ hoặc trong một thanh ghi chung. Vị trí bit được cho bởi độ lệch bit so với bit quan trọng nhất của toán hạng. Giá trị offset có thể được chỉ định dưới dạng giá trị trực tiếp hoặc được chứa trong thanh ghi mục đích chung. Bạn có thể sử dụng kết quả của lệnh bsr và bsf làm giá trị bù đắp. Tất cả các lệnh gán giá trị của bit được chọn cho cờ CE.

1) Toán hạng bt, bit_offset (Bit Test) - kiểm tra bit. Lệnh chuyển giá trị bit sang cờ cf;

2) Toán hạng bts, offset_bit (Bit Test và Set) - kiểm tra và thiết lập một bit. Lệnh chuyển giá trị bit sang cờ CF và sau đó đặt bit được kiểm tra thành 1;

3) Toán hạng btr, bit_offset (Bit Test và Reset) - kiểm tra và đặt lại một bit. Lệnh chuyển giá trị bit sang cờ CF và sau đó đặt bit này thành 0;

4) Toán hạng btc, offset_bit (Bit Test và Convert) - kiểm tra và đảo ngược bit. Lệnh bao bọc giá trị của một bit trong cờ cf và sau đó đảo ngược giá trị của bit đó.

Lệnh Shift

Các lệnh trong nhóm này cũng cung cấp thao tác đối với các bit riêng lẻ của các toán hạng, nhưng theo một cách khác với các lệnh logic đã thảo luận ở trên.

Tất cả các lệnh shift sẽ di chuyển các bit trong trường toán hạng sang trái hoặc phải tùy thuộc vào opcode. Tất cả các lệnh shift đều có cấu trúc giống nhau - toán hạng sao chép, shift_count.

Số bit được dịch chuyển - counter_shifts - nằm ở vị trí của toán hạng thứ hai và có thể được đặt theo hai cách:

1) tĩnh, liên quan đến việc thiết lập một giá trị cố định bằng cách sử dụng một toán hạng trực tiếp;

2) động, có nghĩa là nhập giá trị của bộ đếm dịch chuyển vào thanh ghi cl trước khi thực hiện lệnh dịch chuyển.

Dựa vào thứ nguyên của thanh ghi cl, rõ ràng giá trị của bộ đếm dịch chuyển có thể nằm trong khoảng từ 0 đến 255. Nhưng trên thực tế, điều này không hoàn toàn đúng. Đối với mục đích tối ưu hóa, bộ vi xử lý chỉ chấp nhận giá trị của năm bit quan trọng nhất của bộ đếm, tức là giá trị nằm trong phạm vi từ 0 đến 31.

Tất cả các hướng dẫn thay đổi đặt cờ thực hiện cf.

Khi các bit dịch chuyển ra khỏi toán hạng, đầu tiên chúng đánh vào cờ mang, đặt nó bằng giá trị của bit tiếp theo bên ngoài toán hạng. Vị trí tiếp theo của bit này phụ thuộc vào loại lệnh shift và thuật toán chương trình.

Lệnh Shift có thể được chia thành hai loại theo nguyên lý hoạt động:

1) các lệnh dịch chuyển tuyến tính;

2) Các lệnh dịch chuyển theo chu kỳ.

Lệnh dịch chuyển tuyến tính

Các lệnh loại này bao gồm các lệnh thay đổi theo thuật toán sau:

1) bit tiếp theo được đẩy đặt cờ CF;

2) bit được nhập vào toán hạng từ đầu kia có giá trị 0;

3) khi bit tiếp theo được dịch chuyển, nó đi vào cờ CF, trong khi giá trị của bit được dịch trước đó sẽ bị mất! Các lệnh dịch chuyển tuyến tính được chia thành hai loại con:

1) các lệnh dịch chuyển tuyến tính logic;

2) hướng dẫn dịch chuyển tuyến tính số học.

Các lệnh dịch chuyển tuyến tính hợp lý bao gồm những điều sau:

1) toán hạng shl, counter_shifts (Shift Logical Left) - dịch chuyển logic sang trái. Nội dung của toán hạng được chuyển sang trái theo số bit được chỉ định bởi shift_count. Các số không ở bên phải (ở vị trí của bit ít quan trọng nhất) được nhập;

2) Toán hạng shr, shift_count (Shift Phải logic) - dịch chuyển logic sang phải. Nội dung của toán hạng được chuyển sang phải theo số bit được chỉ định bởi shift_count. Ở bên trái (ở vị trí của bit dấu, quan trọng nhất), các số không được nhập.

Hình 30 cho thấy các lệnh này hoạt động như thế nào.

Cơm. 30. Lược đồ công việc của các lệnh chuyển dịch logic tuyến tính

Các lệnh dịch chuyển tuyến tính số học khác với các lệnh dịch chuyển logic ở chỗ chúng hoạt động trên bit dấu của toán hạng theo một cách đặc biệt.

1) toán hạng sal, shift_counter (Shift Arithmetic Left) - dịch chuyển số học sang trái. Nội dung của toán hạng được chuyển sang trái theo số bit được chỉ định bởi shift_count. Ở bên phải (ở vị trí của bit ít quan trọng nhất), các số không được nhập. Lệnh sal không bảo toàn dấu hiệu, nhưng đặt cờ với / trong trường hợp dấu hiệu thay đổi bởi bit tiếp theo nâng cao. Nếu không, lệnh sal hoàn toàn giống lệnh shl;

2) toán hạng sar, shift_count (Shift Arithmetic Right) - dịch chuyển số học sang phải. Nội dung của toán hạng được chuyển sang phải theo số bit được chỉ định bởi shift_count. Zeros được chèn vào toán hạng bên trái. Lệnh sar bảo tồn dấu hiệu, khôi phục nó sau mỗi lần dịch chuyển bit.

Hình 31 cho thấy các lệnh chuyển số học tuyến tính hoạt động như thế nào.

Cơm. 31. Sơ đồ hoạt động của các lệnh dịch chuyển số học tuyến tính

Xoay lệnh

Các lệnh dịch chuyển theo chu kỳ bao gồm các lệnh lưu trữ các giá trị của các bit được dịch chuyển. Có hai loại hướng dẫn chuyển dịch theo chu kỳ:

1) các lệnh dịch chuyển theo chu kỳ đơn giản;

2) các lệnh dịch chuyển theo chu kỳ thông qua cờ mang cf.

Các lệnh dịch chuyển theo chu kỳ đơn giản bao gồm:

1) toán hạng rol, shift_counter (Xoay trái) - dịch chuyển theo chu kỳ sang trái. Nội dung của toán hạng được chuyển sang trái theo số bit được chỉ định bởi toán hạng shift_count. Các bit dịch sang trái được ghi vào cùng một toán hạng từ bên phải;

2) toán hạng gog, counter_shifts (Rotate Right) - dịch chuyển theo chu kỳ sang phải. Nội dung của toán hạng được chuyển sang phải theo số bit được chỉ định bởi toán hạng shift_count. Các bit dịch sang phải được ghi vào cùng một toán hạng ở bên trái.

Cơm. 32. Sơ đồ hoạt động của các lệnh của một dịch chuyển tuần hoàn đơn giản

Như có thể thấy trong Hình 32, các lệnh của một sự dịch chuyển theo chu kỳ đơn giản trong quá trình làm việc của chúng thực hiện một hành động hữu ích, đó là: bit được dịch chuyển theo chu kỳ không chỉ được đẩy vào toán hạng từ đầu kia, mà đồng thời của nó giá trị trở thành giá trị của cờ CE.

Các lệnh dịch chuyển tuần hoàn thông qua cờ mang CF khác với các lệnh dịch chuyển theo chu kỳ đơn giản ở chỗ bit được dịch chuyển không nhập ngay vào toán hạng từ đầu kia của nó, mà được ghi đầu tiên vào cờ mang CE Chỉ lần thực hiện tiếp theo của lệnh dịch chuyển này ( với điều kiện là nó được thực thi trong vòng lặp) khiến bit nâng cao trước đó được đặt ở đầu kia của toán hạng (Hình 33).

Những điều sau đây liên quan đến các lệnh dịch chuyển theo chu kỳ thông qua cờ mang:

1) Toán hạng rcl, shift_count (Xoay qua Carry Left) - dịch chuyển trái theo chu kỳ qua carry.

Nội dung của toán hạng được chuyển sang trái theo số bit được chỉ định bởi toán hạng shift_count. Các bit được dịch chuyển lần lượt trở thành giá trị của cờ mang cf.

2) Toán hạng rsg, shift_count (Xoay qua bên phải) - dịch chuyển theo chu kỳ sang bên phải thông qua phần mang.

Nội dung của toán hạng được chuyển sang phải theo số bit được chỉ định bởi toán hạng shift_count. Các bit được dịch chuyển lần lượt trở thành giá trị của cờ mang CF.

Cơm. 33. Xoay Hướng dẫn qua Carry Flag CF

Hình 33 cho thấy rằng khi dịch chuyển qua cờ mang, một phần tử trung gian xuất hiện, với sự trợ giúp của nó, đặc biệt, có thể thay thế các bit được dịch chuyển theo chu kỳ, đặc biệt là sự không khớp của chuỗi bit.

Sau đây, sự không khớp của một chuỗi bit có nghĩa là một hành động cho phép theo một cách nào đó bản địa hóa và trích xuất các phần cần thiết của chuỗi này và ghi chúng vào một nơi khác.

Các lệnh thay đổi bổ sung

Hệ thống lệnh của các mẫu bộ vi xử lý Intel mới nhất, bắt đầu với i80386, chứa các lệnh dịch chuyển bổ sung giúp mở rộng các khả năng mà chúng ta đã thảo luận trước đó. Đây là các lệnh dịch chuyển độ chính xác kép:

1) sld operand_1, operand_2, shift_counter - dịch chuyển trái có độ chính xác gấp đôi. Lệnh shld thực hiện thay thế bằng cách dịch chuyển các bit của toán hạng_1 sang trái, điền vào các bit của nó ở bên phải các giá trị của các bit được dịch chuyển khỏi toán hạng_2 theo sơ đồ trong Hình. 34. Số bit cần dịch chuyển được xác định bởi giá trị shift_counter, giá trị này có thể nằm trong khoảng 0... 31. Giá trị này có thể được chỉ định dưới dạng toán hạng ngay lập tức hoặc được chứa trong thanh ghi cl. Giá trị của toán hạng_2 không thay đổi.

Cơm. 34. Lược đồ của lệnh shld

2) shrd operand_1, operand_2, shift_counter - dịch chuyển phải có độ chính xác gấp đôi. Lệnh thực hiện việc thay thế bằng cách dịch chuyển các bit của toán hạng operand_1 sang phải, điền vào các bit của nó ở bên trái các giá trị của các bit được dịch chuyển từ toán hạng_2 theo sơ đồ trong Hình 35. Số bit được dịch chuyển là được xác định bởi giá trị của shift_counter, có thể nằm trong phạm vi 0... 31. Giá trị này có thể được chỉ định bởi toán hạng ngay lập tức hoặc được chứa trong thanh ghi cl. Giá trị của toán hạng_2 không thay đổi.

Cơm. 35. Lược đồ của lệnh shrd

Như chúng ta đã lưu ý, các lệnh shld và shrd dịch chuyển lên đến 32 bit, nhưng do đặc thù của việc chỉ định toán hạng và thuật toán hoạt động, các lệnh này có thể được sử dụng để làm việc với các trường dài tới 64 bit.

2. Các lệnh chuyển điều khiển

Chúng ta đã làm quen với một số lệnh mà từ đó các phần tuyến tính của chương trình được hình thành. Mỗi người trong số họ thường thực hiện một số chuyển đổi hoặc chuyển dữ liệu, sau đó bộ vi xử lý chuyển quyền điều khiển sang lệnh tiếp theo. Nhưng rất ít chương trình hoạt động theo cách nhất quán như vậy. Thường có những điểm trong một chương trình phải đưa ra quyết định về lệnh nào sẽ được thực hiện tiếp theo. Giải pháp này có thể là:

1) vô điều kiện - tại thời điểm này, cần phải chuyển quyền kiểm soát không phải cho lệnh tiếp theo, mà cho một lệnh khác, cách lệnh hiện tại một khoảng cách nào đó;

2) có điều kiện - quyết định về lệnh nào sẽ được thực hiện tiếp theo được thực hiện dựa trên việc phân tích một số điều kiện hoặc dữ liệu.

Chương trình là một chuỗi các lệnh và dữ liệu chiếm một lượng không gian RAM nhất định. Không gian bộ nhớ này có thể liền kề hoặc bao gồm nhiều đoạn.

Lệnh chương trình nào sẽ được thực hiện tiếp theo, bộ vi xử lý học từ nội dung của cặp thanh ghi cs: (e) ip:

1) cs - thanh ghi đoạn mã, chứa địa chỉ vật lý (cơ sở) của đoạn mã hiện tại;

2) eip / ip - thanh ghi con trỏ lệnh, chứa giá trị đại diện cho độ lệch trong bộ nhớ của lệnh tiếp theo sẽ được thực thi so với đầu đoạn mã hiện tại.

Thanh ghi cụ thể nào sẽ được sử dụng phụ thuộc vào chế độ định địa chỉ đã đặt use16 hoặc use32. Nếu sử dụng 16 được chỉ định, thì ip được sử dụng, nếu sử dụng32, thì eip được sử dụng.

Do đó, các lệnh chuyển điều khiển thay đổi nội dung của thanh ghi cs và eip / ip, do đó bộ vi xử lý chọn để thực hiện không phải lệnh chương trình tiếp theo theo thứ tự, mà là lệnh trong một số phần khác của chương trình. Đường ống bên trong bộ vi xử lý được đặt lại.

Theo nguyên lý hoạt động, các lệnh của bộ vi xử lý cung cấp việc tổ chức các chuyển tiếp trong chương trình có thể được chia thành 3 nhóm:

1. Chuyển lệnh điều khiển vô điều kiện:

1) lệnh rẽ nhánh không điều kiện;

2) lệnh gọi một thủ tục và trả về từ một thủ tục;

3) lệnh để gọi các ngắt phần mềm và trả về từ các ngắt phần mềm.

2. Các lệnh chuyển quyền điều khiển có điều kiện:

1) các lệnh nhảy theo kết quả của lệnh so sánh p;

2) các lệnh chuyển tiếp theo trạng thái của một cờ nhất định;

3) hướng dẫn chuyển qua nội dung của thanh ghi esx / cx.

3. Các lệnh điều khiển chu kỳ:

1) lệnh tổ chức chu trình với bộ đếm ехх / сх;

2) lệnh tổ chức chu trình với bộ đếm ех / сх với khả năng thoát khỏi chu trình sớm bằng một điều kiện bổ sung.

Nhảy vô điều kiện

Cuộc thảo luận trước đó đã tiết lộ một số chi tiết về cơ chế chuyển đổi. Lệnh nhảy sửa đổi thanh ghi con trỏ lệnh eip / ip và có thể là thanh ghi đoạn mã cs. Chính xác những gì cần được sửa đổi phụ thuộc vào:

1) về loại toán hạng trong lệnh rẽ nhánh không điều kiện (gần hoặc xa);

2) từ việc chỉ định một bổ ngữ trước địa chỉ bước nhảy (trong lệnh nhảy); trong trường hợp này, bản thân địa chỉ bước nhảy có thể được định vị trực tiếp trong lệnh (bước nhảy trực tiếp), hoặc trong một thanh ghi hoặc ô nhớ (bước nhảy gián tiếp).

Công cụ sửa đổi có thể nhận các giá trị sau:

1) gần ptr - chuyển tiếp trực tiếp đến một nhãn bên trong đoạn mã hiện tại. Chỉ có thanh ghi eip / ip được sửa đổi (tùy thuộc vào loại đoạn mã use16 hoặc use32 được chỉ định) dựa trên địa chỉ (nhãn) được chỉ định trong lệnh hoặc một biểu thức sử dụng ký hiệu trích xuất giá trị - $;

2) xa ptr - chuyển đổi trực tiếp đến một nhãn trong một đoạn mã khác. Địa chỉ bước nhảy được chỉ định dưới dạng toán hạng hoặc địa chỉ (nhãn) tức thì và bao gồm bộ chọn 16 bit và độ lệch 16/32 bit, được tải tương ứng vào các thanh ghi cs và ip / eip;

3) từ ptr - chuyển tiếp gián tiếp sang nhãn bên trong đoạn mã hiện tại. Chỉ eip / ip được sửa đổi (bởi giá trị bù từ bộ nhớ tại địa chỉ được chỉ định trong lệnh hoặc từ một thanh ghi). Kích thước bù đắp 16 hoặc 32 bit;

4) dword ptr - chuyển đổi gián tiếp sang một nhãn trong một đoạn mã khác. Cả hai thanh ghi - cs và eip / ip - đều được sửa đổi (bởi một giá trị từ bộ nhớ - và chỉ từ bộ nhớ, từ một thanh ghi). Từ / dword đầu tiên của địa chỉ này đại diện cho phần bù và được tải vào ip / eip; từ thứ hai / thứ ba được tải vào cs. hướng dẫn nhảy không điều kiện jmp

Cú pháp lệnh cho một bước nhảy không điều kiện là jmp [modifier] jump_address - một bước nhảy không điều kiện mà không lưu thông tin về điểm trả về.

Jump_address là địa chỉ ở dạng nhãn hoặc địa chỉ của vùng nhớ chứa con trỏ nhảy.

Tổng cộng, trong hệ thống lệnh của bộ vi xử lý có một số mã lệnh máy cho bước nhảy không điều kiện.

Sự khác biệt của chúng được xác định bởi khoảng cách chuyển tiếp và cách địa chỉ đích được chỉ định. Khoảng cách nhảy được xác định bởi vị trí của toán hạng jump_address. Địa chỉ này có thể nằm trong phân đoạn mã hiện tại hoặc trong một số phân đoạn khác. Trong trường hợp đầu tiên, quá trình chuyển đổi được gọi là trong phân đoạn, hoặc gần, trong phân đoạn thứ hai - giữa các phân đoạn, hoặc xa. Một bước nhảy trong phân đoạn giả định rằng chỉ nội dung của thanh ghi eip / ip được thay đổi.

Có ba tùy chọn để sử dụng lệnh jmp trong phân đoạn:

1) thẳng ngắn;

2) thẳng;

3) gián tiếp.

Thủ tục

Hợp ngữ có một số công cụ giải quyết vấn đề sao chép các đoạn mã. Bao gồm các:

1) cơ chế của các thủ tục;

2) trình hợp ngữ macro;

3) cơ chế ngắt.

Thủ tục, thường còn được gọi là chương trình con, là đơn vị chức năng cơ bản để phân rã (chia thành nhiều phần) một tác vụ. Thủ tục là một nhóm các lệnh để giải quyết một nhiệm vụ con cụ thể và có phương tiện nhận quyền điều khiển từ điểm mà nhiệm vụ được gọi ở mức cao hơn và trả lại quyền điều khiển cho điểm này.

Trong trường hợp đơn giản nhất, chương trình có thể bao gồm một thủ tục duy nhất. Nói cách khác, một thủ tục có thể được định nghĩa như một tập hợp các lệnh được định dạng tốt, được mô tả một lần, có thể được gọi ở bất kỳ đâu trong chương trình nếu cần thiết.

Để mô tả một chuỗi lệnh như một thủ tục trong hợp ngữ, hai chỉ thị được sử dụng: PROC và ENDP.

Cú pháp mô tả thủ tục như sau (Hình 36).

Cơm. 36. Cú pháp mô tả thủ tục trong chương trình

Hình 36 cho thấy rằng trong tiêu đề thủ tục (chỉ thị PROC), chỉ có tên thủ tục là bắt buộc. Trong số lượng lớn các toán hạng của chỉ thị PROC, [khoảng cách] nên được đánh dấu. Thuộc tính này có thể nhận các giá trị gần hoặc xa và đặc trưng cho khả năng gọi thủ tục từ một đoạn mã khác. Theo mặc định, thuộc tính [distance] được đặt thành gần.

Thủ tục có thể được đặt ở bất kỳ đâu trong chương trình, nhưng theo cách mà nó không ngẫu nhiên có được quyền kiểm soát. Nếu thủ tục được chèn một cách đơn giản vào luồng lệnh chung, thì bộ vi xử lý sẽ coi các hướng dẫn của thủ tục như một phần của luồng này và do đó, sẽ thực hiện các hướng dẫn của thủ tục.

Nhảy có điều kiện

Bộ vi xử lý có 18 lệnh nhảy có điều kiện. Các lệnh này cho phép bạn kiểm tra:

1) mối quan hệ giữa các toán hạng có dấu ("lớn hơn - nhỏ hơn");

2) mối quan hệ giữa các toán hạng không có dấu ("cao hơn - thấp hơn");

3) trạng thái của cờ số học ZF, SF, CF, OF, PF (nhưng không phải AF).

Các lệnh nhảy có điều kiện có cùng cú pháp:

jcc jump_label

Như bạn có thể thấy, mã ghi nhớ của tất cả các lệnh bắt đầu bằng "j" - từ bước nhảy (jump), nó - xác định điều kiện cụ thể được phân tích bởi lệnh.

Đối với toán hạng jump_label, nhãn này chỉ có thể được định vị trong đoạn mã hiện tại; không được phép chuyển điều khiển giữa các đoạn trong các bước nhảy có điều kiện. Về vấn đề này, không có câu hỏi nào về bổ ngữ, đã có trong cú pháp của các lệnh nhảy không điều kiện. Trong các mô hình đầu tiên của bộ vi xử lý (i8086, i80186 và i80286), các lệnh rẽ nhánh có điều kiện chỉ có thể thực hiện các bước nhảy ngắn - từ -128 đến +127 byte từ lệnh sau lệnh rẽ nhánh có điều kiện. Bắt đầu với mô hình bộ vi xử lý 80386, hạn chế này được loại bỏ, nhưng, như bạn có thể thấy, chỉ trong phân đoạn mã hiện tại.

Để đưa ra quyết định về nơi sẽ chuyển quyền điều khiển sang lệnh nhảy có điều kiện, trước tiên phải hình thành một điều kiện, trên cơ sở đó sẽ đưa ra quyết định chuyển quyền điều khiển.

Các nguồn của tình trạng như vậy có thể là:

1) bất kỳ lệnh nào thay đổi trạng thái của cờ số học;

2) lệnh so sánh p, so sánh các giá trị của hai toán hạng;

3) trạng thái của thanh ghi esx / cx.

lệnh so sánh cmp

Lệnh so sánh trang có một cách hoạt động thú vị. Nó hoàn toàn giống với lệnh trừ - toán hạng con, toán hạng 2.

Lệnh p, giống như lệnh phụ, trừ các toán hạng và đặt cờ. Điều duy nhất nó không làm là viết kết quả của phép trừ thay cho toán hạng đầu tiên.

Cú pháp lệnh str - str operand_1, operand_2 (so sánh) - so sánh hai toán hạng và đặt cờ dựa trên kết quả của phép so sánh.

Các cờ được đặt bởi lệnh p có thể được phân tích bằng các lệnh rẽ nhánh có điều kiện đặc biệt. Trước khi xem xét chúng, chúng ta hãy chú ý một chút đến việc ghi nhớ các hướng dẫn nhảy có điều kiện này (Bảng 16). Hiểu được ký hiệu khi hình thành tên của các lệnh nhảy có điều kiện (phần tử trong tên của lệnh jcc, chúng tôi đã chỉ định nó) sẽ giúp chúng dễ dàng ghi nhớ và sử dụng thực tế hơn.

Bảng 16. Ý nghĩa các chữ viết tắt trong tên lệnh jcc Bảng 17. Danh sách các lệnh nhảy có điều kiện cho lệnh p operand_1, operand_2

Đừng ngạc nhiên bởi thực tế là một số mã ghi nhớ khác nhau của các lệnh nhánh có điều kiện tương ứng với các giá trị cờ giống nhau (chúng được phân tách với nhau bằng một dấu gạch chéo trong Bảng 17). Sự khác biệt về tên gọi là do mong muốn của các nhà phát triển bộ vi xử lý để dễ dàng hơn trong việc sử dụng các lệnh nhảy có điều kiện kết hợp với một số nhóm lệnh nhất định. Do đó, các tên khác nhau phản ánh một định hướng chức năng khác nhau. Tuy nhiên, thực tế là các lệnh này phản ứng với các cờ giống nhau làm cho chúng hoàn toàn tương đương và bình đẳng trong chương trình. Do đó, trong Bảng 17, chúng được nhóm lại không phải theo tên mà theo giá trị của các cờ (điều kiện) mà chúng đáp ứng.

Cờ và Hướng dẫn Chi nhánh có Điều kiện

Ký hiệu dễ nhớ của một số lệnh nhảy có điều kiện phản ánh tên của cờ mà chúng hoạt động và có cấu trúc sau: ký tự đầu tiên là "j" (Nhảy, nhảy), ký tự thứ hai là ký hiệu cờ hoặc ký tự phủ định " \ n ", theo sau là tên của lá cờ. Cấu trúc đội ngũ này phản ánh mục đích của nó. Nếu không có ký tự "n", thì trạng thái của cờ được kiểm tra, nếu nó bằng 1, một chuyển đổi sang nhãn nhảy được thực hiện. Nếu ký tự "n" có mặt, thì trạng thái cờ sẽ được kiểm tra bằng 0 và nếu thành công, một bước nhảy đến nhãn nhảy được thực hiện.

Các lệnh nhớ, tên cờ và điều kiện nhảy được thể hiện trong Bảng 18. Các lệnh này có thể được sử dụng sau bất kỳ lệnh nào sửa đổi các cờ được chỉ định.

Bảng 18. Hướng dẫn Nhảy có Điều kiện và Cờ

Nếu bạn quan sát kỹ bảng 17 và 18, bạn có thể thấy rằng nhiều lệnh nhảy có điều kiện trong chúng là tương đương nhau, vì cả hai đều dựa trên việc phân tích các cờ giống nhau.

Hướng dẫn Nhảy có Điều kiện và Thanh ghi esx / cx

Kiến trúc của bộ vi xử lý liên quan đến việc sử dụng cụ thể nhiều thanh ghi. Ví dụ, thanh ghi EAX / AX / AL được sử dụng như một bộ tích lũy, và các thanh ghi BP, SP được sử dụng để làm việc với ngăn xếp. Thanh ghi ECX / CX cũng có một mục đích chức năng nhất định: nó hoạt động như một bộ đếm trong các lệnh điều khiển vòng lặp và khi làm việc với các chuỗi ký tự. Có thể về mặt chức năng, lệnh rẽ nhánh có điều kiện được liên kết với thanh ghi esx / cx sẽ được quy cho nhóm lệnh này một cách chính xác hơn.

Cú pháp cho lệnh rẽ nhánh có điều kiện này là:

1) jcxz jump_label (Nhảy nếu ex là XNUMX) - nhảy nếu cx bằng XNUMX;

2) jecxz jump_label (Jump Equal ех Zero) - nhảy nếu ех bằng XNUMX.

Các lệnh này rất hữu ích khi lặp và khi làm việc với các chuỗi ký tự.

Cần lưu ý rằng có một hạn chế vốn có trong lệnh jcxz / jecxz. Không giống như các lệnh truyền có điều kiện khác, lệnh jcxz / jecxz chỉ có thể giải quyết các bước nhảy ngắn -128 byte hoặc +127 byte từ lệnh theo sau nó.

Tổ chức các chu kỳ

Chu trình, như bạn biết, là một cấu trúc thuật toán quan trọng, nếu không sử dụng nó, có lẽ không chương trình nào có thể làm được. Bạn có thể tổ chức thực hiện theo chu kỳ của một phần nhất định của chương trình, ví dụ, bằng cách sử dụng lệnh điều khiển chuyển giao có điều kiện hoặc lệnh nhảy không điều kiện jmp. Với một tổ chức chu trình như vậy, tất cả các hoạt động cho tổ chức của nó được thực hiện thủ công. Tuy nhiên, do tầm quan trọng của yếu tố thuật toán như một chu trình, các nhà phát triển bộ vi xử lý đã đưa một nhóm ba lệnh vào hệ thống lệnh, điều này tạo điều kiện thuận lợi cho việc lập trình các chu trình. Các hướng dẫn này cũng sử dụng thanh ghi esx / cx làm bộ đếm vòng lặp.

Hãy mô tả ngắn gọn về các lệnh này:

1) vòng lặp transition_label (Vòng lặp) - lặp lại vòng lặp. Lệnh cho phép bạn tổ chức các vòng lặp tương tự như vòng lặp for trong các ngôn ngữ cấp cao với khả năng tự động giảm bộ đếm vòng lặp. Công việc của nhóm là thực hiện những việc sau:

a) giảm thanh ghi ECX / CX;

b) so sánh thanh ghi ECX / CX với 0: nếu (ECX / CX) = XNUMX, thì điều khiển được chuyển sang lệnh tiếp theo sau vòng lặp;

2) Loope / loopz jump_label

Các lệnh loope và loopz là các từ đồng nghĩa tuyệt đối. Công việc của các lệnh là thực hiện các hành động sau:

a) giảm thanh ghi ECX / CX;

b) so sánh thanh ghi ECX / CX với số không;

c) phân tích trạng thái của cờ không ZF nếu (ECX / CX) = 0 hoặc XF = 0, điều khiển được chuyển sang lệnh tiếp theo sau vòng lặp.

3) loopne / loopnz jump_label

Các lệnh loopne và loopnz cũng là những từ đồng nghĩa tuyệt đối. Công việc của các lệnh là thực hiện các hành động sau:

a) giảm thanh ghi ECX / CX;

b) so sánh thanh ghi ECX / CX với số không;

c) phân tích trạng thái của cờ không ZF: nếu (ECX / CX) = 0 hoặc ZF = 1, điều khiển được chuyển sang lệnh tiếp theo sau vòng lặp.

Các lệnh loope / loopz và loopne / loopnz là tương hỗ trong hoạt động của chúng. Chúng mở rộng hoạt động của lệnh vòng lặp bằng cách phân tích thêm cờ zf, giúp có thể tổ chức thoát sớm khỏi vòng lặp, sử dụng cờ này làm chỉ báo.

Nhược điểm của các lệnh lặp loop, loope / loopz và loopne / loopnz là chúng chỉ thực hiện các bước nhảy ngắn (từ -128 đến +127 byte). Để làm việc với các vòng lặp dài, bạn sẽ cần sử dụng các bước nhảy có điều kiện và lệnh jmp, vì vậy hãy cố gắng nắm vững cả hai cách tổ chức các vòng lặp.

Tác giả: Tsvetkova A.V.

Chúng tôi giới thiệu các bài viết thú vị razdela Ghi chú bài giảng, phiếu đánh giá:

Luật thuế. Ghi chú bài giảng

Tâm lý nhân cách. Giường cũi

Khoa nội tiết. Ghi chú bài giảng

Xem các bài viết khác razdela Ghi chú bài giảng, phiếu đánh giá.

Đọc và viết hữu ích bình luận về bài viết này.

<< Quay lại

Tin tức khoa học công nghệ, điện tử mới nhất:

Máy tỉa hoa trong vườn 02.05.2024

Trong nền nông nghiệp hiện đại, tiến bộ công nghệ đang phát triển nhằm nâng cao hiệu quả của quá trình chăm sóc cây trồng. Máy tỉa thưa hoa Florix cải tiến đã được giới thiệu tại Ý, được thiết kế để tối ưu hóa giai đoạn thu hoạch. Công cụ này được trang bị cánh tay di động, cho phép nó dễ dàng thích ứng với nhu cầu của khu vườn. Người vận hành có thể điều chỉnh tốc độ của các dây mỏng bằng cách điều khiển chúng từ cabin máy kéo bằng cần điều khiển. Cách tiếp cận này làm tăng đáng kể hiệu quả của quá trình tỉa thưa hoa, mang lại khả năng điều chỉnh riêng cho từng điều kiện cụ thể của khu vườn, cũng như sự đa dạng và loại trái cây được trồng trong đó. Sau hai năm thử nghiệm máy Florix trên nhiều loại trái cây khác nhau, kết quả rất đáng khích lệ. Những nông dân như Filiberto Montanari, người đã sử dụng máy Florix trong vài năm, đã báo cáo rằng thời gian và công sức cần thiết để tỉa hoa đã giảm đáng kể. ... >>

Kính hiển vi hồng ngoại tiên tiến 02.05.2024

Kính hiển vi đóng vai trò quan trọng trong nghiên cứu khoa học, cho phép các nhà khoa học đi sâu vào các cấu trúc và quá trình mà mắt thường không nhìn thấy được. Tuy nhiên, các phương pháp kính hiển vi khác nhau đều có những hạn chế, trong đó có hạn chế về độ phân giải khi sử dụng dải hồng ngoại. Nhưng những thành tựu mới nhất của các nhà nghiên cứu Nhật Bản tại Đại học Tokyo đã mở ra những triển vọng mới cho việc nghiên cứu thế giới vi mô. Các nhà khoa học từ Đại học Tokyo vừa công bố một loại kính hiển vi mới sẽ cách mạng hóa khả năng của kính hiển vi hồng ngoại. Thiết bị tiên tiến này cho phép bạn nhìn thấy cấu trúc bên trong của vi khuẩn sống với độ rõ nét đáng kinh ngạc ở quy mô nanomet. Thông thường, kính hiển vi hồng ngoại trung bị hạn chế bởi độ phân giải thấp, nhưng sự phát triển mới nhất của các nhà nghiên cứu Nhật Bản đã khắc phục được những hạn chế này. Theo các nhà khoa học, kính hiển vi được phát triển cho phép tạo ra hình ảnh có độ phân giải lên tới 120 nanomet, cao gấp 30 lần độ phân giải của kính hiển vi truyền thống. ... >>

Bẫy không khí cho côn trùng 01.05.2024

Nông nghiệp là một trong những lĩnh vực quan trọng của nền kinh tế và kiểm soát dịch hại là một phần không thể thiếu trong quá trình này. Một nhóm các nhà khoa học từ Viện nghiên cứu khoai tây trung tâm-Hội đồng nghiên cứu nông nghiệp Ấn Độ (ICAR-CPRI), Shimla, đã đưa ra một giải pháp sáng tạo cho vấn đề này - bẫy không khí côn trùng chạy bằng năng lượng gió. Thiết bị này giải quyết những thiếu sót của các phương pháp kiểm soát sinh vật gây hại truyền thống bằng cách cung cấp dữ liệu về số lượng côn trùng theo thời gian thực. Bẫy được cung cấp năng lượng hoàn toàn bằng năng lượng gió, khiến nó trở thành một giải pháp thân thiện với môi trường và không cần điện. Thiết kế độc đáo của nó cho phép giám sát cả côn trùng có hại và có ích, cung cấp cái nhìn tổng quan đầy đủ về quần thể ở bất kỳ khu vực nông nghiệp nào. Kapil cho biết: “Bằng cách đánh giá các loài gây hại mục tiêu vào đúng thời điểm, chúng tôi có thể thực hiện các biện pháp cần thiết để kiểm soát cả sâu bệnh và dịch bệnh”. ... >>

Tin tức ngẫu nhiên từ Kho lưu trữ

Máu tức thì 03.04.2003

Các nhà nghiên cứu Pháp đã tạo ra máu nhân tạo, huyết sắc tố được cố định trên bề mặt của những quả bóng làm bằng một loại polymer vô hại, phân hủy dần trong cơ thể.

Những quả bóng này nhỏ hơn 20 lần so với hồng cầu tự nhiên, vì vậy chúng thâm nhập tốt vào các mao mạch mỏng nhất. Máu nhân tạo thích hợp để truyền cho tất cả mọi người, không phân biệt nhóm máu. Ngoài ra, nó có thể được làm khô để bảo quản và vận chuyển, và pha loãng với nước muối trước khi sử dụng.

Tin tức thú vị khác:

▪ Đồng hồ sinh học của động vật ngày và đêm khác nhau về cấu trúc thần kinh của chúng.

▪ Thuốc kháng sinh từ cần sa

▪ SpaceX nổi sân bay vũ trụ

▪ Mỏ đồng mới ở Đức

▪ Thể thao và ăn chay

Nguồn cấp tin tức khoa học và công nghệ, điện tử mới

 

Tài liệu thú vị của Thư viện kỹ thuật miễn phí:

▪ phần của trang web Ứng dụng vi mạch. Lựa chọn các bài viết

▪ bài báo Máy làm đá viên. Lịch sử phát minh và sản xuất

▪ bài báo Ai đã thắng trận đấu khi Federer chơi nửa sân cỏ và Nadal nửa sân đất nện? đáp án chi tiết

▪ bài viết Tư vấn về thuế và phí. Mô tả công việc

▪ bài viết Hệ thống sưởi ấm năng lượng mặt trời. Bách khoa toàn thư về điện tử vô tuyến và kỹ thuật điện

▪ bài viết tục ngữ và câu nói của Litva. Lựa chọn lớn

Để lại bình luận của bạn về bài viết này:

Имя:


Email (tùy chọn):


bình luận:





Tất cả các ngôn ngữ của trang này

Trang chủ | Thư viện | bài viết | Sơ đồ trang web | Đánh giá trang web

www.diagram.com.ua

www.diagram.com.ua
2000-2024