Note on this tutorial

I wrote this tutorial many years ago (2002-2004). Because of the large number of links pointing to it I've left it up. However, I no longer support it. Please do not contact me asking for help with QBASIC, ASM, or other programming issues. Also, please do not contact me about minor errors in the text: the text of this tutorial is mostly unedited, and only the presentation and markup have been significantly updated (to valid XHTML 1.0 strict). If there are major errors which significantly interfere with readability, though, please feel free to bring them to my attention - Billy Wenge-Murphy
This tutorial is (c) 2002-2007 Billy Wenge-Murphy. All rights reserved. It may not be copied, reproduced, or redistributed in any form without permission. If you wish to share it, please link to it instead of reposting it.

BIT manipulation

Bit manipulation, as you may have guessed, involves manipulating bits - more specifically, changing things on a binary level. Therefore, as a prerequisite you should already be familiar with binary, and different sizes of data (bit, nibble, byte, word, etc).

Firstly, why would this be important? Specifically, I'll be talking about BASIC, but you can apply this information to any language.

In a language such as assembly, bit manipulation is easy - direct access to any bit is built in. For example, you can assign a register to a binary value, like so:

mov ah, 10101010b

not so in BASIC and some other high level languages: the smallest amount of data you can assign is a word (in qbasic/quickbasic the smallest data type is the INT - 16 bits. for other dialects of BASIC this may not be the case):

variable% = 170

You do not have the luxury of assigning a binary number, as with ASM. This can pose a problem in some places where you HAVE TO change certain bits. For example, sometimes it's necessary when dealing with graphics. If you're using 16-bit color, it's usually in a 5-6-5 format: 5 bits red, 6 bits green, 5 bits blue. How do you set a 5-bit or 6-bit value? Well, since they're all going into a word (16-bit number) in the end, don't think of them as 5 or 6 bit numbers, think of them as part of the 16-bit number. Let's make a 16-bit color by putting together each component color

Starting from the lowest bit is blue which occupies 5 bits. The highest possible number we can store in 5 bits is 31, or 11111b. The same is true, then for red. Green, since it has 1 more bit, can have one more power of 2 to deal with: 6-bits can hold the number 63, or 111111b. Therefore, if we were to pick component values, we would be limited to those ranges. Let's choose some random component values

Some BASIC code:

red% = 10
green% = 31
blue% = 15

Now, how can those be combined into one WORD (an int%). As you should know, a binary shift left is the same as a multiplication by 2. (as I said, such knowledge is a prerequisite for this page). Well, BASIC doesn't have a Shift function. However, it DOES have multiplication, so we could make a shift function.


FUNCTION Shift% (var%, numshifts%)
mulval% = 2 ^ numshifts%
var% = var% * mulval%
Shift% = var%
END FUNCTION

There we have a shift function. It simply takes a number and multiplies it by 2 however many times numshifts% says too. Ex: if numshifts% is 1, var% is multiplied by 2 ( 2 = 2 * 1 = 2^1 ). If numshifts% is 4, var% is multiplied by 16 ( 16 = 2*2*2*2 = 2^4 ). If numshifts% is 7, var% is multiplied by 128 ( 128 = 2*2*2*2*2*2*2 = 2^7)... and so on..

By shifting the bits we can align them so we can then put them into one variable.

Right now, here's what each variable looks like in binary (remember, each is 16 bit)

Variable name Binary
red% 0000000000001010
green% 0000000000111111
blue% 0000000000001111

However, we them to look like this if we're going to add them together:

Variable name Binary
red% 0000000000001010
green% 0000011111100000
blue% 0111100000000000

Thefore we can see that we want to leave red% alone, shift green over 5 places, and blue over 11 places.
We could multiply green% by 32 (32 = 2*2*2*2*2 = 2^5), or just use our shift function, which does the same thing
The same goes for blue. To shift using the shift function, simply:

green% = Shift%(green%, 5)
blue% = Shift%(blue%, 11)

then, just add them together:

col16bit% = red% + green% + blue%

if you want to visually see the math:

  0000000000001010
  0000011111100000
+ 0111100000000000
   
= 0111111111101010

So there you have it - you can now do Shifts in QB. However, what if you wanted to mask off some bits, or toggle bits?
Luckily, QB has boolean operators for this (if you're not familiar with boolean operators read this)

First, let's write a Function to mask off all bit positions except one, in effect, returning the value of a single bit.
Although we have some assistance from BASIC, we still have to use multiplication to get the bit we want. Suppose we have some number - let's use 44.

44 is 101100b

If we call the rightmost, or lowest, bit bit #0, then we can seethat

bit 0 is 0
bit 1 is 0
bit 2 is 1
bit 3 is 1
bit 4 is 0
bit 5 is 1

Suppose we wanted to get bit 3. If it was a 0, we'd want our function to return 0, and if it's a 1, it should return one.
Getting the bit is easy enough, because of the AND operator. The AND operator is perfect for masking off bits, because esentially all you have to do is set-up a mask in a variable - set the bits you don't want to 0, and the ones you do want to 1. In this case, our mask would look like (remember, there are extra leading zeros because we're dealing with 16 bit variales):

mask% = 0000000000001000

if we were to AND this with a value or variable, the only possible bit to come out as 1 would be bit 3, because

[anything] AND 0 = 0.

However, it doesn't have to come out 1 just because bit 3 is one. it will only come out 1 if bit 3 of the mask and bit 3 of the variable or value in question are both 1.

This is because

1 AND 1 = 1

anything else equals 0

So, use AND to compare the mask to 44:

44: 0000000000001101
Mask: 0000000000001000
Result: 0000000000001000

now, just shift that to the right 3 times and you have the resulting bit: 1.

Of course, a shift to the right is just the opposite of a shift to the left. If a left shift is multiplication by a power of 2, a right shift (or the reverse) must be division by a power of two. Hopefully this is obvious how to do so I wont go over it.

We saw how to create a mask by hand, but, how do you write a function to return any bit?

Simply enough, you can create a mask by taking the number 1 and shifting it left a given number of times. If you want to return bit 0, for example, shift it left 0 times. If you want to return bit 2, shift it left 2 times.... and so on. Once you mask off all but the wanted bit, you must shift it back to the right however many times you shifted left.

Some code:

' this code uses the shift% function from earlier
' remeber, we're considering the rightmost bit to be bit 0
FUNCTION Getbit% (var%, bitnum%)
mask% = Shift%(1, bitnum%)
Getbit% = (var% AND mask%) / 2^bitnum%
END FUNCTION

The second line of the function masks, shifts back to the right, and returns the value all in one step.

That covers two topics: Shifts and masking. Now, toggling a bit.

This is very similar to the past two. Toggling a bit means changing it to the opposite: 1 goes to 0, 0 goes to 1. And again, there is an operator that does this: XOR.

1 XOR 1 = 0
1 XOR 0 = 1
0 XOR 1 = 1
0 XOR 0 = 0

Two things we can see from that table. Anything, XORing with 0 doesn't changing anything. and 1 XORed with itself switches to 0. This is perfect because we could XOR a bit with 1 forever and with would always flip back and forth from 0 to 1

0 XOR 1 = 1
1 XOR 1 = 0
0 XOR 1 = 1
and so on...

As before, we'll be toggling one bit, so we'll need to move the bit into position - which can be accomplished with the shift function. We can create a mask like before: Anything we want to toggle should be 1, anything to be left alone should be 0.
Let's illustrate this with the number 24, which is 11000b. Let's toggle bit 4 (the leftmost bit).

24: 0000000000011000
Mask: 0000000000010000
XORed Result 0000000000001000

And once again, some code:

' remember, as always, the rightmost bit is considered bit 0
FUNCTION Toggle% (val%, bitnum%)
togglemask% = Shift%(1, bitnum%)
returnval% = val% XOR togglemask%
END FUNCTION

That's it!

Mascot: Billy's Weird Cat Thing