Operators
TSharkRex provides a complete set of operators for arithmetic, comparison, logic, bitwise manipulation, pointer access, and individual bit access. This chapter covers every operator available in the language, with practical examples showing how they are used in real recipes.
Arithmetic Operators
Standard arithmetic operators work on integer and floating-point types. Integer division truncates (rounds toward zero).
| Operator | Description | Example |
|---|---|---|
+ |
Addition | result := 10 + 5; |
- |
Subtraction | result := 100 - 10; |
* |
Multiplication | result := 2 * 10; |
/ |
Division (integer truncation) | result := 200 / 4; |
MOD or % |
Modulo (remainder) | remainder := 10 MOD 3; |
Practical Examples
VAR
rawVoltage : INT;
scaledVoltage : INT;
remainder : INT;
average : INT;
sum : DINT := 0;
count : INT := 0;
END_VAR;
// Scale a raw ADC reading: 0-4095 maps to 0-16000 mV
scaledVoltage := rawVoltage * 16000 / 4095;
// Check if a counter is even or odd
remainder := count MOD 2;
// remainder = 0 means even, 1 means odd
// Running average
sum := sum + rawVoltage;
count := count + 1;
average := sum / count;
rawVoltage is 4095 and you multiply by 16000, the
intermediate result is 65,520,000 - which exceeds the INT range (32,767). Use
DINT for intermediate calculations when working with large values.
VAR
rawValue : INT := 4000;
result_bad : INT;
result_good : DINT;
END_VAR;
// BAD: intermediate overflow in INT
result_bad := rawValue * 16000 / 4095; // Overflows!
// GOOD: use DINT for the calculation
result_good := S_EXT(rawValue, DINT) * 16000 / 4095; // Correct
Comparison Operators
Comparison operators return BOOL (TRUE or FALSE).
They are most commonly used in IF and WHILE conditions.
| Operator | Description | Example |
|---|---|---|
= |
Equal to | IF A = B THEN |
<> |
Not equal to | IF A <> 0 THEN |
> |
Greater than | IF voltage > 12000 THEN |
< |
Less than | IF temp < 50 THEN |
>= |
Greater than or equal | IF count >= 10 THEN |
<= |
Less than or equal | IF level <= MAX THEN |
Practical Examples
VAR
batteryVoltage : INT; // in millivolts
engineTemp : INT; // in degrees C
speed : INT; // in km/h
lowBattWarn : BOOL;
overVoltWarn : BOOL;
normalTemp : BOOL;
isMoving : BOOL;
END_VAR;
// Voltage monitoring
IF batteryVoltage < 11500 THEN
lowBattWarn := TRUE; // Low battery warning
ELSIF batteryVoltage > 14500 THEN
overVoltWarn := TRUE; // Overvoltage warning
END_IF;
// Temperature range check
IF engineTemp >= 90 AND engineTemp <= 110 THEN
normalTemp := TRUE; // Normal operating temperature
END_IF;
// Speed threshold
IF speed <> 0 THEN
isMoving := TRUE; // Vehicle is moving
END_IF;
= for equality comparison (not
== as in C). Assignment uses :=. This distinction prevents the
common C bug of writing if (x = 5) when you meant if (x == 5).
Logical Operators (Boolean)
Logical operators combine boolean expressions. They are used primarily in
IF conditions to build compound tests.
| Operator | Description |
|---|---|
AND |
Logical AND - TRUE only when both operands are TRUE |
OR |
Logical OR - TRUE when at least one operand is TRUE |
NOT |
Logical NOT - inverts TRUE to FALSE and vice versa |
Truth Tables
| A | B | A AND B | A OR B | NOT A |
|---|---|---|---|---|
| FALSE | FALSE | FALSE | FALSE | TRUE |
| FALSE | TRUE | FALSE | TRUE | TRUE |
| TRUE | FALSE | FALSE | TRUE | FALSE |
| TRUE | TRUE | TRUE | TRUE | FALSE |
Practical Examples
VAR_SIGNAL
SIGNAL_TANDNING : BOOL;
SIGNAL_HELLJUS : BOOL;
SIGNAL_HALVLJUS : BOOL;
SIGNAL_BLINKER_V : BOOL;
END_VAR;
VAR
ignAndHigh : BOOL;
blinkerOrHigh : BOOL;
ignOff : BOOL;
complexCond : BOOL;
END_VAR;
// Combine conditions
IF SIGNAL_TANDNING AND SIGNAL_HELLJUS THEN
ignAndHigh := TRUE; // Ignition is on AND high beam is active
END_IF;
// Either condition
IF SIGNAL_BLINKER_V OR SIGNAL_HELLJUS THEN
blinkerOrHigh := TRUE; // Left blinker or high beam is active
END_IF;
// Negate
IF NOT SIGNAL_TANDNING THEN
ignOff := TRUE; // Ignition is OFF - enter sleep mode
END_IF;
// Complex compound condition
IF SIGNAL_TANDNING AND (SIGNAL_HELLJUS OR SIGNAL_HALVLJUS) AND NOT SIGNAL_BLINKER_V THEN
complexCond := TRUE; // Ignition on, some headlight active, left blinker not active
END_IF;
AND, OR, and NOT
are applied to non-boolean types (BYTE, INT, DINT),
they operate bitwise. For explicit bitwise operations, prefer
BAND, BOR, and BNOT to make your intent clear.
Bitwise Operators
Bitwise operators manipulate individual bits within integer values. These are essential for CAN data parsing, register manipulation, and flag management.
| Operator | Description | Example |
|---|---|---|
BAND |
Bitwise AND | result := value BAND 0xFF; |
BOR |
Bitwise OR | result := flags BOR 0x01; |
BNOT |
Bitwise NOT (complement) | result := BNOT mask; |
(a BOR b) BAND BNOT(a BAND b) |
Bitwise XOR (exclusive OR) | result := (a BOR b) BAND BNOT(a BAND b); |
SHL(val, n) |
Shift left by n bits | result := SHL(value, 4); |
SHR(val, n) |
Shift right by n bits | result := SHR(value, 4); |
Practical Examples
VAR
canByte : BYTE;
highNibble : BYTE;
lowNibble : BYTE;
flags : BYTE := 0x00;
END_VAR;
// Extract high and low nibbles from a byte
highNibble := SHR(canByte, 4) BAND 0x0F; // Upper 4 bits
lowNibble := canByte BAND 0x0F; // Lower 4 bits
// Set a flag bit (bit 2)
flags := flags BOR 0x04; // 0x04 = 0000_0100
// Clear a flag bit (bit 2)
flags := flags BAND BNOT 0x04;
// Toggle a flag bit (bit 2) using XOR
// Note: ^ is the dereference operator in TSharkRex, NOT XOR.
// Simulate XOR using: (a BOR b) BAND BNOT(a BAND b)
flags := (flags BOR 0x04) BAND BNOT(flags BAND 0x04);
VAR
CANRECV : CAN_RX;
RECVDATA : ARRAY[0..7] OF BYTE;
combined : INT;
msb : BYTE;
lsb : BYTE;
END_VAR;
// Receive a CAN frame
CANRECV(ENABLE := TRUE, ID := 0x320, EXT := FALSE,
DATALENGTH := 8, DATA := RECVDATA);
// Combine two bytes into a 16-bit value (big-endian CAN data)
IF CANRECV.UPDATED THEN
msb := RECVDATA[2];
lsb := RECVDATA[3];
combined := SHL(Z_EXT(msb, INT), 8) BOR Z_EXT(lsb, INT);
END_IF;
SHL/SHR with BAND
is the standard pattern for extracting multi-byte values from CAN frames. You will use
this pattern constantly when writing CAN libraries.
Assignment Operators
| Operator | Description | Example |
|---|---|---|
:= |
Assignment - stores a value in a variable | counter := 0; |
=> |
Post-assignment - captures a function block output | TMR(IN := TRUE, Q => result); |
Assignment Examples
VAR
counter : DINT := 0;
speed : INT;
isActive : BOOL;
END_VAR;
// Standard assignment
counter := counter + 1;
speed := 100;
isActive := TRUE;
Post-Assignment with Function Blocks
The => operator is used when calling a function block to capture its output
into a variable in a single statement:
VAR
debounce : TON;
isStable : BOOL;
elapsed : TIME;
END_VAR;
VAR_SIGNAL
SIGNAL_TANDNING : BOOL;
END_VAR;
// Call the timer and capture outputs in one statement
debounce(IN := SIGNAL_TANDNING, PT := T#200ms, Q => isStable, ET => elapsed);
// Equivalent to:
debounce(IN := SIGNAL_TANDNING, PT := T#200ms);
isStable := debounce.Q;
elapsed := debounce.ET;
Pointer Operators
TSharkRex supports pointers for advanced memory manipulation, particularly useful when working with variable-length CAN data or building protocol handlers.
| Operator | Description | Example |
|---|---|---|
@ |
Address-of - creates a pointer to a variable | pData := @buffer[0]; |
^ |
Dereference - reads the value a pointer points to | value := pData^; |
Practical Examples
VAR
buffer : ARRAY[0..7] OF BYTE;
pData : POINTER TO BYTE;
value : BYTE;
END_VAR;
// Point to the start of the buffer
pData := @buffer[0];
// Read the value at the pointer
value := pData^; // Same as buffer[0]
// Read next byte by pointing to next element
pData := @buffer[1];
value := pData^; // Same as buffer[1]
// Copy CAN data into a local buffer using array indexing
// (pointer arithmetic is not supported - use array indices instead)
VAR
rxData : ARRAY[0..7] OF BYTE;
RECVDATA : ARRAY[0..7] OF BYTE;
CANRECV : CAN_RX;
i : BYTE;
END_VAR;
// Receive a CAN frame
CANRECV(ENABLE := TRUE, ID := 0x320, EXT := FALSE, MSG_COUNT := 5, DATA := RECVDATA);
IF CANRECV.AVAILABLE > 0 THEN
FOR i := 0 TO 7 DO
rxData[i] := RECVDATA[i];
END_FOR;
END_IF;
ptr + 1) is not supported
in TSharkRex v1.3.8. Use array indexing instead. When you do use pointers, always ensure
they point to valid memory. There is no runtime bounds checking.
Bit Access
TSharkRex allows you to read and write individual bits within any integer variable using dot notation. Bit 0 is the least significant bit (LSB).
// Syntax examples:
// myVar.0 - Access bit 0 (LSB) of myVar
// myVar.7 - Access bit 7 (MSB for BYTE) of myVar
// DATA[3].2 - Access bit 2 of array element 3
// Reading a bit:
// flag := myByte.0;
// Writing a bit:
// myByte.3 := TRUE;
Practical Examples
VAR
statusByte : BYTE;
canData : ARRAY[0..7] OF BYTE;
headlightOn : BOOL;
engineRunning : BOOL;
END_VAR;
// Read individual status bits from a CAN byte
headlightOn := canData[3].5; // Bit 5 of byte 3
engineRunning := canData[3].0; // Bit 0 of byte 3
// Set a specific bit
statusByte.2 := TRUE; // Set bit 2
// Clear a specific bit
statusByte.7 := FALSE; // Clear bit 7
// Toggle based on condition
statusByte.0 := NOT statusByte.0; // Toggle bit 0
Bit access is extremely common when parsing CAN frames, where individual bits often represent different vehicle signals:
VAR_SIGNAL
SIGNAL_HELLJUS : BOOL;
SIGNAL_HALVLJUS : BOOL;
SIGNAL_DIMLJUS : BOOL;
SIGNAL_TANDNING : BOOL;
END_VAR;
VAR
CANRECV : CAN_RX;
RECVDATA : ARRAY[0..7] OF BYTE;
END_VAR;
// Parse lighting status from CAN frame 0x320, byte 3
// Bit 0: ignition
// Bit 1: fog lights
// Bit 5: low beam
// Bit 6: high beam
CANRECV(ENABLE := TRUE, ID := 0x320, EXT := FALSE,
MSG_COUNT := 5, DATA := RECVDATA);
IF CANRECV.AVAILABLE > 0 THEN
SIGNAL_TANDNING := RECVDATA[3].0;
SIGNAL_DIMLJUS := RECVDATA[3].1;
SIGNAL_HALVLJUS := RECVDATA[3].5;
SIGNAL_HELLJUS := RECVDATA[3].6;
END_IF;
Operator Precedence
When multiple operators appear in a single expression, TSharkRex evaluates them according to standard precedence rules. Multiplication and division are performed before addition and subtraction.
VAR
result : INT;
END_VAR;
result := 10 + 10 * 5; // = 60 (multiplication first: 10 + 50)
result := (10 + 10) * 5; // = 100 (parentheses override: 20 * 5)
VAR
a : BOOL;
b : BOOL;
c : BOOL;
doSomething : BOOL;
END_VAR;
// Clear with parentheses
IF (a AND b) OR c THEN
// Unambiguous: both a and b must be true, OR c alone is true
doSomething := TRUE;
END_IF;
IF a AND (b OR c) THEN
// Unambiguous: a must be true, AND either b or c
doSomething := TRUE;
END_IF;
General precedence from highest to lowest:
()- ParenthesesNOT,BNOT- Unary negation*,/,MOD- Multiplicative+,-- AdditiveSHL,SHR- Bit shifts=,<>,<,>,<=,>=- ComparisonAND,BAND- Logical/bitwise ANDOR,BOR- Logical/bitwise OR
Summary
TSharkRex operators cover all the needs of embedded control programming. The key things to remember:
- Use
:=for assignment,=for comparison. - Use
BAND/BOR/BNOTfor explicit bitwise operations. AND/OR/NOTact as bitwise on non-boolean types.- Bit access with
.Nis the most readable way to extract CAN signal bits. - Use parentheses to make operator precedence explicit.
- Watch for integer overflow in arithmetic - use
DINTfor large intermediate values.