🚨 Oracle not porting Rdb to x86 and EOL on Itanium 12/2027 🚨
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.

<!-- Webflow embed (min). Source: acms-to-c-page-technical/acmsc-tech-body-embed-dark.html → python3 website/embeds/minify_webflow_embed.py … --> <div class="s7-tech-embed" data-vxacmscd-body-embed="1" data-technical-mega-embed="1" lang="en"><h2 id="translation">Translating ACMS to C++</h2><p> VX/ACMS translates OpenVMS ACMS <strong>ADF, MDF, GDF, and TDF</strong> definitions into <strong>C++</strong>, 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. </p><div class="table-wrapper"><table><thead><tr><th>Workflow</th><th>What happens</th><th>When to use it</th></tr></thead><tbody><tr><td>One-time conversion</td><td>Translate the ACMS definition files to C++ once, then maintain the C++ from that point onward.</td><td>You are leaving ACMS behind and want C++ as the system of record.</td></tr><tr><td>Continuous integration</td><td>Keep the ACMS sources authoritative and re-run the translator on every build, recompiling and relinking the system automatically.</td><td>You still edit ACMS definitions and want the C++ regenerated on each change.</td></tr></tbody></table></div><div class="callout"><strong>Readable output by design.</strong> 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. </div><h6>From definitions to a running executable</h6><p> 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. </p><style>.s7-tech-embed .acms-flow {margin: 1.5rem 0;padding: 16px;border: 1px solid var(--s7t-border);border-radius: 10px;background: rgba(255, 255, 255, 0.01);}.s7-tech-embed .acms-flow-grid,.s7-tech-embed .acms-relationship {display: grid;gap: 10px;}.s7-tech-embed .acms-flow-grid {grid-template-columns: repeat(3, minmax(0, 1fr));}.s7-tech-embed .acms-card {min-width: 0;padding: 14px 16px;border-radius: 8px;background: var(--s7t-surface);color: var(--s7t-text);}.s7-tech-embed .acms-card strong,.s7-tech-embed .acms-card span:last-child {display: block;}.s7-tech-embed .acms-card strong {margin-bottom: 4px;}.s7-tech-embed .acms-card span:last-child {color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}.s7-tech-embed .acms-label,.s7-tech-embed .acms-connector {font-family: var(--s7t-mono);color: var(--s7t-accent);font-size: 0.76em;font-weight: 700;letter-spacing: 0.08em;text-transform: uppercase;}.s7-tech-embed .acms-label {margin-bottom: 8px;}.s7-tech-embed .acms-connector {padding: 10px;text-align: center;}.s7-tech-embed .acms-stage {border-top: 3px solid var(--s7t-accent);background: var(--s7t-surface2);}.s7-tech-embed .acms-output {border: 1px solid var(--s7t-note-bd);background: var(--s7t-note-bg);}.s7-tech-embed .acms-relationship {grid-template-columns: minmax(0, 1fr) 140px minmax(0, 1fr);align-items: center;}.s7-tech-embed .acms-branches {display: grid;grid-template-columns: repeat(3, minmax(0, 1fr));gap: 10px;margin-top: 10px;}.s7-tech-embed .acms-branch {border-top: 3px solid var(--s7t-accent);background: var(--s7t-surface2);}@media (max-width: 800px) {.s7-tech-embed .acms-flow-grid,.s7-tech-embed .acms-relationship,.s7-tech-embed .acms-branches {grid-template-columns: 1fr;}}</style><div class="acms-flow" aria-label="ACMS source inputs converge through translation and compilation"><div class="acms-label">Inputs</div><div class="acms-flow-grid"><div class="acms-card"><strong>ACMS definitions</strong><span>ADF, GDF, MDF, and TDF sources</span></div><div class="acms-card"><strong>CDD record layouts</strong><span>Shared workspace structures</span></div><div class="acms-card"><strong>.MSG message files</strong><span>VSI message definitions</span></div></div><div class="acms-connector">All inputs converge into translation</div><div class="acms-card acms-stage"><strong><code>adu</code> translate</strong><span>Selects the translator for each input file type.</span></div><div class="acms-connector">Produces</div><div class="acms-card acms-stage"><strong>Readable C++ source</strong><span>Preserves the structure of the original ACMS definitions.</span></div><div class="acms-connector">Optional <code>--compile</code></div><div class="acms-card acms-output"><strong>.so or executable</strong><span>The C++ compiler produces a loadable module or single runtime.</span></div></div><p> 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. </p><h2 id="definition-files">The four definition files</h2><p> ACMS applications are described in four VMS Application Definition Utility source languages. The <code>adu</code> translator compiles all four, plus VSI <code>.MSG</code> message files. The source kind is auto-detected from the file extension, or forced with a flag. </p><div class="table-wrapper"><table><thead><tr><th>Extension</th><th>Name</th><th>Opening line</th><th>Translates to</th><th>Flag</th></tr></thead><tbody><tr><td><code>.TDF</code></td><td>Task Definition File</td><td><code>REPLACE TASK &lt;path&gt;:&lt;name&gt;</code></td><td>Task step program plus workspace declarations.</td><td><code>--tdf</code></td></tr><tr><td><code>.GDF</code></td><td>task Group Definition File</td><td><code>REPLACE GROUP &lt;path&gt;:&lt;name&gt;</code></td><td>One server module per SERVER, plus the procedure table.</td><td><code>--gdf</code></td></tr><tr><td><code>.ADF</code></td><td>Application Definition File</td><td><code>REPLACE APPLICATION &lt;path&gt;:&lt;name&gt;</code></td><td>Application module binding task groups and server pools.</td><td><code>--adf</code></td></tr><tr><td><code>.MDF</code></td><td>Menu Definition File</td><td><code>REPLACE MENU &lt;path&gt;:&lt;name&gt;</code></td><td>Menu module, a C-style menu structure that mimics MENU/TASK definitions.</td><td><code>--mdf</code></td></tr><tr><td><code>.MSG</code></td><td>VSI message file</td><td><code>.FACILITY ... .SEVERITY ...</code></td><td>A <code>.h</code> header, a COBOL copybook, and a runtime message catalog.</td><td><code>--msg</code></td></tr></tbody></table></div><h6>How the four relate</h6><p> 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. </p><div class="acms-flow" aria-label="Relationships among ACMS application, group, task, menu, procedure, and workspace definitions"><div class="acms-relationship"><div class="acms-card"><span class="acms-label">Application</span><strong>ADF</strong><span>Binds task groups, server control attributes, task defaults, and server pools.</span></div><div class="acms-connector">Task group is</div><div class="acms-card acms-stage"><span class="acms-label">Task group</span><strong>GDF</strong><span>Rosters the servers, procedures, workspaces, and tasks used by the application.</span></div></div><div class="acms-connector">GDF branches to its runtime definitions</div><div class="acms-branches"><div class="acms-card acms-branch"><span class="acms-label">Servers are</span><strong>Procedures</strong><span>COBOL or C shared objects</span></div><div class="acms-card acms-branch"><span class="acms-label">Workspaces are</span><strong>CDD records</strong><span>Byte-identical C structs and COBOL copybooks</span></div><div class="acms-card acms-branch"><span class="acms-label">Tasks are</span><strong>TDF</strong><span>One step program for each task</span></div></div><div class="acms-connector">Entries select a task in the application</div><div class="acms-card acms-output"><span class="acms-label">Menu</span><strong>MDF</strong><span><code>ENTRIES: TASK IS &lt;t&gt; IN &lt;app&gt;</code> drives task selection.</span></div></div><h6>A Task Definition File (TDF)</h6><p> 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 <code>CONTROL FIELD</code> action part to branch on the operator's key: </p> <pre><span class="kw">REPLACE TASK</span> banking_task:funds_transfer_task <span class="kw">DEFAULT FORM IS</span> banking_form; <span class="kw">USE WORKSPACES</span> account_wksp, transfer_wksp, sendctrl_wksp, audit_wksp; <span class="kw">TASK ARGUMENTS ARE</span> account_wksp <span class="kw">WITH ACCESS</span> MODIFY, transfer_wksp <span class="kw">WITH ACCESS</span> READ, audit_wksp <span class="kw">WITH ACCESS</span> WRITE; <span class="kw">BLOCK WITH FORM I/O</span> get_request: <span class="kw">EXCHANGE</span> <span class="kw">TRANSCEIVE RECORD</span> account_wksp, transfer_wksp <span class="kw">SENDING</span> account_wksp <span class="kw">RECEIVING</span> account_wksp, transfer_wksp <span class="kw">WITH SEND CONTROL</span> sendctrl_wksp; <span class="kw">ACTION IS</span> <span class="kw">CONTROL FIELD</span> sendctrl_wksp.ctrl_key <span class="str">"QUIT"</span> : <span class="kw">EXIT TASK</span>; <span class="str">"CANCL"</span> : <span class="kw">EXIT TASK</span>; NOMATCH : <span class="kw">MOVE</span> <span class="str">" "</span> <span class="kw">TO</span> sendctrl_wksp.ctrl_key; <span class="kw">END CONTROL</span>; post_debit: <span class="kw">PROCESSING</span> <span class="kw">CALL PROCEDURE</span> debit_account_proc <span class="kw">IN</span> banking_write_server <span class="kw">USING</span> account_wksp, transfer_wksp, audit_wksp; <span class="kw">ACTION IS</span> <span class="kw">IF</span> (ACMS$T_STATUS_TYPE = <span class="str">"B"</span>) <span class="kw">THEN</span> <span class="kw">GET MESSAGE INTO</span> audit_wksp.messagepanel; <span class="kw">GOTO PREVIOUS EXCHANGE</span>; <span class="kw">END IF</span>; <span class="kw">END DEFINITION</span>;</pre> <h6>A Group Definition File (GDF)</h6><p> The GDF binds servers, procedures, workspaces, and tasks. Both the plural <code>PROCEDURES ARE</code> and singular <code>PROCEDURE IS</code> server clauses are accepted. The <code>adu --gdf</code> pass emits one server module per SERVER, with a procedure table indexed by procedure id: </p> <pre><span class="kw">SERVERS ARE</span> banking_read_server: <span class="kw">PROCEDURE SERVER IMAGE IS</span> <span class="str">"banking_default:banking_read_server.exe"</span>; <span class="kw">INITIALIZATION PROCEDURE IS</span> banking_read_init; <span class="kw">RUNDOWN ON CANCEL IF INTERRUPTED</span>; <span class="kw">PROCEDURES ARE</span> find_customer_proc, read_account_proc, account_details_proc; banking_write_server: <span class="kw">PROCEDURE SERVER IMAGE IS</span> <span class="str">"banking_default:banking_write_server.exe"</span>; <span class="kw">INITIALIZATION PROCEDURE IS</span> banking_write_init; <span class="kw">PROCEDURE IS</span> debit_account_proc; <span class="kw">END SERVER</span>; <span class="kw">TASKS ARE</span> funds_transfer_task : <span class="kw">TASK IS</span> banking_task:funds_transfer_task; enquiry_task : <span class="kw">TASK IS</span> banking_task:enquiry_task; <span class="kw">END TASKS</span>;</pre> <h2 id="control-flow">Task control flow in C++</h2><p> 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. </p><h6>Supported TDF control structures</h6><div class="kw-grid"><div>IF THEN ELSE</div><div>WHILE DO</div><div>SELECT FIRST TRUE</div><div>CONTROL FIELD</div><div>GOTO</div></div><h6>Full step and statement set</h6><div class="table-wrapper"><table><thead><tr><th>Statement kind</th><th>TDL construct</th><th>Role</th></tr></thead><tbody><tr><td>BLOCK</td><td><code>BLOCK ... END BLOCK</code> (incl. <code>WITH FORM I/O</code>, <code>WITH TRANSACTION</code>)</td><td>Step-block container.</td></tr><tr><td>EXCHANGE</td><td><code>EXCHANGE ... TRANSCEIVE / SEND RECORD</code></td><td>Form I/O step.</td></tr><tr><td>PROCESSING</td><td><code>PROCESSING ... CALL PROCEDURE ... IN ... USING</code></td><td>Server-procedure call step.</td></tr><tr><td>IF</td><td><code>IF (cond) THEN ... ELSE ... END IF</code></td><td>Conditional.</td></tr><tr><td>SELECT FIRST TRUE</td><td><code>SELECT FIRST TRUE (cond): ... OTHERWISE:</code></td><td>Multi-way select.</td></tr><tr><td>CONTROL FIELD</td><td><code>CONTROL FIELD &lt;f&gt; "lit": ... NOMATCH:</code></td><td>Value dispatch.</td></tr><tr><td>WHILE</td><td><code>WHILE DO</code></td><td>Loop.</td></tr><tr><td>MOVE</td><td><code>MOVE &lt;src&gt; TO &lt;dst&gt;</code></td><td>Field assignment (multi-pair).</td></tr><tr><td>GOTO</td><td><code>GOTO (NEXT | PREVIOUS) (STEP | EXCHANGE | PROCESSING) [label]</code></td><td>Relative or labelled jump.</td></tr><tr><td>REPEAT</td><td><code>REPEAT STEP</code> / <code>REPEAT TASK</code></td><td>Backward-goto loops (safety-capped).</td></tr><tr><td>EXIT</td><td><code>EXIT BLOCK</code> / <code>EXIT TASK</code> / <code>CANCEL TASK</code></td><td>Flow exits and task cancel.</td></tr><tr><td>ATOMIC</td><td><code>... WITH TRANSACTION</code> / <code>ATOMIC</code> block</td><td>Transaction bracket.</td></tr><tr><td>COMMIT / ROLLBACK</td><td><code>COMMIT TRANSACTION</code> / <code>ROLLBACK TRANSACTION</code></td><td>Explicit transaction statements (executed by the database layer).</td></tr><tr><td>RAISE</td><td><code>RAISE ... EXCEPTION</code></td><td>Raise a condition.</td></tr><tr><td>GET MESSAGE</td><td><code>GET [ERROR] MESSAGE INTO &lt;field&gt;</code></td><td>Message retrieval into a workspace field.</td></tr></tbody></table></div><div class="callout"><strong>CDD workspaces become C structs.</strong> CDD workspace definitions are translated into packed C structures with fixed-width, space-padded text fields, pinned to the original byte layout with <code>static_assert</code>. 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. </div><h2 id="runtime">Executable structure and runtime</h2><p> 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. </p><h6>Procedure servers and dynamic linking</h6><p> Procedure servers follow the same rules as ACMS, using dynamic linking. VX/ACMS maps ACMS dynamic calls onto the Linux equivalents <code>dlopen</code> and <code>dlsym</code>, 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 <code>.so</code> that exports one registry symbol, which the server-process host loads and validates by ABI version before dispatch: </p> <pre><span class="kw">typedef struct</span> acms_server_module_v1 { uint32_t abi_version; <span class="cmt">/* == ACMS_MODULE_ABI_VERSION (1) */</span> <span class="kw">const char</span> *server_name; <span class="cmt">/* matches SERVER name in the GDF */</span> <span class="kw">const char</span> *runtime; <span class="cmt">/* "c" | "cxx" | "netcobol" | "gnucobol" */</span> uint32_t n_procedures; <span class="kw">const</span> acms_proc_t *procedures; <span class="cmt">/* static, indexed by proc_id (0..N-1) */</span> 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; <span class="cmt">/* over all workspace layouts */</span> } acms_server_module_v1;</pre> <p> 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 <code>{ name, entry, n_workspaces }</code>, and the entry signature passes workspace handles by reference so a procedure reads and writes the shared workspace directly. </p><h6>Procedure-callable services</h6><p> A step procedure may call only the three exception raisers. Because <code>$</code> is not a portable C identifier character, the link symbols use underscores, and the VX tools reconcile the real <code>ACMS$RAISE_*</code> callers: </p><div class="table-wrapper"><table><thead><tr><th>Service</th><th>Effect</th><th>Status</th></tr></thead><tbody><tr><td><code>ACMS_RAISE_STEP_EXCEPTION</code></td><td>Raise a step exception; the enclosing block's exception handler runs and the step is abandoned.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>ACMS_RAISE_TRANS_EXCEPTION</code></td><td>Raise a transaction exception; the surrounding atomic block rolls back.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>ACMS_RAISE_NONREC_EXCEPTION</code></td><td>Raise a non-recoverable exception; the task is cancelled.</td><td><span class="ac-live">live</span></td></tr></tbody></table></div><h6>ACMS$ service surface</h6><p> The agent-facing service library exposes the OpenVMS <code>ACMS$</code> callable API. Condition values follow the VMS convention, where bit 0 set means success. </p><div class="table-wrapper"><table><thead><tr><th>Group</th><th>Services</th><th>Status</th></tr></thead><tbody><tr><td>Session</td><td><code>ACMS$SIGN_IN</code>, <code>ACMS$SIGN_OUT</code>, <code>ACMS$GET_SUBMITTER_INFO</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Procedure-callable</td><td><code>ACMS$RAISE_STEP / TRANS / NONREC_EXCEPTION</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Agent forwarding</td><td><code>AGENT_CALL_ACMS</code> (server-to-server, cross-instance)</td><td><span class="ac-live">live</span></td></tr><tr><td>Task calls</td><td><code>ACMS$CALL</code>, <code>ACMS$START_CALL</code>, <code>ACMS$WAIT_FOR_CALL_END</code>, <code>ACMS$CANCEL_CALL</code>, <code>ACMS$QUEUE_TASK</code></td><td><span class="ac-v1">v1 soon</span></td></tr></tbody></table></div><h2 id="transactions">Transactions, menus, and RMS</h2><h6>Transaction handling</h6><p> 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. </p><div class="callout"><strong>Structure preserved, execution delegated.</strong> 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. </div><h6>Menus and user interfaces</h6><p> MDF (Menu Definition Files) generate a C-style menu structure that closely mimics MENU and TASK definitions. The menus can be displayed in <strong>VX/DECFORMS</strong> or processed into standard console-based menu systems, so legacy ACMS menus behave the same way inside the Linux and Windows runtime. </p><h6>RMS recovery</h6><p> 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. </p><h2 id="adu">The adu compiler</h2><p><code>adu</code> 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 <code>.so</code>. The source kind is detected from the file extension, or forced with <code>--tdf</code>, <code>--gdf</code>, <code>--mdf</code>, <code>--adf</code>, or <code>--msg</code>. </p><h6>Command-line flags</h6><div class="table-wrapper"><table><thead><tr><th>Flag</th><th>Purpose</th></tr></thead><tbody><tr><td><code>--tdf</code> / <code>--gdf</code> / <code>--mdf</code> / <code>--adf</code></td><td>Force the source kind, rather than auto-detecting by extension.</td></tr><tr><td><code>--msg</code></td><td>Compile a VSI <code>.MSG</code> message file into a header, a COBOL copybook, and a catalog.</td></tr><tr><td><code>--cdd &lt;db&gt;</code></td><td>Open a CDD dictionary so workspace layouts come from CDD rather than the built-in registry.</td></tr><tr><td><code>--compile</code></td><td>After translating, invoke the C++ compiler to produce a <code>.so</code>.</td></tr><tr><td><code>--parse-only</code></td><td>Parse and gate only; emit no output.</td></tr><tr><td><code>--strict</code></td><td>Fail on any lexer or grammar-gap diagnostic.</td></tr><tr><td><code>--pedantic</code></td><td>Also fail when a construct was tolerantly skipped, an include was missing, or a step lowered to a no-op.</td></tr><tr><td><code>--skip-report</code></td><td>Print the skipped-construct inventory after parsing.</td></tr><tr><td><code>--decompile</code></td><td>Parse a source file and print its intermediate representation back out (round-trip check).</td></tr><tr><td><code>--manifest &lt;project.man&gt;</code></td><td>Batch-compile a whole project (CDD, output directory, and file lists).</td></tr><tr><td><code>--standin</code></td><td>Emit a self-contained stand-in server so an application can be built and hosted with no COBOL or database.</td></tr></tbody></table></div><div class="callout"><strong>The compile flag is <code>--compile</code>.</strong> When set, <code>adu</code> runs the C++ compiler on the generated source (for example <code>c++ -std=c++20 -shared -fPIC ... out.cpp -o out.so</code>). This is the flag that turns a translation into a loadable module. </div><h6>Build steps</h6><div class="table-wrapper"><table><thead><tr><th>Step</th><th>Command</th><th>Input</th><th>Output</th></tr></thead><tbody><tr><td>Ingest dictionary</td><td><code>vxcdd --cdd=cdd.db --adddir=CDD/</code></td><td><code>.CDO</code> or SQL DDL</td><td><code>cdd.db</code></td></tr><tr><td>Compile a task</td><td><code>adu --cdd cdd.db --tdf task.tdf out.cpp</code></td><td><code>.TDF</code> plus CDD</td><td><code>task_&lt;NAME&gt;.cpp</code></td></tr><tr><td>Compile a group</td><td><code>adu --gdf group.gdf out.cpp</code></td><td><code>.GDF</code></td><td><code>&lt;server&gt;_transfer.cpp</code> per server</td></tr><tr><td>Compile app / menu</td><td><code>adu --adf app.adf out.cpp</code> / <code>adu --mdf menu.mdf out.cpp</code></td><td><code>.ADF</code> / <code>.MDF</code></td><td>Application / menu module</td></tr><tr><td>Compile messages</td><td><code>adu --msg vrmsg.msg out</code></td><td><code>.MSG</code></td><td><code>out.h</code> + <code>.cob</code> + catalog</td></tr><tr><td>Translate and build <code>.so</code></td><td><code>adu --compile ... src out.cpp</code></td><td>Any of the above</td><td><code>out.cpp</code> plus <code>out.so</code></td></tr></tbody></table></div><p> 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. </p><h2 id="status">Compatibility and status</h2><p> 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. </p><div class="table-wrapper"><table><thead><tr><th>Capability</th><th>Status</th><th>Notes</th></tr></thead><tbody><tr><td>Parse all four definition grammars (TDF, GDF, ADF, MDF)</td><td><span class="ac-live">live</span></td><td>The full sample application parses with no skipped constructs.</td></tr><tr><td>TDF to C++ codegen (all control flow lowered)</td><td><span class="ac-live">live</span></td><td>No remaining no-ops in task codegen.</td></tr><tr><td>GDF to server-module codegen</td><td><span class="ac-live">live</span></td><td>One server module per SERVER, procedures indexed by id.</td></tr><tr><td>MDF / ADF module emission</td><td><span class="ac-v1">partial</span></td><td>Menu and application module loading has landed; some emission is still called partial.</td></tr><tr><td><code>.MSG</code> message compiler</td><td><span class="ac-live">live</span></td><td>Header, copybook, and catalog output, including multiline and quoted text.</td></tr><tr><td>Runtime: controller, server pool, task engine, workspace shm, IPC</td><td><span class="ac-live">live</span></td><td>The multi-process runtime is in place.</td></tr><tr><td>Menu display through VX/DECFORMS</td><td><span class="ac-live">live</span></td><td>Real DECforms rendering via the forms shim.</td></tr><tr><td><code>ACMS$CALL</code> family C-API entry points</td><td><span class="ac-v1">v1 soon</span></td><td>In-process and remote call paths exist; the C-API entry points are still being finished.</td></tr><tr><td>Transaction commit and rollback execution</td><td><span class="ac-no">delegated</span></td><td>Owned by Oracle or the database layer by design; VX/ACMS keeps the ACMS structure.</td></tr><tr><td>VX/RMS recovery logic</td><td><span class="ac-v2">v2</span></td><td>RMS Recovery syntax is supported; the recovery behaviour itself is planned.</td></tr><tr><td>Distributed two-phase commit</td><td><span class="ac-no">out of scope</span></td><td>No distributed transactions in v1.</td></tr></tbody></table></div><h6>Divergences from OpenVMS ACMS, and why</h6><div class="table-wrapper"><table><thead><tr><th>Divergence</th><th>Reason</th></tr></thead><tbody><tr><td>No transaction monitor required; components can bundle into one executable.</td><td>Linux has no ACMS system service, so the C++ runtime integrates the tiers directly.</td></tr><tr><td>Commit and rollback delegated to the database.</td><td>VX/ACMS keeps the ACMS commit and rollback structure, but the database layer executes it.</td></tr><tr><td><code>$</code> in <code>ACMS$</code> symbols mapped to <code>_</code>.</td><td><code>$</code> is not a portable C identifier character; the VX tools reconcile the real callers.</td></tr><tr><td>Dispatch by procedure id, not by name.</td><td>Matches the VMS transfer-module contract: one indexed load and one indirect call per step.</td></tr><tr><td>Workspaces shared by reference in POSIX shared memory.</td><td>The Linux equivalent of ACMS workspace sharing; only slot indices cross the process boundary.</td></tr><tr><td>Forms rendering links the real VX/DECFORMS library.</td><td>The runtime reuses the existing forms engine rather than reimplementing form rendering.</td></tr><tr><td>CDD is used through its callable API, not reimplemented.</td><td>All workspace codegen resolves record layouts through VX/CDD.</td></tr></tbody></table></div><h2 id="quickref">Quick reference</h2><h6>Definition file types</h6><div class="kw-grid"><div>.TDF task</div><div>.GDF task group</div><div>.ADF application</div><div>.MDF menu</div><div>.MSG messages</div></div><p>Each opens with <code>REPLACE &lt;KIND&gt; &lt;path&gt;:&lt;name&gt;</code> and closes with <code>END DEFINITION;</code>.</p><h6>adu command line</h6> <pre>adu [--cdd db] [--tdf|--gdf|--mdf|--adf|--msg] [--compile] [--parse-only] [--strict] [--pedantic] [--skip-report] &lt;src&gt; &lt;out&gt; adu --decompile &lt;src&gt; adu --manifest project.man adu --standin</pre> <h6>Compile to a shared object</h6> <pre>adu --compile ... out.cpp runs: c++ -std=c++20 -shared -fPIC ... out.cpp -o out.so</pre> <h6>Supported TDF control structures</h6><div class="kw-grid"><div>IF THEN ELSE</div><div>WHILE DO</div><div>SELECT FIRST TRUE</div><div>CONTROL FIELD</div><div>GOTO</div><div>EXCHANGE</div><div>PROCESSING</div><div>MOVE</div><div>REPEAT STEP</div><div>REPEAT TASK</div><div>RAISE</div><div>GET MESSAGE</div></div><h6>Procedure-callable services</h6><div class="kw-grid"><div>ACMS_RAISE_STEP_EXCEPTION</div><div>ACMS_RAISE_TRANS_EXCEPTION</div><div>ACMS_RAISE_NONREC_EXCEPTION</div></div><h6>Session API</h6><div class="kw-grid"><div>ACMS$SIGN_IN</div><div>ACMS$SIGN_OUT</div><div>ACMS$GET_SUBMITTER_INFO</div></div><h6>Success test</h6><p>Condition values follow the VMS convention: <strong>bit 0 set means success</strong>. <code>ACMS$_NORMAL</code> is the canonical success code.</p><h6>Runtime dependencies</h6><div class="kw-grid"><div>C++20</div><div>CMake / Ninja</div><div>SQLite (for VX/CDD)</div><div>VX/DECFORMS (forms)</div></div></div>

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 comprehensive migration tool that translates OpenVMS ACMS applications (ADF, MDF, GDF, and TDF files) into C++ while maintaining a runtime environment that mirrors the original VMS/OpenVMS ACMS functionality. This enables seamless execution of ACMS applications on Linux without requiring a transaction monitor.

The tool preserves legacy workflows while ensuring compatibility with modern environments, making it ideal for organizations looking to modernize their ACMS applications. To discuss your ACMS migration requirements, contact us.

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 step-based processing approach that many users value, ensuring execution flow remains within a single compiled executable while maintaining the integrity of ACMS workflows.

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.