Technical Capabilities
Having trouble finding what you need?
Get in touch with us, so we can answer your specific questions directly.
Get In Touch

VX/TDMS – Terminal Data Management System

Overview

VX/TDMS is a native reimplementation of DEC TDMS (Terminal Data Management System) for Linux. It provides source-compatible form handling for migrated OpenVMS applications: existing FDU form listings, RDU request definitions, LDF request libraries, and CDD record layouts compile unchanged through the VX/TDMS compiler, and existing calls to TSS$ services link and run without modification.

The compiler emits reviewable, versioned JSON p-code rather than opaque binary. Every mapping is semantically validated against compiled form and record layouts, and every construct it cannot represent is counted and named rather than silently dropped. VX/TDMS also feeds two modernization paths that reuse the same compiled inventory: conversion to VSI DECforms, and conversion to VX/DECFORMS on Linux.

Architecture and components

VX/TDMS is a compiler and a linked runtime library, not a long-running daemon. There is no server process to administer: the "runtime" is libvtdms_forms, linked directly into the migrated application, and it reads compiled form/request/library/record definitions from disk the moment a program opens a request library. The table maps each OpenVMS TDMS component to its VX/TDMS counterpart.

OpenVMS TDMS componentVX/TDMS counterpartRoleStatus
FDU (Form Definition Utility)tdmsc compile (form path)Parses FDU LIST FORM listings into form p-code: geometry, fields, attributes, validators, access order.live
RDU (Request Definition Utility)tdmsc compile (request path) + C++ front endParses .rdf request definitions into request p-code.live
CDD / DDL record definitionstdmsc compile (record path)Parses CDD DDL into record p-code with exact byte layout, OCCURS/ARRAY, and variants.live
LDF / RLB request librarytdmsc compile (library path)Parses .ldf library definitions into library p-code; models request-lookup membership.live
TSS$ form manager (linked runtime)libvtdms_formsC++ runtime exporting the TSS$ call surface; loads p-code, binds records, renders forms, executes requests.live
FDU interactive form editorBrowser-based form editorEdits VX/TDMS form p-code (form / layout / assign / order phases).partial: p-code editor, not a terminal emulator
DECforms conversion (VSI OpenVMS)IFDL translation pathTranslates TDMS assets to VSI DECforms IFDL; rewrites TSS$ calls to FORMS$.partial
VX/DECFORMS target (Linux)VX/DECFORMS compiler, extendedLowers TDMS request syntax directly into VX/DECFORMS.v1
No server to administer. Unlike ACMS-style multi-process runtimes, VX/TDMS has no daemon and no IPC substrate to configure. The compiled p-code is read directly by the runtime library at TSS$OPEN_RLB time.

Call lifecycle / data flow

The compile-time flow runs offline, once per estate:

Source inputs
FDU form listingLIST FORM input in .lis format
RDU requestRequest definition input in .rdf format
LDF libraryLibrary definition input in .ldf format
CDD DDLRecord definition input in .ddl format
01 – ConvergeCompile the estatecompile or compile-all selects the parser for each source type.
02Write JSON p-codeProduce the form, request, library, and record p-code formats.
03Validate semanticsvalidate-semantics checks mappings against the compiled form and record definitions.
04 – Optional outputGenerate bindingsCreate language bindings when the migrated application needs them.

The run-time flow happens per interactive session, inside the migrated program:

01Open channelThe program calls TSS$OPEN and receives a channel.
02Open request libraryTSS$OPEN_RLB(lib-file) returns a library ID while the runtime loads request, form, and record p-code.
03 – TSS$REQUEST(ch, lib, name, %REF rec1, rec2)
03ADecode recordsRead the caller's record buffers by reference.
03BApply OUTPUT mappingsMove record values into the form fields.
03CRender formPaint the form through the ANSI terminal path.
03DCollect INPUTAccept the interactive field values.
03EValidateRun the form and request validation rules.
03FWrite recordsReturn values through the same record buffers.
03GRun request actionsExecute actions such as RESET, REVERSE, and RING BELL.
04Close request libraryTSS$CLOSE_RLB releases the loaded library.
05 – DoneClose channelTSS$CLOSE(ch, clear) ends the interactive session.

The record buffers passed to TSS$REQUEST are opaque to the caller and shared with the runtime by reference; the runtime reads and writes fields at the byte offsets recorded in that request's record p-code. This is the crux of source-level compatibility: the program's RECORD variable is the same bytes the runtime edits.

Source languages

VX/TDMS compiles four related definition file types. Only the RDF request language is a full imperative form language; the others are structured metadata.

File typeOpenVMS originContents
*_form.lisFDU LIST FORM outputForm name, formwide attributes, screen width, help form, field access order, per-field name / picture / length / default / help / attributes / validators / video / scrolling / indexing.
*_req.rdfRDU request sourceForm binding, record binding, mappings, control fields, program keys, video actions, waits.
*_lib.ldfLDF library sourceREQUEST IS / REQUESTS ARE, FILE IS, membership.
*.ddlCDD/DDL record sourceDEFINE RECORD, STRUCTURE, nested structures, OCCURS/ARRAY, variants, field types.

The RDF request language

A request binds one or more program records to a form and moves data between them. The example below is a representative request showing the constructs VX/TDMS handles end to end: record binding with an alias, a program key, top-level and nested CONTROL FIELD cases, subscripted array mappings, and %LINE loop variables.

FORM IS ACCOUNT_INQUIRY_FORM;

RECORDS ARE ACCOUNT_REC WITH NAME ACCTREC,
            CDD$TOP.BANKING.DATA.SYST_STNDFORM_REC WITH NAME STNDFORM;

DESCRIPTION
/* Displays outstanding statements for an account. Control field is FLAG. */;

RESET FIELD %ALL;
USE FORM ACCOUNT_INQUIRY_FORM;

PROGRAM KEY IS GOLD "E"
     NO CHECK;
     RETURN "ABORT   " TO STNDFORM.RECORD_LOCK;
END PROGRAM KEY;

OUTPUT
     ACCTREC.ACCOUNT_ID                 TO ACCOUNT_ID,
     ACCTREC.STATEMENT_NUMBER[1 TO 30]  TO STATEMENT_NUMBER[1 TO 30],
     ACCTREC.SELECT_STATEMENT[1 TO 30]  TO SELECT_STATEMENT[1 TO 30];

CONTROL FIELD IS FLAG

     'START  ':
          OUTPUT STNDFORM.PGM_NAME TO PGM_NAME,
                 ""                 TO STATEMENT_DATE[1 TO 30];
          INPUT  ACCOUNT_ID          TO ACCTREC.ACCOUNT_ID;

     'ACCT   ':
          REVERSE FIELD ACCOUNT_ID;
          INPUT ACCOUNT_ID TO ACCTREC.ACCOUNT_ID;

     'DISPLAY':
          CONTROL FIELD IS DATE_FLAG[%LINE = 1 TO 30]
               "X":
                    OUTPUT ACCTREC.STATEMENT_DATE[%LINE]  TO STATEMENT_DATE[%LINE];
                    INPUT  SELECT_STATEMENT[%LINE]         TO ACCTREC.SELECT_STATEMENT[%LINE];
          END CONTROL FIELD;

END CONTROL FIELD;

END DEFINITION;

An empty string literal ("") as an OUTPUT source clears the receiving field. A CONTROL FIELD nests arbitrarily and its cases are matched against the named field's value. %LINE and %ENTRY are loop variables over subscript ranges, and field references may be dotted CDD record paths.

RDF instruction set

InstructionMeaning
FORM IS <name> / USE FORM / DISPLAY FORMBind / select / paint a form (name may be a dotted CDD path).
RECORD IS / RECORDS ARE … WITH NAMEBind one or more program records, with an optional alias.
DESCRIPTION /* … */Comment block.
RESET FIELD %ALL | <list>Clear all or listed fields.
PROGRAM KEY IS GOLD "k" [NO CHECK] … END PROGRAM KEYGOLD-prefixed key handler.
OUTPUT <src> TO <formfield>, …Move record or literal value to the form.
INPUT <formfield> TO <recfield>, …Move form value to the record.
RETURN <lit> TO <field>Assign a literal (also stands alone in control-field arms and program keys).
CONTROL FIELD IS f[subscript] … END CONTROL FIELDCase dispatch on a field value; nests arbitrarily.
[NO] REVERSE / BLINK / BOLD / UNDERLINE FIELD <list>Video attribute on listed fields.
RING BELLSound the terminal bell.
SIGNAL OPERATOR / SIGNAL MODE IS REVERSEOperator signal / signal mode.
WAIT / NO WAITBlock / don't block for input.
CHECK / NO CHECKEnable / skip field validation.
CLEAR SCREENClear the screen.
END DEFINITIONRequest terminator.

Subscripts are multi-dimensional with loop variables: [1 TO 30], [%LINE], [%LINE = 1 TO 100, %ENTRY = 1 TO 25].

Parenthesised multi-receiver mappings. Some DEC documentation mentions a parenthesis form for multiple mapping receivers, e.g. (field1, field2) …. VX/TDMS's compiler accepts the parenthesis-free list form throughout, which is the canonical RDU form. We have not yet seen a production request that requires the parenthesised variant.

FDU form field attributes

All represented in form p-code:

  • Display Only: field not mapped for input.
  • No Echo: data entered is not displayed.
  • Autotab: cursor advances automatically when the field fills.
  • Default value: shown at run time unless a request overrides it.
  • Field help: message shown on the HELP key.
  • Clear character: the fill character in empty positions.
  • Clear / zero suppression: suppress a leading zero for a null numeric field.
  • Validation: RANGE, CHOICE, SIZE, CHECK DIGIT.
  • Video attributes: BOLD, REVERSE, BLINK, UNDERLINE.
  • Access order: left-to-right, top-to-bottom field visitation.

Reserved keys: HELP (PF2/F15), MENU (GOLD-KP7), RETURN/ENTER saves field values, GOLD is the prefix for program keys.

The TSS$ runtime API

VX/TDMS ships a C++ runtime (libvtdms_forms) that exports the OpenVMS TSS$ call surface so recompiled application code links unchanged. Each routine is exported under three names: the VMS dollar-sign name (via an assembler alias, so existing object references resolve), a portable TSS_ C alias, and a C++ class behind both.

VMS ABI

Return convention: the function result is a VMS condition value returned by value: odd means success, even means failure. Callers test ODD(...). Canonical signatures:

ret = TSS$OPEN      (channel.wlu.r [, device.rt.dx])
ret = TSS$CLOSE     (channel.rlu.r [, clear-screen.rlu.r])
ret = TSS$OPEN_RLB  (rlb-file-spec.rt.dx, library-id.wlu.r)
ret = TSS$CLOSE_RLB (library-id.rlu.r)
ret = TSS$REQUEST   (channel.rlu.r, library-id.rlu.r, request-name.rt.dx
                     [, record1.mz.r [, ... recordN.mz.r]])
ret = TSS$READ_MSG_LINE (channel.rlu.r, response-text.wt.dx
                     [, message-prompt.rt.dx] [, response-length.wwu.r])
ret = TSS$WRITE_MSG_LINE(channel.rlu.r, message-text.rt.dx)
ret = TSS$SIGNAL                                  ; signal last condition, no args
ret = TSS$COPY_SCREEN (channel.rlu.r [, file-spec.rt.dx] [, append-flag.rlu.r])
ret = TSS$DECL_AFK  (channel.rlu.r, key-id.rlu.r, key-efn.rlu.r,
                     key-astadr.szem.r [, key-astprm.rlu.v])

Routine table

RoutinePurposeStatus
TSS$OPENOpen a terminal channel (writes channel out).live
TSS$CLOSEClose channel, optional clear-screen.live
TSS$OPEN_RLBOpen a request library by file spec (writes library-id out).live
TSS$CLOSE_RLBClose a request library.live
TSS$REQUESTExecute a named request against zero or more record buffers (variadic, by reference).live
TSS$SIGNALSignal the last condition value.live
TSS$READ_MSG_LINERead a response from the message line.live
TSS$WRITE_MSG_LINEWrite text to the message line.live
TSS$CANCELCancel outstanding I/O on a channel.live
TSS$COPY_SCREENCopy screen to a hardcopy file (honours the TSS$HARDCOPY logical).live
TSS$STRACE_ON / TSS$STRACE_OFFScreen-trace to a portable trace file.live
TSS$DECL_AFK / TSS$UNDECL_AFKDeclare / remove an async function-key handler.live
TSS$OPEN_A / TSS$REQUEST_A / TSS$CLOSE_AAsync variants.partial: complete synchronously, status delivered through the RSB pointer

TSS$HARDCOPY is honoured as the default TSS$COPY_SCREEN destination when set as a logical name / environment variable. TSS_TRIGGER_AFK (and interactive AFK:<key-id> input) fires a declared async function-key callback with the channel, key-id, and the caller's AST parameter.

Request-execution semantics

TSS$REQUEST does the real work. On every call it:

  • Loads the request / form / record p-code for the named request.
  • Decodes raw record buffers at compiled offsets: TEXT, signed/unsigned integer, DATE, F_FLOATING, G_FLOATING, UNSIGNED QUADWORD, and BIT all decode to display text and encode back to native storage bytes on input.
  • Applies RDF OUTPUT mappings: record/literal to form, including control-field cases and indexed repeated fields.
  • Renders the form: live terminal output uses ANSI cursor addressing and SGR video attributes; copy-screen output stays plain text for deterministic hardcopy comparison.
  • Applies RDF INPUT mappings, validates the value (required, uppercase, numeric, choice, range, size, check digit, length), and writes it back into the raw application record buffer.
  • Runs request actions: field reset, video attributes, RING BELL, SIGNAL OPERATOR, WAIT, NO WAIT.

Real call path (VAX Pascal)

A migrated program links this unchanged against libvtdms_forms; note the ODD() status test and %REF (by-reference) record passing:

{ open channel + request library }
IF NOT ODD (TSS$OPEN(TDMS_CHANNEL))                       THEN TDMS_STATUS := TSS$SIGNAL;
IF NOT ODD (TSS$OPEN_RLB('BANKING_RLB:ACCT_INQUIRY_LIB',
                          TDMS_LIB_ID))                   THEN TDMS_STATUS := TSS$SIGNAL;

{ run a request against two program records, passed by reference }
IF NOT ODD (TSS$REQUEST(TDMS_CHANNEL,
                        TDMS_LIB_ID,
                        'ACCOUNT_INQUIRY_REQ',
                        (STNDFORM),      { %REF record 1 }
                        (ACCTREC)))      { %REF record 2 }
THEN TDMS_STATUS := TSS$SIGNAL;

{ … application reads/writes fields of ACCTREC, which the runtime just filled … }

{ tear down }
IF NOT ODD (TSS$CLOSE(TDMS_CHANNEL, CLEAR_SCREEN))        THEN TDMS_STATUS := TSS$SIGNAL;
IF NOT ODD (TSS$CLOSE_RLB(TDMS_LIB_ID))                   THEN TDMS_STATUS := TSS$SIGNAL;

The equivalent portable C entry point is declared as extern "C" int TSS_REQUEST(int channel, int library_id, const char* request_name, ...);, returning the condition value by value.

Data model: JSON p-code

The compiler emits versioned JSON p-code, one file per source artifact, in four formats. Every file carries format, version, source, diagnostics, and summary keys; readers ignore unknown future keys and error on missing required keys. This is deliberate: reviewable p-code is what makes a migration regression-testable against the original OpenVMS behaviour, instead of trusting an opaque binary translation.

FormatFromKey contents
VXTDMS-FORM-PCODE.lismeta (form name, line range, screen width), access_order, fields, ops.
VXTDMS-REQUEST-PCODE.rdfname, form_name, records (in TSS argument order), actions, mappings, program_keys.
VXTDMS-LIBRARY-PCODE.ldfname, file, requests.
VXTDMS-RECORD-PCODE.ddlname, root, byte_size, fields (flattened with byte layout).

A form field entry carries name, source line, row/col, length, display length, datatype, picture type, attributes, occurs count, index direction, scroll flag, and validators. An op entry is clear, draw_text, or field. A request mapping entry carries direction (OUTPUT/INPUT/RETURN), source, target, source line, and the active CONTROL FIELD conditions. A record field entry carries name, full nested CDD path, datatype, element size, byte offset, byte size, and dimensions (from OCCURS/ARRAY) with per-element offsets.

Integrity properties enforced by the compiler

  • Per-element offsets, not contiguous-array assumptions, so nested STRUCTURE OCCURS and CDD variant overlays are represented faithfully. Fields from different variant branches may legally share the same offset.
  • Fixed record byte size is compiled and used to size the raw buffer the runtime decodes and writes.
  • Semantic validation resolves every mapping operand against compiled form fields and record paths, accepting dotted paths and unique nested-suffix references, and reports form/record reference errors, missing fields, static subrange violations, occurrence-count mismatches, and type-category mismatches, each with source path, RDF line, and severity.
Not a black box. Every construct the compiler cannot represent is counted and named on stderr with filename, line, and construct text, never silently dropped. Duplicate form names are uniquified with a warning, not silently.

Build and compile pipeline

The compiler driver is tdmsc. The C++ runtime side builds with CMake ≥ 3.16 and C++17.

Pipeline steps

StepCommandInputOutput
Compile one artifacttdmsc compile <file> [-o out.json]One .lis / .rdf / .ldf / .ddl (type by extension)One *.tdms.json
Compile a whole estatetdmsc compile-all <root> -o <dir> [--validate-semantics]Tree of forms / requests / libraries / recordsDirectory of p-code + counters
Validate semanticstdmsc validate-semantics <pcode-dir>Compiled p-code directoryMapping diagnostics + counters
Render p-codetdmsc render <pcode> [--values v.json] [--html f.html]Form p-codeText or HTML screen
Render a requesttdmsc render-request --form F --request R [--records recs.json]Form + request p-code + record dataRendered screen after OUTPUT mappings
Validate valuestdmsc validate <pcode> <values.json>Form p-code + field valuesValidation messages
Generate bindingstdmsc gen-bindings <record-pcode> -o <dir> [--language c|pascal|all]Record p-code.h / .inc byte-layout bindings

Worked example

# 1: compile the whole estate
tdmsc compile-all /opt/tdms/banking/cdd -o /tmp/tdms_pcode
#    → compiled=500 fields=8740 ops=22087 warnings=3 unsupported=0 errors=0

# 2: validate every mapping against compiled forms + records
tdmsc validate-semantics /tmp/tdms_pcode
#    → semantic_requests=109 semantic_mappings=5030 semantic_warnings=2 semantic_errors=0

# 3: generate C + Pascal byte-layout bindings for the record p-code
tdmsc gen-bindings /tmp/tdms_pcode -o /tmp/tdms_bindings --language all

On a representative production-scale estate this breaks down as 105 forms, 109 requests, 64 libraries, and 222 records: 500 files, 8,740 fields, 22,087 rendering ops, and 5,030 request mappings, all validated with zero semantic errors.

Skip/fail discipline. The compiler is not silent best-effort. Every run prints warnings=, unsupported=, and errors= counters; an unrepresentable construct is named on stderr with filename, line, and construct text. Exit code is non-zero on errors.

CLI reference

SubcommandArgs / flagsNotes
compileinput, -o/--outputType chosen by extension; exit 1 on errors.
compile-treeroot, -o (required), --patternForms only.
compile-allroot, -o (required), --validate-semanticsForms + RDF + LDF + DDL; uniquifies duplicate output names with a warning.
renderpcode, --values, --html, --no-fields, --no-defaults
render-request--form (required), --request (required), --records
validatepcode, valuesExit 1 if any messages.
validate-semanticspcode_dirExit 1 on semantic errors.
gen-bindingsrecord_pcode, -o (required), --language {c,pascal,all}

Incremental rebuilds

compile recompiles a single artifact to a single p-code file. There is no daemon to reload. The runtime reads p-code fresh at TSS$OPEN_RLB time, so a recompiled p-code file takes effect on the next run. The browser form editor writes a timestamped backup beside the file before replacing it.

Modernization paths and divergences

VX/TDMS supports three target runtimes off the same compiled inventory: form listings, requests, libraries, and record metadata. Path 1 keeps the application on VX/TDMS's own TSS$ runtime on Linux; Paths 2 and 3 convert the application to a DECforms-family target.

ItemPath 1: VX/TDMS runtimePath 2: TDMS → VSI DECformsPath 3: TDMS → VX/DECFORMS (Linux)
Compile forms/requests/libraries/records to reviewable p-codelivelive (source for the translator)live
Run original TSS$ calls unchangedliven/a: calls rewritten to FORMS$n/a: calls rewritten to FORMS$
Semantic validation of all mappingslivelivelive
Translate to DECforms IFDLout of scopepartialvia VX/DECFORMS TDMS extensions
TSS$ → FORMS$ program conversionout of scopepartialpartial
TDMS request syntax lowered directly by the forms compilern/aout of scope: VSI DECforms is nativev1: design target 99.99% automated
Async TSS$*_A fidelitypartial: synchronous completion via RSBreportedreported
Interactive FDU terminal-keypad editingout of scope: p-code editor onlyout of scopeout of scope

What DEC's own converter drops, and what VX/TDMS preserves

Path 2 targets the native DECforms product and inherits DEC's own Converter limitations, documented in the DECforms TDMS Conversion Guide. That guide states plainly that the Converter converts forms and requests but does not convert the program, and, even for forms and requests, does not convert eight specific things. VX/TDMS was built to preserve every one of them, because it compiles from source rather than from a built .RLB file.

DEC Converter will not convertWhy DEC drops itVX/TDMS status
CHECK DIGIT field validatorDECforms has no CHECK DIGIT validator.Preserved: VX/TDMS compiles and executes the validator.
Default key definitionsDECforms binds built-in functions to keys; only ERROR/GOLD/HARDCOPY are emulated.Modelled: PROGRAM KEY IS GOLD "k" … END PROGRAM KEY is represented natively.
Dependent ranges + nested CONTROL FIELD ISDECforms has no looping mechanism and no case structure, so you redesign by hand.Preserved: the biggest single differentiator. Nested control fields and %LINE/%ENTRY dependent ranges parse and validate cleanly.
DESCRIPTION instructionDEC reads the built .RLB, which does not retain comment text.Available: VX/TDMS compiles from RDF source, so the text is present.
LIGHT LIST instructionNot every terminal has the referenced LEDs.Path-3 mapping to an operator message is a planned equivalent.
Operator input order (ORDER phase)The Converter doesn't maintain the field order you specify.Preserved: VX/TDMS records access_order in every form p-code.
Non-default scale factorsThe Converter converts only the default scale factor from the picture.Preserved: VX/TDMS emits the compiled scale_factor into form p-code fields.
Values in RING BELL (up to 255)DECforms SIGNAL takes no count.Not yet exercised against a request that specifies a count.
DEC's own prerequisites for Path 2. The Converter only handles forms and requests built with TDMS ≥ 1.7. It also does not support CDD/Plus record variants or all CDD/Plus data types. DEC requires removing variants and changing unsupported field types by hand before conversion. VX/TDMS represents variant branches as overlay offsets and decodes the full datatype set directly, so this prerequisite does not apply on the VX paths.

Path 3: the compatibility superset

Rather than force every TDMS concept into standard DECforms, VX/DECFORMS is extended to understand TDMS request syntax and runtime behaviour directly, and the same migration rewrites TSS$ calls to FORMS$. Because the compiler lowers TDMS syntax natively, most of the limitations above simply don't arise on this path: nested CONTROL FIELD IS, dependent ranges, program keys, explicit and subrange mappings, video attributes, and operator signals are all recognized constructs. Where a behaviour still needs project-specific policy, the compiler emits a compatibility diagnostic rather than silently changing meaning.

Scope honesty. Path 3's DECforms-lowering and the TSS$ → FORMS$ rewrite are under active development today. The claim above is grounded in VX/TDMS's verified p-code coverage of these constructs, not yet in a measured end-to-end Path 3 conversion of a full production estate.

Deliberate divergences (Path 1 runtime behaviour)

DivergenceReason
TSS$OPEN_A / TSS$REQUEST_A / TSS$CLOSE_A complete synchronously, writing status through the RSB pointerLinux has no VMS AST/$WAITFR kernel primitive; the caller's $WAITFR/RSB sequence still observes completion correctly.
Live rendering uses ANSI/SGR; TSS$COPY_SCREEN output is plain textDeterministic hardcopy and byte-stable regression comparison against reference captures.
TSS$STRACE_ON/OFF writes a portable trace fileNo VMS screen-trace facility exists on Linux; the trace is logged messages, screens, and copies.
The form editor edits VX/TDMS JSON p-code, not byte-for-byte CDD/FDU storageIt is a p-code editor, not an FDU terminal emulator; full FDU keypad equivalence is future work.
Validation-failure behaviour, message-line geometry, and .RLB/.FRM binary layouts are VX-defined in placesThe most detailed DEC Request/Programming/Forms/Reference manuals are unrecoverable; where public documentation is silent, VX/TDMS defines and documents its own behaviour.

Operator and runtime management

There is no server to administer. Runtime behaviour is controlled by logical names (honoured as environment variables) and a handful of files.

ControlEffect
TSS$HARDCOPY / TSS_HARDCOPYDefault destination file for TSS$COPY_SCREEN when no file argument is given.
VXTDMS_PCODE_DIRDirectory the runtime loads compiled p-code from.
VXTDMS_INPUT_<FIELD>Supplies an INPUT field value in non-interactive runs (otherwise the current form value; interactive runs prompt on stdin).
AFK:<key-id> (interactive input) / TSS_TRIGGER_AFKFires a declared async function-key callback.
Screen-trace file (TSS$STRACE_ON/OFF)Portable log of rendered request screens, messages, and copy-screen operations.

Form editor

The browser-based form editor moves through a form → layout → assign → order phase model. It loads and saves the same JSON p-code the runtime consumes, validates on save, and writes a timestamped backup before replacing the file. It lists valid form p-code files in the current directory and counts and reports files that fail validation rather than hiding them. It binds to a configurable local address and port, with a no-browser flag for headless use.

Verified against a real DEC TDMS reference install. The runtime's test suite executes DEC's own Installation Verification Procedure: add, display, change, display, delete, negative-message, exit, all through real TSS_REQUEST calls, and compares the rendered screens against a golden capture taken from a live OpenVMS TDMS install, field for field, with zero mismatches.

Quick reference

tdmsc subcommands

compile
compile-tree
compile-all
corpus
render
render-request
validate
validate-semantics
gen-bindings

RDF verbs

FORM IS
USE FORM
DISPLAY FORM
RECORD IS
RECORDS ARE
WITH NAME
RESET FIELD
PROGRAM KEY IS GOLD
NO CHECK
END PROGRAM KEY
OUTPUT
INPUT
RETURN
CONTROL FIELD IS
END CONTROL FIELD
REVERSE FIELD
BLINK FIELD
BOLD FIELD
UNDERLINE FIELD
RING BELL
SIGNAL OPERATOR
SIGNAL MODE IS REVERSE
WAIT
NO WAIT
CHECK
CLEAR SCREEN
END DEFINITION

TSS$ routines

TSS$OPEN
TSS$CLOSE
TSS$OPEN_RLB
TSS$CLOSE_RLB
TSS$REQUEST
TSS$SIGNAL
TSS$READ_MSG_LINE
TSS$WRITE_MSG_LINE
TSS$CANCEL
TSS$COPY_SCREEN
TSS$STRACE_ON
TSS$STRACE_OFF
TSS$DECL_AFK
TSS$UNDECL_AFK
TSS$OPEN_A
TSS$REQUEST_A
TSS$CLOSE_A

Return convention: odd = success, even = failure; test ODD(...).

P-code formats

VXTDMS-FORM-PCODE
VXTDMS-REQUEST-PCODE
VXTDMS-LIBRARY-PCODE
VXTDMS-RECORD-PCODE

CDD datatypes decoded / written by the runtime

TEXT
signed integer
unsigned integer
DATE
F_FLOATING
G_FLOATING
UNSIGNED QUADWORD
BIT

Validators

RANGE
CHOICE
SIZE
CHECK DIGIT
required
uppercase
numeric
length

Key file paths

PathContents
compiler/tdmsc, compiler/tdms_formsCompiler driver and per-format parsers.
src/form_runtime.cpp, include/vtdms/form_runtime.hppThe TSS$ runtime.
tools/tdms_form_editorBrowser-based form p-code editor.
docs/PCODE_SCHEMA.mdP-code format schema reference.
docs/SEMANTICS.mdSemantic validation rules reference.

Frequently Asked Questions

Curious about how Sector7 can facilitate your application migration? Explore our FAQs for expert insights.

What is VX/DECFORMS and how does it help migrate DECforms?

VX/DECFORMS is Sector7's reimplementation of OpenVMS DECforms for Linux and Windows. It compiles your existing IFDL form definitions to C++ and provides the FORMS$ Form Driver runtime, so DECforms applications keep running with the same screens and behaviour after migrating off OpenVMS.

What is FORMS$ and does VX/DECFORMS keep that API?

FORMS$ is the DECforms Form Driver callable interface. VX/DECFORMS provides a library of FORMS$ APIs that mirror the original DECforms API, including ENABLE, DISABLE, SEND, RECEIVE, TRANSCEIVE, and CANCEL, so it stays compatible with the way OpenVMS applications call DECforms.

Can VX/CDD preserve existing dictionary-based application workflows?

Yes. VX/CDD is designed to retain established CDD concepts such as repositories, directories, fields, records, qualified names, and language-specific declarations. Existing applications can continue to use familiar dictionary references and compatible interfaces, helping modernization teams preserve application structure instead of replacing the dictionary model.

What is VX/CDD and how does it support OpenVMS modernization?

VX/CDD is Sector7's implementation of the OpenVMS Common Data Dictionary and CDD/Repository workflow for Linux. It stores shared, language-independent field and record definitions, then makes them available through familiar utilities, compatible callable interfaces, and language-specific extraction. This helps organisations modernise onto Linux while retaining established data definitions and application integration points.

Which terminals does VX/DECFORMS support?

It supports all VT terminals from VT100 upward, including DECterm under DECwindows, and can be extended for non-VTxxx terminals. Its built-in C-based terminal drivers work across Linux and Windows without curses, termcap, or external terminal configuration files.

Transform Your Legacy Software Today!

Get In Touch
Unlock the potential of your legacy software with our expert migration services.