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/ACMS – OpenVMS ACMS(TDF,MDF,GDF,ADF) to C++

Overview

VX/ACMS is an ACMS migration tool that translates OpenVMS ACMS ADF, MDF, GDF, and TDF definitions into C++ and provides a runtime that mirrors the original VMS ACMS. Migrated ACMS applications run on Linux and Windows without a transaction monitor: the ADU toolchain compiles the four definition-file types into loadable modules, and the C++ runtime binds the task, menu, and procedure components directly into a single compiled executable.

The translated C++ stays close to the original ACMS structure, so the code remains readable and maintainable. Translation can be a one-time conversion, where you keep the generated C++ from then on, or part of a continuous-integration workflow that recompiles and relinks the ACMS system on every build. Control flow, workspaces, procedure servers, and step processing are all preserved, while commit and rollback are delegated to the database layer such as Oracle.

Translating ACMS to C++

VX/ACMS translates OpenVMS ACMS ADF, MDF, GDF, and TDF definitions into C++, preserving the logic and structure of the original ACMS application. The generated code follows the shape of the original definitions closely, so an existing ACMS system migrates with minimal rewriting and the result reads like the ACMS it came from.

WorkflowWhat happensWhen to use it
One-time conversionTranslate the ACMS definition files to C++ once, then maintain the C++ from that point onward.You are leaving ACMS behind and want C++ as the system of record.
Continuous integrationKeep the ACMS sources authoritative and re-run the translator on every build, recompiling and relinking the system automatically.You still edit ACMS definitions and want the C++ regenerated on each change.
Readable output by design. The compiled C++ structure closely follows the original ACMS syntax, so developers who know the ACMS application can read and maintain the translated code without learning a new internal representation.

From definitions to a running executable

At its simplest, VX/ACMS bundles the TDF, MDF, and procedures (built as dynamic libraries) into a single executable unit. This mirrors how ACMS applications behave on VMS, but instead of requiring a separate transaction monitor, VX/ACMS integrates these components directly into the C++ runtime.

Inputs
ACMS definitionsADF, GDF, MDF, and TDF sources
CDD record layoutsShared workspace structures
.MSG message filesVSI message definitions
All inputs converge into translation
adu translateSelects the translator for each input file type.
Produces
Readable C++ sourcePreserves the structure of the original ACMS definitions.
Optional --compile
.so or executableThe C++ compiler produces a loadable module or single runtime.

The same codebase serves two integration levels. The simplest embedded deployment binds the components into one executable with no transaction monitor. The full deployment is a live, multi-process ACMS runtime built from the same translated modules, so a project can start embedded and grow into the process model without re-translating.

The four definition files

ACMS applications are described in four VMS Application Definition Utility source languages. The adu translator compiles all four, plus VSI .MSG message files. The source kind is auto-detected from the file extension, or forced with a flag.

ExtensionNameOpening lineTranslates toFlag
.TDFTask Definition FileREPLACE TASK <path>:<name>Task step program plus workspace declarations.--tdf
.GDFtask Group Definition FileREPLACE GROUP <path>:<name>One server module per SERVER, plus the procedure table.--gdf
.ADFApplication Definition FileREPLACE APPLICATION <path>:<name>Application module binding task groups and server pools.--adf
.MDFMenu Definition FileREPLACE MENU <path>:<name>Menu module, a C-style menu structure that mimics MENU/TASK definitions.--mdf
.MSGVSI message file.FACILITY ... .SEVERITY ...A .h header, a COBOL copybook, and a runtime message catalog.--msg

How the four relate

The MDF names the tasks a submitter can select; the ADF binds task groups and server pools; the GDF rosters servers, procedures, workspaces, and tasks; each TDF is one task's step program. Workspace record layouts come from CDD, which keeps a workspace's C struct and its COBOL copybook byte-identical.

ApplicationADFBinds task groups, server control attributes, task defaults, and server pools.
Task group is
Task groupGDFRosters the servers, procedures, workspaces, and tasks used by the application.
GDF branches to its runtime definitions
Servers areProceduresCOBOL or C shared objects
Workspaces areCDD recordsByte-identical C structs and COBOL copybooks
Tasks areTDFOne step program for each task
Entries select a task in the application
MenuMDFENTRIES: TASK IS <t> IN <app> drives task selection.

A Task Definition File (TDF)

A TDF declares its workspaces and task arguments, then a labelled sequence of steps. This banking demo task reads a customer record through an exchange step, dispatches to a server procedure, and uses a CONTROL FIELD action part to branch on the operator's key:

REPLACE TASK banking_task:funds_transfer_task

DEFAULT FORM IS banking_form;

USE WORKSPACES account_wksp,
               transfer_wksp,
               sendctrl_wksp,
               audit_wksp;

TASK ARGUMENTS ARE account_wksp  WITH ACCESS MODIFY,
                   transfer_wksp WITH ACCESS READ,
                   audit_wksp    WITH ACCESS WRITE;

BLOCK WITH FORM I/O

get_request:
    EXCHANGE
        TRANSCEIVE RECORD   account_wksp, transfer_wksp
        SENDING             account_wksp
        RECEIVING           account_wksp, transfer_wksp
        WITH SEND CONTROL   sendctrl_wksp;
    ACTION IS
        CONTROL FIELD sendctrl_wksp.ctrl_key
        "QUIT"  : EXIT TASK;
        "CANCL" : EXIT TASK;
        NOMATCH : MOVE "     " TO sendctrl_wksp.ctrl_key;
        END CONTROL;

post_debit:
    PROCESSING
        CALL PROCEDURE debit_account_proc
        IN    banking_write_server
        USING account_wksp, transfer_wksp, audit_wksp;
    ACTION IS
        IF (ACMS$T_STATUS_TYPE = "B") THEN
            GET MESSAGE INTO audit_wksp.messagepanel;
            GOTO PREVIOUS EXCHANGE;
        END IF;

END DEFINITION;

A Group Definition File (GDF)

The GDF binds servers, procedures, workspaces, and tasks. Both the plural PROCEDURES ARE and singular PROCEDURE IS server clauses are accepted. The adu --gdf pass emits one server module per SERVER, with a procedure table indexed by procedure id:

SERVERS ARE
    banking_read_server:
      PROCEDURE SERVER IMAGE IS "banking_default:banking_read_server.exe";
      INITIALIZATION PROCEDURE IS banking_read_init;
      RUNDOWN ON CANCEL IF INTERRUPTED;
      PROCEDURES ARE find_customer_proc, read_account_proc,
                     account_details_proc;
    banking_write_server:
      PROCEDURE SERVER IMAGE IS "banking_default:banking_write_server.exe";
      INITIALIZATION PROCEDURE IS banking_write_init;
      PROCEDURE IS debit_account_proc;
END SERVER;

TASKS ARE
      funds_transfer_task : TASK IS banking_task:funds_transfer_task;
      enquiry_task        : TASK IS banking_task:enquiry_task;
END TASKS;

Task control flow in C++

All TDF control statements are supported, and the translated C++ stays close to the original TDF logic. The task step engine and the TDL parser agree on the same set of statement kinds, so a step that parses is a step that runs. The commonly cited control structures are a subset of the full step set below.

Supported TDF control structures

IF THEN ELSE
WHILE DO
SELECT FIRST TRUE
CONTROL FIELD
GOTO

Full step and statement set

Statement kindTDL constructRole
BLOCKBLOCK ... END BLOCK (incl. WITH FORM I/O, WITH TRANSACTION)Step-block container.
EXCHANGEEXCHANGE ... TRANSCEIVE / SEND RECORDForm I/O step.
PROCESSINGPROCESSING ... CALL PROCEDURE ... IN ... USINGServer-procedure call step.
IFIF (cond) THEN ... ELSE ... END IFConditional.
SELECT FIRST TRUESELECT FIRST TRUE (cond): ... OTHERWISE:Multi-way select.
CONTROL FIELDCONTROL FIELD <f> "lit": ... NOMATCH:Value dispatch.
WHILEWHILE DOLoop.
MOVEMOVE <src> TO <dst>Field assignment (multi-pair).
GOTOGOTO (NEXT | PREVIOUS) (STEP | EXCHANGE | PROCESSING) [label]Relative or labelled jump.
REPEATREPEAT STEP / REPEAT TASKBackward-goto loops (safety-capped).
EXITEXIT BLOCK / EXIT TASK / CANCEL TASKFlow exits and task cancel.
ATOMIC... WITH TRANSACTION / ATOMIC blockTransaction bracket.
COMMIT / ROLLBACKCOMMIT TRANSACTION / ROLLBACK TRANSACTIONExplicit transaction statements (executed by the database layer).
RAISERAISE ... EXCEPTIONRaise a condition.
GET MESSAGEGET [ERROR] MESSAGE INTO <field>Message retrieval into a workspace field.
CDD workspaces become C structs. CDD workspace definitions are translated into packed C structures with fixed-width, space-padded text fields, pinned to the original byte layout with static_assert. This keeps the code compatible even on platforms that do not run a common data dictionary, so a workspace's layout is identical whether it is read from C or from a COBOL copybook.

Executable structure and runtime

Many teams value the step-based processing model of ACMS. VX/ACMS preserves that model, but keeps the execution flow inside a single compiled executable. This removes the need for an external transaction monitor while maintaining the integrity of the ACMS workflow: the same steps run in the same order, with the same workspace semantics.

Procedure servers and dynamic linking

Procedure servers follow the same rules as ACMS, using dynamic linking. VX/ACMS maps ACMS dynamic calls onto the Linux equivalents dlopen and dlsym, so compiled ACMS procedures behave like their VMS counterparts. This also lets ACMS tasks call VX/DCL when the TDF permits it. A server image is a .so that exports one registry symbol, which the server-process host loads and validates by ABI version before dispatch:

typedef struct acms_server_module_v1 {
    uint32_t            abi_version;    /* == ACMS_MODULE_ABI_VERSION (1) */
    const char         *server_name;    /* matches SERVER name in the GDF */
    const char         *runtime;        /* "c" | "cxx" | "netcobol" | "gnucobol" */
    uint32_t            n_procedures;
    const acms_proc_t  *procedures;     /* static, indexed by proc_id (0..N-1) */
    acms_status_t     (*init)  (acms_runtime *rt);
    acms_status_t     (*term)  (acms_runtime *rt);
    acms_status_t     (*cancel)(acms_runtime *rt, uint32_t task_id);
    uint64_t            layout_hash;    /* over all workspace layouts */
} acms_server_module_v1;

Dispatch is by index (procedure id), not by name: the controller learns the name-to-id mapping from the server's startup frame, then issues indexed calls. Each procedure entry is { name, entry, n_workspaces }, and the entry signature passes workspace handles by reference so a procedure reads and writes the shared workspace directly.

Procedure-callable services

A step procedure may call only the three exception raisers. Because $ is not a portable C identifier character, the link symbols use underscores, and the VX tools reconcile the real ACMS$RAISE_* callers:

ServiceEffectStatus
ACMS_RAISE_STEP_EXCEPTIONRaise a step exception; the enclosing block's exception handler runs and the step is abandoned.live
ACMS_RAISE_TRANS_EXCEPTIONRaise a transaction exception; the surrounding atomic block rolls back.live
ACMS_RAISE_NONREC_EXCEPTIONRaise a non-recoverable exception; the task is cancelled.live

ACMS$ service surface

The agent-facing service library exposes the OpenVMS ACMS$ callable API. Condition values follow the VMS convention, where bit 0 set means success.

GroupServicesStatus
SessionACMS$SIGN_IN, ACMS$SIGN_OUT, ACMS$GET_SUBMITTER_INFOlive
Procedure-callableACMS$RAISE_STEP / TRANS / NONREC_EXCEPTIONlive
Agent forwardingAGENT_CALL_ACMS (server-to-server, cross-instance)live
Task callsACMS$CALL, ACMS$START_CALL, ACMS$WAIT_FOR_CALL_END, ACMS$CANCEL_CALL, ACMS$QUEUE_TASKv1 soon

Transactions, menus, and RMS

Transaction handling

VX/ACMS does not execute commit and rollback itself. That responsibility is assumed to belong to Oracle or another modern transaction manager. The original ACMS commit and rollback structure is kept intact in the translated code, but the database layer is now what actually executes it.

Structure preserved, execution delegated. The task keeps its original ACMS commit and rollback statements, so the control flow is unchanged. At runtime, those statements drive the database layer's transaction, rather than a VMS transaction monitor.

RMS recovery

VX/ACMS supports the RMS Recovery syntax, so a task that uses it still translates and runs. Recovery logic itself is not yet implemented in VX/RMS; RMS support is planned to extend based on client needs. Tasks that rely on RMS recovery therefore translate cleanly today, with the recovery behaviour arriving in a later VX/RMS update.

The adu compiler

adu is the ACMS Application Definition Utility: the command that translates the four definition-file types (and message files) into C++, and optionally invokes the C++ compiler to produce a loadable .so. The source kind is detected from the file extension, or forced with --tdf, --gdf, --mdf, --adf, or --msg.

Command-line flags

FlagPurpose
--tdf / --gdf / --mdf / --adfForce the source kind, rather than auto-detecting by extension.
--msgCompile a VSI .MSG message file into a header, a COBOL copybook, and a catalog.
--cdd <db>Open a CDD dictionary so workspace layouts come from CDD rather than the built-in registry.
--compileAfter translating, invoke the C++ compiler to produce a .so.
--parse-onlyParse and gate only; emit no output.
--strictFail on any lexer or grammar-gap diagnostic.
--pedanticAlso fail when a construct was tolerantly skipped, an include was missing, or a step lowered to a no-op.
--skip-reportPrint the skipped-construct inventory after parsing.
--decompileParse a source file and print its intermediate representation back out (round-trip check).
--manifest <project.man>Batch-compile a whole project (CDD, output directory, and file lists).
--standinEmit a self-contained stand-in server so an application can be built and hosted with no COBOL or database.
The compile flag is --compile. When set, adu runs the C++ compiler on the generated source (for example c++ -std=c++20 -shared -fPIC ... out.cpp -o out.so). This is the flag that turns a translation into a loadable module.

Build steps

StepCommandInputOutput
Ingest dictionaryvxcdd --cdd=cdd.db --adddir=CDD/.CDO or SQL DDLcdd.db
Compile a taskadu --cdd cdd.db --tdf task.tdf out.cpp.TDF plus CDDtask_<NAME>.cpp
Compile a groupadu --gdf group.gdf out.cpp.GDF<server>_transfer.cpp per server
Compile app / menuadu --adf app.adf out.cpp / adu --mdf menu.mdf out.cpp.ADF / .MDFApplication / menu module
Compile messagesadu --msg vrmsg.msg out.MSGout.h + .cob + catalog
Translate and build .soadu --compile ... src out.cppAny of the aboveout.cpp plus out.so

The ADU command-line interface is intentionally minimal today. VX/ACMS is designed to move ACMS applications forward into C++, rather than to perpetuate the traditional ACMS command surface, so the focus is on translation quality rather than reproducing every legacy operator command.

Compatibility and status

The table below is the honest have-versus-need for the migration path. It reflects what translates and runs today, what is arriving soon, and what is delegated elsewhere by design.

CapabilityStatusNotes
Parse all four definition grammars (TDF, GDF, ADF, MDF)liveThe full sample application parses with no skipped constructs.
TDF to C++ codegen (all control flow lowered)liveNo remaining no-ops in task codegen.
GDF to server-module codegenliveOne server module per SERVER, procedures indexed by id.
MDF / ADF module emissionpartialMenu and application module loading has landed; some emission is still called partial.
.MSG message compilerliveHeader, copybook, and catalog output, including multiline and quoted text.
Runtime: controller, server pool, task engine, workspace shm, IPCliveThe multi-process runtime is in place.
Menu display through VX/DECFORMSliveReal DECforms rendering via the forms shim.
ACMS$CALL family C-API entry pointsv1 soonIn-process and remote call paths exist; the C-API entry points are still being finished.
Transaction commit and rollback executiondelegatedOwned by Oracle or the database layer by design; VX/ACMS keeps the ACMS structure.
VX/RMS recovery logicv2RMS Recovery syntax is supported; the recovery behaviour itself is planned.
Distributed two-phase commitout of scopeNo distributed transactions in v1.

Divergences from OpenVMS ACMS, and why

DivergenceReason
No transaction monitor required; components can bundle into one executable.Linux has no ACMS system service, so the C++ runtime integrates the tiers directly.
Commit and rollback delegated to the database.VX/ACMS keeps the ACMS commit and rollback structure, but the database layer executes it.
$ in ACMS$ symbols mapped to _.$ is not a portable C identifier character; the VX tools reconcile the real callers.
Dispatch by procedure id, not by name.Matches the VMS transfer-module contract: one indexed load and one indirect call per step.
Workspaces shared by reference in POSIX shared memory.The Linux equivalent of ACMS workspace sharing; only slot indices cross the process boundary.
Forms rendering links the real VX/DECFORMS library.The runtime reuses the existing forms engine rather than reimplementing form rendering.
CDD is used through its callable API, not reimplemented.All workspace codegen resolves record layouts through VX/CDD.

Quick reference

Definition file types

.TDF task
.GDF task group
.ADF application
.MDF menu
.MSG messages

Each opens with REPLACE <KIND> <path>:<name> and closes with END DEFINITION;.

adu command line

adu [--cdd db] [--tdf|--gdf|--mdf|--adf|--msg] [--compile]
    [--parse-only] [--strict] [--pedantic] [--skip-report] <src> <out>
adu --decompile <src>
adu --manifest project.man
adu --standin

Compile to a shared object

adu --compile ... out.cpp
    runs: c++ -std=c++20 -shared -fPIC ... out.cpp -o out.so

Supported TDF control structures

IF THEN ELSE
WHILE DO
SELECT FIRST TRUE
CONTROL FIELD
GOTO
EXCHANGE
PROCESSING
MOVE
REPEAT STEP
REPEAT TASK
RAISE
GET MESSAGE

Procedure-callable services

ACMS_RAISE_STEP_EXCEPTION
ACMS_RAISE_TRANS_EXCEPTION
ACMS_RAISE_NONREC_EXCEPTION

Session API

ACMS$SIGN_IN
ACMS$SIGN_OUT
ACMS$GET_SUBMITTER_INFO

Success test

Condition values follow the VMS convention: bit 0 set means success. ACMS$_NORMAL is the canonical success code.

Runtime dependencies

C++20
CMake / Ninja
SQLite (for VX/CDD)
VX/DECFORMS (forms)

Frequently Asked Questions

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

What is VX/ACMS and how does it help modernize OpenVMS ACMS applications?

VX/ACMS is a native C++20 reimplementation of HP/VSI ACMS for Linux. It delivers source-compatible task execution for migrated OpenVMS applications: your existing TDF, GDF, and ADF sources compile through the ADU, existing COBOL and C step procedures run unchanged, and calls to ACMS$ services resolve against the same signatures and status codes as on VMS. VX/ACMS targets the single-server deployment model used by most production ACMS estates.

Which ACMS file types does VX/ACMS support for migration?

VX/ACMS supports all core ACMS file types including ADF (Application Definition Files), MDF (Menu Definition Files), GDF (Group Definition Files), and TDF (Task Definition Files) for translation to C++.

How does the ACMS to C++ translation process work?

VX/ACMS translates ACMS files into C++ while preserving the original logic and structure. The process can be used as a one-time conversion or integrated into continuous integration workflows for automated builds.

Does VX/ACMS maintain the original ACMS step-based processing model?

Yes. VX/ACMS preserves the ACMS component model and step-driven task interpretation. The EXC task interpreter drives the same step types you already use: PROCESSING CALL, EXCHANGE, ATOMIC, IF, WHILE, REPEAT, GOTO, MOVE, and EXCEPTION HANDLER. Workspaces still flow between submitters, EXC, and procedure servers through the familiar TASK, USER, GROUP, and SYSTEM lifetimes. EXCHANGE steps need the DECforms (CP) integration, which is still in progress.

How does VX/ACMS handle menu definition files and user interfaces?

VX/ACMS converts MDF (Menu Definition Files) into C-style menu structures that closely mimic MENU/TASK definitions. These menus can be displayed in VX/DECFORMS or processed into standard console-based menu systems, ensuring legacy ACMS menus function identically within the Linux runtime.

What TDF control structures are supported in the C++ translation?

VX/ACMS supports all TDF control statements including IF THEN ELSE, WHILE DO, SELECT FIRST TRUE, CONTROL FIELD, and GOTO, maintaining nearly identical structure to the original TDF logic.

How does VX/ACMS handle transaction management and Oracle integration?

VX/ACMS preserves the original ACMS commit/rollback structure but delegates actual transaction management to Oracle or other modern transaction managers. The tool supports dynamic linking for procedure servers using Linux equivalents (dlopen and dlsym), ensuring compiled ACMS procedures behave identically to their VMS counterparts.

This approach maintains familiar ACMS commands while leveraging modern database transaction capabilities for improved reliability and performance.

Does VX/ACMS require an ACMS transaction monitor to run migrated applications?

No. VX/ACMS bundles the TDF, MDF, and procedures (as dynamic libraries) into a single compiled executable and integrates them directly into the C++ runtime, so migrated applications run on Linux and Windows without a transaction monitor.

Can I run the ACMS to C++ translation as part of an automated build?

Yes. The translation can be a one-time conversion, where you convert the definitions to C++ and maintain the C++ from then on, or part of a continuous-integration workflow that recompiles and relinks the ACMS system on each build. Adding the --compile flag makes the adu translator invoke the C++ compiler after translating the ACMS definition files.

How does VX/ACMS handle transaction commit and rollback?

VX/ACMS does not execute commit or rollback itself. It assumes Oracle or another modern transaction manager owns that. The original ACMS commit and rollback structure is preserved in the translated code, but the database layer is now responsible for executing it.

What happens to ACMS menus (MDF) after migration?

MDF (Menu Definition Files) are translated into a C-style menu structure that closely mimics MENU and TASK definitions. The menus can be displayed in VX/DECFORMS or processed into standard console-based menu systems, so legacy menus behave the same way in the Linux and Windows runtime.

How are ACMS procedure servers called at runtime?

Procedure servers use dynamic linking. VX/ACMS maps ACMS dynamic calls onto the Linux equivalents dlopen and dlsym, so compiled ACMS procedures behave like their VMS counterparts. This also lets ACMS tasks call VX/DCL when the TDF permits it.

Does VX/ACMS preserve the step-based processing model of ACMS?

Yes. VX/ACMS keeps ACMS step-based processing, but the execution flow stays inside a single compiled executable. This removes the external transaction monitor while maintaining the integrity of the ACMS workflow.

How are CDD workspace definitions translated?

CDD workspace definitions become packed C structures with fixed-width, space-padded text fields, pinned to the original byte layout with static_assert. This keeps the code compatible even on platforms that have no common data dictionary, so the ACMS logic stays readable and maintainable in C++.

Is RMS recovery supported?

VX/ACMS supports the RMS Recovery syntax, so tasks that use it still translate and run. Recovery logic itself is not yet implemented in VX/RMS; RMS support is planned to extend in future updates based on client needs.

Transform Your Legacy Software Today!

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