Storing a 32bit value in two 16 bit registers

I’m working with a developer who is writing the firmware for a motion control unit. We will be using modbus to communicate with the devices. Modbus basically stores values in registers on the device, and we can read from or write to these registers. In almost all cases, the values are 16bit unsigned integers. There are two cases where we have to deal with 32bit signed integers, because we’re tracking the number of steps taken by a motor, and these could be very large numbers. His suggestion is:

Usually for 32bit values you split the data into two 16 bit values and stick them in consecutive registers. Same idea for 32-bit floating point.

Something like this in C.

 int32_t intVal=0x12345678;
 uint16_t lowWord=(intVal & 0xFFFF);
 uint16_t highWord=((intVal>>16)) & 0xFFFF);
 //lowWord is 0x5678
 //highWord is 0x1234

I’d like to do this in Xojo but I don’t know enough C to really understand what he’s doing here. How would I split the data from a 32 bit value into two 16 bit values in Xojo, and vice versa? Also, he’s using hex values in this example. I’m waiting to hear back from him about whether the data stored in the registers would be decimal or hex.

You only need bitwise functions.

BitwiseAnd with &hFFFF for lower part.
And Bitwise.ShiftRight with 16 for higher part.

1 Like

So I don’t think I’m understanding how to do this.

The following code gets me a value of -1 in bottomValue and 32767 in topValue

var startValue as uint32 = 2147483647 //max 32 bit value
var bottomValue as int16 = BitwiseAnd(startValue,&hFFFF)
var topValue as int16 = ShiftRight(startValue,16)

Assuming I’m trying to store a value of 2147483647 in two 16bit registers, what should I expect the values of those registers to be?

In the C example above, intVal is 305419896 (Decimal). lowWord = 22136 and highWord = 4660. So I guess I’m not understanding two things:

  1. How to do the bitwise stuff to split up the data across the two registers
  2. How to convert the two registers back into the value of the 32bit integer

Go easy on me. my entire art school math and science requirement was satisfied with a single class called “Math for Artists” - that I didn’t even have to take because I was able to transfer a history of western science credit from my previous school! My grandfather and my kid got the math genes.

You are missing an operation for topValue, that is the BitwiseAnd:

var topValue as int16 = BitwiseAnd(ShiftRight(startValue,16), &hFFFF)

And you need uint16 as data type.

1 Like

Ok, so it’s now:

var startValue as uint32 = 2147483647 //max 32 bit value
var bottomValue as uint16 = BitwiseAnd(startValue,&hFFFF)
var topValue as uint16 = BitwiseAnd(ShiftRight(startValue,16), &hFFFF)

And my resulting values are:

bottomValue = 65535 (max value of a 16bit int)
topValue = 32767 (which is the same result as before)

What am I missing here? What should I expect to see in bottomValue and topValue after they’re split?

Unnecessary - the low word gets shifted out to the right into oblivion and zeroes should get shifted into the high word. It’s a shift, not a rotate.

Max uint32 value is 0xFFFFFFFF = 4294967295.

Those values are correct for 2147483647.

Get yourself a hex calculator :slight_smile: I like this one, because unlike many it can do fixed word sizes.

1 Like

Unsigned int32, yes. I’m using a uint32 here but it should probably be signed, to match the specs the developer has given me. My mistake. So in my head I was thinking max of 2147483647.

Well a calculator isn’t going to help me if I don’t understand how to combine those values to get back to 2147483647. Can someone explain in plain english how this works and what is happening here? I’ve been reading about this for the past three hours and still don’t get it.

If you start with a 32-bit value, then it has (gasp!) 32 bits. Any number up to 4 billion or so will fit in that and, viewed as binary, has 32 bits that may be either 0 or 1. So if you want the left 16 bits and the right 16 bits as separate numbers, you can get the right 16 bits by setting the left 16 bits to all zeroes and putting the result into a 16-bit variable. You get the left 16 bits by shifting the whole 32 bit number rightwards by 16 bits, with the leftmost bits being replaced by zeroes and the right 16 bits falling off the end into the bit bucket.

That’s it.

1 Like

A “grab it directly” approach could be

Var m64 As New MemoryBlock(8)   // 64 bit area for any sketch

m64.UInt32Value(0) = &h87654321 // The 32 bit value

Var lowWord, highWord As Uint16

#If TargetLittleEndian Then     // CPU aware
  lowWord = m64.UInt16Value(0)
  highWord = m64.UInt16Value(2)
#Else
  highWord = m64.UInt16Value(0)
  lowWord = m64.UInt16Value(2)
#EndIf

break

Thanks. That makes a lot of sense. I still don’t think I understand how to reverse it though, to get to 2147483647 from the 16 bit low and high variables

It took me longer to format this to paste it in here than it did to generate it.

Convert the following code into Xojo

 int32_t intVal=0x12345678;
 uint16_t lowWord=(intVal & 0xFFFF);
 uint16_t highWord=((intVal>>16)) & 0xFFFF);
Dim intVal As Int32 = &h12345678
Dim lowWord As UInt16 = intVal And &hFFFF
Dim highWord As UInt16 = (intVal >> 16) And &hFFFF

Xojo doesn’t use >> for shifting

You’re correct! Xojo uses the Bitwise.ShiftRight operator to perform a bitwise right shift. Here’s the updated code:

Dim intVal As Int32 = &h12345678
Dim lowWord As UInt16 = intVal And &hFFFF
Dim highWord As UInt16 = Bitwise.ShiftRight(intVal, 16) And &hFFFF

This should give you the same result as the original C++ code.

Please explain the code

Certainly! Let me break down the code line by line:

Dim intVal As Int32 = &h12345678

This line declares a variable intVal of type Int32 (a 32-bit integer), and assigns it the hexadecimal value &h12345678.

Dim lowWord As UInt16 = intVal And &hFFFF

This line declares a variable lowWord of type UInt16 (a 16-bit unsigned integer), and assigns it the result of a bitwise AND operation between intVal and the hexadecimal value &hFFFF (which is equivalent to binary 1111111111111111). This operation effectively extracts the lower 16 bits (the “low word”) of intVal.

Dim highWord As UInt16 = Bitwise.ShiftRight(intVal, 16) And &hFFFF

This line declares a variable highWord of type UInt16, and assigns it the result of two operations. The first operation is a bitwise right shift of intVal by 16 bits, which effectively moves the upper 16 bits (the “high word”) of intVal into the lower 16 bits. The second operation is a bitwise AND with &hFFFF, which effectively clears the upper 16 bits and leaves only the lower 16 bits. This gives us the upper 16 bits (the “high word”) of the original intVal.

So, overall, these three lines of code extract the lower 16 bits and upper 16 bits of a 32-bit integer value, using bitwise operations to mask and shift the bits as needed.

Reverse the process to end up with an Int32

Sure thing! Here’s how you can reverse the process to combine the lowWord and highWord variables into an Int32:

Dim lowWord As UInt16 = &h5678
Dim highWord As UInt16 = &h1234

Dim intVal As Int32 = Bitwise.ShiftLeft(highWord, 16) Or lowWord

This code assumes you have already extracted the lower 16 bits (lowWord) and upper 16 bits (highWord) of an Int32 value, and assigns them to variables of type UInt16.

The first line declares a variable lowWord and assigns it the value &h5678 (which is the hexadecimal representation of the lower 16 bits).

The second line declares a variable highWord and assigns it the value &h1234 (which is the hexadecimal representation of the upper 16 bits).

The third line uses bitwise operations to combine lowWord and highWord into a single Int32. The Bitwise.ShiftLeft operator shifts the bits in highWord 16 positions to the left, effectively moving them to the upper 16 bits of the resulting Int32. The Or operator then combines the shifted highWord with lowWord, effectively combining the upper and lower 16 bits into a single 32-bit value.

So, overall, these three lines of code reverse the process of extracting the lower and upper 16 bits from an Int32, and combine them back into a single Int32 value.

Var m64 As New MemoryBlock(8) // 64 bit area for any sketch

Var lowWord, highWord As Uint16

highWord = &h8765
lowWord = &h4321

#If TargetLittleEndian Then
  m64.UInt16Value(0) = lowWord
  m64.UInt16Value(2) = highWord
#Else
  m64.UInt16Value(0) = highWord
  m64.UInt16Value(2) = lowWord
#EndIf

Var get32bit As UInt32 = m64.UInt32Value(0) // &h87654321
1 Like

If you have:

Var  upper, lower as uint16, total as uint32

// assume these have values in  them, then:

total = ShiftLeft (upper, 16) + lower

This is conceptually correct but perhaps not actually, as it sems by default ShiftLeft and Right work on 64 bit values.

Funny - My “I don’t think I understand how to reverse” was in reference to the bitwise method.

I think I more intuitively understand what you’re doing here and was in the middle of posting almost this exact code below, to demonstrate how I’d reverse it. Thanks!

1 Like

There’s an error there, it is

highWord = &h8765
lowWord = &h4321

or

Var lowWord, highWord As Uint16

highWord = &h8765
lowWord = &h4321

Var get32bit As UInt32 = highWord * &h10000 + lowWord // &h87654321

Being unsigned ints are key here

Decomposing also using math:

Var lowWord, highWord As Uint16

Var the32bit As UInt32 = &h87654321

highWord = the32bit \ &h10000
lowWord = the32bit            // Xojo does silent truncations

// highWord = &h8765
// lowWord = &h4321

break

Again, unsigned math.

1 Like