Siemens S7 LAR1 Pointer: SLD 3 and SLD 2 Conversion Explained

David Krause12 min read
HMI ProgrammingSiemensTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

1. Overview: Why a Byte Pointer Must Be Reformatted for LAR1

In a SIMATIC S7-300/S7-400 CPU, the address registers AR1 and AR2 do not hold a raw byte number. They hold a bit-addressable pointer in which the byte address has already been multiplied by eight. When a programmer stores a plain byte address (or a record index) in a double word such as MD0 and then issues LAR1, the CPU loads that raw value into the address register without reformatting it. The result is that indirect accesses such as L DBW [AR1,P#0.0] read the wrong memory location.

The classic fix is two SLD (Shift Left Double) operations before LAR1:

L  MD0     // raw byte address or record index stored as DWORD
SLD 3      // shift out the 3-bit bit-address field
SLD 2      // scale by the record size (DWORD = 4 bytes = 2 bits)
LAR1       // load the formatted pointer into AR1

Each SLD n instruction is a logical shift left of accumulator 1 by n bit positions, with zeros shifted in from the right and bits shifted out at the left discarded. The pair of shifts is functionally equivalent to SLD 5 (a single shift of five positions) but is written as two statements so the two logical operations — bit-address removal and record scaling — are visible in the code.

This article documents the bit layout, the math behind each shift, working STL examples, and the verification steps an engineer should run on a real CPU (or PLCSIM) to confirm the pointer is correct.

2. The 32-Bit Pointer Format Used by AR1 / AR2

The internal representation of an S7-300/400 address-register pointer is documented in the SIMATIC S7-300/400 Programming Manuals and reproduced in the table below. For area-internal pointers (intra-DB or intra-bit-memory) the bit fields are interpreted as follows.

Bit position Width Meaning Typical value
31 … 24 8 bits Reserved / DB number (cross-area only) 0 for intra-DB
23 … 04 20 bits Byte address (left-shifted by 3, i.e. byte value × 8) 0 … 8 388 608 (1 MB)
03 … 01 3 bits Bit address (0 … 7) 0
00 1 bit Reserved / fixed 0

For example, the pointer that addresses DB byte 100, bit 3 is built as:

P#100.3  ==  0000 0000 0000 0000 0000 0000 0110 0100 0001 1000
            |                byte 100 × 8              |bit 3|

The bit-address field occupies the three least-significant bits. Anything stored in MD0 that is to become the byte address must first be shifted left by 3 so that the bit-address field is empty and the byte address lands in bits 3 … 31.

3. LAR1, LAR2 — Loading an Address Register

According to the SIMATIC S7-300 Instruction List, the LAR1 and LAR2 instructions load the contents of accumulator 1 into address register 1 or 2. The CPU does not reformat the value; whatever the user supplies is the literal bit pattern that the register will hold. Because every memory-indirect access — L, T, L DBB, T DBW, ==I, LAR1 chained access, etc. — interprets AR1 as the bit-addressable layout above, supplying a raw byte number without a shift produces a misaligned read.

Field note: LAR1 has no error flag. If the pointer is malformed, the next memory-indirect access quietly returns or writes the wrong cell. Always verify in PLCSIM or with a watch table on the first build.

4. The SLD Instruction

SLD <n> shifts the contents of ACCU 1 left by n bit positions, where n is a constant in the range 0 … 32. The shift is logical: bits shifted out at the high end are lost, and zeros are shifted in at the low end. Status-word bits CC 1 and CC 0 reflect the result, and OV is set if the shift count is invalid.

Mnemonic Operands Effect on ACCU 1 Status bits touched
SLD 0 No-op; result unchanged CC1/CC0/OV unchanged
SLD 3 constant 3 ×8 (3 binary zeros appended on the right) CC1/CC0 set per result sign; OV=0
SLD 2 constant 2 ×4 (2 binary zeros appended on the right) CC1/CC0 set per result sign; OV=0
SLD 5 constant 5 ×32 (functionally equivalent to SLD 3 + SLD 2) CC1/CC0 set per result sign; OV=0

Stack effect: SLD is a pure ACCU 1 operation. ACCU 2, ACCU 3, ACCU 4 and the address registers are untouched. This makes it safe to chain SLD 3 immediately followed by SLD 2 with no intermediate T (transfer).

5. Why the First Shift Must Be SLD 3

The bit-address field of an S7 pointer is exactly three bits wide (values 0 … 7). To reuse a value stored in MD0 as a byte pointer, those three bits must be vacated — shifted out — so that subsequent memory-indirect decoding does not interpret any stray low-order bits as a bit offset.

Concretely, if the programmer wants to address DB byte 100 via LAR1 and later read it with L DBB [AR1,P#0.0], the correct bit pattern for AR1 is:

byte 100  →  100 × 8  →  800 decimal  →  0x0000_0320  (binary ...0000 0011 0010 0000)

Multiplying by 8 is exactly what SLD 3 does in one instruction. If MD0 already contains the formatted value 0x00000320, the SLD 3 is harmless (it would simply multiply by 8 again, producing 0x0000_1900 = byte 400). For this reason the canonical pre-AR1 sequence always starts from a raw byte number or a raw record index, never from a pre-formatted pointer.

6. Why the Second Shift Is SLD 2

The second SLD 2 exists to convert a record index into a byte address before the bit-address shift. In a typical pattern, a DB holds an array of DWORD records. If MD0 contains the record number (0, 1, 2 …) rather than the byte offset, the byte address of record N is:

byte_offset = N × 4      (because each DWORD is 4 bytes)

Multiplying by 4 is exactly SLD 2. After both shifts the accumulator contains:

ACCU1  =  (MD0 × 4) × 8  =  MD0 × 32
        =  record_index × 32   (which is record_index × 4 bytes × 8)

This is the value LAR1 needs to point at the start of the N-th DWORD in the DB. The two-shift decomposition is purely didactic: writing SLD 5 achieves the identical accumulator content, but splitting the shift makes the formula index × 4 × 8 explicit in the source.

Order independence: SLD 3 followed by SLD 2 produces the same result as SLD 2 followed by SLD 3. The CPU executes both instructions on ACCU 1 sequentially; multiplication by 2 is commutative, so the order is purely a readability choice. Convention in SIMATIC code is to perform the bit-address shift first.

7. Worked Example — Indexed Access to a DB of Records

Consider DB100, which holds 50 DWORD records starting at byte 0 (length 200 bytes total). The program must read record N (stored in MD0) into MD100:

//  Record N is at byte offset N × 4 in DB100
//  AR1 must therefore hold (N × 4 × 8) in the bit-addressable format.

      L     MD0            //  raw record index N
      SLD   3              //  vacate bit-address field (×8)
      SLD   2              //  scale by DWORD size (×4)
      LAR1                  //  AR1 = N × 32, points at record N, bit 0

      L     DBD [AR1,P#0.0]
      T     MD100

To verify by hand, set MD0 = 5. The expected byte offset is 5 × 4 = 20. The expected AR1 value is 20 × 8 = 160 (decimal) = 0x0000_00A0. Trace through:

  1. L MD0 → ACCU 1 = 5 (0x00000005).
  2. SLD 3 → ACCU 1 = 40 (0x00000028). The 3 low-order bits are gone.
  3. SLD 2 → ACCU 1 = 160 (0x000000A0). The byte address 20 is now in bits 3 … 31.
  4. LAR1 → AR1 = 0x000000A0.

A subsequent L DBD [AR1,P#0.0] then reads DB100.DBD20, which is exactly the 6th DWORD in the array (record index 5, zero-based).

8. Worked Example — Pointer Already in P# Form

If MD0 is loaded elsewhere with L P#200.4 (i.e. it already contains a properly formatted pointer 0x00000640), the SLD instructions must not be used — they would corrupt the pointer by multiplying the byte address by 32. The correct sequence is:

      L     P#200.4
      LAR1                  //  AR1 = 0x00000640 — bit-addressable pointer

      L     DBD [AR1,P#0.0]
      T     MD100

The rule is therefore: SLD is needed only when the value in MD0 is a raw byte number or record index, not when it is a P#-formatted pointer.

9. Verification on a Live CPU (or PLCSIM)

Three checks should be performed on every build that uses the SLD / LAR1 pattern.

  1. Watch the AR1 register. Open a watch table in STEP 7, force the index value (e.g. MD0 = 5), single-step through SLD 3 / SLD 2 / LAR1, and confirm AR1 matches the expected bit pattern (5 × 32 = 160 = 0xA0 for the example above).
  2. Watch the target cell. With the same index, place a known constant in DB100.DBD20 (e.g. DW#16#12345678) and verify that MD100 takes that value after the L DBD [AR1,P#0.0] / T MD100 pair executes.
  3. Boundary tests. Run with MD0 = 0, the maximum legal record index, and one past the maximum (e.g. 50 for a 50-record DB). The CPU will not trap on the out-of-range case — the read will silently return bytes from the next DB or from global memory. Add a < L#50 comparison before the indirect access to defend against this.

10. Common Pitfalls and Field-Notes

Symptom Likely cause Remedy
Reads return values from the wrong cell, offset by a power of two One of the SLD operations was omitted or duplicated Re-derive the formula: AR1 = index × 4 × 8 → exactly two shifts summing to 5
Reads always return zero The DB was not opened with OPN DB100 before the indirect access Insert OPN DB100 above the LAR1 block
Bit 0 of the read value is corrupted The bit-address field was not cleared (forgot SLD 3) Always run SLD 3 on raw byte numbers before LAR1
AR1 is correct in PLCSIM but wrong on the real CPU Step 7 compiled with a different firmware target; LAR1 behaviour is identical for S7-300/400, but some S7-1500 firmware (V2.x and later) uses a 64-bit pointer layout On S7-1500, prefer the P# literal, the PEEK/POKE instructions, or the explicit LAR1 P#… form
Compiler reports "invalid operand" The shift constant is outside 0 … 32 or contains a syntax error Verify the constant is a plain integer literal separated by a single space

11. SLD 3 + SLD 2 vs. SLD 5 — Performance and Readability

Functionally, SLD 3 followed by SLD 2 is identical to SLD 5. All three are single micro-instructions on an S7-300/400 and execute in a fixed time of a few microseconds; there is no measurable performance difference. The reason engineers split the shift is documentation: the two lines map directly to the two factors in the formula, making code review and FMEA easier.

//  Equivalent forms:
      L     MD0
      SLD   5              //  one-line, fastest to write
      LAR1

      L     MD0
      SLD   3
      SLD   2              //  split for documentation
      LAR1

Both compile to the same machine code size. Use whichever the local coding standard mandates; the verification steps in §9 are identical.

12. S7-1500 and the Modern Pointer Layout

From SIMATIC S7-1500 / ET 200SP firmware V1.0 onward, the CPU still supports SLD and LAR1 for source compatibility, but the canonical pointer layout is now 64 bits (an 8-byte area-cross pointer) and the LAR1 instruction has a stricter operand set. STEP 7 in TIA Portal will warn if a 32-bit STL block is downloaded to a 1500 with the legacy bit-shift pattern. Two safe options exist for new S7-1500 code:

  1. Use a literal pointer: LAR1 P#"DB100".DBD0 with a calculated offset, e.g. LAR1 P#"DB100".DBD[MW0] inside an SCL IF block.
  2. Use the PEEK / POKE helper blocks from the standard library, which accept a plain byte offset and handle the bit-address shift internally.

For cross-portability between S7-300/400 and S7-1500, wrap the SLD 3 / SLD 2 / LAR1 triplet in a dedicated FB so the platform-specific call site is in one place.

13. Glossary

Term Definition
Pointer A data item that holds a memory address; in C, an explicit type, in S7, a bit pattern interpreted by LAR1 / LAR2 and the memory-indirect addressing modes. Wikipedia: Pointer (computer programming)
AR1 / AR2 The two address registers of the S7-300/400 CPU; they hold pointers used by the bracketed-address operands such as [AR1,P#2.0].
SLD Shift Left Double word. Logical shift of ACCU 1 left by n bit positions.
LAR1 / LAR2 Load Address Register 1 / 2 from ACCU 1.
P# Pointer literal in STEP 7 STL/SCL; pre-formats a value in the bit-addressable layout.
DBD / DBW / DBB Data Block Double word / Word / Byte — operand prefix for cross-area indirect access via the currently opened DB.
PLCSIM STEP 7 / TIA Portal simulation target that emulates an S7 CPU on a PC; supports watch tables and single-stepping for pointer verification.

Frequently Asked Questions

Why do I need SLD 3 before LAR1 in STEP 7 STL?

Because the value in MD0 is typically a raw byte address, but LAR1 loads a bit-addressable pointer in which the byte address is pre-multiplied by 8. SLD 3 shifts the byte number into the upper 29 bits, exactly the format AR1 expects, and clears the 3-bit bit-address field.

Is SLD 3 + SLD 2 the same as SLD 5?

Yes. Logical left shift is cumulative, so shifting by 3 and then by 2 produces the same bit pattern as a single shift by 5. Use whichever form your local coding standard mandates — execution time and code size are identical.

What value ends up in AR1 when MD0 contains a record index N for an array of DWORDs?

AR1 will hold N × 32 (decimal), which the CPU decodes as byte address N × 4 (DWORD N), bit 0. For example, MD0 = 5 produces AR1 = 160 = 0xA0, pointing at DB byte 20.

Can I load a P# pointer directly into AR1 without the SLD instructions?

Yes. L P#200.4 followed by LAR1 loads a fully formatted pointer and requires no shifts. The SLD sequence is only needed when MD0 holds a raw byte number or record index, not a P#-formatted value.

Does the SLD 3 / SLD 2 / LAR1 pattern work on an S7-1500?

It compiles and runs for backward compatibility, but TIA Portal prefers the 64-bit pointer layout and will warn on legacy 32-bit STL blocks. For new S7-1500 code, use the P# literal, the PEEK / POKE standard-library blocks, or SCL array indexing instead.

Back to blog