View previous topic - View next topic |
Author |
Message |
DeveloperX 202192397
Joined: 04 May 2003 Posts: 1626 Location: Decatur, IL, USA
|
Posted: Wed Sep 21, 2005 10:17 pm Post subject: Bit Operations |
[quote] |
|
Okay, I have a vague idea of how this works.
Perhaps I'm wrong, because I have yet to experiment, fearing causing major havok.
Okay, I have a variable that is 2 Bytes (16 Bits) that I want to
use to store 16 values. (they obviously can only be 1 or 0, and they are to determine game-storyline stuff, and also for scripting..anyway)
Lets say for instance that I want to store a 1 in the first (leftmost) bit.
How would I do this?
Would it be something like this?
Code: |
u16 flags;
flags |= 0x1;
|
also, how do you check the 'bitpattern' (for lack of a better word) of the variable?
Code: |
if (flags & 0x1 == 0x1){doSomething();}
|
???
please give me a hand if you can.
and dont simply refer me to online searches please.
thanks. _________________ Principal Software Architect
Rambling Indie Games, LLC
See my professional portfolio
|
|
Back to top |
|
|
tcaudilllg Dragonmaster
Joined: 20 Jun 2002 Posts: 1731 Location: Cedar Bluff, VA
|
Posted: Wed Sep 21, 2005 11:20 pm Post subject: |
[quote] |
|
Flags = Flags | 2^16
What you had would set the rightmost bit. (the (L)east (S)ignificant (B)it)
Last edited by tcaudilllg on Wed Sep 21, 2005 11:22 pm; edited 1 time in total
|
|
Back to top |
|
|
janus Mage
Joined: 29 Jun 2002 Posts: 464 Location: Issaquah, WA
|
Posted: Wed Sep 21, 2005 11:21 pm Post subject: |
[quote] |
|
Next time, just experiment first, because the code examples you posted are more or less correct. (Just make sure to remove any existing value of a bit before setting a new value with |=)
|
|
Back to top |
|
|
DeveloperX 202192397
Joined: 04 May 2003 Posts: 1626 Location: Decatur, IL, USA
|
Posted: Wed Sep 21, 2005 11:31 pm Post subject: |
[quote] |
|
thanks.
here is what I've got now:
Code: |
#include <iostream>
#include <stdlib.h>
using namespace std;
typedef unsigned short u16;
u16 mask16[] =
{
0x01 , 0x02 , 0x04 , 0x08 , 0x10 , 0x20 , 0x40 , 0x80,
0x100, 0x200, 0x400, 0x800, 0x1000, 0x2000, 0x4000, 0x8000
};
u16 flags;
void varbin (void)
{
for (u16 masked_bit = 0; masked_bit < 16; masked_bit++)
{
if (flags & mask16[masked_bit])
{
cout << "1";
}
else
{
cout << "0";
}
}
}
int main(int argc, char *argv[])
{
flags = 0x8000;
flags |= 0x4000;
flags |= 0x2000;
flags ^= 0x4000;
cout << "flags = " << flags << endl << endl;
cout << "in binary = '"; varbin(); cout << "'" << endl;
system("PAUSE");
return 0;
}
|
decided to try some experiments on the pc....I forgot that I had devc++ on this computer.
:)
Thanks for your fast replies. _________________ Principal Software Architect
Rambling Indie Games, LLC
See my professional portfolio
|
|
Back to top |
|
|
DeveloperX 202192397
Joined: 04 May 2003 Posts: 1626 Location: Decatur, IL, USA
|
Posted: Wed Sep 21, 2005 11:35 pm Post subject: |
[quote] |
|
problem.
why is it that the results are wrong when output using the above code?
Code: |
flags = 40960
in binary = '0000000000000101'
Press any key to continue . . .
|
confused...
I thought that 40960 was 1010000000000000 _________________ Principal Software Architect
Rambling Indie Games, LLC
See my professional portfolio
|
|
Back to top |
|
|
janus Mage
Joined: 29 Jun 2002 Posts: 464 Location: Issaquah, WA
|
Posted: Thu Sep 22, 2005 12:15 am Post subject: |
[quote] |
|
DeveloperX wrote: | problem.
why is it that the results are wrong when output using the above code?
Code: |
flags = 40960
in binary = '0000000000000101'
Press any key to continue . . .
|
confused...
I thought that 40960 was 1010000000000000 | I think you're printing the output in the wrong order.
|
|
Back to top |
|
|
LeoDraco Demon Hunter
Joined: 24 Jun 2003 Posts: 584 Location: Riverside, South Cali
|
Posted: Thu Sep 22, 2005 6:36 am Post subject: Re: Bit Operations |
[quote] |
|
DeveloperX wrote: |
Code: |
u16 flags;
flags |= 0x1;
|
|
You should be careful with that; make sure that you zero out "flags" prior to setting any values in it, else your program will be susceptible to random garbage.
Quote: | Code: |
if (flags & 0x1 == 0x1){doSomething();}
|
|
Due to the wonderful nature of C/C++, that extra boolean check is not strictly needed; that is, of 0x1 is on in "flags", the bitwise-and operator will return a non-zero value; otherwise, it should return a zero value. While, obviously, it isn't a terrific idea to rely upon such functionality, I throw that out there for your perusal.
Also, as I'm sure you are aware, you will, intrinsically, be limited to the number of flags you can store in a single integer; the (current?) maximum on most 32 bit systems being 64 (unsigned long longs), which may not be supported on all systems. While this is fine, if you are simply looking to cut down your memory footprints, if you have access to the STL, you might consider using a bit_vector, which should be optimized to hold an open-ended number of flags. While there is, obviously, an amount of overhead, if you are simply looking for an efficient way of storing a large quantity of flags, and are not terribly concerned with time-constraints, it might be a good option.
Another thing: should you want to unset a flag in a bit-packed integer, use the xor-operator:
Code: | unsigned short foo = 0x5; // foo = 0...0101
foo ^= 0x1; // foo = 0...0100 |
This might be nice if you have some flag that should only be on if another flag isn't. (I.e., instead of simply or-ing, you xor, which would set the flag if it isn't set, and unset if it is.)
Edit: about that other problem, while Janus' observation is more than likely what is happening, I'll throw out two more things to keep in mind: first, a "word" on most modern systems is sized to 4-bytes and most memory accesses are done on the word-level; so, you might experience some disorder when retrieving quantities less than that. Second, there is the whole byte-ordering buisness, that specifies which order bytes appear in a given word. (Neither of these seems to be the case, but keep them in mind.)
It looks like you did something like (assuming a C-dialect):
Code: | for( mask = 0x1; mask != 0xFF; mask <<= 1 ){
printf( "%d", flags & mask );
} |
where what you should do is reverse the direction. _________________ "...LeoDraco is a pompus git..." -- Mandrake
|
|
Back to top |
|
|
RuneLancer Mage
Joined: 17 Jun 2005 Posts: 441
|
Posted: Thu Sep 22, 2005 2:41 pm Post subject: |
[quote] |
|
Setting a bit: Var |= FLAG;
Clearing a bit: Var &= ~FLAG;
Testing the presence of a bit: if(Var & FLAG) ...
Anyone with some basic assembly experience would be familiar with this kind of work. Most hardware uses flags like these to cumulate multiple states. The SNES controller, for instance, has a 16 bit register for each possible controller (4 of 'em) and 12 of these bits contain a button (up/down/left/right, select/start, a/b/x/y, r/l)
You can test multiple bytes at once quickly by cumulating the flags before the test.
if(CharStatus | 0x83) // bits 0x80, 0x02, and 0x01 indicate disabling statuses
{ ... }
Which is far simpler to do than...
if((CharStatus | 0x80) || (CharStatus | 0x02) || (CharStatus | 0x01))
Though not as readable as...
if(CharStatus | (0x80 | 0x02 | 0x01))
You should NEVER work with actual values, as you'll get horribly confused when you come back to your code in a few months. #define them instead.
#define ST_DEAD 0x80
#define ST_PETRIFIED 0x02
#define ST_SLEEP 0x01
if(CharStatus | (ST_DEAD | ST_PETRIFIED | ST_SLEEP))
{...}
Some advanced tricks: learn to use bitshifts. You could, for instance, split up a byte like this...
Bit 8: Window shown if set.
Bit 7-4: Windowset.
Bit 3-1: Window palette.
#define WIN_SHOWN 0x80
#define WIN_SET 0x78
#define WIN_PAL 0x07
if(WinVar & WIN_SHOWN)
{
WinSet = WinVar & WIN_SET; // Clear the other bits.
WinSet = WinSet >> 3; // Bitshift. 01111000 shifted 3 places to the right: 00001111.
WinPal = WinVar & WIN_PAL; // No need to shift this one, it already starts at 1.
}
Modern systems don't require us to conserve memory so tightly. But if you work on an older system (console systems come to mind) you'll find a lot of this.
Bitshifts can also do one more thing: very, very fast multiplication/division by a power of two. Doing x << 4 multiplies by 2 ^ 4 (16). x >> 3 divides by 8 (2 ^ 3). What about non-power of two values? Find two powers that add up: x * 12 is like (x * 8) + (x * 4). So, X = (X << 3) + (X << 2). Past two steps, though, you no longer benefit from this kind of optimisation. And frankly, you won't find much of a use for this beyond microoptimisation. Always good to know though.
One last trick. You probably won't ever use this, but a fast way to clear a variable (mainly used in assembly) is to AND it with 0. It's faster than assigning a value to it, being a bitwise operation, but unless you write an uber-tight loop in assembly you won't ever need to do this. I think most compilers automatically do it for you when they can anyways, as an optimisation. Same goes for obvious bitshifts (ie, x = x * 8 will be converted to x <<= 3 by some compilers.) _________________ Endless Saga
An OpenGL RPG in the making. Now with new hosting!
|
|
Back to top |
|
|
janus Mage
Joined: 29 Jun 2002 Posts: 464 Location: Issaquah, WA
|
Posted: Thu Sep 22, 2005 8:25 pm Post subject: |
[quote] |
|
Since I haven't seen much mention of this yet and the code examples are getting bigger and bigger, I'd just like to point something out:
More often than not, these days you have very little reason to muck with bits manually. Really. Compilers are WAY better at that stuff than people are, and the expense of using eight or even sixteen bits per flag instead of 1 is often far outweighed by the simplicity of being able to treat values as VALUES instead of having to constantly mask bits in and out of a container field. The cost of performing multiple bitwise operations and moves may often be HIGHER than the cost of simply copying a byte or two into a register - caches grow bigger every month, and less necessary operations like bitshifts get slower and slower. (Starting with the Pentium 4, intel's processors no longer execute bitshifts as fast as they used to - in some cases, a bitshift for a multiply by two can actually be SLOWER than simply doing an addition - the number of cycles required to perform these operations has increased considerably, while they're still usually not as slow as multiplication or division.) Hand-optimizing your code for current architectures and performance considerations is always dangerous, because it means that future hardware/software changes may make your optimizations worthless, or even worse, it may make them a detriment to your application's performance. Allowing the compiler to optimize means that the compiler authors can do most of the work for you, and that new hardware and software changes can be dealt with by simply updating and recompiling.
Storing your bitmasks in lookup tables is definitely a suspect optimization in my mind - a table lookup is often going to be MORE expensive than simply shifting a 1 to the left or the right. Table lookups involve at least one imul instruction in most cases, while the bitshift just involves... a bitshift. Only use optimizations like lookup tables when you are ABSOLUTELY certain that they are necessary, and only use them once you have at least casually tested them to see if they provide a performance improvement.
Also, if memory serves C and C++ both have bitfield support that works like this:
Code: | struct somebitset {
bool value1 : 1;
bool value2 : 1;
bool value3 : 1;
... etc ...
} |
where the compiler will automatically combine multiple variables into one bitfield when possible. In this case, the compiler can intelligently decide whether combining variables will improve performance, and your code isn't made unreadable as a result.
|
|
Back to top |
|
|
DeveloperX 202192397
Joined: 04 May 2003 Posts: 1626 Location: Decatur, IL, USA
|
Posted: Thu Sep 22, 2005 9:13 pm Post subject: |
[quote] |
|
I am working with the NES, SNES, GB, GBC, GBA.
I am trying to learn as much as I can about console development.
I lack the resources to dabble with PS1/2/P systems.
I wouldnt touch the Xb0X.
I have no clue where to try NGC, and I've tried Genesis before and fell on my face.
So, anyway.
I'm limited in memory, I cant use STL, I cant use alot of things really.
Anyway, I've got my problems figured out.
thanks for all the help guys. _________________ Principal Software Architect
Rambling Indie Games, LLC
See my professional portfolio
|
|
Back to top |
|
|
tcaudilllg Dragonmaster
Joined: 20 Jun 2002 Posts: 1731 Location: Cedar Bluff, VA
|
Posted: Thu Sep 22, 2005 10:51 pm Post subject: |
[quote] |
|
Were you able to get that interaction system I wrote translated?
|
|
Back to top |
|
|
RuneLancer Mage
Joined: 17 Jun 2005 Posts: 441
|
Posted: Fri Sep 23, 2005 3:06 pm Post subject: |
[quote] |
|
Out of curiousity, what do you mean by "work with" these systems? I highly doubt you've done any serious assembly code without being able to use bit operations, because they constitute the near entirety of a game's code. ;)
The only processors I can lay claim to knowing are the 65816 and its predecessor. While I've touched briefly on the others, I wouldn't know enough to write my own interpretor for any of them (which is the point where I consider knowing a system well enough to lay claim to, well, knowing it. :P) Aside from knowing what the mnemonics stand for, the key to understanding these chipsets is to understanding how the processor flags are affected by operations. Then the rest is pretty easy to deal with. Your newfound knowhow with bitwise operations will most certainly help there.
Most games on these platforms use bitwise operations to pack data into as little space as possible. FF3, for instance, stores its configuration menu settings in just a few bytes. The battle speed is packed in 4 bits along with a few other settings (such as wether the battle is active or wait), all in just one byte. Good luck figuring out what certain ram offsets contain when you're confronted with a dozen bitshifts and ORs. :)
Colors are where you have to play the most with bits. The SNES runs in 15bpp. Extracting the RGB components can be pretty hairy. If you have access to a dump of FF3's code, check the routines at C1:FC99 and C1:FD00. They add and subtract from the individual R, G, and B components of the color set in the accumulator. Best example I've ever seen of manipulating colors on an SNES.
Good luck. There's no better way to program than with low, low, low-level code. :D _________________ Endless Saga
An OpenGL RPG in the making. Now with new hosting!
|
|
Back to top |
|
|
Ninkazu Demon Hunter
Joined: 08 Aug 2002 Posts: 945 Location: Location:
|
Posted: Sun Sep 25, 2005 5:41 am Post subject: |
[quote] |
|
I had to find this out myself today while working on my map editor and I saw that it wasn't meantioned here...
Flip a bit:
var = ~var ^ ~(1<<bitnum);
flipping is better than checking and setting/clearing.
|
|
Back to top |
|
|
Rainer Deyke Demon Hunter
Joined: 05 Jun 2002 Posts: 672
|
Posted: Sun Sep 25, 2005 6:44 am Post subject: |
[quote] |
|
Ninkazu wrote: |
Flip a bit:
var = ~var ^ ~(1<<bitnum);
|
Flip a bit without the unnecessary operations:
var = var ^ (1 << bitnum);
|
|
Back to top |
|
|
LeoDraco Demon Hunter
Joined: 24 Jun 2003 Posts: 584 Location: Riverside, South Cali
|
Posted: Sun Sep 25, 2005 8:34 am Post subject: |
[quote] |
|
Rainer Deyke wrote: | Ninkazu wrote: |
Flip a bit:
var = ~var ^ ~(1<<bitnum);
|
Flip a bit without the unnecessary operations:
var = var ^ (1 << bitnum); |
Even shorter! (yeah, I'm reaching):
Code: | var ^= (1 << bitnum); |
If you look up above, that's mostly something I mentioned (although I did not have the bit-select thing, the concept is there). _________________ "...LeoDraco is a pompus git..." -- Mandrake
|
|
Back to top |
|
|
|
Page 1 of 2 |
All times are GMT Goto page 1, 2 Next
|
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|