Siemens S5 to S7 Migration: Replacing BS 60 and OB 160 Calls

David Krause21 min read
S7-300SiemensTechnical 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: The S5 to S7 Migration Problem

When you convert a Siemens SIMATIC S5 program that uses BS 60 references and OB 160 calls, the STEP 7 / TIA Portal converter either flags the statement as a syntax error or generates a SF (System Fault) on the S7 CPU at the first execution. The reason is architectural: BS (system status) registers and the variable time-loop OB 160 are S5-115U hardware features that have no direct equivalent in the S7-300 / S7-400 / S7-1500 register set. A successful migration requires understanding what the original S5 instructions did at the silicon level so the behavior can be re-implemented with S7-300 instructions.

The example below shows a typical migration task. The source is a SIMATIC S5-115U STL program that performs a counted block transfer with a per-iteration delay. The destination is a STEP 7 V5.x or TIA Portal STL program for an S7-300 CPU (for example, CPU 315-2 PN/DP, order number 6ES7315-2EH14-0AB0 or later).

Field-proven caveat: Do not rely on the STEP 7 S5/S7 converter to handle BS registers or OB 160 silently. The converter stops at the first unknown operand and either aborts or produces a syntactically invalid S7 block that the CPU will reject with SF and a diagnostic buffer entry such as OB not loaded or not possible (event ID 0x8001 in STEP 7 classic diagnostic buffer format).

2. BS Registers in S5-115U: Why They Don't Exist in S7

BS in S5-115U nomenclature refers to the system status byte area of the operating system. BS 60 is the 61st byte (zero-indexed) of the internal system data area. It is not a freely usable marker; its meaning depends on the operating mode of the CPU and on the bus peripherals configured. In an S5-115U CPU that has a SINEC L1 slave interface configured, BS 60 carries the address of the CBS (Coordination Byte Send) used by the L1 protocol handler. Other BS bytes serve the scan-time accumulator, the interrupt stack, and the process-image update flags.

The S7-300 / S7-400 / S7-1500 architectures eliminated the BS area in favor of:

  • The local stack (L stack) for block-private temporaries.
  • The process image of inputs (PII) and outputs (PIQ) for I/O consistency.
  • System status lists (SSL) queried with SFC 51 RDSYSST (S7-300/400) or with RD_SINFO / RDREC on S7-1500.
  • Direct I/O access (I/O area with :P qualifier) for time-critical access outside the process image.

There is no BS area in S7 and there is no global mapping. If your S5 source touches BS 60, locate the original intent in the S5-115U manual and re-implement it with a system function or a hardware-aware user program. The Siemens Industry Online Support portal hosts the legacy S5-115U manual (entry search by order number 6ES5 998-xABxx) and is the authoritative source for the BS mapping.

3. OB 160: Variable Time Loop on the S5-115U

OB 160 on the S5-115U (CPU 941, 942, 943, 944 family) is a Variable Time Loop provided by the operating system to generate a CPU-independent wait time. From the S5-115U instruction list:

OB 160 simulates operation execution times. This makes you independent of the different operation execution times of the various CPUs and you can program wait times uniformly for all CPUs of the S5-115U range. The waiting time must be loaded into ACCU 1 in microseconds. Range: 120 to 65535 (78H to FFFFH).

Example from the S5-115U manual:

L  KF +1000    // Load 1000 us (1 ms) into ACCU 1
JU  OB 160      // Wait 1 ms, then return

The S7-300 family does not provide an OB 160. Time-based waits in S7 are produced with:

Mechanism Use Case Typical Precision
IEC timers SFB 4 / SFB 5 (TON, TOF) Standard on-delay / off-delay 1 ms tick base (S7-300) or 1 ms times multiplier
SFC 47 "WAIT" CPU-busy wait, ms-granular 1 ms (S7-300) / 100 us (S7-1500)
OB 1 cycle-time accumulator + flag Loop-body delay via cycle counting One OB 1 cycle (1-10 ms typical)
Time-of-day / hardware timer (SFB 1, or S5TI_TIM via the S5 migration tool) Reuse of S5 timer semantics 10 ms base on the S5 timer area

For the per-iteration delay in a counted block-move loop, the S5 OB 160 inside the loop is almost always a workaround for the S5-115U scan-time variation between the 941 / 942 / 943 / 944. On S7-300 the cleanest replacement is SFC 47 "WAIT" with a microsecond-equivalent wait parameter. If the original 1 ms wait is non-critical, you can usually drop it entirely because the S7-300 OB 1 cycle is bounded and the modern scan-time jitter is far smaller than on the S5-115U range.

Do not assume the wait is required. OB 160 in an S5 source is frequently a defensive idiom left by the original programmer to handle 115U scan-time variability. When porting to S7-300, evaluate the calling context: is the loop body meant to pace a peripheral, throttle a bus transaction, or debounce a relay? If it is the third, use a hardware interrupt OB (OB 40) instead of a polled wait.

4. Decoding the Original S5 STL Source

The two S5 networks in the source perform a counted data-block-to-data-block word copy. The parameter interface is implicitly defined by the function block header (not shown) and exposes:

  • #_DB - target data block number (AUF #_DB opens the DB).
  • #DW - first target data word (offset in 16-bit words, written to MW 220).
  • #ANZ - number of words to copy (loop counter, written to MW 220 as a seed and incremented each pass).
  • #WERT - per-iteration value placed into MW 222 (the loop body writes #WERT indirectly through AR1).

The loop body in Network 2 saves ACCU 1, ACCU 2, and the status word around an indirect store, increments the pointer, and continues until the counter rolls over. The body also calls OB 160 (the line flagged // CALL OB 160; Need to be replace), which is the only statement in the loop that has no S7 equivalent. The SPB M001 branch uses RLO set by the preceding INC 1 to loop while the counter is still non-zero.

Once you understand that intent, the BS 60 reference disappears from the conversation: BS 60 is not touched directly by this snippet, but the S5-115U system code may be using BS 60 internally for the L1 CBS pointer, which is why the snippet is referenced in a migration context. The S7-300 port should not contain any BS reference and should re-implement only the documented user intent of the loop.

5. S5-to-S7 STL Opcode Translation Table

The snippet uses only S5-115U STL operations. The following table maps each to STEP 7 V5.x / TIA Portal STL on the S7-300. The mnemonics shown are German (default for S5); STEP 7 also supports English, but the converter preserves the German form, so it is useful to read both.

S5 Mnemonic English S5 S7-300 Equivalent Notes
AUF #_DB CDB OPN DB [#_DB] Open a data block. S7-300 allows OPN DB with a 16-bit word operand from CPU 31x firmware V2.0 onward; older firmware requires LAR1 and OPN DB [AR1,P#0.0].
L #DW L #DW L #DW Local interface variable load. Identical in S7 FB/FC parameter interface.
T MW 220 T MW 220 T MW 220 Marker word store. Identical in S7, but global M is non-re-entrant; re-bind to TEMP for IEC 61131-3 compliance.
T MW 222 T MW 222 T MW 222 Identical semantics, same re-binding caveat.
TAK TAK TAK Swap ACCU 1 / ACCU 2. Identical.
L STW L STW L STW Load status word. Identical.
L MB 221 L MB 221 L MB 221 Identical.
SLW 4 SLW 4 SLW 3 Shift left word. On S5-115U the 4-bit shift converts a word index to a packed byte address. On S7-300, the canonical conversion is SLW 3 (word index x 8 bytes). See Section 8.
LAR1 LAR1 LAR1 Load AR1 from ACCU 1. Identical.
L #conv_akku1 L #conv_akku1 L #conv_akku1 In/out parameter on FB. Identical once the S5 STAT variable is mapped to a TEMP/STAT on the S7 FB.
T DBW [AR1,P#0.0] T DBW [AR1,P#0.0] T DBW [AR1,P#0.0] Indirect DBW write via AR1. Identical syntax in S7-300.
INC 1 INC 1 + 1 (or INC 1 where supported) STEP 7 replaces INC with the + arithmetic operator. The semantics of "increment ACCU 1 by 1" are preserved. Note that + does not set RLO; a separate <I test is required to drive a conditional jump.
SPB M001 JC M001 JC M001 Conditional jump if RLO = 1. S5 SPB = S7 JC.
JU OB 160 CALL OB 160 Replace with CALL SFC 47 with the wait parameter in microseconds, or remove if not functionally required. See Section 3.
JU FB / JU OB / JU PB / JU SB CALL FB / CALL OB / CALL PB / CALL SB CALL FB / CALL FC / CALL OB / CALL SFB / CALL SFC S5 PB/SB blocks must be converted to FC/FB on S7; the S5 conversion tool can re-parse most PB into FCs.
BS xx n/a No equivalent Resolve by reading the S5-115U manual and re-implementing intent with SFC 51 RDSYSST, SFC 20 BLKMOV, or PROFIBUS DP I/O.

6. Building the S7-300 Replacement Architecture

Two practical replacement strategies are available. Choose based on the S5 block type you are converting.

6.1 Strategy A: Re-implement as a System Function (FC) wrapper

Best for PB (program blocks) and SB (step blocks) that contain a self-contained copy routine. Encapsulate the loop in an S7-300 FC with the same parameter interface as the original S5 PB. Use marker words (MW 200..MW 254) for the loop counter and the pointer seed, or pass them as IN/OUT parameters and let the caller allocate them in an instance DB.

6.2 Strategy B: Lift the snippet into an FB with multi-instance capability

Best when the loop body is one of several copy operations in a larger S5 FB. The S5 parameters #conv_akku1, #conv_akku2, #conv_stw are already FB STAT/TEMP variables; re-create them as TEMP in the S7 FB and assign them via a STAT block of the multi-instance DB. The S7 FB multi-instance model is the cleanest target for re-entrancy: every OB that calls the FB gets its own static state.

For both strategies, define a clean interface:

Parameter Direction Type Original S5 S7-300
i_DB INPUT WORD / INT #_DB DB number to open
i_DW INPUT WORD / INT #DW First target DW
i_ANZ INPUT INT #ANZ Number of words to copy
i_WERT INPUT WORD / INT #WERT Per-iteration value
i_WaitMs INPUT INT (none, implicit in OB 160) Optional per-iteration wait in ms (0 = no wait)
o_status OUTPUT WORD (none) Return value: number of words actually written, or a status / error code

Marker words MW 220, MW 222, MB 221 used in the S5 source must be replaced with TEMP variables in the S7 FB, not with the global bit memory (M) area, to keep the routine re-entrant and IEC 61131-3 compliant. Re-entrancy is essential: the S5-115U has no multi-instance concept, but the S7-300 OB priority model (OB 1, OB 35, OB 40, ...) can call the same FB concurrently, and global M will be clobbered if you do not re-bind.

Safety and re-entrancy: The S5-115U has no concept of multi-instance. The S5 snippet wrote to global markers MW 220, MW 222, MB 221. If you port it to S7-300 without re-binding those markers to TEMP, two callers in different priority classes (for example, OB 1 and OB 35) will corrupt each other. Always re-bind to TEMP inside the FB, and make sure the L stack (local data) of OB 1 is configured to at least 256 bytes in HW Config to hold the new TEMPs.

7. Network 1 Replacement in STEP 7 STL

The S5 Network 1 sets up the pointer seed (MW 220) and the value (MW 222) before entering the loop. The S7-300 equivalent:

//  S7-300 STL, Network 1 - setup
//  Replaces S5 Reseau 1: AUF #_DB; L #DW; T MW 220; L #ANZ; T MW 222
      OPN   DB [#i_DB]                 //  Open target data block
      L     #i_DW                      //  Load first DW offset
      T     #t_ptr_seed                //  Save as loop pointer seed (TEMP WORD)
      L     #i_ANZ                     //  Load count
      T     #t_counter                 //  Save as loop counter (TEMP INT)
      L     #i_WERT                    //  Load per-iteration value
      T     #t_value                   //  Save for the loop body

Notes:

  • OPN DB [#i_DB] accepts a 16-bit operand on S7-300 (CPU 31x) as of firmware V2.0. For older CPU 312 / CPU 313 with firmware V1.x, the DB number must be loaded into ACCU 1 first and the indirect form OPN DB [AR1,P#0.0] used after LAR1.
  • All markers MW 220 / MW 222 are now TEMP variables #t_ptr_seed / #t_value. The instance DB carries them per call.
  • If the FB is configured with optimized block access (the default in TIA Portal from V13 onward), indirect access to instance data via AR1 must use the AR2-based standard access path; either disable optimized access for this FB, or pass the data via a global DB that you control with a fixed layout.

8. Network 2 Loop Replacement in STEP 7 STL

The S5 loop body saves ACCU 1, ACCU 2, and the status word, computes the byte offset, performs the indirect store, increments the pointer, and loops. In S7-300, two implementation choices are common. The literal translation follows:

//  S7-300 STL, Network 2 - loop body
//  Replaces S5 Reseau 2: ... SPB M001 ; with OB 160 removed
M002: NOP   0

      L     #t_value                  //  Load per-iteration value
      T     #t_akku1                  //  Save ACCU 1 (was #conv_akku1)

      TAK                             //  Swap ACCU 1 / ACCU 2
      T     #t_akku2                  //  Save ACCU 2 (was #conv_akku2)

      L     STW                       //  Load status word
      T     #t_stw                    //  Save status word

//  Build byte pointer: word offset to byte address
      L     #t_ptr_seed               //  Pointer seed (16-bit word offset)
      SLW   3                         //  Convert word offset to byte offset
      LAR1                            //  AR1 := byte pointer

      L     #t_stw
      T     STW                       //  Restore status word
      L     #t_akku2
      L     #t_akku1
      T     DBW [AR1, P#0.0]          //  Indirect write into target DB

//  Increment pointer seed (16-bit word units)
      L     #t_ptr_seed
      +     1
      T     #t_ptr_seed

//  Increment and test loop counter
      L     #t_counter
      +     1
      T     #t_counter

      L     #t_counter
      L     #i_ANZ
      <I                               //  S7-300: ACCU 2 < ACCU 1 ?
      JC    M002

Three important corrections versus a literal S5-to-S7 line-for-line translation:

  1. The S5 snippet shifts the byte pointer left by 4 (SLW 4) and uses LAR1. In S5-115U bit-memory addressing, that effectively converts a 16-bit "data word index" to a byte address. In S7-300 the canonical conversion is SLW 3 (left-shift by 3 multiplies the word index by 8 bytes per word). The extra SLW 1 in the S5 source corresponds to the 115U's byte-into-word packing; S7 stores data block words in pure byte addressing, so the shift count changes.
  2. The S5 SPB M001 uses RLO from the INC 1 operation: INC sets RLO when the result is non-zero, so the loop continues while the counter is non-zero. The S7 replacement tests the counter directly with <I / JC because STEP 7's + instruction does not set RLO. The S7 idiom is the most common and the easiest to read at the maintenance stage.
  3. If a per-iteration wait is functionally required (per Section 3), insert CALL SFC 47 with a wait time in microseconds immediately before the JC M002 branch:
      L     #i_WaitMs                //  e.g., 1 (1 ms)
      ITD
      SLD   3
      CALL  SFC 47                    //  WAIT
      IN:   WT
      L     #t_counter
      L     #i_ANZ
      <I
      JC    M002

SFC 47 documentation: SFC 47 "WAIT" pauses the user program for the number of microseconds passed in WT (range 1 us to 32767 us on S7-300, 100 ns to 1 s on S7-1500). A 1 ms wait is more than the S7-300 SFC 47 minimum, so pass WT := 1000 (microseconds). The CPU remains in RUN, OB 1 cycle time grows by exactly the wait amount, and the time-interrupt OBs (OB 30..OB 38) are not serviced during the wait. If the loop must remain interruptible, replace SFC 47 with an OB 35 cycle-tick counter or an IEC timer.

9. The CBS Coordination Byte on SINEC L1

If the original S5-115U was used as a SINEC L1 slave, BS 60 carried the address of the CBS (Coordination Byte Send) used by the L1 protocol handler. The S5-115U manual (CPU 941 to 944 family) documents this in chapter 12.2.3. On S7-300, the SINEC L1 interface is no longer present. The migration path depends on what the higher-level system expects:

Original S5-115U + L1 Topology S7-300 Replacement Notes
L1 master, multiple S5 slaves PROFIBUS DP master (CPU 31x with DP port) talking to S5-95U slaves (or migrated slaves) Use SFB 52 / SFB 53 RDREC / WRREC in place of L1 coordination bytes. The S5-95U can act as a DP slave with a stock GSD file.
L1 slave, the program reads/writes the CBS pointer at BS 60 PROFIBUS DP slave, exchange data via the S7-300 DP slave interface (GSD / GSDML) Drop the BS 60 pointer logic; the DP master sets the input image directly into the S7-300 process image.
Standalone S5-115U, no L1 traffic, BS 60 unused No equivalent needed; remove the BS 60 reference entirely Verify the original program does not touch BS 60 indirectly via system blocks (FY 60 / FY 61 on the S5-115U sometimes alias BS 60).
Point-to-point link via L1 to a third-party device Serial interface (CP 340 / CP 341 / CP 440 / CP 441) or PROFINET IO Use the PtP parameter assignment tool to set up the protocol on the CP.

If you are uncertain whether the L1 interface is active, the S5-115U manual chapter 12.2.3 lists the system data area assignments. On S7-300, no byte behaves like BS 60. The closest S7-300 analogy is the SSL (system status list) partial list 0x0019 / 0x0119, which returns Communication status information. Query with SFC 51 RDSYSST if the original program used BS 60 for diagnostics. The S7-300 SFCs are documented in the Siemens Industry Online Support portal under entry "Standard and System Functions for S7-300/400."

10. CPU 941-944 Reference Matrix and Chapter 12.2.3

The S5-115U CPU family covered by the S5-115U manual (chapter 12.2.3) is summarized below. Use this table to confirm which CPU your original program targeted before you port the BS 60 logic.

CPU Order Number (S5-115U) Scan Time Reference OB 160 Available BS 60 / CBS Role
CPU 941 6ES5941-7UB11 70 ms / 1 K AWL operations Yes Standard: scan-time accumulator. L1 slave: CBS pointer.
CPU 942 6ES5942-7UB11 30 ms / 1 K AWL operations Yes Same as 941.
CPU 943 6ES5943-7UB11 / 6ES5943-7UB21 18 ms / 1 K AWL operations Yes Same as 941; -7UB21 adds integrated L1 slave port.
CPU 944 6ES5944-7UB11 / 6ES5944-7UB21 10 ms / 1 K AWL operations Yes Same as 941; -7UB21 adds integrated L1 slave port.

The reference for chapter 12.2.3 is the S5-115U Programmable Controller, CPU 941/942/943/944 System Manual. Confirm the exact chapter numbering against the manual edition bundled with the S5 package you are migrating from; Siemens released several editions (6ES5998-xAB01, -xAB11, and later printings). Use the Siemens Industry Online Support entry search with the order number 6ES5 998-xABxx to retrieve the matching PDF.

Edition caveat: The behavior of OB 160 and the BS 60 mapping is identical across the CPU 941/942/943/944 family, but the manual printings differ in the chapter numbering. The 1991 printing uses 12.2.3, the 1995 printing uses 13.2.3, and the 1998 printing uses A.4.3. The substance is unchanged; only the section number differs.

11. Commissioning, Verification, and Troubleshooting

11.1 Offline static checks

  • Open the converted FB/FC in STEP 7 and confirm no S5 mnemonics (AUF, JU PB, SPB, SLD in their S5 sense) survive. The cross-reference view (Ctrl+Shift+F3 on the classic STEP 7 editor) on the S7 project should show zero hits in the converted block for any of the S5-only mnemonics.
  • Confirm all markers used in the S5 source have been re-bound to TEMP or to a clearly documented DB.
  • Confirm any OB 160 calls have been replaced with SFC 47 "WAIT," IEC timers, or removed entirely. Use the project-wide Find in Project dialog with the search strings OB 160 and OB160 (no space) to catch both forms.
  • Run PLC > Check Block Consistency across the S7 program. Resolve every warning before downloading.

11.2 Online commissioning on the S7-300 CPU

  1. Download the converted blocks to the CPU in STOP mode.
  2. Switch to RUN-P. Open the diagnostic buffer (PLC > Module Information > Diagnostic Buffer) and confirm no SF entries.
  3. Trigger the calling OB 1 segment manually with the Monitor/Modify tool. Verify the target DB is populated correctly by reading back DBW offsets through the Monitor view.
  4. Use the S7-300 cycle-time viewer (SFC 78 READ_RTM + SFC 79 SET_RTM, or the PLC > Module Information > Scan Cycle Time tab) to confirm the loop body does not exceed the configured OB 1 max cycle time (default 150 ms on S7-300, 6 s on S7-1500). If it does, extend OB1_PRG_TIME via HW Config or shorten the loop with a different copy strategy (for example, SFC 20 BLKMOV for bulk copy).
  5. On the first full run, set a breakpoint on the loop label M002 and single-cycle to confirm the pointer arithmetic is correct for the first three iterations.

11.3 Troubleshooting matrix

Symptom on S7-300 Likely Root Cause Fix
CPU goes to SF, diagnostic buffer OB not loaded or not possible (event ID 0x8001) OB 160 still referenced in the converted block Replace with SFC 47 or remove
CPU goes to SF, Area length error reading/writing (event ID 0x8032) Pointer seed out of range, or target DB too short for the #ANZ count Check target DB length; clamp i_DW + i_ANZ in the FB; or use SFC 24 TEST_DB
Copy succeeds but data is wrong by one word Incorrect shift count when converting word offset to byte offset Use SLW 3 for S7 (not SLW 4 as on the S5-115U)
Copy succeeds, but inconsistent data when called from OB 35 and OB 1 Global markers MW 220 / MW 222 still used; two priority classes clobbering each other Re-bind to TEMP variables inside the FB; raise the L stack size in HW Config
Copy takes much longer on S7-300 than on S5-115U Per-iteration wait removed; loop body now dominated by OB 1 overhead Either accept the speedup (usually desired) or add SFC 47 with the original wait value
Copy completes but communication to a downstream L1 device fails BS 60 was acting as the CBS pointer; L1 stack has no S7-300 replacement Re-target to PROFIBUS DP, replace L1 coordination bytes with PROFIBUS I/O
Converting an FB that uses S5 timer area (T 0..T 31) produces wrong time bases STEP 7 timer semantics differ; S5 timers run on 100 ms base, S7 default is 10 ms Use S5TI_TIM from the S5 conversion tool, or migrate to IEC timers explicitly with the documented time base
Indirect OPN DB on CPU 312 / CPU 313 returns Parameter assignment error Older CPU firmware does not accept OPN DB [#i_DB] Switch to LAR1; OPN DB [AR1,P#0.0] or upgrade CPU firmware

12. Frequently Asked Questions

What is BS 60 on an S5-115U?

BS 60 is the 61st byte of the S5-115U system data area. In a CPU with the SINEC L1 slave interface (CPU 943-7UB21 / 944-7UB21) it holds the address of the CBS (Coordination Byte Send) used by the L1 protocol handler. In a CPU without the L1 port, BS 60 participates in the scan-time accumulator. S7-300 / S7-400 / S7-1500 have no BS area; consult the S5-115U manual chapter 12.2.3 to find the intent and re-implement with SFC 51 (RDSYSST) or PROFIBUS DP I/O.

Why does the CPU stop when the S5 block runs on S7?

The S7-300 CPU rejects the S5-only statements BS 60 and OB 160 as unknown opcodes. The CPU enters STOP with a diagnostic buffer entry such as OB not loaded or not possible (event ID 0x8001) on the first scan. Convert the BS 60 reference per the S5-115U manual and replace the OB 160 call with SFC 47 "WAIT" or remove it.

How do I replace an S5 OB 160 variable time loop on S7-300?

Use SFC 47 "WAIT" with a microsecond parameter. For a 1 ms wait, call SFC 47 with WT := 1000. If the wait must not block higher-priority OBs, use an OB 35 cycle-tick counter or an IEC timer (SFB 4 "TON" / SFB 5 "TOF") instead.

Can the STEP 7 S5/S7 converter handle OB 160 and BS registers automatically?

No. The converter stops at the first unknown operand and either leaves a syntax error in the generated S7 block or aborts the conversion. Manual code review is required for every block that contains OB 1xx (other than OB 1, OB 100, OB 101, OB 102) or any BS reference. Run Check Block Consistency after every conversion pass.

Do I have to keep the per-iteration wait from OB 160 in the S7 replacement?

Usually not. The OB 160 wait in an S5 source is most often a defensive idiom to mask the S5-115U scan-time jitter across the CPU 941/942/943/944 family. On S7-300 the scan-time jitter is far smaller and a 1 ms wait inside a tight loop is usually redundant. Evaluate the calling context: if the wait paced a peripheral, throttled a bus, or debounced a relay, replace the wait with a hardware interrupt (OB 40) or a time-tick OB (OB 30..OB 38). If the wait was purely cosmetic, drop it.

Back to blog