Lord KJWilliams
Member
Before I got interested in GML or GMS, I programmed in C ( in Linux and MS-DOS ) and created functions that I had a need for a program that I was building for a game that I was working on. The two functions, that I want to share with community, are used for binary field rotation and the other for displaying the bit field ( which you can use separately ), its merely for diagnostics to see what the result of a rotated bit-field.
The bit rotation function does what bit shifting does not do, it wraps the binary field to the other side when you shift it left or right. Normally when you use >> or << , anything that gets shifted off the bit field is lost forever. So I decided to wrap it to the other side. So the limit of how many bits a bit field can be rotated is dependent on the integer size. In C you have, char , short int, int, long ( double and float are not used due to IEEE's method of remembering the mantissa - its not the way you think it is for integers in base 2 ) , just to keep things simple.
The program that I want to port to GML from C uses this source code :
This is the function that displays the binary bit field
So the problem with importing this code to GML, is that my programs depend on integer sizes in bytes, which define the number of bits. In C if I want to know how many bytes a variable uses I use an expression where bytesn = ( sizeof ) myvar; , however GML does not have this. I have not found in the manual for GML, a means where you can check to see how many bytes your var is, because that's what I need to know. If I am not using a variable for text strings, or for decimal ( using IEEE mantissa ) - and I am just using the var for remembering a value ( e.g. 0xDEADBEEF ).
( Please note, I use hexadecimal for representing large integer numbers. )
So how does bit_rotation work?
Lets say you have a variable with the value : 0x0F and you want to rotate it 4 bits to the right, which is the same as rotating it 4 bits to the left..
( this is one byte )
00001111
you get :
11110000
and how has the value : 0xF0
You can take a value : 0xDEADBEEF ( 32-bits ) and you can rotate it to the left or the right by 16 bits and get the the same result , 0xBEEFDEAD .
If you shift a value by 1/2 the bits counted in the number of bytes, either left or right you get the same result.
It is possible to get :
0xEFDEADBE
0xADBEEFDE
Your not truncating or changing the size of the bytes used, your just shifting the bit-fields around while saving the byte size. I know the byte sizes because in C , there are defined integer sizes. I did not use ( sizeof ) or for that matter limits.h , because I didn't think would not need them if I knew already the possible sizes.
A few things that I did not implement in my function was to test the value being rotated has a maximum value that looks like 0xFF, 0xFFFF, or 0xFFFFFFFF , because rotating 11111111 ( or 00000000 ) alone will not produce any different result, not like 0x0F or 0xDEADBEEF ( shown above )
So the problem in GML is that I do not know how to determine how many bytes uses, and I do not want to promote my values to a new byte range accidentally ( e.g. a 1 byte rotated to the left now is represented as 2 bytes, which is wrong ).
The other reason that I posted it here is that I thought that it might be useful to someone who needs it. It does have a use, but thats for you to decide in the application that you need it for. You can also use the function that displays prints the variable in binary, print_bytes(), but it has the same problem as bit_rotation() .
So can this work in GML and what do I need to change or add ( I know its represented in user defined header for C, which you cant do in GML ) ?
Thanks
The bit rotation function does what bit shifting does not do, it wraps the binary field to the other side when you shift it left or right. Normally when you use >> or << , anything that gets shifted off the bit field is lost forever. So I decided to wrap it to the other side. So the limit of how many bits a bit field can be rotated is dependent on the integer size. In C you have, char , short int, int, long ( double and float are not used due to IEEE's method of remembering the mantissa - its not the way you think it is for integers in base 2 ) , just to keep things simple.
The program that I want to port to GML from C uses this source code :
Code:
#ifndef BITROT_H
#define BITROT_H
#include <stdio.h>
#include <math.h>
#include "p_bytes.h"
unsigned long rotate_bits(unsigned long, signed char, char);
//-------------------------------------------------------------------
/*
headers, functions, and global variables used:
stdio.h
math.h
print_bytes();
**
make note of this abreviation key:
orig = original value that will be rotated.
mask = marks the range of bits that will be pushed off.
copy = copy of the bit values, that will be pushed off.
_posA = copy value, shifted in the opposite direction.
_posB = is the result of shifting the original bit pattern.
_newv = _posA value + _posB value, creates the new rotated position.
rotate_bits() rotates the bits of a value, that is not zero, to
a desired position. The function will not promote or demote
values, to a greater or lesser variable of other number
ranges in the operation. This function uses print_bytes().
values:
1 - 255 .......... will be treated as a unsigned char
256 - 65535 ........ will be treated as a unsigned short int
65535 - 4294967295 ... will be treated as a unsigned long
rotate_bits uses three arguments,
The first arugument, a unsigned long, is the target value that will
be rotated around. The target value must be in the values from 2^0
to 2^31.
The second argument is the position setting, which rotates the
binary pattern around to. The position setting range depends on the
target variable.
note: rotate_bits will not accept a position setting of 0.
If the target value:
... is a unsigned char, then the range is from -8 to 8.
... is a unsigned short int, then the range is from -16 to 16.
... is a unsigned long, then the range is from -32 to 32.
The third argument is a diagnostic display flag, which if is
1 then the operation of rotate_bits() will be displayed. Other
values turn off the diagnostic display.
rotate_bits returns a unsigned long that is the result of
the targets new rotated_bit setting.
*/
unsigned long rotate_bits(unsigned long a, signed char b, char y)
{
signed char m, j = 0, x, z, k = 0;//temp variables
unsigned long mask = 0, lost_part = 0, new_posA, new_posB, _newvar;
//test rotation position value before using it.
if(b > 31 || b < -31)
{
printf("ERROR: Rotation position value can only be (-31 to 31)\n");
printf("to rotate the target value left or right. Process aborted.\n");
return 0;
}
//check to see if the rotation value is 0
if(b == 0)
{
printf("ERROR: rotate_bits() requires a value for rotating the target value\n");
printf(" thats greater or less than 0. Process aborted.\n");
return 0;
}
//check variable a for 0 value
if(a == 0)
{
printf("ERROR: rotate_bits() requires a target value greater than 0.\n");
printf(" The target value must be from ( 1 - 4294967295 ).\n");
printf(" Process aborted.\n");
return 0;
}
// test the value to see how many bytes(of bits) it has
if(a > 65536) { m = 31; } // 4 bytes
if(a > 255 && a < 65536) { m = 15; k = 1; } // 2 bytes
if(a <= 255) { m = 7; k = 2; } // 1 byte
if(b < 0) j = b * -1;// create a positive value from the negative
// shifting value. -3 becomes 3
// test rotation value to see if it is greater than m
if(j > m || b > m)
{
printf("ERROR: The requested rotation position is greater than the\n");
printf(" number of bits of the value, process aborted.\n");
return 0;
}
// RIGHT SHIFTING
// if rotation value is (1 to 31) the rotation starts on 0th bit
if(b > 0)
{
if(y == 1) printf("RIGHT(%02d) >>\n",b);
j = b;
for(x = 0; x < (j); x++)
{
mask += (unsigned long)(pow(2,x));
}
//copy the part that will be shoved off to the right
lost_part = (mask & a);
//reshift lost part
new_posA = lost_part << ((m+1)-j);
//shift over a
new_posB = a >> j;
}
// LEFT SHIFTING
// if rotation value is (-1 to -31) the rotation starts on the 31st bit
if(b < 0)
{
if(y == 1) printf("LEFT(%02d) <<\n",j);
z = m - j;// this is where z is used, so that j does not change
for(x = m; x > (z); x--)
{
mask += (unsigned long)(pow(2,x));
}
//copy the part that will be shoved off to the left
lost_part = (mask & a);
//reshift lost_part
new_posA = lost_part >> ((m+1)-j);
//shift over a
new_posB = a << j;
}
//(unsigned long) new_posA + new_posB
_newvar = new_posA + new_posB;
//trim unsigned long into a unsigned int
if(k == 1)
{
new_posA = (unsigned short int)new_posA;
new_posB = (unsigned short int)new_posB;
_newvar = (unsigned short int)_newvar;
}
//trim unsigned long into a unsigned char
if(k == 2)
{
new_posA = (unsigned char)new_posA;
new_posB = (unsigned char)new_posB;
_newvar = (unsigned char)_newvar;
}
//diagnosis messages
if(y == 1)
{
printf(" orig:");print_bytes(a,-1);
printf(" mask:");print_bytes(mask,-1);
printf(" copy:");print_bytes(lost_part,-1);
printf("_posA:");print_bytes(new_posA,-1);
printf("_posB:");print_bytes(new_posB,-1);
printf("_newv:");print_bytes(_newvar,-1);
}
return _newvar;
}
//-------------------------------------------------------------------
#endif
This is the function that displays the binary bit field
Code:
/*
p_bytes.h (Print Bytes) by K.J. Williams
*/
#ifndef P_BYTES_H
#define P_BYTES_H
#include <stdio.h>
#include <math.h>
void print_bytes(unsigned long, signed char);
/*
headers, functions, or global variables used:
stdio.h
math.h
**
print_bytes() displays values in their binary representation,
but also in decimal and hexadecimal values. Any value from
0 - 4294967295 can be displayed in binary.
print_bytes has two arguments,
The first argument is a unsigned long, the value that will be
printed in a binary, hexadecimal, and/or integer representation.
The second argument is a signed char which determines how the
first argument's format will be displayed as well as it's bit length.
if the signed char is:
..-1* or 1 then the value will show the value in integer & hexadecimal
along with the binary format.
..-2* or 2 then the value will show the value in hexadecimal along
with the binary format.
..-3* or 3 then the value will show the value in integer display along with
with the binary format.
If the signed char is 0 or any other values greater than 3 or less
than -3 then only the binary format is printed.
* Note: negative values override the automatic formatted binary print,
in which four bytes of binary bits will be displayed no matter
what the value is.
print_bytes returns no value.
*/
void print_bytes(unsigned long a, signed char d)
{
// temp variables
unsigned char z = 1;// needed for printing a " ".
signed char x, m, tu = 0;
unsigned long y = 0, j = 4294967295;// highest int value
// if d is less than -3 or greater than 3 then set d to 0.
if(d < -3 || d > 3) d = 0;
if (d < 0) tu = d * -1;// test to see if d is a negative number
/* if the numbers are beyond the highest int value
set them to them to the highest long int value allowed */
if (a > j) a = j;
// hexadecimal or integer equivalence display
if (tu >= 1 && tu <= 3 || d >= 1 && d <= 3)
{
// hex & int.
if (tu == 1 || d == 1) printf(" [%08lX]:[%010lu] = ",a,a);
// hex display
if (tu == 2 || d == 2) printf(" [%08lX] = ",a);
// integer display
if (tu == 3 || d == 3) printf(" [%010lu] = ",a);
}
// determine range of bits to print
// if d is a positive number, tu will stay 0.
if (tu == 0)
{
if (a > 65536) m = 31;// 4 bytes
if (a > 255 && a < 65536) m = 15;// 2 bytes
if (a <= 255) m = 7;// 1 byte
}
// if d is a negative number, tu will equal the absolute
// value of d which is a positive number.
if (tu >= 1 && tu <= 3) m = 31;// default to 4 bytes
/* cycle from 2^x to 2^0 to show each bit */
for (x = m;x > -1; x--,z++)
{
y = (unsigned long)(pow(2,x));// where math.h is needed
// print a 1,0, or a space at every 8th count.
if ((a & y) == y) { printf("1"); }
if ((a & y) != y) { printf("0"); }
if (z == 8) { printf(" "); z = 0; }
}
printf("\n");
}
#endif
( Please note, I use hexadecimal for representing large integer numbers. )
So how does bit_rotation work?
Lets say you have a variable with the value : 0x0F and you want to rotate it 4 bits to the right, which is the same as rotating it 4 bits to the left..
( this is one byte )
00001111
you get :
11110000
and how has the value : 0xF0
You can take a value : 0xDEADBEEF ( 32-bits ) and you can rotate it to the left or the right by 16 bits and get the the same result , 0xBEEFDEAD .
If you shift a value by 1/2 the bits counted in the number of bytes, either left or right you get the same result.
It is possible to get :
0xEFDEADBE
0xADBEEFDE
Your not truncating or changing the size of the bytes used, your just shifting the bit-fields around while saving the byte size. I know the byte sizes because in C , there are defined integer sizes. I did not use ( sizeof ) or for that matter limits.h , because I didn't think would not need them if I knew already the possible sizes.
A few things that I did not implement in my function was to test the value being rotated has a maximum value that looks like 0xFF, 0xFFFF, or 0xFFFFFFFF , because rotating 11111111 ( or 00000000 ) alone will not produce any different result, not like 0x0F or 0xDEADBEEF ( shown above )
So the problem in GML is that I do not know how to determine how many bytes uses, and I do not want to promote my values to a new byte range accidentally ( e.g. a 1 byte rotated to the left now is represented as 2 bytes, which is wrong ).
The other reason that I posted it here is that I thought that it might be useful to someone who needs it. It does have a use, but thats for you to decide in the application that you need it for. You can also use the function that displays prints the variable in binary, print_bytes(), but it has the same problem as bit_rotation() .
So can this work in GML and what do I need to change or add ( I know its represented in user defined header for C, which you cant do in GML ) ?
Thanks