Persistent Memory allows you to store values to reuse them at a later time when powering on the T2 again and read settings of an Interactive Configuration.
The available functions:
Init: pmem_load
Read: pmem_read
Write: pmem_save pmem_write
ATTENTION: None of the pmem_ functions should be called on every interaction of main.
To use the pmem_ functions you need to know the Storage Size of the Data Types of your variables.
Not caring about their sizes will result in getting wrong values back when trying to read them from pmem again.
Each slot of the Titan Two has 128 bytes (0..127) of PMEM storage available.
How many variables/data you can store into this space depends on the data types you want to store.
A bit is the smallest unit of storage. A bit stores just a 0 or 1.
1 bytes is a group of 8 bits (0..7). 2 bytes == 16 bits, 4bytes == 32 bits.
You can think of the PMEM storage like a table consisting of 128 rows with 8 columns.
Byte Offset | Bit offsets | |||||||
---|---|---|---|---|---|---|---|---|
0 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
2 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
.. | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
127 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
You can read and write this table only by byte_offset(rows) directly, not by bit_offset(columns).
Example:
The size of a variable of type fix32 is 4 bytes, 32bits : fix32 antirecoilX=13.5;
You can store it starting at any byte_offset.row you want, but because of its size it will write to the next 3 rows too. This limits the valid starting byte_offsets to 0..124, as writing it to 124 will write to 124,125,126,127 too.
A byte_offset.row can only be used by a single variable. The start byte_offset + the size of the data type can not overlap with the starting byte_offset.row of another one.
When storing a 4 byte size variable to byte_offset 0 (uses 0,1,2,3) the next available byte_offset.row is 4.
To store a variable of type int16 (2 byte==16bits) you can't use 0,1,2,3 as its already in use, but you don't have to use 4, you can use any other starting byte_offset.row where all next 16bits are not already in use.
To store a boolean (0 or 1) you need 1 byte==8 bits. This sounds wrong at first. You have to remember we can write to the byte_offsets.rows only, not to a single bit_offset.
You can use a single variable to hold 8 bits(8bits==1byte) and write this variable to a byte_offset.row.
The downside of this is you need a bit of code to handle this correctly.
If you have enough unused byte-offsets.rows available it is easier to just use one byte offset for one boolean (like an on/off toggle flag).
ATTENTION: None of the pmem_ functions should be called on every interaction of main.
To be able to access the persistent memory contents you have to use the function pmem_load first.
pmem_load() and pmem_read(…) should be called once at the init section of the script
and
pmem_write(…) and pmem_save() only after a change of a values of a variable you want to update in pmem.
On pmem_read you prefix your variable with & | pmem_read(1,&yourvariable); |
On pmem_write you do NOT use & | pmem_write(1,yourvariable); |
Rapid Fire example with saving/loading speed adjustments and on/off toggle state
bool RapidFire; // 1 byte == 8bits uint16 RFPress; // 2 bytes==16bits uint16 RFRelease;// 2 bytes==16bits init { pmem_load(); // this loads the memory correlated with the current memory slot pmem_read(0,&RapidFire); // rapid fire on/off state, byteoffset 0, 8bit (0) pmem_read(1,&RFPress); // rapid fire press time, byteoffset 1, 16bit (1+2) pmem_read(3,&RFRelease); // rapid fire release time, byteoffset 3, 16bit (3+4) if (RFPress==0) RFPress=20; // use maximum speed when no value if (RFRelease==0) RFRelease=20; // use maximum speed when no value // info output to GTuner IV Output Panel printf("Rapid Fire state (1=On,0=Off) : %d , Press Time: %d, Release Time: %d" ,RapidFire,RFPress,RFRelease); } main { // hold L2/LT & press DPad_UP to toggle rapid fire if (get_actual(BUTTON_8) && event_active(BUTTON_10)) { RapidFire=!RapidFire; // invert current state printf("Rapid Fire is now: %d",RapidFire); pmem_write(0,RapidFire); // write to pmem, is not saved yet pmem_save(); // save it now } // hold L2/LT & press DPad_Left to increase speed / decrease wait & press time if (get_actual(BUTTON_8) && event_active(BUTTON_12)) { RFRelease=RFPress=clamp(RFPress -=10,20,1000); // limit values to 20..1000 printf("Rapid Speed is now: %d | %d",RFPress,RFRelease); pmem_write(1,RFPress); // write to pmem, is not saved yet pmem_write(3,RFRelease); // write to pmem, is not saved yet pmem_save(); // save it now } // hold L2/LZ & press DPad_Right to decrease speed/increase wait&press time if (get_actual(BUTTON_8) && event_active(BUTTON_13)) { RFRelease=RFPress=clamp(RFPress +=10,20,1000); // limit values to 20..1000 printf("Rapid Speed is now: %d | %d",RFPress,RFRelease); pmem_write(1,RFPress); // write to pmem, is not saved yet pmem_write(3,RFRelease); // write to pmem, is not saved yet pmem_save(); // save it now } // when enabled and fire button is active run rapid fire combo if (RapidFire && get_actual(BUTTON_5)) combo_run(cRapidFire); } combo cRapidFire { set_val(BUTTON_5,100); wait(RFPress); // press button set_val(BUTTON_5,0); wait(RFRelease); // release button }