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.
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.
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:
-
L MD0→ ACCU 1 = 5 (0x00000005). -
SLD 3→ ACCU 1 = 40 (0x00000028). The 3 low-order bits are gone. -
SLD 2→ ACCU 1 = 160 (0x000000A0). The byte address 20 is now in bits 3 … 31. -
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.
-
Watch the AR1 register. Open a watch table in STEP 7, force the index value (e.g.
MD0 = 5), single-step throughSLD 3 / SLD 2 / LAR1, and confirmAR1matches the expected bit pattern (5 × 32 = 160 = 0xA0 for the example above). -
Watch the target cell. With the same index, place a known constant in
DB100.DBD20(e.g. DW#16#12345678) and verify thatMD100takes that value after theL DBD [AR1,P#0.0]/T MD100pair executes. -
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#50comparison 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:
- Use a literal pointer:
LAR1 P#"DB100".DBD0with a calculated offset, e.g.LAR1 P#"DB100".DBD[MW0]inside an SCLIFblock. - Use the
PEEK/POKEhelper 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.