Three questions, short answers first, then evidence.
| Path | Uses IPO? |
|---|---|
Coding & identity (SG_CODIEREN, CODIERDATEN_LESEN, CODIERDATEN_SCHREIBEN, FGNR_LESEN, FA_LESEN, ZCS_LESEN, SG_IDENT, CODIERINDEX_LESEN, …) |
Yes. Routed through the per-CABD A_*.ipo script (e.g. A_KMB46.ipo). cabimain switches on the job name and calls handler functions like Cod, Lesen, FgnrLesen, ZcsLesen, Ident, CILesen. The handlers issue EDIABAS work through the CDH* bridge into api32.dll. |
Kernfunktionen menu ([CODING].FktKernfunktionen=1 / [INDIVID].FktKernfunktionen=1) |
Yes. The selected Kernfunktion loads the matching SGDAT/*.ipo and runs it under NCSEXPER's embedded IPO interpreter. |
.ssd / ZUT verifier |
No. Different format — record-tag-driven (see ssd-zut-format.md). |
Doc history note. An earlier version of this table claimed coding "doesn't use IPO — COAPI calls apiJob directly". That was wrong, and disassembly of
A_KMB46.ipo(2026-05-19) corrects it. The misreading came from over-weighting one paragraph in the original ghidra notes; the actual evidence (the IPO'scabimainswitch +Cod/Lesenhandlers) is unambiguous.
NCSEXPER's .text segment carries a full embedded IPO interpreter (TEST-Infotext loader, the INPA system-function table, opcode dispatcher — confirmed by __inpa_startup__, __inpa_shutdown__, INPA Bridge16 Window, INPA Return Window, INPA-PrintScreen_%05d strings in .rdata). It runs the same .ipo files INPA itself runs (same TEST-Infotext header — see inpax/docs/ipo-file-structure.md). Both NCSEXPER's MFC main-screen coding flow and the Kernfunktionen menu pass through this interpreter.
The per-CABD-module script (e.g. A_KMB46.ipo for the KMB cluster on E46) is the workhorse. Inpax CLI disassembly confirms the shape — cabimain is a job-name switch:
local[0] := JOBNAME (passed by NCSEXPER's MFC UI)
if JOBNAME == "JOB_ERMITTELN" → call Jobs (emits the protocol-header job list)
if JOBNAME == "INFO" → call InfoJob
if JOBNAME == "CODIERINDEX_LESEN" → setjobstatus + call CILesen
if JOBNAME in (SG_CODIEREN | TEILBEREICH_CODIEREN | FGNR_SCHREIBEN | ZCS_SCHREIBEN | ZCS_LOESCHEN)
→ setjobstatus + call Cod(JOBNAME)
if JOBNAME == "CODIERDATEN_LESEN" → setjobstatus + call Lesen
if JOBNAME == "FGNR_LESEN" → setjobstatus + call FgnrLesen
if JOBNAME == "ZCS_LESEN" → setjobstatus + call ZcsLesen
if JOBNAME == "NETTODATEN_SCHREIBEN" → setjobstatus + call NettoDat
if JOBNAME == "SG_IDENT" → setjobstatus + call Ident
The handlers do more than just call apiJob. Lesen (146 ops) writes to the protocol report (PEMProtokollAusgabe), checks CDH error state (TestCDHFehler), updates UI (digitalout), drives a state machine (setstate, setjobstatus), and even scriptchanges into another IPO (e.g. "ID_COD_INDEX") for coding-index resolution. The actual EDIABAS work goes through CDH* functions statically linked into NCSEXPER.EXE — see §4.
The "Change Job" dropdown source. The IPO's Jobs function emits the JOB[1]..JOB[N] strings into the PEM report (PEMSGZ_Kopfzeile). NCSEXPER's MFC UI reads that list to populate the "Change Job" modal. So the dropdown contents are per-CABD-module, sourced from the IPO itself — not from EDIABAS SGBD job-table reflection.
Unlike INPA.EXE, NCSEXPER.EXE has no startup IPO. Evidence:
NCSEXPER/CFGDAT/contains onlyCOAPI.INI,INPA.INI, locale.eng/.gerstrings, and.TXTfiles. No.ipofiles anywhere outsideSGDAT/.strings NCSEXPER.EXE | grep -c '\.ipo'returns 2 — both are the bare extensions.ipo/.IPO, used to append to a basename constructed at runtime from SGAUSWAHL data.- The
.exeis dense in MFC class strings:CDialog,CFormView,CMFCStatusBar,CMFCMenuBar,CMFCPopupMenu,CMFCToolBar,CMFCRibbonBar,CMFCVisualManager*. The main UI is a full MFC application.
INPA.EXE is the opposite shape. Its CFGDAT contains startus.ipo / startger.ipo (locale variants) whose inpainit function paints the main menu via setmenu/setscreen syscalls. INPA's main UI is genuinely IPO-driven; NCSEXPER's isn't.
For NCSEXPER, IPOs are entered only at job-execute time — never at startup, never for the chassis/ECU/job pickers themselves. The flow is:
WinMain (MFC) → main dialog → user picks ECU + job → "Execute job" click
↓
NCSEXPER loads A_<cabd>.ipo from SGDAT (basename from the SGAUSWAHL row)
Embedded interpreter runs cabimain(JOBNAME)
→ switch on JOBNAME → calls handler (Lesen, Cod, FgnrLesen, ZcsLesen, Ident, …)
→ handler calls CDH* (statically linked into NCSEXPER.EXE)
→ CDH* calls api32.dll::apiJob → SGBD (.prg) → ECU
Our current wire.applyCodingPlan calls apiJob(SGBD, "SG_CODIEREN", hex) directly. The bytes on the wire are identical to what NCSEXPER's A_*.ipo::Cod handler produces, so the SG accepts both. What we lose by bypassing the IPO:
- Protocol-report writes (
PEM*) — we don't need these; equivalent to the TRC files we already chose to skip. - UI feedback inside the IPO (
digitalout,infobox) — we have our own UI. - Error-recovery state machine (
TestCDHFehler,TestApiFehler) — minor; the EDIABASJOB_STATUSis the same signal. - Multi-step orchestration (
scriptchangechains, e.g. CODIERDATEN_LESEN → ID_COD_INDEX) — this can matter for SGs that gate reads on auth/CI lookups.
For full NCS-faithful behaviour we need to run the IPO via the inpax interpreter plus a CABI binding layer that resolves CDH* into ncsx packages. See cabi-binding-plan.md (planned) for the per-function mapping. Until then, direct apiJob is good enough for single-PSW edits but will hit limits on more complex coding modes.
Disassembling shipped IPOs shows two distinct styles:
| Style | Entry points | Purpose | Typical syscalls |
|---|---|---|---|
A — CABI-style (e.g. 00EK9272.ipo) |
cabimain, cabiexit |
Per-SG diag-job dispatcher. The Jobs function in the IPO switches by job name and calls INPAapiJob(SGBD, JOB, params). Used by INPA's job-runner menu and by NCSEXPER's Kernfunktionen / Job-Liste features. |
INPAapiJob, PEMSGZ_*, setjobstatus, OutputDebugString, plus CDH* calls into the CABI DLL (see §4 below). |
B — INPA-style (e.g. ews.ipo) |
inpainit, inpaexit |
Full interactive diagnostic. Live-data screens, menu navigation, fault-memory views. Used by NCSEXPER's Kernfunktionen flow when the script is interactive (rather than batch-job). | setscreen, setmenu, userbox*, ftextout, digitalout, analogout, INPAapiResult* |
"CABI-style" labels a calling convention, not an cabi.ipo shared library. The script's
entry points (cabimain/cabiexit) and its calls to CDH* / apiJob functions are
resolved through the external-DLL bridge described in §4 — they do not import from
another .ipo file. There is no IPO-to-IPO import mechanism; cross-script transfer only
happens via scriptchange / scriptselect (whole-script replace).
NCSEXPER/SGDAT/ ships 1,798 .ipo files (counted on a current install). The same
directory is shared verbatim with INPA (INPA/SGDAT/ is the same set, often a symlink
or sync target — file counts match exactly). The IPOs split into four naming buckets,
roughly:
| Pattern | Count | Style | What it is |
|---|---|---|---|
A_*.ipo |
168 | CABI-style | Per-CABD-module batch dispatchers (e.g. A_KMB46.ipo). |
D_*.ipo |
240 | INPA-style | Per-diagnose-group interactive scripts (D_KFS.ipo, …). |
| Digit-prefix | 713 | CABI-style | Part-number / EK-job-keyed batch scripts (00EK9272.ipo, 001MVD1722.ipo, 00MDS410.ipo). |
| Functional name | 1,081 | INPA-style | Subsystem diagnostics named by what they do (abs_uc.ipo, airbag.ipo, acc.ipo, afs_read.ipo). |
Zero C_*.ipo files. This trips people up: C_KMB46 is an SGBD basename, but
SGBDs are compiled BEST/2 bytecode and live in EDIABAS/Ecu/ as .prg files. They are
read by api32.dll's interpreter, not by NCSEXPER's IPO interpreter. The two interpreters
share zero code and run on different bytecodes; the name overlap is purely
chassis-vocabulary coincidence.
None of these 1,798 files drive the main NCSEXPER screens. They're all behind the
Basic-Functions (Kernfunktionen) button — when the user clicks one, NCSEXPER's
embedded IPO interpreter loads the matching file, opens an "INPA Bridge16 Window" child
window, and runs the script inside it (see §3). The main coding flow
(SG_CODIEREN / CODIERDATEN_LESEN) never touches any of them.
Aggregated from disassembling five sample SGDAT IPOs (top 30, in order of frequency):
| # of calls | syscall | category |
|---|---|---|
| 236 | PEMSGZ_Kopfzeile |
PEM (report header line) |
| 179 | multianalogout |
UI — multi-gauge |
| 171 | exitwindows |
flow |
| 160 | scriptchange |
flow — load another IPO |
| 90 | ftextclear |
UI — clear formatted text |
| 84 | PEMInitialisiere |
PEM (start report) |
| 80 | select |
UI — choose item |
| 58 | userboxopen |
UI — open text panel |
| 55 | userboxftextout |
UI — write to userbox |
| 53 | PEMProtokollAusgabe |
PEM (emit protocol line) |
| 50 | PEMTrennLinie |
PEM (divider line) |
| 48 | setjobstatus |
flow — EDIABAS job status |
| 39 | digitalout |
UI — digital gauge |
| 34 | winhelpkey |
UI — F1 help binding |
| 30 | scriptselect |
flow — chooser |
| 28 | setscreen |
INPA UI — screen |
| 21 | setmenutitle |
INPA UI — menu title |
| 20 | infobox |
UI — popup |
| 20 | analogout |
UI — single gauge |
| 19 | userboxclose |
UI |
| 18 | setitem |
UI |
| 16 | returnstatemachine |
flow |
| 15 | setstate |
flow |
| 13 | userboxclear |
UI |
| 13 | setcolor |
UI |
| 12 | setmenu |
INPA UI — menu |
| 10 | callstatemachine |
flow |
| 6 | hexdump |
format |
| 5 | stop / viewclose / INPAapiJob |
flow / UI / EDIABAS |
Other syscalls observed at lower frequency (in the ews.ipo sample alone — an interactive INPA script):
INPAapiResultText (50), INPAapiResultDigital (29), ftextout (297), viewopen (2), printfile, printscreen, messagebox, start, INPAapiInit, INPAapiFsLesen, INPAapiFsMode, INPAapiCheckJobStatus, INPAapiResultInt, inputhex, inttostring, inttohexstring, realtostring, stringtoreal, strlen, midstr, settitle, deselect.
| Group | Syscalls |
|---|---|
| EDIABAS bridge | INPAapiInit, INPAapiJob, INPAapiCheckJobStatus, INPAapiResultInt/Text/Digital/Analog/Binary/Sets/Format/Number, INPAapiFsLesen, INPAapiFsMode |
| Print Element Manager (PEM) — writes to the Protokoll report | PEMInitialisiere, PEMSGZ_Kopfzeile, PEMProtokollAusgabe, PEMTrennLinie |
| INPA UI — screens / menus | setscreen, setmenu, setmenutitle, settitle, select, deselect, setitem, setcolor, ftextout, ftextclear, infobox, messagebox, viewopen, viewclose, printscreen |
| INPA UI — userbox widgets | userboxopen, userboxclose, userboxclear, userboxftextout |
| Visualisation | analogout, multianalogout, digitalout |
| Flow / state | callstatemachine, returnstatemachine, setstate, scriptchange, scriptselect, exit, exitwindows, stop, start, setjobstatus, winhelpkey |
| Formatting helpers | hexdump, hexstring, inttostring, inttohexstring, realtostring, stringtoreal, midstr, strlen, inputhex, input2int, printfile |
| Debug | OutputDebugString, OutputDebugInt, OutputDebugBool |
Yes — every IPO it runs does. Both styles call INPA's screen/menu/userbox primitives. NCSEXPER must implement them all in its embedded interpreter — Ghidra confirms by listing the same names in .rdata:
setmenu,setmenutitle,setscreen—0x0048dd44,0x0048f034INPA Bridge16 Window—0x0048f0ecINPA Return Window—0x0048f140INPA-PrintScreen_%05d—0x0048fda0
So NCSEXPER hosts a full INPA UI runtime alongside its main MFC dialog. When an IPO calls setscreen or userboxopen, NCSEXPER opens an INPA-style child window (the "INPA Bridge16 Window" / "INPA Return Window") and routes the output there. The "Protokoll" pane in the MFC dialog displays the PEM output.
The split:
- NCSEXPER's own UI (MFC) — profile/chassis/FA/ZCS entry, ECU picker, Kernfunktionen launcher, coding status bar. Built in C++ MFC.
- INPA-style UI (hosted) — anything an IPO renders via
setscreen/setmenu/userbox*. The same widget primitivesinpa.exeuses, re-implemented inside NCSEXPER. - Protokoll report — assembled by
PEM*syscalls inside IPOs; displayed by NCSEXPER's report window. Printable viaprintfile/printscreen.
CABI-style IPO scripts call functions like CDHGetCabdName, CDHapiJob,
CDHGetFswPswDataFromCbd, etc. These functions are not in another .ipo, and —
despite the misleading opcode name — they are not in a separate DLL either. They are
implemented directly inside NCSEXPER.EXE itself.
| File | What it actually is |
|---|---|
NCSEXPER/BIN/Cabiger.dll (58 KB) |
Localisation resource DLL — German UI/error strings |
NCSEXPER/BIN/CabiUS.dll (55 KB) |
Localisation resource DLL — US English strings |
NCSEXPER/SGDAT/CABI.H (391 lines) |
C-header with 97 extern declarations, fed to the IPO compiler |
Confirmed empirically:
- Both DLLs have a single PE export:
entry(the stock Visual C++DllMainCRT wrapper). NoCDH*,api*, orcabi*symbols are exported. - The
.textsegment is ~11 KB of pure Microsoft Visual C++ runtime helpers (_strncpy,_amsg_exit, etc.). No CABI strings appear in.text. - The bulk of each file (
.rsrc, ~28 KB) is a Windows resource section — localized string tables that NCSEXPER reads withLoadStringA(hCabiDll, id, …)to render German vs English UI text.
The DLLs serve the same purpose as a .po / .mo translation bundle on Linux: load
one or the other at startup to swap the language.
The CDH function bodies are statically linked into the main executable. Ghidra's trace
strings at 0x005af* (e.g. CDHGetCabdName at 0x005afbc4,
CDHGetCodierBaureihe at 0x005afc60 — see
ecu-selection.md §8.5) point at the implementations in
NCSEXPER.EXE's .text:
FUN_00432500— body ofCDHGetCabdName(trivial getter returningDAT_0061a6d4)FUN_00427610— body ofcoapiReadCbdFromBrFUN_00434060— body ofcoapiGetAllSgetData- …
IPO bytecode ───CALLE 0x0D, const_idx──> "CDHGetCabdName" (const-pool string)
│
▼
NCSEXPER's embedded IPO interpreter
looks the string up in its
built-in symbol table (in .data)
│
▼
FUN_00432500 in NCSEXPER.EXE's .text
The opcode is named "Call external DLL" for historical reasons: INPA's earlier 16-bit
design (whose INPA Bridge16 Window window class still appears in NCSEXPER's
.rdata) did resolve these names against real DLL exports. The 32-bit ports linked
the implementations into the host executable instead but kept the opcode name.
The IPO compiler reads CABI.H to typecheck the calls (in/out parameter directions and
types) and emits 0x0D CALLE instructions with the function name in the IPO's constant
pool. At runtime the embedded interpreter resolves the name against NCSEXPER.EXE's
internal symbol table and marshals arguments per the header's signature.
Inpax's interpreter-analysis.md §0x0D
documents the same opcode for INPA.EXE — same model: a built-in symbol table inside the
host executable.
When we add the Kernfunktionen runner, we'll need a CABI binding package that registers
all 97 CABI functions with the inpax interpreter's system-function table, routing each
to the appropriate ncsx package. The contract (signatures, parameter directions) comes
from CABI.H; we don't need to reverse-engineer NCSEXPER's binary implementations
unless behaviour is ambiguous from the header alone. Detailed plan + per-function
package mapping in cabi-binding-plan.md.
- IPOs are the coding hot path. The per-CABD
A_*.ipo(e.g.A_KMB46.ipo) is loaded by NCSEXPER's embedded interpreter and itscabimainswitch dispatches every coding/identity job (Cod,Lesen,FgnrLesen,ZcsLesen,NettoDat,Ident,CILesen,InfoJob) to a handler that calls the CDH* bridge →apiJob. - The "Change Job" dropdown contents come from the IPO's
Jobsfunction, not from SGBD job-table reflection. - IPOs also power the Kernfunktionen menu (diagnostic jobs and live data) — same interpreter, different IPO files (functional-named, not
A_*). SGDAT/holds 1,798 IPOs in four buckets (A_*/D_*/ digit-prefix / functional name) — zeroC_*.ipofiles because SGBDs live inEDIABAS/Ecu/as.prg.- Two styles of IPO live in
SGDAT/: CABI-style (cabimain/cabiexit) for batch job dispatch, INPA-style (inpainit/inpaexit) for interactive screens. - All IPOs use the same INPA UI syscalls (
setscreen,setmenu,userbox*, gauges) plus EDIABAS bridge calls (INPAapiJob,INPAapiResult*) and the PEM print-output API. - NCSEXPER embeds the full INPA UI runtime to make these syscalls work — confirmed by the linked-in window-class strings and the embedded interpreter's function table.
- The
CDH*functions CABI-style scripts call are statically linked into NCSEXPER.EXE itself and reached via the IPO interpreter's built-in symbol table (opcode0x0D CALLE). TheCabiger.dll/CabiUS.dllfiles ship localised resource strings only — no CABI functions are exported from either.
For ncsx: when we get to "run a Kernfunktion" feature, we can reuse the @emdzej/inpax interpreter as-is. The system-function table we need to implement is the INPA one — same names, same signatures — plus the PEM print-output helpers (route to a log buffer instead of a print spool) and the CABI surface (see cabi-binding-plan.md).