Dealing with negative variable sign extension

LarrylLix

Senior Member
After having some problems with negative temperatures I attempted to investigate how the CAI WC PLC handled negative integers conversion back and forth between 32 bit to 16 bit variables.
 
Well it turns out that a negative integer represented as a 32 bit two's  complement number converts to a 16 bit variable just fine due to simple truncation of the upper 16 bits works out mathematically correct.  This math, of course only works for integers closer to zero (making use of less then 16 bits)  than +3276710 (0xFFFF 7FFF) and -3276810 (0xFFFF 8000)
 
However, conversion in reverse, becomes a problem for the WC PLC. 16 bit signed values are not converted correctly when being converted from 16 bit to 32 bit or being sent elsewhere (using the WEBSET opcode) to get data into my ISY994i home automation computer.  My  ISY994i seemed to be receiving some really weird and incorrect results into it's assigned variable locations.
 
Some investigation proved the conversion process has forgotten to extend the sign bit when converting from 16 bit to 32 bits. The conversion works just fine when values are positive  or used as unsigned representations but when my temperature reading goes below zero it becomes a problem.
 
Here's an sample math demo of this
 
Converting 32 bit value  --> 16 bit value (eg: SET RAM1L RAM2)
-12810 = 0xFFFF 8000   -->  0x8000 = -12810
 
Converting 16 bit value --> 32 bit value (eg: SET RAM2 RAM1L)
-12810 = 0x8000            --> 0x0000 8000 = +3276910  an incorrect result !!!
 
To correct this problem here is a work there is a workaround, inside the test code (below), I set up to verify what was happening. It should also be noted I tried many different register data massaging methods to identify any results along the way. I have included a snapshot of the variable results, showing negative four degrees (-4)  found in my ISY994i HA controller to demonstrate the result of many data automatic conversions in the CAI WC.
 
My solution:
-Copy 16 bit register to a 32 bit register,
       SET  RAM5  RAM4H
 
-Test the original value for negative
       TSTGE  RAM4H  0x8000
 
If negative extend the sign bit into the 32 bit variable's  upper 16 bits
       ORB  RAM5  0xFFFF8000  RAM5
 
 
Enjoy!
 
START
TSYNC:
TSTLE CYEAR 2013
GOTO TSYNC
INIT:
DELAY 3000
SET RAM1 10
LOOP:
SUB RAM1 1 RAM1
WEBSET URL1 RAM1
WEBSET URL2 RAM1H
WEBSET URL3 RAM1L
RAM2:
SET RAM2 RAM1
WEBSET URL4 RAM2
RAM3L:
SET RAM3L RAM2
WEBSET URL5 RAM3L
RAM4H:
SET RAM4H RAM3L
WEBSET URL6 RAM4H
RAM5:
SET RAM5 RAM4H
WEBSET URL7 RAM5
TSTGE RAM4H 0x8000
ORB RAM5 0xFFFF8000 RAM5
WEBSET URL8 RAM5
DELAY 5000
GOTO LOOP
END
 
 
The behavior you describe is exactly what I would expect.
The premise that somehow RAMxH or RAMxL is somehow a "signed number" is wrong, and I don't believe I ever saw, anywhere, an implication that it was otherwise.
 
RAMxH is merely the "top 16 bits", and RAMxL the "bottom 16 bits".
 
Without an explicit cast (which doesn't exist in the WC), or a way to differentiate that a register needs to be handled somehow AS a signed number, the problem is fundamental and not restricted to the WC hardware or its running code.
 
To explicitly force all RAMxH and RAMxL as signed 16-bit integers will cause just as many errors and incorrect interpretations as treating them as unsigned.
How about the consistency of the RAMx0, RAMx1, RAMx2 and RAMx3 reference to 8-bit parts of the same variables? Should they be handled as signed or unsigned?
 
I think the bottom line is, as with ANY language/platform, you need to understand what you want to do, what the device is going to do, and how it's going to be interpreted. There isn't a "wrong" way as such, but there are many ways in which it can be wrongly interpreted or wrongly used.
 
For what it's worth, I encountered the same problem with the pressure sensors (BMP180), only there it was even worse.
The calibration constants in the device itself are all stored as 16-bit numbers. *SOME* of them are signed and *SOME* are unsigned!
Handling them all the same way *DOES* result in nonsense answers.
It comes back to knowing what I'm being offered and how I need to use it in the context.
 
Similarly the AM2321 humidity and temperature sensor. It returns a 16-bit (signed) value for temperature.
Since I pass that to my server unaltered (the WC doesn't do floating point, so I just pass it as it is as it's in 10ths of a degree already).
At the server end, and use
 
     if(t>=32768) t=32768-t
 
Simple enough, but it again - it's up to ME to know and interpret those 16 bits of data, it's not CAIs problem.
(That said, yes, a way to cast or tell the PLC how to handle a particular number would be useful)
 
Hopefully my post will help some other users attempting follow the User Guide.
 
It was exactly what I suspected and having a lot of past bit-twiddling experience from the 70s helped me understand what was happening. The biggest problem was finding out where the conversion problem was happening  without some known behaviour examination point.
 
Here is a cut'n paste from the 3.2.18a User Guide.
Code:
RAM1 32 bit signed integer general purpose RAM 1...8. Delay
RAM2 operator is not valid on these. Not displayed anywhere
RAM3
RAM4
RAM5
RAM6
RAM7
RAM8
RAM1H 16 bit signed integer general purpose RAM 1...8. Delay
RAM2H operator is not valid on these. Not displayed anywhere
RAM3H store in the SAME RAM location as RAM1-8 higher 16 bits
RAM4H
RAM5H
RAM6H
RAM7H
RAM8H
RAM1L 16 bit signed integer general purpose RAM 1...8. Delay
RAM2L operator is not valid on these. Not displayed anywhere
RAM3L store in the SAME RAM location as RAM1-8, lower 16 bits
RAM4L
RAM5L
RAM6L
RAM7L
RAM8L
RAM10 8 bit signed integer general purpose RAM 1...8. Delay
RAM20 operator is not valid on these. Not displayed anywhere
 
Thanks for the discussions, we are sure they will be helpful for others to use this feature on WebControl.
We implemented the bit access, byte access, and 16bit access to the RAMs. That is for helping users to have different ways to access those memory locations. 
 
Because user may want to tweak a bit, or a byte location, we internally treat those access all as unsigned, so that the sign change will not cause unexpected value change to the whole number.
 
If need to assign value with sign, then use the whole number, say
SET RAM5  T5
if T5 goes to negative number, RAM5 should be a 32 bit signed number with proper negative value in it.
 
If you have any suggestion to better handle them, please do let us know.  We are listening :)
 
LarrylLix said:
Here is a cut'n paste from the 3.2.18a User Guide.



RAM1H 16 bit signed integer
RAM1L 16 bit signed integer
RAM10 8 bit signed integer
 
Ahhh, ok, good catch. Seems the manual is misleading.
 
Ross and Larry,
 
Good catch.  I think RAM1H is signed, even we handle it like unsigned, when access the whole number it will be signed. But all others are unsigned for sure, otherwise, it will affect other bits and bytes.  As both of you already know, these storage share their memory locations with whole 32 bit RAMx. 
 
It is easier to treat whole RAMx as signed 32 bit number, and treat all partial access to RAMx as unsigned.
 
We changed the manual to reflect the fact that all access in part is treated as unsigned.
 
AS a note to that memory is not signed, unsigned, hexadecimal, decimal or otherwise. Memory and variables are only binary representations of values. How we interpret those binary images is what determines signed, unsigned, ASCII etc..
 
In light of that the variables need to not be defined for style but rather how the opcodes use each variable style.

WEBSET a b a: URL1-8, b: number or VAR, RAM or any other readable

Perhaps something like
WEBSET a b   a:URL1-8, b:32 bit value. See **Type conversion rules.
 
**Type conversion rules
- PLC opcodes are designed to work with 32 bit values. 8 and 16 bit operands my be used adhering to the conversion rules (below)
- when 32 bit values are assigned to 8 or 16 bit locations the MS 24 or 16 bits will be truncated and lost. Only the 8 or 16  LSbits will be used.
- when 8 bit and 16 bit values are assigned to locations or used ias 32 operand values will be left filled with all 0 bits. Negative values will not be negative sign extended and may present erroneous results. This may be corrected by various methods before or after operations or transmission to other devices.
- etc... etc... examples etc..
 
One good descriptive paragraph, showing the conversion expectations,  with references from Opcodes and affected areas could do the trick. 
 
IIRC some of the Motorola processors 6809, 68000 had an opcode to handle this.
 
SEX = sign extend. When transferring a 8 bit to a 16 register you could follow it up with a SEX opcode extending the MSbit of the LSByte into the MSByte.
 
Thus
 
SETX  A   # X=0x00FA,    A=0xFA
SEX X      #  X=0xFFFA
 
Or make it automatic
 
   SET RAM1 RAM3H   # RAM1=0xFFFA,  RAM3H=0xFA
 
    WEBSET URL1 RAM1H # sends 0xFFFA, RAM1H = 0xFA
 
but that presents problems if users want an unsigned value so we end up with manual conversion for user choice.
 
When negative values are required.
     SET RAM2 RAM1H
     TSTGE RAM1H 0x8000 (or SEX RAM2)
     ORB RAM2 0xFF00         "      "      "
     WEBSET URL1 RAM2
 
LarrylLix said:
IIRC some of the Motorola processors 6809, 68000 had an opcode to handle this.
 
SEX = sign extend. When transferring a 8 bit to a 16 register you could follow it up with a SEX opcode extending the MSbit of the LSByte into the MSByte.
 
Thus
 
SETX  A   # X=0x00FA,    A=0xFA
SEX X      #  X=0xFFFA
 
Or make it automatic
 
   SET RAM1 RAM3H   # RAM1=0xFFFA,  RAM3H=0xFA
 
    WEBSET URL1 RAM1H # sends 0xFFFA, RAM1H = 0xFA
 
but that presents problems if users want an unsigned value so we end up with manual conversion for user choice.
 
When negative values are required.
     SET RAM2 RAM1H
     TSTGE RAM1H 0x8000 (or SEX RAM2)
     ORB RAM2 0xFF00         "      "      "
     WEBSET URL1 RAM2
 
Could probably work with that.
How about a new opcode,     sex
It would take exactly two operands. In no particular order, #bits and register to change.
 
   set ram1 -1  # -1
   set ram2L ram1
   set ram3 ram2L     # ram3 now has 0xffff which is as close as a 16-bit var can come to -1
   sex ram3 16          # fix ram3 by copying "pseudo-sign" bit 15 into bits 16-31
                                 # ram3 now has 0xffffffff which is -1 in WC registers.
 
Similarly
 
   sex ram3 8      #  copy sign-bit from an 8-bit number (bit 7 copied to bits 8-31)
 
Since it's just an operand, I suggest permitting any "arbitary" number range to be so handled.
24 bit particularly, but we also come across other "signed numbers" - I've recently come across 12, 14, 19, 20, 21, 22 and 24 bit "signed numbers"
 
Thinking more on this the new opcode could be
 
SEX or SETX (set with sign eXtend)  (do we really like SEX?  :D )
 
so syntax may be
 
SET   RAM3 RAM1H   #RAM3=0x0000 8000, RAM1H=0x8000
 
SETX RAM3 RAM1H   #RAM3=0xFFFF 8000, RAM1H=0x8000
 
SETX RAM3 RAM13    #RAM3=FFFF FF85,  RAM13=0x85
 
 
If the Opcode were just improved to handle signed operands with automatic sign extension, how much hardship would it cause?
 
WEBSET URL1 RAM1H  # sends=0xFFFF A5A5,  RAM1H=0xA5A5
WEBSET URL1 RAM1H  # sends=0x0000 75A5,  RAM1H=0x75A5
 
I am not sure how the other opcodes are handling negative signed values
ADD RAM3 RAM1H RAM4  #RAM3=0x0000 0000, RAM1H=0xFFFF (-1), RAM4=????  I have not set out to experiment yet.
Most importantly, all the opcodes must be consistent and any exceptions defined.
 
I think when you set RAM or VAR as a whole number, it is being treated as signed, not unsigned.
Also AIP5-8 are signed 16 bit numbers.
 
How each parameter being treated should be same, does not matter WEBSET or just SET,
WEBSET when taking its 2nd parameter, that is signed number, does not matter that is 8bit, 16 bit or 32bit, they are treated as 32bit signed number and formatted in HTML code as signed long. 
 
However, the portions of RAM are all treated as unsigned for reason that user may do bit manipulation on them.  I understand memory location is memory location. But how we approach those locations will cause compiler to treat that memory location as signed or unsigned.  The variables inside firmware defines when you access that portion of RAM, it will be unsigned, or signed.
 
Back
Top