So sánh 2 chuỗi trong asem

## **1. Tổng Quan Về Assembly.** ### :diamonds: Các thành phần của một câu lệnh assembly. ![][//hackmd.io/_uploads/BJfopZ2Oh.png] :::info * **Label** là một chuỗi kí tự kết thúc bằng ":", nằm ở đầu câu lệnh assembly. Nó dùng để đánh dấu một điểm trong code của chương trình và để các câu lệnh nhảy khác có thể tham chiếu đến. * **Mnemonic** là một từ đại diện cho opcode của một câu lệnh assembly. Nó được dùng như là một từ gợi nhớ dễ hiểu để mô tả hoạt động của lệnh assembly. * **Operand** chính là thứ mà các câu lệnh assembly sẽ tương tác. Lệnh assembly có thể đọc hoặc ghi dữ liệu vào toán hạng. Có 3 loại toán hạng: * **Register operand** là toán hạng thanh ghi, câu lệnh assembly có thể đọc và ghi dữ liệu vào toán hạng này. * **Immidiate operand** là toán hạng hằng số, câu lệnh assembly chỉ có thể đọc dữ liệu từ nó. Nếu ta cố ý ghi dữ liệu vào nó thì chương trình sẽ nảy sinh lỗi. * **Memory operand** là toán hạng bộ nhớ, chúng ta có thể dùng nó để ghi và đọc dữ liệu tại một vị trí bất kỳ trên bộ nhớ của tiến trình. * **Comment** dùng để ghi chú thông tin thêm về chức năng của chương trình. ::: * ## Phân loại thanh ghi trong kiến trúc x86. ### :diamonds: General purpose register ![][//hackmd.io/_uploads/rkSoRbhd3.png] :::info * **Data register** * **EAX** [accumulator] được sử dụng hầu như trong các câu lệnh số học và các câu lệnh I/O. * **EBX** [base register] được sử dụng trong quá trình tính toán indexed address. * **ECX** [count register] được sử dụng để lưu biến đếm cho các câu lệnh loop và rep. * **EDX** [data register] được dùng trong các hoạt động I/O của máy tính. Ngoài ra EDX cùng EAX còn được sử dụng như một cặp thanh ghi để thực hiện phép tính nhân và chia trong chương trình assembly. * **Index register** * **EDI** [destination index] dùng để lưu địa chỉ hoặc dữ liệu, nó được sử dụng hầu hết trong các câu lệnh thao tác chuỗi. * **ESI** [source index] cũng được sử dụng trong các câu lệnh thao tác chuỗi. Nói chung là làm đi rồi sẽ hiểu :kissing:. * **Pointer register** * **ESP** [stack pointer] được dùng để lưu địa chỉ của đỉnh stack. Ta có thể mở rộng hay thu hẹp stack bằng cách thay đổi giá trị của ESP. * **EBP** [base pointer] được dùng để trỏ đến vị trí bắt đầu stack frame của procedure hiện tại. Nói chung là học tới stack là sẽ biết thôi :kissing_smiling_eyes:. * **EIP** [instruction pointer] lưu trữ địa chỉ của lệnh tiếp theo mà processor sẽ thực thi. Lưu ý rằng trong kiến trúc tập lệnh x86, ta không thể dùng bất kỳ một câu lệnh nào để thay đổi giá trị của EIP theo như ý muốn của mình. EIP là một thanh ghi đặc biệt, nó chỉ được điều khiển và thao tác bởi processor. ::: ### :diamonds: Control register ![][//hackmd.io/_uploads/S1QNyMnd3.png] :::info **Control register** là thanh ghi 16 [32] bit được sử dụng để theo dõi trạng thái của processor. Mỗi bit trong thanh ghi này đại diện cho một trạng thái nào đó của processor hoặc không được sử dụng. Các bit trong control register được chia thành hai loại sau đây. * **Status bits** * **ZF** [zero flag] dùng để xác định kết quả của các phép toán số học hoặc so sánh. ZF=1 nếu kết quả của phép toán bằng 0 và ZF=0 nếu kết quả của phép toán khác 0. * **SF** [sign flag] dùng để xác định dấu của kết quả trong các phép toán số học. SF=1 nếu most significant bit [MSB] của kết quả bằng 1 và SF=0 nếu ngược lại. * **OF** [overflow flag] là cờ đặc trưng cho các phép toán số học trên **số có dấu**, ta thường dùng cờ OF để xác định kết quả của phép toán có sai hay không. OF=1 nếu phép toán nảy sinh hiện tượng **signed overflow** và OF=0 nếu ngược lại. * **AF** [auxiliary carry flag] được sử dụng nhiều trong các câu lệnh tính toán số BCD bởi vì số BCD được biểu diễn bằng 4 bit :grin:. Nếu trong quá trình cộng hai số nhị phân, có bit carry xuất hiện từ vị trí bit thứ 3 sang vị trí bit thứ 4 thì AF=1 và ngược lại thì AF=0. * **PF** [parity flag] dùng để theo dõi số lượng bit có trong kết quả. Cụ thể, nếu PF=1 thì số lượng bit 1 trong kết quả là số chẵn và PF=0 nếu ngược lại. * **CF** [carry flag] là cờ đặc trưng cho các phép toán số học trên **số không dấu**. CF=1 khi kết quả của phép toán số học không đúng, tức là phép toán này xảy ra hiện tượng **overflow** và CF=0 nếu ngược lại. Lưu ý rằng hiện tượng signed overflow và overflow là hai hiện tượng khác nhau khi cộng trừ hai số nhị phân. * **Control bits** * **TF** [trap flag] được sử dụng chủ yếu trong quá trình debug. Khi TF=1 thì processor kích hoạt một exception, việc này làm cho chương trình bị ngắt và luồng thực thi được chuyển cho exception handler. Sau khi exception handler xử lý xong, luồng thực thi được trả và chương trình tiếp tục thực thi. Ta có thể tương tác với exception handler để quan sát trạng thái của chương trình. ![][//hackmd.io/_uploads/BymZ9U3O3.png] * **IF** [interrupt flag] được xử dụng để kiểm soát việc xử lý các external interrupt của processor. Các external interrupt có thể là các interrupt được kích hoạt từ các thiết bị ngoại vi [key board, mouse, ...]. Cụ thể, nếu IF=1 thì các external interrupt được xử lý, lúc này quá trình thực thi của chương trình bị ngắt ngay lập tức và luồng thực thi được chuyển đến interrupt handler. Ngược lại nếu IF=0 thì các external interrupt từ các nguồn bên ngoài sẽ bị vô hiệu hóa, lúc này chương trình sẽ thực thi một cách không bị gián đoạn. ![][//hackmd.io/_uploads/HyJyjU2_n.png] * **DF** [direction flag] xác định hướng là trái hoặc phải cho các string instruction. Nếu DF=1 thì các string instruction sẽ hoạt động từ trái qua phải nếu DF=0 thì hoạt động từ phải qua trái. ::: ### :diamonds: Segment register :::info * **SS** [stack segment] được dùng để trỏ đến base address của stack segment trong các bộ vi xử lý sử dụng cơ chế quản lý bộ nhớ bằng segmented model. * **DS** [data segment] cũng được dùng để trỏ đến base address của data segment tương tự như SS. * **CS** [code segment] cũng được sử dụng để trỏ đến base address của code segment tương tự như SS. * **ES, FS, GS** được sử dụng bởi hệ điều hành cho một vài chức năng nhất định. ::: :::warning * Trong các bộ vi xử lý x86 sử dụng cơ chế quản lý độ nhớ theo segmented model thì các thanh ghi SS, CS, DS không lưu trữ trực tiếp base address của các segment mà thay vào đó nó chứa giá trị 16-bit gọi là segment selector. Dựa vào giá trị này ta có thể truy xuất đến descriptor table để trích xuất base address của các segment. * Các bộ vi xử lý hiện đại ngày nay không còn sử dụng cơ chế quản lý bộ nhớ theo segmented model nữa mà thay vào đó sử dụng flat memory model. Cho nên phần lớn các segment register không còn được dùng để tham chiếu đến base address nữa mà thay vào đó nó đóng một chức năng khác tùy thuộc vào hệ điều hành. ::: ## **2. Phân Loại Lệnh Assembly Theo Chức Năng.** ### :diamonds: Arithmetic Instruction [Lệnh Số Học]. :::info * **ADD** thực hiện cộng destination operand và source operand và kết quả được lưu vào destination operand. Khá đơn giản đúng không :sunglasses:. ::: ![][//hackmd.io/_uploads/ryvvnbTdn.png] - :::info * **SUB** thực hiện việc trừ destination operand cho source operand và kết quả được lưu vào destination operand. Cũng đơn giản không kém :yum:. ::: ![][//hackmd.io/_uploads/Sy_i2ZTd3.png] - :::info * **MUL, IMUL** lần lượt là lệnh nhân hai số nhị phân không dấu và có dấu. ![][//hackmd.io/_uploads/BJg-bM2O3.png] Đối với việc nhân hai số nhị phân 8 bit, giá trị multiplicand [số bị nhân] mặc định được lưu trong thanh ghi AL. Kết quả của phép nhân hai số 8 bit được lưu trữ trong cặp thanh ghi AH : AL. ![][//hackmd.io/_uploads/H11z-Gn_3.png] Tương tự như phép nhân hai số nhị phân 8 bit, chỉ khác ở chỗ là kết quả được lưu vào cặp thanh ghi DX : AX. ![][//hackmd.io/_uploads/ryzmZM2On.png] Tương tự, kết quả phép nhân được lưu vào cặp thanh ghi EDX : EAX. ::: ![][//hackmd.io/_uploads/Hk5_Rb6_n.png] :::warning * MUL không thể gây ra hiện tượng overflow bởi vì kích thước của destination operand gấp đôi multiplicand và multiplier, trong trường hợp của MUL nếu OF và CF được gán bằng 1 thì upper half [phần bit nửa trên] của kết quả sẽ có giá trị khác 0. IMUL cũng tương tự, nó không thể gây ra hiện trượng signed overflow. Tuy nhiên đối với IMUL, nếu OF và CF được gán bằng 1 thì dấu của lower half [phần bit nửa dưới] không mở rộng sang upper half. Dễ thấy rằng ta có thể dựa vào CF và OF để xác định xem rằng có nên phớt lờ upper half hay không :smiling_face_with_smiling_eyes_and_hand_covering_mouth:. ::: - :::info * **DIV, IDIV** lần lượt là lệnh chia số nhị phân không dấu và có dấu. ![][//hackmd.io/_uploads/Syl3ef3u3.png] Đây là trường hợp chia dividend 16 bit được lưu trong AX cho divisor 8 bit, quotient và remainder được lưu lần lượt ở AL và AH. ![][//hackmd.io/_uploads/H1O6lGnOh.png] Trong trường hợp này, dividend 32 bit được lưu trong cặp thanh ghi DX : AX. Quotient và remainder được lưu lần lượt ở AX và DX. ![][//hackmd.io/_uploads/ryxyZfnO2.png] 64 bit dividend được lưu trong cặp thanh ghi EDX : EAX. Quotient và remainder lần lượt được lưu ở EAX và EDX. ::: ![][//hackmd.io/_uploads/ryjGRZauh.png] :::warning * Lưu ý rằng lệnh DIV/IDIV có thể gây ra hiện tượng **overflow**, lời khuyên của mình là nên dùng DIV/IDIV cho 32 hoặc 64 bit dividend, việc này có thể làm giảm khả năng nảy sinh exception và khiến cho chương trình đang thực thi bị dừng :grin:. Ngoài ra khi thực hiện phép chia số nhị phân có dấu, ta bắt buộc phải mở rộng dấu cho dividend trước khi chia, để mở rộng dấu cho dividend ta dùng các câu lệnh như **CBW**, **CWD** và **CDQ**. ::: - :::info * **NEG** được sử dụng để thay đổi dấu của destination operand. Dương thì đổi thành âm còn âm thì đổi thành dương thôi. Âm dương lẫn lộn :scream:. ::: ![][//hackmd.io/_uploads/ByvvHz6dn.png] - :::info * **ADC** thực hiện phép cộng giữa destination operand, source operand và cả giá trị CF, kết quả được lưu vào destination operand. ADC được sử dụng rất nhiều trong trường hợp cộng hai số nhị phân mà kích thước của nó lớn hơn kích thước của thanh ghi được dùng để lưu trữ kết quả. ::: ![][//hackmd.io/_uploads/rJluZdga_3.png] - ![][//hackmd.io/_uploads/Bk2MOl6_h.png] - ![][//hackmd.io/_uploads/S1SyhWpu2.png] - :::info * **SBB** cũng khá giống ADC, nhưng khác ở chỗ chúng ta phải trừ destination cho CF. ::: ![][//hackmd.io/_uploads/BJelWKe6On.png] - ![][//hackmd.io/_uploads/r1SW3bpdh.png] - :::info * **INC** cộng thêm 1 cho destination operand. Hãy luôn nhớ là INC không làm thay đổi cờ CF. ::: ![][//hackmd.io/_uploads/r1nhAZTd3.png] - :::info * **DEC** giảm đi 1 cho destination operand. DEC cũng không làm thay đổi cờ CF, nhớ giùm một cái đi :relaxed:. ::: ![][//hackmd.io/_uploads/SydlRW6_2.png] - ### :diamonds:Data Transfer Instruction [Lệnh Chuyển Dữ Liệu]. :::info * **MOV** là lệnh di chuyển dữ liệu từ destination operand sang source operand, kích thước của hai toán hạng phải bằng nhau. ::: ![][//hackmd.io/_uploads/HJ3Lveauh.png] - ![][//hackmd.io/_uploads/ryYgrzpO2.png] :::warning * Lưu ý đầu tiên là source operand của MOV không thể là hằng số vì di chuyển dữ liệu vào hằng số không có ý nghĩa :grin:. Lưu ý thứ hai là trong lệnh MOV, source operand và destination operand không thể cùng là memory operand, chúng ta không thể di chuyển dữ liệu từ vị trí bộ nhớ này sang vị trí bộ nhớ khác mà không thông qua thanh ghi. ::: - :::info * **LEA** tính toán effective address của source operand và gán nó cho destination operand. LEA được sử dụng rất nhiều trong quá trình thao tác trên con trỏ vì nó lưu địa chỉ mà :sunglasses:. ::: ![][//hackmd.io/_uploads/H1nNVzTun.png] :::warning * Lưu ý rằng source operand của LEA phải là memory operand. ::: - :::info * **MOVSX** được dùng để mở rộng dấu [**sign-extend**] cho source operand dựa vào MSB của nó. Nếu MSB=1 thì quá trình mở rộng dấu được diễn ra, kết quả sau khi mở rộng dấu được lưu vào destination operand. Ngược lại nếu MSB=0 thì rõ ràng source operand là số không âm nên quá trình mở rộng dấu không diễn ra. Bạn đọc coi hình đi cho dễ hiểu, mình nói nhiều cũng mệt :weary:, ước gì có ai bank cho ly tà tưa để khỏi mệt :kissing:. ::: ![][//hackmd.io/_uploads/Byrlvg6_2.png] - ![][//hackmd.io/_uploads/SJv97gpd2.png] - ![][//hackmd.io/_uploads/SJQGBfpdh.png] :::warning * Lưu ý rằng trong MOVSX, destination operand luôn có kích thước lớn hơn source operand bởi vì destination opearnd là nơi lưu kết quả sau khi mở rộng dấu. ::: - :::info * **MOVZX** là lệnh dùng để mở rộng kích thước của source operand bằng cách sao chép giá trị của nó vào destination operand và thêm các bit 0 phía trước, quá trình này còn được gọi là **zero-extend**. ::: ![][//hackmd.io/_uploads/By9zvx6O2.png] - ![][//hackmd.io/_uploads/HJJLHlauh.png] - ![][//hackmd.io/_uploads/Skq7Szadh.png] :::warning * Trong MOVZX, destination operand cũng phải luôn có kích thước lớn hơn source operand. Lưu ý này giống với MOVSX :sunglasses:. ::: - :::info * **XCHG** là lệnh hoán đổi giá trị cho hai toán hạng có cùng kích cỡ. Việc hoán đổi giá trị cho hai toán hạng có thể được thực thi bằng một vài lệnh MOV nhưng nó lâu, xài XCHG cho mau :yum:. ::: ![][//hackmd.io/_uploads/Hy4aUg6u2.png] - ![][//hackmd.io/_uploads/S1PvKGpO2.png] - :::info * **LAHF** thực hiện việc sao chép giá trị của các cờ SF, ZF, AF, PF, và CF vào trong thanh ghi AH. ::: ![][//hackmd.io/_uploads/rk4EY-6_h.png] - :::info * **SAHF** lấy giá trị của thanh ghi AH để gán vào các cờ SF, ZF, AF, PF, và CF. ::: ![][//hackmd.io/_uploads/SJawt-pu2.png] ### :diamonds: Logic Instruction [Lệnh Logic]. :::info * **AND** thực hiện phép toán logic and giữa destination operand và source operand, kết quả được lưu ở destination operand. ::: ![][//hackmd.io/_uploads/r1Lg6-6On.png] - :::info * **OR** thực hiện phép toán logic and giữa destination operand và source operand, kết quả được lưu ở destination operand. ::: ![][//hackmd.io/_uploads/BkBqBfau3.png] - :::info * **NOT** thực hiện phép not lên destination operand và kết quả được lưu tại destination operand. ::: ![][//hackmd.io/_uploads/BkFFHz6d3.png] - :::info * **XOR** thực hiện phép toán logic xor giữa destination operand và source operand, kết quả được lưu ở destination operand. ::: ![][//hackmd.io/_uploads/B17tKGp_n.png] ### :diamonds: Shift Instruction [Lệnh Dịch Bit]. :::info * **SAR** thực hiện phép dịch phải toán học. Nếu MSB của destination operand bằng 1 thì ta tiến hành dịch bit dựa vào giá trị của source operand. Sau đó, các bit 1 được thêm vào đầu của destination operand. Nếu MSB=0 thì quá trình dịch bit cũng diễn ra, nhưng các bit 0 được thêm vào đầu của destination operand. Giá trị của bit thoát ra từ phía bên phải sẽ được gán cho CF. ::: ![][//hackmd.io/_uploads/SJogzz2d3.png] - ![][//hackmd.io/_uploads/H1NPhM6Oh.png] - :::info * **SAL** thực hiện phép dịch trái toán học. Bất kể LSB bằng 0 hay 1 thì các bit 0 vẫn được thêm vào đuôi của destination operand. MSB thoát ra được thêm vào CF. ::: ![][//hackmd.io/_uploads/SkuMzf2_n.png] ![][//hackmd.io/_uploads/B1NO3zpOn.png] - :::info * **SHR** thực hiện phép dịch phải logic. Vì đây là phép logic nên dấu của destination operand không cần được giữ lại trong quá trình dịch. Kể cả MSB bằng 0 hay 1 thì các bit 0 đều được thêm vào từ MSB của destination operand. LSB được gán vào CF. ::: ![][//hackmd.io/_uploads/rkXJfM2_3.png] - ![][//hackmd.io/_uploads/rJoK2GpO2.png] - :::info * **SHL** chức năng tương tự như SAL :scream:. ::: ![][//hackmd.io/_uploads/SyBGMMhu3.png] - ![][//hackmd.io/_uploads/SkAK3M6_h.png] ### :diamonds: Rotate Instruction [Lệnh Xoay Bit]. :::info * **ROR** thực hiện quá trình xoay phải bit, LSB thoát ra được gán vào cả CF và MSB. ::: ![][//hackmd.io/_uploads/HyQjfM2d2.png] - ![][//hackmd.io/_uploads/BkBihGTO3.png] - :::info * **ROL** thực hiện quá trình xoay trái bit, MSB thoát ra được gán vào cả CF và LSB. ::: ![][//hackmd.io/_uploads/SyaYGz3On.png] - ![][//hackmd.io/_uploads/HkpjnMpO2.png] - :::info * **RCR** cũng thực hiện quá trình xoay phải bit. Quá trình này diễn ra bằng cách CF được gán cho MSB và sau đó LSB được gán cho CF. ::: ![][//hackmd.io/_uploads/HJppzzhun.png] - ![][//hackmd.io/_uploads/ryG0nfpd3.png] - :::info * **RCL** thực hiện quá trình xoay trái bit. Quá trình này diễn ra bằng cách CF được gán cho LSB và sau đó MSB được gán cho CF. ::: ![][//hackmd.io/_uploads/ryi2zMh_h.png] - ![][//hackmd.io/_uploads/rJ603fad2.png] ### :diamonds: Control Flow Instruction [Lệnh Điều Khiển Luồng]. :::info * **Compare Instruction [lệnh so sánh].** * **CMP** là lệnh dùng để so sánh giá trị của destination operand và source operand. Việc só sánh này được thực hiện bởi phép trừ destination operand cho source operand. Phép trừ giữa hai toán hạng có thể gây ra sự thay đổi trạng thái của các cờ trong [E]FLAGS, ta sẽ dựa vào giá trị của các cờ để so sánh giá trị của hai toán hạng. ![][//hackmd.io/_uploads/ryQ9a-6dh.png] * **TEST** cũng là lệnh so sánh giá trị của hai toán hạng giống như CMP nhưng việc so sánh này được diễn ra bằng phép toán logic **and**. Bạn đọc có thể dễ thấy rằng mức độ ảnh hưởng của các cờ khi thực hiện việc so sánh bằng phép toán số học và phép toán logic sẽ khác nhau :sunglasses:. ![][//hackmd.io/_uploads/ByilTGp_3.png] * **Branch Instruction [lệnh rẽ nhánh].** * **Conditional Jump [nhảy có điều kiện].** * **Signed Jump [nhảy có dấu].** * **JG/JNLE** nhảy nếu lớn hơn [nhảy nếu không nhỏ hơn hoặc bằng]. Điều kiện nhảy là **ZF=0 và SF=OF**. * **JL/JNGE** nhảy nếu nhỏ hơn [nhảy nếu không lớn hơn hoặc bằng]. Điều kiện nhảy là **SFOF**. * **JGE/JNL** nhảy nếu lớn hơn hoặc bằng [nhảy nếu không nhỏ hơn]. Điều kiện nhảy là **SF=OF**. * **JLE/JNG** nhảy nếu nhỏ hơn hoặc bằng [nhảy nếu không lớn hơn]. Điều kiện nhảy là **ZF=1 và SF=OF**. * **Unsigned Jump [nhảy không dấu].** * **JA** nhảy nếu lớn hơn [nhảy nếu không nhỏ hơn hoặc bằng]. Điều kiện nhảy là **CF=0 và ZF=0**. * **JB** nhảy nếu nhỏ hơn [nhảy nếu không lớn hơn hoặc bằng]. Điều kiện nhảy là **CF=0**. * **JAE** nhảy nếu lớn hơn hoặc bằng [nhảy nếu không nhỏ hơn]. Điều kiện nhảy là **CF=1**. * **JBE** nhảy nếu nhỏ hơn hoặc bằng [nhảy nếu không lớn hơn]. Điều kiện nhảy là **CF=1 hay ZF=1**. * **Single Flag Jump [nhảy dựa vào một cờ].** * **JE/JZ** nhảy nếu bằng. Điều kiện nhảy là **ZF=1**. * **JNE/JNZ** nhảy nếu không bằng. Điều kiện nhảy là **ZF=0**. * **JC** nhảy nếu có carry. Điều kiện nhảy là **CF=1**. * **JNC** nhảy nếu không có carry. Điều kiện nhảy là **CF=0**. * **JS** nhảy nếu có sign. Điều kiện nhảy là **SF=1**. * **JNS** nhảy nếu không có sign. Điều kiện nhảy là **SF=0**. * **JO** nhảy nếu có overflow. Điều kiện nhảy là **OF=1**. * **JNO** nhảy nếu không có overflow. Điều kiện nhảy là **OF=0**. * **JP/JPE** nhảy nếu có parity. Điều kiện nhảy là **PF=1**. * **JNP/JPO** nhảy nếu không có parity. Điều kiện nhảy là **PF=0**. * **Unconditional Jump [nhảy không điều kiện].** * **JUMP** nhảy đến code label được xác định trong operand của nó. JUMP có ba loại là short jump, near jump và far jump. Phạm vi nhảy của short jump nằm trong trong khoảng -128 đến 127 byte tính từ vị trí nhảy. Phạm vi nhảy của near jump thì nằm trong code segment của vị trí nhảy. Cuối cùng là phạm vi nhảy của far jump thì ra ngoài code segment hiện tại [có thể nhảy đến bất kỳ code segment khác]. ![][//hackmd.io/_uploads/Hko0mGTun.png] * **CALL** có hai loại là near call và far call. Đối với near call thì procedure được gọi nằm trong cùng code segment với lệnh gọi hàm và giá trị của [E]IP được đẩy vào stack. Còn với far call thì procedure được gọi nằm khác code segment với lệnh gọi hàm và cặp thanh ghi CS : [E]IP được đẩy vào stack. ![][//hackmd.io/_uploads/HJMG6-ad3.png] * **LOOP** giảm [E]CX đi 1 và đồng thời nhảy đến short label nếu như [E]CX khác 0. Vì LOOP nhảy đến short label nên phạm vi nhảy nằm trong khoảng -128 byte đến 127 byte tính từ vị trí nhảy. ![][//hackmd.io/_uploads/H1Yt4faOn.png] * **LOOPE/LOOPZ** giảm [E]CX đi 1 và đồng thời nhảy đến short label nếu [E]CX khác 0 và ZF=0. ![][//hackmd.io/_uploads/H1Yt4faOn.png] * **LOOPNE/LOOPNZ** giảm [E]CX đi 1 và đồng thời nhảy đến short label nếu [E]CX khác 0 và ZF=1. ![][//hackmd.io/_uploads/H1Yt4faOn.png] * **IRET** được gọi sau khi interrupt handler được thực hiện xong. IRET lấy các giá trị trong **kernel stack** để gán vào [E]IP, CS và các flag, ý nghĩa của việc này là để khôi phục context của tiến trình. Để hiểu rõ hơn về cách xử lý interrupt, bạn đọc có thể xem video tại [đây][//www.youtube.com/watch?v=KqgMGZyiLnU&list=PLEJxKK7AcSEGPOCFtQTJhOElU44J_JAun&index=13&t=32s]. ![][//hackmd.io/_uploads/rkQoNf6_n.png] ::: ### :diamonds: String Operation Instruction. :::info * **MOVS, MOVSB, MOVSW, MOVSD** Sao chép một byte hoặc word từ địa chỉ bộ nhớ được trỏ bởi DS : [E]SI đến vị trí bộ nhớ được trỏ bởi ES : [E]DI. MOVSB sao chép một byte, MOVSW sao chép một word, và cuối cùng MOVSD sao chép một doubleword. [E]SI và [E]DI được tăng hay giảm phụ thuộc vào kích thước của toán hạng và trạng thái của DF. Bây giờ thì chúng ta đã thấy tác dụng của DF rồi đúng không :sunglasses: ?. ::: ![][//hackmd.io/_uploads/B1d0EGTu2.png] - :::info * **LODS, LODSB, LODSW, LODSD** Tải một byte hoặc word ở vị trí được xác định bởi DS : [E]SI vào trong thanh ghi accumulator [AL, AX, EAX]. LODSB tải một byte vào AL, LODSW tải một word vào AX, và LODSD tải một doubleword vào EAX. Giá trị của [E]SI được tăng hay giảm phụ thuộc vào kích thước của toán hạng và trạng thái của DF. ::: ![][//hackmd.io/_uploads/S1kKNza_3.png] - :::info * **STOS, STOSB, STOSW, STOSD** Lưu thanh ghi accumulator [AL, AX, EAX] vào trong vị trí bộ nhớ được xác định bởi ES : [E]DI. STOSB lưu AL vào bộ nhớ, STOSW lưu AX vào bộ nhớ, và STOSD lưu EAX vào bộ nhớ. [E]DI được tăng hay giảm phụ thuộc vào kích thước toán hạng và trạng thái của DF. ::: ![][//hackmd.io/_uploads/ryAzPvauh.png] - :::info * **SCAS, SCASB, SCASW, SCASD** Quét một chuỗi trong bộ nhớ được trỏ đến bởi ES : [E]DI để tìm giá trị phù hợp với accumulator. SCASB quét để tìm giá trị phù hợp cho AL, SCASW quét để tìm giá trị phù hợp cho AX, và SCASD quét để tìm quá trị phù hợp cho EAX. Giá trị của [E]DI tăng hay giảm phụ thuộc vào kích thước của toán hạng và trạng thái của DF. ::: ![][//hackmd.io/_uploads/BytLPvTd2.png] - :::info * **CMPS, CMPSB, CMPSW, CPMSD** So sánh hai chuỗi trong bộ nhớ được trỏ đến bởi DS : [E]SI và ES : [E]DI. CMPS sẽ thực hiện phép trừ destination operand cho source operand để thực hiện việc so sánh giá trị của hai chuỗi. CMPSB so sánh các byte, CMPSW so sánh các word, và CMPSD so sánh các doubleword. Tương tự, [E]SI và [E]DI được tăng hoặc giảm phụ thuộc vào kích thước toán hạng và trạng thái của DF. ::: ![][//hackmd.io/_uploads/SyFo6Za_3.png] - :::info * **REP, REPE/REPZ, REPNE/REPNZ** Được dùng để lặp lại một câu lệnh thao tác chuỗi cho đến khi nào [E]CX=0 hoặc một điều kiện cụ được xảy ra. REPE/REPZ lặp lại lệnh khi ZF=1, REPNE/REPNZ lặp lại khi ZF=0. ::: ![][//hackmd.io/_uploads/B1GZwvadh.png] :::warning * Lưu ý rằng chúng ta chỉ nên dùng SCAS và CMPS với lệnh REP có điều kiện, bởi vì CMPS và SCAS là những lệnh thao tác trên chuỗi duy nhất có thể thay đổi giá trị ZF. ::: ### :diamonds: Stack Operation Instruction. :::info * **PUSH** có thể đẩy 2 hoặc 4 byte tùy thuộc vào kích thước của destination operand vào đỉnh stack và đồng thời giảm giá trị ESP tương ứng cho 2 hoặc 4. ::: ![][//hackmd.io/_uploads/Hy3z8za_3.png] - :::info * **POP** lấy 4 byte từ đỉnh stack để gán vào destination operand và đồng thời tăng giá trị ESP cho 4. ::: ![][//hackmd.io/_uploads/rkqcuPpun.png] - :::info * **PUSHW, PUSHD** lần lượt đẩy 2 byte hoặc 4 byte vào đỉnh stack và đồng thời giảm ESP đi cho 2 byte hoặc 4 byte. ::: ![][//hackmd.io/_uploads/SyZAHz6d3.png] - :::info * **PUSHA, PUSHAD** Đôi khi ta cần lưu lại trạng thái cho các thanh ghi vì một số lí do nhất định. Ví dụ khi ta gọi một subroutine, ta phải có nghĩa vụ lưu lại trạng thái các thanh ghi của routine hiện tại trước khi thực hiện chức năng của subroutine bởi vì trong quá trình thực hiện subroutine giá trị các thanh ghi có thể bị thay đổi và khiến cho dữ liệu của routine hiện bị mất mát. Quá trình này được gọi là callee/caller saved register, bạn đọc có thể tìm hiểu thêm về quá trình này tại [đây][//www.cs.williams.edu/~tom/courses/434/outlines/lect18_2.html]. Sau khi subroutine thực thi xong thì ta có thể khôi phục lại trạng thái các thanh ghi của routine hiện tại. PUSHA[PUSHAD] được sinh ra với mục đích như vậy, PUSHA[PUSHAD] lần lượt đẩy các general register 16[32] bit vào trong stack nhưng ngoại trừ [E]IP. Lí do vì sao không lưu [E]IP vào stack thì xem lại phần giải thích về thanh ghi [E]IP :sunglasses:. ::: ![][//hackmd.io/_uploads/BJfJUzp_h.png] - :::info * **POPA, POPAD** Có PUSH thì phải có POP chứ đúng không :grin:. POPA[POPAD] thực hiện việc lấy giá trị các general register từ stack để khôi phục trạng thái cho chúng. ::: ![][//hackmd.io/_uploads/Byr9dfauh.png] - :::info * **PUSHF, PUSHFD** đẩy 16[32] bit của thanh ghi [E]FLAGS vào trong stack. Việc này giúp ta lưu lại trạng thái của các flag. ::: ![][//hackmd.io/_uploads/HyeVUf6dn.png] - :::info * **POPF, POPFD** lấy 16[32] bit giá trị từ stack và gán nó vào trong [E]FLAGS. Việc này giúp ta khôi phục lại trạng thái của [E]FLAGS. ::: ![][//hackmd.io/_uploads/S1vWIMTd2.png] - :::info * **ENTER** có chức năng khởi tạo stack frame cho procedure nhận các tham số từ stack và sử dụng biến cục bộ :grin:. Toán hạng đầu tiên của ENTER là để xác định số lượng byte được dùng cho biến cục bộ và toán hạng thứ hai dùng để xác định nesting level của procedure. Thật ra mình cũng không hiểu toán hạng thứ hai có chức năng gì :sunglasses:, trong lúc sử dụng ENTER thì mình mặc định gán toán hạng thứ hai bằng 0 thôi. ::: ![][//hackmd.io/_uploads/rJBr0-6u2.png] - :::info * **LEAVE** được sử dụng để hủy stack frame của procedure hiện tại, dễ thấy rằng chức năng của nó trái ngược với ENTER :grin:. Nó sẽ thực hiện việc khôi phục giá trị của [E]SP và [E]BP trở về trạng thái như lúc bắt đầu gọi procedure. ::: ![][//hackmd.io/_uploads/rkMOVMTun.png] - :::info * **RET, RETN, RETF** RETN [return near] lấy giá trị từ đỉnh stack và gán vào [E]IP. RETF [return far] lấy giá trị từ đỉnh stack và gán vào [E]IP đầu tiên, sau đó tiếp tục gán cho CS. RET có thể là return near hoặc return far phụ thuộc vào attribute được xác định ở directive PROC. ::: ![][//hackmd.io/_uploads/ByygOv6u2.png] :::warning * Hãy học assember **MASM** để hiểu rõ hơn về các attribute và directive được đề cập trên. ::: ### :diamonds: Flag Operation Instruction. :::info * **STD** có chức năng gán DF=1. ::: ![][//hackmd.io/_uploads/SkYoTfTd2.png] - :::info * **CLD** có chức năng gán DF=0. ::: ![][//hackmd.io/_uploads/SydD6-T_3.png] - :::info * **STC** có chức năng gán CF=1. ::: ![][//hackmd.io/_uploads/HJyppf6_n.png] - :::info * **CLC** có chức năng xóa CF=1. ::: ![][//hackmd.io/_uploads/SkhSTb6d3.png] - :::info * **STI** có chức năng gán IF=1. ::: ![][//hackmd.io/_uploads/BkG0TMTu3.png] - :::info * **CLI** có chức năng xóa IF=1. ::: ![][//hackmd.io/_uploads/HJ9uaZ6dh.png] ### :diamonds: Other Instruction. :::info * **NOP** [no operation] là lệnh không làm gì cả. Nó chỉ gây tốn thời gian thực thi của CPU. ::: ![][//hackmd.io/_uploads/HkyYBGaO3.png] - :::info * **INT** là lệnh được dùng để kích hoạt một interrupt. Ứng dụng nhiều nhất của INT trong tiến trình là gọi system call. ::: ![][//hackmd.io/_uploads/SysCCZpO2.png] - :::info * **IN** ::: ![][//hackmd.io/_uploads/ryR5Abpdn.png] - :::info * **OUT** ::: ![][//hackmd.io/_uploads/BJnjSzTd3.png]

Chủ Đề