Komputer tidak memproses satu bit dalam satu waktu, melainkan sekelompok bit yang berjalan berdampingan pada kumpulan kabel paralel yang disebut bus, yakni jalur data multi-bit yang membawa satu nilai utuh. Topik ini membahas konvensi penulisan bus, gerbang multi-bit yang bekerja bit demi bit, hubungan antara lebar bit dan banyaknya nilai yang dapat direpresentasikan, serta penerjemahan semua itu ke dalam operator bitwise Python. Setelah topik ini, notasi seperti in[16] dapat dibaca dengan tepat, dan hasil operator &, |, ^, serta ~ dapat diprediksi sebelum dijalankan.
Konvensi bus dan notasi#
Sebuah bus dengan lebar bit terdiri atas kabel paralel, dan setiap kabel membawa satu bit dari nilai yang sama. Notasi in[16] dibaca sebagai bus bernama in dengan lebar 16 bit, dan angka di dalam tanda kurung menyatakan lebarnya, bukan indeksnya. Bit diberi nomor mulai dari 0 pada ujung kanan hingga pada ujung kiri, sehingga in[0] adalah bit paling tidak signifikan dan in[15] adalah bit paling signifikan pada bus 16 bit. Satu nilai pada bus 16 bit dapat dibaca sebagai satu bilangan bulat dari 0 sampai 65.535, dan pembacaan inilah yang membedakan bus dari sekadar kumpulan kabel. Pada HDL nand2tetris, bus dideklarasikan dengan bentuk yang sama, misalnya IN in[16], dan pin bus diteruskan tanpa penulisan indeks apabila seluruh bitnya ikut serta.
Gerbang multi-bit#
Gerbang multi-bit menerapkan operasi satu bitnya secara terpisah pada setiap pasangan bit yang sejajar. Chip And16, misalnya, memuat enam belas gerbang AND yang masing-masing bekerja pada pasangan bit ke- dari a dan b, tanpa komunikasi apa pun di antara posisi-posisi bit tersebut. Sifat tanpa komunikasi inilah yang membedakan operasi bitwise, yakni operasi yang diterapkan bit demi bit, dari penjumlahan, karena penjumlahan harus meneruskan simpanan (carry) dari satu posisi ke posisi berikutnya. Pemanggilannya pun tetap satu baris pada HDL, sebagaimana terlihat pada fragmen berikut.
// tidak-dijalankan
// Enam belas AND bekerja serentak, satu untuk setiap posisi bit
And16(a=a, b=b, out=hasil);Baris tunggal itu sudah cukup karena lebar busnya tercantum pada deklarasi pin, bukan pada pemanggilannya. Konvensi yang sama berlaku untuk Not16, Or16, dan Xor16, serta untuk Mux16 yang memilih antara dua bus 16 bit berdasarkan satu bit sel. Dengan konvensi ini, chip yang sama dapat dipakai ulang untuk lebar berapa pun dengan mengganti deklarasinya.
Lebar bit menentukan kapasitas#
Sebuah bus dengan lebar bit hanya mengenal pola berbeda, sehingga lebar bit menetapkan batas kapasitas sebelum perangkat kerasnya dibangun. Hitungannya dimulai dari kasus kecil: satu bit menghasilkan nilai, empat bit menghasilkan nilai, dan delapan bit menghasilkan nilai. Pada lebar yang dipakai sepanjang modul ini berlaku
sedangkan pada 32 bit jumlah polanya melonjak menjadi . Kaitan kapasitas ini paling mudah dilihat pada bus alamat, yaitu bus yang memilih lokasi memori: bus alamat 16 bit hanya dapat menunjuk 65.536 lokasi, sehingga memori yang lebih besar menuntut bus alamat yang lebih lebar. Arah sebaliknya juga berguna, karena kebutuhan lokasi dapat diterjemahkan menjadi lebar minimum bit. Menetapkan lebar bus dengan demikian merupakan keputusan arsitektur, bukan sekadar urusan penulisan kode.
Operator bitwise pada Python#
Python menyediakan operator & untuk AND, | untuk OR, ^ untuk XOR, dan ~ untuk NOT, dan keempatnya bekerja bit demi bit pada bilangan bulat, persis seperti gerbang multi-bit. Program berikut menetapkan dua bus 16 bit sebagai bilangan biner, menerapkan keempat operator itu, lalu mencetak pola bitnya.
"""Operasi bitwise pada bus 16-bit.
Setiap bus direpresentasikan sebagai bilangan bulat. Operator & (AND),
| (OR), ^ (XOR), dan ~ (NOT) milik Python menerapkan operasinya bit demi
bit, persis seperti gerbang multi-bit pada perangkat keras.
"""
LEBAR = 16
PENYARING = (1 << LEBAR) - 1 # 16 bit yang seluruhnya bernilai 1
def biner(n):
"""Kembalikan pola 16 bit dari sebuah bilangan."""
return format(n & PENYARING, "016b")
a = 0b0000110010101111
b = 0b0011111100001100
print(f"a = {biner(a)} ({a & PENYARING})")
print(f"b = {biner(b)} ({b & PENYARING})")
print(f"a & b = {biner(a & b)} ({a & b})")
print(f"a | b = {biner(a | b)} ({a | b})")
print(f"a ^ b = {biner(a ^ b)} ({a ^ b})")
print(f"~a = {biner(~a)} ({~a & PENYARING})")a = 0000110010101111 (3247)
b = 0011111100001100 (16140)
a & b = 0000110000001100 (3084)
a | b = 0011111110101111 (16303)
a ^ b = 0011001110100011 (13219)
~a = 1111001101010000 (62288)Baris ~a pada keluaran menampilkan pola kebalikan a. Python merepresentasikan bilangan bulat tanpa batas lebar, sehingga ~a menghasilkan bilangan negatif, dan pola 16 bitnya dipulihkan dengan menyaring hasil melalui & PENYARING. Penyaringan (masking) seperti ini adalah kebiasaan kerja yang tepat setiap kali perilaku Python yang bebas lebar perlu dipadankan dengan perangkat keras yang lebarnya tetap.
Kesalahan umum#
Kesalahan pertama adalah membaca angka pada in[16] sebagai indeks terakhir, padahal angka itu menyatakan lebar bus sehingga bitnya bernomor 0 sampai 15. Kesalahan kedua adalah mengabaikan tanda kurung pada ekspresi campuran, karena & mengikat lebih kuat daripada operator perbandingan pada Python, sehingga a & b == c dievaluasi sebagai (a & b) == c. Tambahkan tanda kurung secara eksplisit pada setiap ekspresi bitwise yang berdampingan dengan perbandingan. Kesalahan ketiga adalah mencampur operasi bitwise dengan operasi yang memerlukan simpanan antar bit, misalnya mengira ^ dapat dipakai untuk penjumlahan. XOR memang berperan pada penjumlah biner, tetapi penjumlahan tetap memerlukan rangkaian khusus yang meneruskan simpanan dari satu posisi bit ke posisi berikutnya. Terakhir, keliru menghitung kapasitas dengan menambah lebar bit secara linier, padahal hubungannya berbentuk pangkat, sehingga penambahan satu bit menggandakan kapasitas.
Ringkasan#
- Bus adalah kumpulan kabel paralel, dan notasi
in[16]menyatakan lebar 16 bit, bukan indeks. - Gerbang multi-bit menerapkan operasinya secara terpisah pada setiap posisi bit tanpa simpanan antar bit.
- Bus dengan lebar bit merepresentasikan nilai, sehingga lebar bit menetapkan kapasitas jalur data dan bus alamat.
- Operator
&,|,^, dan~pada Python bekerja bit demi bit dan meniru gerbang multi-bit, dengan penyaringan bit untuk membatasi lebar.
Referensi#
- Computer Systems: A Programmer’s Perspective — Carnegie Mellon University — Buku teks yang membahas operasi bitwise dan representasi data dari sisi pemrogram.
- The Elements of Computing Systems, Second Edition — Nand2Tetris — Bab proyek yang mendefinisikan chip 16 bit dan konvensi bus pada HDL.