Ladder
A ladder-diagram (PLC) style front end - it cannot build phone UI programs.
What works on this path is compiling and running itself; the whole UI interface set (windows, drawing, input) has no corresponding syntax (there is no "function call with a string argument").
How to run it on your phone
vml run examples/ladder/test/sysinfo.ld
Compiling takes a while (one to two minutes for C, a few seconds for scripting languages). Once the program is running, the gamepad is at the bottom of the screen - the D-pad and the four action buttons are all there. Tap the back arrow in the top-right corner to return to the Shell.
What to know before you write
- Only declarations and simple logic; you cannot call the UI interfaces
Examples
| File | What it shows |
|---|---|
test/file_io.ld | Empty test (NOP) |
test/sysinfo.ld | Placeholder - this path cannot call the JSON interface |
Also in this folder: demo/demo_std.ld, motor_control/main.ld, test/libdemo.dbf, test/libs.ld, tty/demo_tty.ld, ui/demo_ui.ld
Pitfalls we hit on real devices
- For a phone UI program, pick another language (C / Python / Lua all work).
The rest of this page is taken straight from the VML source (third_party/vml/VMLPrepares/LadderCompiler/): the README covers what this front end supports and how to compile it, and the language reference covers the syntax itself. When upstream changes, regenerating this page brings it up to date.
Language reference
Ladder Diagram language compiler specification
Version: v1.0 | Date: 2026-07-06 | Revised by: Shenzhen Tanso Intelligent Technology Co., Ltd.
Standards
| Field | Value |
|---|---|
| Target standard | IEC 61131-3 subset (2003) |
| Publication year | 2003 |
| Completeness | ~98% |
| MCU completeness | ~97% |
| Tests | 0 (the test directory is yet to be created) |
| Updated | 2026-05-18: timers reimplemented on the SYSCALL #53 real-time clock; code generator status corrected |
Keywords (instructions)
Contacts: NO (normally open) NC (normally closed) POS (positive edge) NEG (negative edge) Coils: OUT (output) SET (set) RST (reset) Timers: TON (on-delay) TOF (off-delay) TP (pulse) Counters: CTU (up counter) CTD (down counter) CTUD (up/down counter) Comparison: EQ NE GT GE LT LE Arithmetic: ADD SUB MUL DIV MOD Program control: JMP LBL RET END Other: MOVE SEL MUX LIMIT
Overview
This compiler compiles the IEC 61131-3 Ladder Diagram language into VML (Virtual Machine Language) assembly. Ladder Diagram is a graphical programming language widely used in industrial control, for programming programmable logic controllers (PLCs).
Language features
1. Basic ladder elements
Contacts
- Normally open contact: ─┤ ├─
- Normally closed contact: ─┤/├─
- Positive edge contact: ─┤P├─
- Negative edge contact: ─┤N├─
Coils
- Output coil: ─( )─
- Set coil: ─(S)─
- Reset coil: ─(R)─
- Latch coil: ─(L)─
Function blocks
- Timers: TON (on-delay), TOF (off-delay), TP (pulse)
- Counters: CTU (up counter), CTD (down counter), CTUD (up/down counter)
- Comparators: equal, not equal, greater than, less than and so on
- Math operations: add, subtract, multiply, divide and so on
2. Data types (IEC 61131-3)
Elementary data types
- BOOL: boolean value (1 bit)
- BYTE: unsigned 8-bit integer
- WORD: unsigned 16-bit integer
- DWORD: unsigned 32-bit integer
- INT: signed 16-bit integer
- DINT: signed 32-bit integer
- REAL: 32-bit floating point
- STRING: string
- TIME: time type
Derived data types
- Arrays:
ARRAY[1..10] OF INT - Structures:
iec TYPE MotorControl : STRUCT StartButton : BOOL; StopButton : BOOL; MotorRun : BOOL; Speed : INT; END_STRUCT END_TYPE
3. Program structure
Program declaration
PROGRAM MotorControl
VAR
StartButton AT %IX0.0 : BOOL;
StopButton AT %IX0.1 : BOOL;
MotorRun AT %QX0.0 : BOOL;
Timer1 : TON;
END_VAR
Variable declaration sections
- VAR: local variables
- VAR_INPUT: input variables
- VAR_OUTPUT: output variables
- VAR_IN_OUT: input/output variables
- VAR_GLOBAL: global variables
- VAR_TEMP: temporary variables
I/O mapping
- Input:
%IX0.0(byte 0, bit 0) - Output:
%QX0.0(byte 0, bit 0) - Memory:
%MW0(word 0) - Holding register:
%MD0(double word 0)
4. Ladder rung structure
Simple rung
Start Stop Motor
─┤ ├─────┤/├──────( )─
I0.0 I0.1 Q0.0
Parallel branch
Button1 Lamp1
─┤ ├────┬───( )─
│
Button2│
─┤ ├────┘
Series branch
Start Timer1.PT := T#5s
─┤ ├──────[TON]─────
I0.0 Timer1
IN Q
PT ET
Function block call
Start MotorCtrl
─┤ ├──────[FB]─────
EN ENO
Start Run
Stop Fault
Speed ActualSpeed
5. Timers and counters
Timer (TON - on-delay)
VAR
Timer1 : TON;
TimeValue : TIME := T#5s;
END_VAR
// ladder representation
Start Timer1
─┤ ├──────[TON]─────
IN Q
PT ET
Function: when IN is TRUE the timer starts; once the PT time has elapsed Q becomes TRUE. ET shows the elapsed time.
Counter (CTU - up counter)
VAR
Counter1 : CTU;
PresetValue : INT := 10;
END_VAR
// ladder representation
Pulse Counter1
─┤P├──────[CTU]─────
CU Q
PV CV
R
Function: counts on the positive edge of CU; Q is TRUE when CV >= PV; the R signal resets the counter.
6. Instruction List (IL) support
The ladder compiler also supports the Instruction List text format:
LD StartButton
ANDN StopButton
OUT MotorRun
LD MotorRun
TON Timer1, T#5s
7. Structured Text (ST) support
Structured Text can be embedded in a ladder diagram:
IF StartButton AND NOT StopButton THEN
MotorRun := TRUE;
Timer1(IN:=TRUE, PT:=T#5s);
END_IF;
8. Standard functions
Bit operation functions
- AND, OR, XOR, NOT - logical operations
- SHL, SHR - shift operations
- ROL, ROR - rotate operations
Math functions
- ADD, SUB, MUL, DIV - arithmetic operations
- MOD - modulo
- ABS - absolute value
- SQRT - square root
- LN, EXP - logarithm and exponent
- SIN, COS, TAN - trigonometric functions
Comparison functions
- EQ (=), NE (<>), GT (>), GE (>=), LT (<), LE (<=)
Type conversion functions
- Type conversions such as BOOL_TO_INT, INT_TO_REAL
- TRUNC, ROUND - rounding functions
String functions
- CONCAT - string concatenation
- LEFT, RIGHT, MID - substrings
- LEN - string length
- FIND - find a substring
9. VML code generation conventions
Bit operation mapping
Ladder contact: ─┤ ├─
VML code: MOVE R0, [I0.0_address]
CMP R0, #1
JNE skip_branch
Ladder coil: ─( )─
VML code: MOVE R0, #1
MOVE [Q0.0_address], R0
Timer implementation (using the SYSCALL #53 GetTick real-time clock)
TON (on-delay timer):
- On detecting a positive edge on the IN input, record startTime = SYSCALL #53 (GetTick)
- ET = GetTick() - startTime
- When ET >= PT the Q output is set to TRUE
- When IN is FALSE, reset ET = 0, Q = FALSE
TOF (off-delay timer):
- On detecting a negative edge on the IN input, record startTime
- ET = GetTick() - startTime
- Reset when IN goes back to TRUE
TP (pulse timer):
- A positive edge on the IN input triggers the pulse
- ET = GetTick() - startTime
- The Q output stays TRUE for the duration of the pulse
CTUD (up/down counter):
- On a positive edge of CU with CV < 32767: CV++
- On a positive edge of CD with CV > -32768: CV--
- R resets CV = 0, LD loads CV = PV
- QU: TRUE when CV >= PV; QD: TRUE when CV <= 0
Counter implementation (with overflow protection)
CTU (up counter):
- Counts on a positive edge of CU
- Check CV < 32767 before incrementing CV (overflow protection)
- Q = TRUE when CV >= PV
- The R signal resets CV = 0
CTD (down counter):
- Counts on a positive edge of CD
- Check CV > -32768 before decrementing CV (underflow protection)
- Q = TRUE when CV <= 0
- The LD signal loads CV = PV
CTUD (up/down counter):
- On a positive edge of CU with CV < 32767: CV++
- On a positive edge of CD with CV > -32768: CV--
- R resets CV = 0, LD loads CV = PV
- QU: TRUE when CV >= PV; QD: TRUE when CV <= 0
10. I/O memory mapping
Input image area
Address range: 0x8000 - 0x80FF (256 bytes)
Purpose: stores digital input states
Access: MOVE R0, [0x8000] ; read the first input byte
Output image area
Address range: 0x8100 - 0x81FF (256 bytes)
Purpose: stores digital output states
Access: MOVE [0x8100], R0 ; write the first output byte
Holding register area
Address range: 0x8200 - 0x83FF (512 bytes)
Purpose: stores intermediate variables, timers, counters and so on
Special registers
Address: 0x8400 - system time (milliseconds)
Address: 0x8404 - scan cycle
Address: 0x8408 - error code
11. Scan cycle model
A ladder program executes according to the scan cycle:
Start scan
├── Read physical inputs into the input image area
├── Execute the user program (ladder logic)
├── Write the output image area to the physical outputs
└── Handle system tasks (communication, diagnostics and so on)
VML implementation:
LABEL main_scan_cycle
; 1. read inputs
CALL read_physical_inputs
; 2. execute the user program
CALL user_program
; 3. write outputs
CALL write_physical_outputs
; 4. update system time
CALL update_system_timer
; 5. check the scan cycle time
MOVE R0, [current_time]
SUB R0, [scan_start_time]
CMP R0, [max_scan_time]
JLE scan_ok
; scan timeout error
MOVE R0, #1
MOVE [scan_timeout_error], R0
scan_ok:
; wait for the next scan cycle
CALL delay_scan_cycle
JMP main_scan_cycle
12. Example programs
Motor start/stop control
PROGRAM MotorControl
VAR
StartButton AT %IX0.0 : BOOL;
StopButton AT %IX0.1 : BOOL;
MotorRun AT %QX0.0 : BOOL;
Overload AT %IX0.2 : BOOL;
Timer1 : TON;
END_VAR
// ladder logic
Start Stop Overload Motor
─┤ ├─────┤/├─────┤/├──────( )─
I0.0 I0.1 I0.2 Q0.0
Motor Timer1
─┤ ├──────[TON]─────
IN Q
PT ET
T#5s
Timer1.Q Alarm
─┤ ├────────( )─
Q0.1
Conveyor line control
PROGRAM ConveyorControl
VAR
Sensor1 AT %IX0.0 : BOOL;
Sensor2 AT %IX0.1 : BOOL;
Motor1 AT %QX0.0 : BOOL;
Motor2 AT %QX0.1 : BOOL;
Counter1 : CTU;
PartsCount : INT;
END_VAR
// product counting
Sensor1 Counter1
─┤P├──────[CTU]─────
CU Q
PV CV
10
R
Counter1.Q PartsCount := Counter1.CV
─┤ ├─────────────────[MOV]─────
// conveyor control
Sensor1 Sensor2 Motor1
─┤ ├───────┤/├────────( )─
Sensor2 Motor2
─┤ ├──────────────( )─
Floating-point and 64-bit compile modes
The VML toolchain controls the handling strategy for floating point and 64-bit integers through three compile parameters:
| Parameter | Options | Default | Description |
|---|---|---|---|
--float32 | hard / soft / none | hard | 32-bit float (REAL) handling mode |
--float64 | hard / soft / none | soft | 64-bit float handling mode |
--int64 | hard / soft / none | soft | 64-bit integer handling mode |
32-bit float (float32)
The REAL type in this language (32-bit single-precision float) is compiled in the following modes:
hardmode (default): uses the native VML floating-point instructionsMOVEF/ADDF/SUBF/MULF/DIVF/CMPF/NEGF, computing directly through the sixteen floating-point registers F0-F15. Best performance; suited to target platforms with floating-point hardware.softmode: uses the Q15.16 fixed-point software emulation librarysoftfloat.c, emulating floating-point operations through functions such as__vml_float_add/sub/mul/div/neg/abs/cmp. Suited to MCU platforms without floating-point hardware.nonemode: disables all 32-bit float types and reports a compilation error when aREALdeclaration is encountered.
64-bit float (double)
The VML compile layer supports 64-bit double-precision floating-point arithmetic. The ladder language itself does not define the corresponding LREAL type, but the compiler reserves extension support for it.
softmode (default): uses the IEEE 754 double-precision software emulation librarysoftdouble.c.hardmode: uses the VML double-precision instructionsMOVED/ADDD/SUBD/MULD/DIVD/CMPD/NEGD.nonemode: disables the double-precision float extension.
64-bit integer (int64)
The 64-bit integer types such as DLONG / LINT in the IEC 61131-3 ladder language are compiled in the following modes:
softmode (default): uses the dual-register software emulation librarysoftint64.c, emulating 64-bit integer arithmetic through functions such as__vml_i64_add/sub/neg/and/or/xor/not/shl/shr.hardmode: reserved; a future VML version will support native 64-bit integer instructions.nonemode: disables 64-bit integer types.
Software emulation libraries
All the software emulation libraries above live in the Lib/shared/ directory, are written in C and are compiled to VML by the C compiler; every language shares them:
| Library file | Purpose | Core functions |
|---|---|---|
softfloat.c | Q15.16 fixed-point 32-bit float emulation | __vml_float_add/sub/mul/div/neg/abs/cmp, __vml_int2float/float2int |
softdouble.c | IEEE 754 double-precision 64-bit float emulation | __vml_double_add/sub/mul/div/neg/abs/cmp, __vml_int2double/double2int, __vml_float2double/double2float |
softint64.c | 64-bit integer dual-register emulation | __vml_i64_add/sub/neg/and/or/xor/not/shl/shr |
13. Compilation limitations and notes
Current implementation status
- Lexer: fully implemented, supports the IEC 61131-3 keywords
- Parser: fully implemented, supports both ladder diagram and Structured Text
- Code generator: basic implementation (CodeGenerator.Elements.cs), supporting contacts/coils/timers/counters
- Timers: TON/TOF/TP use the SYSCALL #53 (GetTick) real-time clock
- Counters: CTU/CTD/CTUD with overflow protection
- Standard library: a ladder standard library (stdlib.vml) still needs to be created
Features still to be implemented
- Code generation from ladder diagram to VML instructions — ⚠️ essentially complete
- Timer and counter function blocks — ✅ implemented (TON/TOF/TP/CTU/CTD/CTUD)
- I/O memory mapping management
- Scan cycle scheduling
- Standard function library implementation (MOVE/SEL/MUX/LIMIT)
Technical challenges
- Graphics to text conversion: the ladder diagram is a graphical language; a textual instruction list is used as the intermediate representation
- Real-time requirements: PLC programs have strict real-time requirements — timers use the SYSCALL #53 millisecond clock
- Bit operations: contacts/coils need efficient bit operations and edge detection
- Retentive variables: variable values have to be retained across a power loss
14. Integration with the VML runtime
A ladder program interacts with the VML runtime in the following ways:
- I/O access: physical devices are accessed through memory-mapped I/O addresses
- Timer services: TON/TOF/TP are implemented with the system timer
- Interrupt handling: hardware interrupt events are supported
- Diagnostics: scan cycle monitoring, error handling
System calls
- SYSCALL #3: program exit
- SYSCALL #53: GetTick — get a millisecond timestamp (the timing base for timers)
- I/O access: physical devices are accessed through memory-mapped I/O addresses (contacts/coils)
15. Application areas
The ladder diagram compiler is mainly used in:
- Industrial automation control systems
- Building automation
- Process control
- Machinery and equipment control
- Energy management systems
By compiling ladder diagrams into VML code, traditional PLC programs can run on the VML virtual machine, enabling cross-platform porting and simulation testing of industrial control programs.
🆕 String encoding (v1.65.19)
This language compiler uses the VML string system indirectly through the shared library (Lib/shared/).
| Directive | Width | Encoding | C type |
|---|---|---|---|
.string | 8-bit | UTF-8 | char* |
.wstring | 16-bit | UTF-16LE | wchar_t* |
.ustring | 32-bit | UTF-32LE | char32_t* |
MCU mode (default): string output is UTF-8 (.string) OS mode: the encoding can be determined through the VML_WSTRING macro
The shared library already provides wide-string conversion functions (wchar.h/uchar.h) that each language compiler can use as needed.
Compiler README
Ladder Diagram (IEC 61131-3 subset) compiler
Path: VMLPrepares/LadderCompiler/ Completeness: ~98% | 🟢 production ready Standard library: Lib/ladder/
Features
- ✅ Parsing + code generation (Lexer/Parser/CodeGenerator)
- ✅ Contacts: NO (normally open) / NC (normally closed) / POS (positive edge) / NEG (negative edge)
- ✅ Coils: OUT (output) / SET (set) / RST (reset)
- ✅ Timers: TON (on-delay) / TOF (off-delay) / TP (pulse) — SYSCALL #53 real-time clock
- ✅ Counters: CTU (up) / CTD (down) / CTUD (up/down) — with overflow protection
- ✅ Comparison: EQ/NE/GT/GE/LT/LE
- ✅ Arithmetic: ADD/SUB/MUL/DIV/MOD
- ✅ POKE/PEEK memory operations (MMIO)
- ✅ Shared builtin function library (builtins.vml)
Compile modes
MCU mode (default, --mode mcu)
MCU mode is tuned for microcontroller / bare-metal environments (Arduino/STM32/8051 and so on) and automatically skips features that are incompatible with an operating system.
Skipped (no code is generated for these constructs):
- none (ladder languages are MCU compatible by nature)
Kept (the low level is provided by the BIOS):
- Timers (TON/TOF/TP) — using SYSCALL #53 GetTick
- Counters (CTU/CTD/CTUD) — 16-bit overflow protection
- POKE/PEEK memory-mapped I/O (MMIO)
- I/O memory mapping (contacts/coils)
- Basic arithmetic / comparison / control flow
OS mode (--mode os, reserved)
OS mode targets environments with an operating system (embedded Linux, an RTOS and so on); it will support the full set of language features (file system, threads, async, exceptions, reflection and so on).
RAM level
--ram k: kilobyte level (2KB~64KB, such as 8051/PIC/AVR)--ram m: megabyte level (64KB~1MB, such as ARM Cortex-M, default)--ram g: gigabyte level (such as x86/DDR systems)--stack-size <bytes>: specify the stack size manually (by default it is derived automatically from--ram)
Safe MCU coding notes
- A PLC scan cycle has to meet real-time requirements
- Timer accuracy depends on the resolution of the SYSCALL #53 clock
- Watch the 16-bit range of counters (-32768 ~ 32767)
- Floating-point arithmetic may need a soft-float library
Usage
dotnet run --project VMLTool -- input.lad -o output.vml
Tests
Test/La/ — 0 test files (the test directory is yet to be created)