🔍
v1.3.8

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;
Watch out for integer overflow: When multiplying large values, intermediate results can overflow. If 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;
Note: TSharkRex uses = 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

ABA AND BA OR BNOT A
FALSEFALSEFALSEFALSETRUE
FALSETRUEFALSETRUETRUE
TRUEFALSEFALSETRUEFALSE
TRUETRUETRUETRUEFALSE

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;
Dual behavior: When 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;
Tip: Combining 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;
Danger: Pointer arithmetic (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)
Tip: When in doubt, use parentheses. They make your intent explicit and prevent subtle bugs. This is especially important in compound boolean expressions:
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:

  1. () - Parentheses
  2. NOT, BNOT - Unary negation
  3. *, /, MOD - Multiplicative
  4. +, - - Additive
  5. SHL, SHR - Bit shifts
  6. =, <>, <, >, <=, >= - Comparison
  7. AND, BAND - Logical/bitwise AND
  8. OR, BOR - Logical/bitwise OR

Summary

TSharkRex operators cover all the needs of embedded control programming. The key things to remember: