Bitwise operation

Trong ngôn ngữ máy tính, các phép toán trên thao tác bit (tiếng Anh: bitwise operation) được thực hiện trên một hoặc nhiều chuỗi bit hoặc số nhị phân tại cấp độ của từng bit riêng biệt. Các phép toán này được thực hiện nhanh, ưu tiên, được hỗ trợ trực tiếp bởi vi xử lý, và được dùng để điều khiển các giá trị dùng cho so sánh và tính toán.

AND

Toán tử thao tác bit AND lấy 2 toán hạng nhị phân có chiều dài bằng nhau và thực hiện phép toán lý luận AND trên mỗi cặp bit tương ứng bằng cách nhân chúng lại với nhau. Nhờ đó, nếu cả hai bit ở vị trí được so sánh đều là 1, thì bit hiển thị ở dạng nhị phân sẽ là 1 (1 x 1 = 1); ngược lại thì kết quả sẽ là 0 (1 x 0 = 0).

Bảng chân trị thao tác AND:

A B A&B
0 0 0
0 1 0
1 0 0
1 1 1

Ví dụ:

    0101 (số thập phân 5)
AND 0011 (số thập phân 3)
=   0001 (số thập phân 1)

Phép toán này có thể được sử dụng để xác định xem nếu một bit được thiết đặt (1) hoặc trống (0). Ví dụ: Cho trước dãy bit 0011 (số 3 trong hệ thập phân), để xác định xem bit thứ 2 có được thiết đặt hay không, ta sử phép toán thao tác bit AND với một dãy bit có chứa số 1 duy nhất ở bit thứ 2, ví dụ:

    0011 (số thập phân 3)
AND 0010 (số thập phân 2)
=   0010 (số thập phân 2)

Vì kết quả 0010 là khác 0, ta biết là bit thứ 2 trong dãy bit ban đầu đã được thiết đặt. Điều này được gọi là che đậy bit. (Bằng phép loại suy, công dụng của mặt nạ, các phần không nên bị thay thế hoặc các phần không được quan tâm. Trong trường hợp này, các giá trị 0 che đậy cho các bit không được quan tâm).

Nếu ta lưu trữ kết quả, nó có thể được sử dụng để lưu trữ để xóa các bit được lựa chọn trong một thanh ghi. Cho ví dụ 0110 (số 6 trong hệ thập phân), bit thứ 2 có thể được xóa đi bằng cách sử dụng phép toán thao tác bit AND với dãy có một số 0 duy nhất ở bit thứ 2:

    0110 (số thập phân 6)
AND 1101 (số thập phân 13)  
=   0100 (số thập phân 4)

Vì đặc tính này, việc kiểm tra tính chẵn lẻ của số nhị phân trở nên dễ dàng bằng cách kiểm tra giá trị của bit có giá trị thấp nhất. Sử dụng ví dụ phía trên ta có:

    0110 (số thập phân 6)
AND 0001 (số thập phân 1)  
=   0000 (số thập phân 0)

Trong C, C++, Java, C#, toán tử thao tác bit AND được biểu diễn bằng ký hiệu “&” (dấu và):

x = y & z;

Trong Pascal, toán tử này là “and”. Ví dụ:

x:= y and z;

NOT

Toán tử thao tác bit NOT, hay còn gọi là còn được gọi là toán tử lấy phần bù (complement), là toán tử một ngôi thực hiện phủ định luận lý trên từng bit, tạo thành bù 1 (one’s complement) của giá trị nhị phân cho trước. Bit nào là 0 thì sẽ trở thành 1, và 1 sẽ trở thành 0. Ví dụ:

NOT 0111 (số thập phân 7)
=   1000 (số thập phân 8)

Bảng chân trị cho NOT:

A NOT A
0 1
1 0

Phép toán thao tác bit lấy phần bù sẽ tương đương với bù 2 (two’s complement) của giá trị được tính trừ đi 1. Nếu phép toán bù 2 được sử dụng, như vậy:

NOT x = -x – 1

Đối với các số nguyên không âm, phép toán thao tác bit lấy phần bù của một số là “hình ảnh phản chiếu” của số đó tính tới điểm giữa của giới hạn số nguyên không âm. Vi dụ: đối với số nguyên 8-bit, NOT x = 255 – x, có thể được biểu diễn trên đồ thị dưới dạng một đường thẳng đi xuống mà đường thẳng đó “lật” một dãy tăng dần từ 0 đến 255, đến một dãy giảm dần từ 255 xuống 0. Một ví dụ đơn giản nhưng dễ hình dung là việc đảo ngược một hình ảnh trắng đen mà mỗi pixel trong đó được coi là một số nguyên không âm.

Trong các ngôn ngữ lập trình C, C++, Java, C#, toán tử thao tác bit NOT được biểu diễn bằng ký hiệu “~” (dấu ngã). Trong Pascal, toán tử này là “not”. Ví dụ:

x = ~y; // C

Hay

x:= not y; { Pascal }

Câu lệnh trên sẽ gán cho x giá trị “NOT y” - tức phần bù của y. Chú ý rằng, toán tử này không tương đương với toán tử luận lý “not” (biểu diễn bằng dấu chấm than “!” trong C/C++). Về vấn đề này, xin xem ở bài toán tử hoặc các bài về ngôn ngữ C/C++.

Toán tử NOT hữu dụng khi ta cần tìm bù 1 của một số nhị phân. Nó cũng có thể được sử dụng làm bước đầu tiên để tìm số bù 2.

OR

Phép toán trên thao tác bit OR lấy hai dãy bit có độ dài bằng nhau và thực hiện phép toán lý luận bao hàm OR trên mỗi cặp bit tương ứng. Kết quả ở mỗi vị trí sẽ là 0 nếu cả hai bit là 0, ngược lại thì kết quả là 1. Ví dụ:

   0101 (số thập phân 5)
OR 0011 (số thập phân 3)
=  0111 (số thập phân 7)

Bảng chân trị cho OR:

A B A OR B
0 0 0
0 1 1
1 0 1
1 1 1

Trong C, C++, Java, C#, toán tử thao tác bit OR được biểu diễn bằng ký hiệu “|” (vạch đứng). Trong Pascal, toán tử này là “or”. Ví dụ:

x = y | z; // C

Hay:

x:= y or z; { Pascal }

Câu lệnh trên sẽ gán cho x kết quả của “y OR z”. Chú ý rằng toán tử này không tương đương với toán tử luận lý “or” (biểu diễn bằng cặp vạch đứng “||” trong C/C++). Về vấn đề này, xin xem ở bài toán tử hoặc các bài về ngôn ngữ C/C++.

Phép toán thao tác bit OR có thể được sử dụng để thiết đặt bit được chọn thành 1. Ví dụ: Nó có thể được sử dụng để bật (set) một bit (hoặc cờ) trong thanh ghi, trong đó mỗi bit đại diện cho một trạng thái trong phép logic đúng sai (boolean). Vì thế, 0010 (số 2 thập phân) có thể được xem là một bộ 4 cờ, trong đó cờ thứ nhất, thứ ba và thứ tư là trống (0) và cờ thứ hai được bật (1). Cờ thứ tư có thể được bật bằng cách thực hiện phép toán thao tác bit OR giữa giá trị này và một dãy bit với duy nhất bộ bit thứ 4:

   0010 (số thập phân 2)
OR 1000 (số thập phân 8)
   1010 (số thập phân 10)

Kỹ thuật này là một cách hiệu quả để lưu trữ một số trong những giá trị phép toán logic đúng sai (boolean) sử dụng ít bộ nhớ nhất có thể.

Khi làm việc với các máy không có nhiều không gian bộ nhớ trống, các lập trình viên thường áp dụng kĩ thuật trên. Lúc đó, thay vì khai báo tám biến kiểu bool (C++) độc lập, người ta sử dụng từng bit riêng lẻ của một byte để biểu diễn giá trị cho tám biến đó.

XOR

Phép toán thao tác bit XOR lấy hai dãy bit có cùng độ dài và thực hiện phép toán logic bao hàm XOR trên mỗi cặp bit tương ứng. Kết quả ở mỗi vị trí là 1 chỉ khi bit đầu tiên là 1 hoặc nếu chỉ khi bit thứ hai là 1, nhưng sẽ là 0 nếu cả hai là 0 hoặc cả hai là 1. Ở đây ta thực hiện phép so sánh hai bit, kết quả là 1 nếu hai bit khác nhau và là 0 nếu hai bit giống nhau. Ví dụ:

    0101 (số thập phân 5)
XOR 0011 (số thập phân 3)
    0110 (số thập phân 6)

(cách nhớ dễ nhất là: 2 bit giống nhau trả về 0, 2 bit khác nhau trả về 1)

Bảng chân trị cho XOR:

A B A XOR B
0 0 0
0 1 1
1 0 1
1 1 0

Phép toán thao tác bit XOR có thể được sử dụng để đảo ngược các bit được lựa chọn trong thanh ghi (còn được gọi là bật (set) hoặc lật (flip)). Bất kỳ bit nào được bật bằng cách thực hiện phép toán thao tác bit XOR nó với 1. Ví dụ: cho dãy bit 0010 (số 2 thập phân), bit thứ hai và thứ tư có thể được kích hoạt bằng cách sử dụng phép toán thao tác bit XOR với một dãy bit có chứa 1 ở vị trí thứ hai và thứ tư:

    0010 (số thập phân 2)
XOR 1010 (số thập phân 10)  
=   1000 (số thập phân 8)

Kỹ thuật này có thể được sử dụng để điều khiển dãy bit biểu hiện các bộ chứa phép toán logic đúng sai (boolean).

Trong C, C++, Java, C#, toán tử thao tác bit XOR được biểu diễn bằng ký hiệu “^” (dấu mũ). Trong Pascal, toán tử này là “xor”. Ví dụ:

x = y ^ z; // C

Hay:

x:= y xor z; { Pascal }

Câu lệnh trên sẽ gáp trình viên hợp ngữ (Assembly) thường sử dụng toán tử XOR để gán giá trị của một thanh ghi (register) về 0. Khi thực hiện phép toán XOR cho một mẫu bit với chính bản thân nó, mẫu nhị phân nhận được sẽ toàn bit 0. Trên nhiều kiến trúc máy tính, sử dụng XOR để gán 0 cho một thanh ghi sẽ được CPU xử lý nhanh hơn so với chuỗi thao tác tương ứng để nạp và lưu giá trị 0 vào thanh ghi.

Nguồn: wikipedia

Mask or Bitmask

Trong khoa học máy tính, mặt nạ hoặc mặt nạ bit là dữ liệu được sử dụng cho các hoạt động bit, đặc biệt là trong bit field. Sử dụng mặt nạ, nhiều bit trong một byte hoặc word có thể được bật, tắt hoặc đảo trong một thao tác bit duy nhất.

Sử dụng bitmask trong lập trình vi điều khiển giúp đơn giản thuật toán, rút ngắn thời gian xử lý.

Tách lấy trạng thái của IO

Ví dụ: Tách lấy trạng thái của PC0 từ thanh ghi PINC, ta dùng mask B0000.0001.

bool inp = PINC&B0000.0001;

Giả sử PC0 ở mức cao thì PINC = Bxxxx.xxx1, AND với mask B0000.0001 sẽ cho ra inp = B0000.0001 = 1. Ngược lại inp = Bxxx.xxx0&B0000.0001 = Bxxx.xxx0 = 0.

Set hoặc Clear trạng thái bit trên thanh ghi

Một lỗi phổ biến mà mọi người thường mắc phải khi lập trình vi điều khiển là đặt giá trị cho toàn bộ thanh ghi trong khi chỉ cần thay đổi giá trị của một bit duy nhất.

Ví dụ, trên atmega328p, PD0 và PD1 là ngoại vi UART, được điều khiển thông qua thanh ghi PORTD, nếu trong code có sử dụng UART mà sử dụng cả lệnh gán PORTD = B1000.000; chẳng hạn, sẽ gây ra xung đột với ngoại vi UART. Trường hợp ghi chồng chéo, xung đột các thanh ghi config trong các dự án lớn có nhiều người cùng tham gia xây dựng sẽ gây ra nhiều khó khăn và tốn nhiều thời gian khi debug.

Để xử lý vấn đề trên, ta nên sử dụng bitmask để set/clear giá trị bit trong thanh ghi. Mask và phép OR được dùng để set giá trị bit, còn OR để clear giá trị.

Quay trở lại ví dụ trên, giả sử ta chỉ cần set PD7 lên mức cao trong 1 giây sau đó clear PD0 xuống mức thấp, code như sau:

PORTD = PORTD | B10000000; // set giá trị PD7
delay(1000);
PORTD = PORTD & B01111111; // clear giá trị PD7

~~~ Updating…