Introduction
Overview
NCL is the native programming language of the NCS/e.
It is a small, assembly-like language built around registers, numbered program lines, direct control flow, and an extensible instruction interface.
NCL programs execute directly against the NCS/e CPU and may communicate with system services, expansion modules, and peripherals using EXT instructions.
A simple program might look like:
MOVE r0 10
$loop
D.TXT "Hello!\n"
DEC r0
BNEQ $loop r0 0
D.BLT
HALT
NCL deliberately exposes much of the machine's execution model. Programs work directly with registers and the program counter, while higher-level conveniences such as labels, constants, named registers, and relative line references are resolved before execution begins.
Programs
NCL programs consist of numbered program lines.
Each line contains one or more instructions:
MOVE r0 10
ADD r1 r0 5
D.TXT "Hello!"
Several instructions may share a line when separated by semicolons:
MOVE r0 10; INC r0; D.TXT r0
Instructions on a line execute sequentially.
Programs normally proceed from one line to the next. Branch instructions change this flow by writing a new line number to the program counter.
See Syntax for the complete source format and coalesced-line execution rules.
CPU and EXT instructions
NCL instructions may be implemented directly by the CPU or provided through the EXT interface.
CPU instructions provide the core operations required for computation and control flow:
MOVE r0 10
MUL r0 r0 2
BEQ $done r0 20
EXT instructions provide access to functionality outside the core CPU:
D.TXT "Hello!"
MATH.SQRT r0 2000
SYS.SLEEP 5
From an NCL program, both forms use the same instruction syntax.
An EXT operation may communicate with a system service, expansion module, or peripheral. Synchronous EXT operations pause execution until the operation completes.
This allows the NCS/e instruction set to be extended without requiring every operation to be implemented by the CPU itself.
Registers
The CPU provides separate integer and string registers.
| Registers | Type |
|---|---|
r0–r31 |
32-bit signed Integer |
s0–s15 |
String |
Programs manipulate these registers directly:
MOVE r0 42
SMOVE s0 "Hello!"
Several special-purpose registers are also provided:
| Register | Purpose |
|---|---|
pc |
Program counter. |
sp |
Value stack pointer. |
sv |
Top value on the stack. |
er |
Error state. |
Some instructions and EXT operations may modify registers as a side effect. The documentation for each operation identifies the registers it may modify.
Constants may also be used to give registers descriptive names:
#SCORE r0
#NAME s0
MOVE #SCORE 100
SMOVE #NAME "Alice"
These names are resolved before execution and do not create additional storage.
Program counter
The pc register controls which program line executes next.
Under normal sequential execution, the CPU advances through the program one line at a time.
Branch instructions modify pc directly:
JUMP $loop
BEQ $done r0 0
Subroutine calls and returns also modify the program counter.
Unlike most CPU state, pc may also be modified by EXT operations. This allows system services and expansion hardware to participate directly in program control when appropriate.
Writing to pc immediately ends execution of the current program line.
See Branching for branch and subroutine instructions.
Source pre-pass
NCL provides several source-level conveniences while retaining a simple runtime execution model.
Before execution begins, the complete program undergoes a pre-pass.
The pre-pass resolves features including:
- String escape sequences.
- Constants.
- Named registers.
- Labels.
- Relative line references.
For example:
#COUNTER r0
MOVE #COUNTER 10
$loop
DEC #COUNTER
BNEQ $loop #COUNTER 0
By the time these instructions reach execution, symbolic references such as #COUNTER and $loop have already been resolved.
This allows NCL source to remain readable without requiring the CPU to maintain variables or symbolic branch targets at runtime.
See Syntax for the complete pre-pass behavior.
Errors
NCS/e distinguishes between recoverable runtime errors and fatal CPU errors.
Recoverable errors
Recoverable errors occur when a valid instruction cannot be completed normally.
Examples may include:
- Division by zero.
- Invalid input to an operation.
- A peripheral or expansion operation reporting failure.
Recoverable errors set the er register and allow the program to decide how to proceed.
DIV r0 r1 r2
BSNEQ $error er ""
A recoverable error does not inherently terminate the program.
Fatal CPU errors
Fatal CPU errors occur when a program violates the processor's execution model.
Examples include:
- Value stack overflow.
- Value stack underflow.
- Call stack overflow.
- Call stack underflow.
Fatal errors immediately terminate execution and return control to the shell.
The running program cannot recover from a fatal CPU error.