GPC Developer Guides
Advanced Samples

Bit-Packing SPVARs

this sample shows how you can save data across multiple spvars when needed and utilize the spvars in the most efficient way possible gpc // define 6 variables to use in this example int var1, var2, var3, var4, var5, var6; init { &#x9;load(); // load our settings from flash or set the defaults&#x9; } main { &#x9; &#x9;// display the various values in trace 1 to trace 6 so we can see what happens when we press the buttons below &#x9;set val(trace 1, var1); &#x9;set val(trace 2, var2); &#x9;set val(trace 3, var3); &#x9;set val(trace 4, var4); &#x9;set val(trace 5, var5); &#x9;set val(trace 6, var6);&#x9; &#x9; &#x9;// press right on the d pad to increment the values &#x9;if(event press(ps4 right)) { &#x9; var1 = clamp(var1 + 1, 0, 30); &#x9; var2 = clamp(var2 + 20, 0, 30000); &#x9; var3 = clamp(var3 + 10, 99, 99); &#x9; var4 = clamp(var4 + 1, 0, 50); &#x9; var5 = clamp(var5 + 40, 200, 200); &#x9; var6 = clamp(var6 + 10, 0, 60); &#x9; &#x9;} &#x9;// press left on the d pad to decrement the values &#x9;if(event press(ps4 left)) { &#x9; var1 = clamp(var1 1, 0, 30); &#x9; var2 = clamp(var2 20, 0, 30000); &#x9; var3 = clamp(var3 10, 99, 99); &#x9; var4 = clamp(var4 1, 0, 50); &#x9; var5 = clamp(var5 40, 200, 200); &#x9; var6 = clamp(var6 10, 0, 60); &#x9; &#x9;} &#x9; &#x9;// press a/cross to save &#x9;if(event press(ps4 cross)){ &#x9; save(); &#x9;} &#x9;// press x/square to load &#x9;if(event press(ps4 square)){ &#x9; load(); &#x9;} } // this is an example of how this can be used for loading values note the ranges here must match what you have in your save function! function load() { &#x9;reset spvar(); // always reset the spvar state before reading to ensure that we're reading from the same location as we last saved &#x9;if (read spvar(0, 1, 0)) { // read and check the first bit, if it's set, we know something should've been saved, otherwise we fall back on our default setting &#x9; var1 = read spvar( 0, 30, 0); // read var1 with a value within the range 0 to 30 with a default value of 0 &#x9; var2 = read spvar( 0, 30000, 0); // read var2 with a value within the range 0 to 30000 with a default value of 0 &#x9; var3 = read spvar( 99, 99, 0); // read var3 with a value within the range 99 to 99 with a default value of 0 &#x9; var4 = read spvar( 0, 50, 0); // read var4 with a value within the range 0 to 50 with a default value of 0 &#x9; var5 = read spvar( 200, 200, 0); // read var5 with a value within the range 200 to 200 with a default value of 0 &#x9; var6 = read spvar( 0, 60, 0); // read var6 with a value within the range 0 to 60 with a default value of 0 &#x9;} &#x9;else { &#x9; var1 = 1; // set var1 to it's default value of 1 &#x9; var2 = 2; // set var2 to it's default value of 2 &#x9; var3 = 5; // set var3 to it's default value of 5 &#x9; var4 = 4; // set var4 to it's default value of 4 &#x9; var5 = 5; // set var5 to it's default value of 5 &#x9; var6 = 6; // set var6 to it's default value of 6 &#x9;} } // this is an example of how this can be used for saving values note the ranges here must match what you have in your load function! function save(){ &#x9;reset spvar(); // always reset the spvar state before saving to ensure that we're saving at the same location as we will later read &#x9;save spvar( 1, 0, 1); // save a constant 1 to denote previously saved data, this range uses 1 bit &#x9;// at this point we're using 1 bit in spvar 1 &#x9;save spvar(var1, 0, 30); // save var1 with a range between 0 and 30, this range uses 5 bits &#x9;// at this point we're using 6 bits in spvar 1 &#x9;save spvar(var2, 0, 30000); // save var2 with a range between 0 and 30000, this range uses 15 bits &#x9;// at this point we're using 21 bits in spvar 1 &#x9;save spvar(var3, 99, 99); // save var3 with a range between 99 and 99, this range uses 8 bits &#x9;// at this point we're using 29 bits in spvar 1 &#x9;save spvar(var4, 0, 50); // save var4 with a range between 0 and 50, this range uses 6 bits &#x9;// at this point we're using 32 bits in spvar 1 and 3 bits in spvar 2 var4 is saved across both spvar 1 (the last 3 bits) and spvar 2 (the first 3 bits) &#x9;save spvar(var5, 200, 200); // save var5 with a range between 200 and 200, this range uses 9 bits &#x9;// at this point we're using 32 bits in spvar 1 and 18 bits in spvar 2 &#x9;save spvar(var6, 0, 60); // save var6 with a range between 0 and 60, this range uses 6 bits &#x9;// at this point we're using 32 bits in spvar 1 and 24 bits in spvar 2 } // function used to reset the spvar state to where we begin, this one you can change if you like, the rest you should leave as is or you risk breaking the logic of this you have been warned! function reset spvar() { &#x9;spvar current slot = spvar 1; // change this to say where it's safe to start storing data &#x9;spvar current bit = 0; // should always be 0, unless you're using part of the first spvar in which case you should also change the next line to include the value you are storing in the bits you are using &#x9;spvar current value = 0; } // do not touch anything below this line unless you know what you are doing! int spvar current bit, // variable used to keep track of the next available bit &#x9;spvar current slot, // variable used to keep track of the currently used spvar slot &#x9;spvar current value, // variable used to keep track of the current value with all the bits from the previous variables saved in the current spvar &#x9;spvar tmp, // variable used temporarily during the various calculation steps &#x9;spvar bits; // variable used to keep track of the number of bits required to represent the currently saved/loaded variable // function used to count the number of bits used by the given value function get bit count(val) { &#x9;spvar tmp = 0; // we need to start at 0, we use spvar tmp here as we need to track the bits during our loop below &#x9;while (val) { // loop while val is anything but 0 &#x9; spvar tmp++; // increment the bit count by 1 &#x9; val = abs(val >> 1); // shift the value down 1 bit, once we have no more bits set this will result in 0, unless the value is negative in which case this will be endless, we do abs here to make it always &#x9;} &#x9;return spvar tmp; } // function used to count the number of bits used by 2 given values function get bit count2(val1, val2) {&#x9; &#x9;spvar tmp = max(get bit count(val1), get bit count(val2)); // get the highest bit count required for either min or max &#x9;if (is signed2(val1, val2)) { // check if we need to know if the value is negative or not &#x9; spvar tmp++; // if we need to track if the saved value is negative, we need 1 bit for that specifically the others are used to store the actual value &#x9;} &#x9;return spvar tmp; } // function used to determine if either of 2 given values is negative function is signed2(val1, val2) { return val1 < 0 || val2 < 0; } // function used to generate a bitmask for the sign bit, this will always be the highest bit in the range we're requesting it for, to do that we need to start with the lowest bit set and move it up the number of steps there is between 1 and the bits we need, this needs to be a maximum of 31 but can never be negative function make sign(bits) { return 1 << clamp(bits 1, 0, 31); } // function used to generate a full bitmask (essentially all bits set up to and including the number of bits given) function make full mask(bits) {&#x9; &#x9;if (bits == 32) { // if we're wanting a bitmask for all bits, we can simply return 1 (which is all bits set to 1) &#x9; return 1; &#x9;} &#x9;return 0x7fffffff >> (31 bits); // what we do here is basically take a value with all bits except the highest set and shift them down as many times as we need to get a mask that fits the bit count we're looking for } // function used to generate a bitmask for just the bits required for the value part of a signed range, this means all the bits below the sign bit function make sign mask(bits) { return make full mask(bits 1); } // function used to pack a value that has potential for being negative in a way that we use the least number of bits we really need to represent the value function pack i(val, bits) { &#x9;if (val < 0) { // check if we have a negative value, if so handle it accordingly &#x9; return (abs(val) & make sign mask(bits)) | make sign(bits); // get the positive version of the value and keep the bits that are within range of what we're doing and add the sign bit since we have a negative value and return the result &#x9;} &#x9;return val & make sign mask(bits); // get the bits that are within our range } // function used to unpack (restore) a value that has potential for being negative, essentially reversing what pack i does above function unpack i(val, bits) { &#x9;if (val & make sign(bits)) { // check if the stored value is supposed to ve negative &#x9; return 0 (val & make sign mask(bits)); // retrieve the stored positive value and subtract it from 0 (resulting in the same value except negative), return the result &#x9;} &#x9;return val & make sign mask(bits); // retrieve the stored positive value and return it } // function used to read the value of a spvar without any limits function read spvar slot(slot) { return get pvar(slot, 0x80000000, 0x7fffffff, 0); } // function used to save your value in the spvars, this is the function you'll be calling when saving a value you need to provide the value to save aswell as the range (minimum and maximum value, this is how we determine how many bits to use when saving this value) function save spvar(val, min, max) { &#x9;spvar bits = get bit count2(min, max); // set spvar bits to the number of bits we need for this range &#x9; &#x9;val = clamp(val, min, max); // make sure the value is within our defined range to begin with &#x9; &#x9;if (is signed2(min, max)) { // if either min or max is negative, we need to pack this value as a possibly negative value &#x9; val = pack i(val, spvar bits); // pack as signed value (possibly negative) &#x9;} &#x9;val = val & make full mask(spvar bits); // pack as unsigned value (always positive), this essentially just makes the resulting value not have any extra bits set it's safe to use after the signed packing since we're not using any bits outside of the unsigned range anyways &#x9; &#x9;if (spvar bits >= 32 spvar current bit) { // check if there is not enough bits remaining to save this value as is if there aren't enough bits, we save what we can here and store the remaining bits in the next spvar, if this means we're hitting the end, we can make this smaller by handling the case where we use all bits here aswell &#x9; spvar current value = spvar current value | (val << spvar current bit); // add what we can to the current value where there is bits available to use &#x9; set pvar(spvar current slot, spvar current value); // save the current spvar before advancing to the next one &#x9; spvar current slot++; // move to the next slot &#x9; spvar bits = (32 spvar current bit); // update the required bits according to our needs for the next slot, if we don't do this here, we'll screw up the saved value by moving it too far out of range &#x9; val = val >> (32 spvar current bit); // move the remaining bits down, discarding the bits we've already saved &#x9; spvar current bit = 0; // reset the current bit counter since we're starting with a new spvar &#x9; spvar current value = 0; // reset our value so we start clean, we aren't currently using any bits anyways &#x9;} &#x9; &#x9;spvar current value = spvar current value | (val << spvar current bit); // merge the current spvar value with our currently value where there is space to keep our value &#x9;spvar current bit += spvar bits; // move up the counter of next available bit to where we are currently saving data at &#x9;if (!spvar current bit) { &#x9; spvar current value = 0; // reset our value so we start clean, we aren't currently using any bits anyways &#x9;} &#x9;set pvar(spvar current slot, spvar current value); // save the spvar with the current value, this won't write anything to flash unless the value changed so we can do this for each variable saved to no risk missing anything } // function used to read your value from the spvars, this is the function you'll be calling when reading a value you need to provide the range (minimum and maximum value, this is how we determine how many bits to use when reading the value) aswell as a default value if what we read is out of range function read spvar(min, max, def) { &#x9;spvar bits = get bit count2(min, max); // set spvar bits to the number of bits we need for this range &#x9; spvar current value = (read spvar slot(spvar current slot) >> spvar current bit) & make full mask(spvar bits); // read the current spvar value from flash and shift them into position, we'll handle split values next &#x9; &#x9;if (spvar bits >= 32 spvar current bit) { // check if we are dealing with a split spvar value, essentially if the current position means we're using more than 32 bits in the spvar, we need to retrieve the missing bits from the next spvar and put them back to our current value, we use the same space saving trick here as in the save function &#x9; spvar current value = (spvar current value & make full mask(32 spvar current bit)) | ((read spvar slot(spvar current slot + 1) & make full mask(spvar bits (32 spvar current bit))) << (32 spvar current bit)); &#x9; //below is a breakdown of the line above, with each step done one at a time instead of all at once this however increases codesize the below code is to explain how it all works tho &#x9; //spvar tmp = read spvar slot(spvar current slot + 1); // read the spvar slot coming after the initial one we used to spvar tmp from flash, we need to maintain the data we've read thus far, but also add on what we have in flash for the next spvar &#x9; //spvar tmp = spvar tmp & make full mask(spvar bits (32 spvar current bit)); // extract the bits we need need (the ones that didn't fit in the previous spvar) &#x9; //spvar tmp = spvar tmp << (32 spvar current bit); // move the bits into their original position, they were stored at the beginning of the new spvar but belong at the top of the currently read value &#x9; //spvar current value = (spvar current value & make full mask(32 spvar current bit)) | spvar tmp; // put all bits together again with the part read from the first spvar cleaned up to only include the bits from this variable/value and not all bits set in the upper range like they normally are &#x9;}&#x9; &#x9;spvar current bit += spvar bits; // move up the counter of next available bit to where we are will be reading data from next &#x9;spvar current value = spvar current value & make full mask(spvar bits); // extract all bits included for this value and discard any other bits &#x9;if (spvar current bit >= 32) { &#x9; spvar current slot++; // move to the next spvar slot &#x9; spvar current bit = 32; // remove 32 from the spvar current bit tracker since we've gone beyond what we can do here &#x9;} &#x9; &#x9;if (is signed2(min, max)) { // check if the value can be negative and handle it accordingly &#x9; spvar current value = unpack i(spvar current value, spvar bits); // restore the signed, possibly negative value &#x9;} &#x9; &#x9;if (spvar current value < min || spvar current value > max) { // check if the value is below our specified min or above our specified max, if so return the default value instead &#x9; return def; // this can be changed to min instead as a reasonable default with the default parameter being removed if you don't need to have a override value for the default when out of range, that will save a bit of code size &#x9;} &#x9; &#x9;// return the retrieved value to the user since it's within the expected range &#x9;return spvar current value; }