Sign in to follow this  
zerotri

not used to bitwise operators...

Recommended Posts

I am working on a simple VM for testing out scripting language design, and seem to be having issues with storing the values. I'm trying to set the VM up so that each instruction is a 32 bit value(DWord) that actually contains 3 separate bytes of data(and a padding byte). I've been having problems with storing the structure into a DWord however, and I am sure it is due to my lack of experience using bitwise operators. the instruction structure is:
struct nslOpcodeType{
        nslByte instruction;   //instruction
        nslByte arg1;          //argument 1
        nslByte arg2;          //argument 2
        nslByte _padding;
};
and I have the following functions that convert the Opcode structure to and from a DWord:
nslDWord	nsl_OpcodeToDWord(nslOpcodeType op);
nslOpcodeType	nsl_DWordToOpcode(nslDWord dw);

So since the structure is(should be) four bytes, I can store it like so in a DWord, right?
//i represents instruction
//a represents arg1
//n represents arg2
//format: iiiiiiii  aaaaaaaa  nnnnnnnn  00000000
the functions I am using to convert between DWord and Opcode are here:
nslDWord nsl_OpcodeToDWord(nslOpcodeType op)
    {
    	nslDWord dwReturnValue = 0;
    	dwReturnValue =  (nslDWord)op.instruction << 24;
    	dwReturnValue |= (nslDWord)op.arg1 << 20;
    	dwReturnValue |= (nslDWord)op.arg2 << 16;
    	return dwReturnValue;
    };
    nslOpcodeType nsl_DWordToOpcode(nslDWord dw)
    {	//iiiiiiii	aaaaaaaa	nnnnnnnn	00000000
    	//	FF	      FF	      FF	      00
    	nslOpcodeType opReturn;
    	opReturn.instruction 	= (dw >> 24)	& 0xFF000000;
    	opReturn.arg1 		= (dw >> 12)	& 0x00FF0000;
    	opReturn.arg2 		= (dw >> 16)	& 0x0000FF00;
    	return opReturn;
    };
...anyone see what I am doing wrong here? thanks for any help, and I'm sorry to waste your guys' time. -Wynter Woods(aka Zerotri)

Share this post


Link to post
Share on other sites
You have some strange numbers for your bitshifts. Shouldn't they be 24, 16, and 8, respectively, rather than the 24, 20, and 16 you have in one place, and 24, 12, and 16 you have in another?

Share this post


Link to post
Share on other sites
dude...I am an idiot...
yeah...that seems right.
I forgot to change those values when I changed the opcode struct from 2 bytes to three. originally the struct looked like this:
struct nslOpcodeType{
nslByte instruction; //instruction
nslByte arg1:4; //argument 1
nslByte arg2:4; //argument 2
nslWord _padding;
};

and I had to change the arguments because the compiler was complaining that the variables would always return 1 when compared with an enum I made. Also, that comment you made on &'ing before shifting makes sense...and I dunno why I am doing that wrong...guess I'm just rushing to get that to work and I should be thinking more logically right now(possibly also practice more with bitwise operators).

thanks for the quick reply,
-Wynter Woods(aka Zerotri)

Share this post


Link to post
Share on other sites
In C++, consider using std::bitset instead.

Also, use named constants for your field widths, to reduce mainentance effort.

Also in C++, use namespaces instead of tagging your names like that.

Oh, and don't put 'Type' into class names unless you really mean it - i.e., you're doing some kind of meta-typing thing where the objects themselves describe some kind of type. Classes are *always* types, so it's redundant and potentially confusing (because experienced programmers may be looking for that non-existent meta-typing).


namespace nsl {
const int[] masks = {
// field widths in the middle column
// v
1 << 8 - 1,
1 << 4 - 1,
1 << 4 - 1
}; // Or change to 8, 8, 8 if needed :)

const int[] positions = {
0,
instruction_width,
instruction_width + arg1_width // etc. for more.
};
const int size = instruction_width + arg1_width + arg2_width;

// You shouldn't need to pad manually - the compiler's struct alignment
// should take care of that automatically.

struct Opcode {
enum field { instruction, arg1, arg2 };
std::bitset<size> storage;
OpcodeType(int i_value, int a1_value, int a2_value) {
(*this)[instruction] = i_value;
(*this)[arg1] = a1_value;
(*this)[arg2] = a2_value;
}
explicit OpcodeType(DWord d) : storage(static_cast<unsigned long>(d)) {}
int& operator[](field f) {
return (storage >> positions[f]) & masks[f];
}
int operator[] const(field f) {
return (storage >> positions[f]) & masks[f];
}
DWord toDWord() {
return static_cast<DWord>(storage.to_ulong());
}
};
}



This way, conversion to and from DWords is as simple as a little casting (assuming they're the same size as unsigned long - otherwise you'll want to be a little more rigorous), and if the field widths change, all you need to change are the mask values. (If the *order* changes, more work is involved, but I really don't think there's a way around that.) Instead of accesing fields of the struct with member selection, you access them like "elements", using the subscript operator (and providing one of the enumeration values as a subscript). Thus 'myOpcode.arg1' becomes 'myOpcode[Opcode::arg1]'.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this