Bitwise Arithmetic - Jimmer1/Chip16 GitHub Wiki
Given the binary nature of computers, it is helpful to have instructions that allow you to work with register values in their binary representation.
Bitwise operations treat the 16-bit registers as a string of 1s and 0s and perform an operation on each individual bit in the operands in turn.
A logical or takes two binary digits and returns 1 if either value is 1 and 0 otherwise. A logical and takes two binary digits and returns 1 if and only if both values are 1 and 0 otherwise. A logical xor takes two binary digits and returns 1 if one value is 1 and 0 if neither of them are 1 or both of them are 1. A logical not takes a single binary digit and returns 1 if the digit is 0 and 0 if the digit is 1.
These operations are done for every single bit of the 16-bit register operands.
Chip16 has 6 instructions with which to perform bitwise arithmetic which are as follows:
Mnemonic - Opcode - Description
- OR - 8XY1 - Computes the logical or of register X and register Y and stores the result in register X.
- AND - 8XY2 - Computes the logical and of register X and register Y and stores the result in register X.
- XOR - 8XY3 - Computes the logical xor of register X and register Y and stores the result in register X.
- NOT - 8X0F - Computes the logical not of register X and stores the result in register X.
I shall demonstrate the use of these opcodes as follows:
OR
code = [
0x8B, 0x11 # or r11, r1 # r11 |= r1
]
AND
code = [
0x83, 0x82 # and r3, r2 # r3 &= r2
]
XOR
code = [
0x86, 0x83 # xor r6, r8 # r6 ^= r8
]
NOT
code = [
0x8B, 0x0F, # not r11 # r11 = ~r11
]
Shift Instructions:
Shift instructions move every single bit in the register operand either to the left or right by a specified number of places. This naturally leads to bits being shifted in and shifted out. Shifted in bits have the value of 0 in our case as all values are unsigned, this is shown below using 4 bit values for convenience:
1111 shifted right once = 0111 1011 shifted right thrice = 0001
1111 shifted left once = 1110 1011 shifted left thrice = 1000
The shift instructions essentially shift register values by one place multiple times with the carry flag being the value of the last bit to get shifted out, so in the case above:
1111 shifted right once = 0111, carry = 1 1011 shifted right thrice = 0001, carry = 0
1111 shifted left once = 1110, carry = 1 1101 shifted left thrice = 1000, carry = 0
The shift opcodes are as follows:
Mnemonic - Opcode - Description
- SHR - 8XY6 - Shifts register X right by Y places. The carry flag is set to hold the value of the Yth bit.
- SHL - 8XYE - Shifts register X left by Y places. The carry flag is set to hold the value of the (16-Y)th bit.
The encoding for shift instructions is a little different to what we are used to as the Y operand is an integer in the range 0 <= Y <= 15 as opposed to a register index.
I shall demonstrate the use of these opcodes as follows:
SHR
code = [
0x85, 0x36 # shr r5, 3 # r5 >>= 3
]
SHL
code = [
0x87, 0xFE # shl r7, 0xF # r7 <<= 0xF
]
If you struggle with some of these concepts, I recommend a combination of further reading the Wikipedia page about Bitwise Operations and playing around with these operations in a Python terminal.
Bitwise operations are indispensable to programmers and you will make extensive use of them throughout this course.
Next: Unconditional Jumps