🚨 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/BASIC - OpenVMS BASIC to ANSI C

Overview

VX/BASIC is a source-to-source translator that reads VAX and OpenVMS BASIC and emits ANSI C. It is not an interpreter and not a direct-to-machine-code compiler: the generated C is compiled by the host's own toolchain (gcc, clang, or cl) and linked against the VX/BASIC runtime library, producing a native executable for Linux, Windows, or macOS that keeps VAX BASIC semantics intact.

Because C is used as an intermediate language, the migrated application stays portable and can be maintained, built, and debugged with standard modern tools. Descriptor-based strings, RECORD and MAP memory layout, packed DECIMAL arithmetic, RMS record I/O, and ON ERROR handling are all preserved, so an application behaves the same way after conversion as it did on OpenVMS.

<!-- Webflow embed (min). Source: basic-page-technical/basic-tech-body-embed-dark.html → python3 website/embeds/minify_webflow_embed.py … --> <div class="s7-tech-embed" data-vxbasicd-body-embed="1" data-technical-mega-embed="1" lang="en"><h2 id="architecture">Architecture and translation model</h2><p> VX/BASIC is a translator plus a linked runtime library. The driver reads a BASIC source file and emits ANSI C; that C is then compiled by the host C compiler and linked against the VX/BASIC runtime (<code>libvxbas</code>) to produce a native executable. The translator and the runtime are decoupled: the runtime is callable from any C program that includes the master runtime header, so a hand-written runtime call produces the same result as a translated statement. </p><h6>Compilation pipeline</h6><p>By default the driver runs its preprocessor first, then translates to C. The generated C is ordinary source that any modern toolchain can build and debug:</p><style>.s7-tech-embed .process-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 .process-endpoint {display: grid;grid-template-columns: 72px minmax(0, 1fr);gap: 14px;align-items: center;padding: 14px 16px;border-radius: 8px;background: var(--s7t-surface);color: var(--s7t-text);}.s7-tech-embed .process-output {border: 1px solid var(--s7t-note-bd);background: var(--s7t-note-bg);}.s7-tech-embed .process-label,.s7-tech-embed .process-number {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 .process-endpoint div,.s7-tech-embed .process-stage {min-width: 0;}.s7-tech-embed .process-endpoint strong,.s7-tech-embed .process-endpoint span:last-child,.s7-tech-embed .process-stage strong,.s7-tech-embed .process-stage span:last-child {display: block;}.s7-tech-embed .process-endpoint strong,.s7-tech-embed .process-stage strong {margin-bottom: 4px;color: var(--s7t-text);}.s7-tech-embed .process-endpoint span:last-child,.s7-tech-embed .process-stage span:last-child {color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}.s7-tech-embed .process-stages {display: grid;grid-template-columns: repeat(3, minmax(0, 1fr));gap: 10px;margin: 10px 0;}.s7-tech-embed .process-stage {padding: 15px;border-top: 3px solid var(--s7t-accent);border-radius: 6px;background: var(--s7t-surface2);color: var(--s7t-text);}.s7-tech-embed .process-stage .process-number {margin-bottom: 10px;}@media (max-width: 800px) {.s7-tech-embed .process-stages {grid-template-columns: 1fr;}.s7-tech-embed .process-endpoint {grid-template-columns: 1fr;gap: 5px;}}</style><div class="process-flow" aria-label="VX/BASIC compilation pipeline"><div class="process-endpoint"><span class="process-label">Input</span><div><strong><code>foo.bas</code></strong><span>VAX BASIC source</span></div></div><div class="process-stages" role="list" aria-label="Compilation stages"><div class="process-stage" role="listitem"><span class="process-number">01</span><strong><code>bpp</code></strong><span>Expands <code>%INCLUDE</code>, <code>%IF</code>, and <code>%LET</code>, producing <code>foo.bas.p</code>.</span></div><div class="process-stage" role="listitem"><span class="process-number">02</span><strong><code>vxbasic</code></strong><span>Tokenises the intermediate, builds the symbol table, and generates ANSI C as <code>foo.c</code>.</span></div><div class="process-stage" role="listitem"><span class="process-number">03</span><strong>Host C compiler</strong><span><code>cc</code>, <code>clang</code>, or <code>cl</code> compiles <code>foo.c</code> into <code>foo.o</code>.</span></div><div class="process-stage" role="listitem"><span class="process-number">04</span><strong>Link</strong><span><code>cc -lvxbas -ll -lm</code> links the object with the VX/BASIC runtime and support libraries.</span></div></div><div class="process-endpoint process-output"><span class="process-label">Output</span><div><strong><code>foo</code></strong><span>Native executable with VAX BASIC semantics intact</span></div></div></div><p> The <code>-a</code> option turns off the separate preprocessor pass and lets the translator handle <code>%</code> directives directly; <code>-g</code> and <code>-G</code> collapse the whole pipeline by handing the generated C to the bundled host-compile scripts. Input is normally <code>.bas</code> or <code>.BAS</code> (also <code>.sb</code> for embedded SQL and <code>.bp2</code> for BASIC-PLUS-2); output is <code>.c</code> by default. </p><h6>Component map</h6><div class="table-wrapper"><table><thead><tr><th>Component</th><th>Role</th></tr></thead><tbody><tr><td>Translator driver</td><td>Entry point, option scan, file open, and the top-level translate loop.</td></tr><tr><td>Reserved-word tables</td><td>Per-letter keyword tables mapping each BASIC keyword to its code-generation action, C name, and parameter shape.</td></tr><tr><td>Expression evaluator</td><td>Operator-precedence expression tree covering arithmetic, relational, logical, and concatenation operators.</td></tr><tr><td>Statement code generators</td><td>One action routine per keyword for control flow, <code>OPEN</code>/<code>GET</code>/<code>PUT</code>, <code>PRINT</code>, records, declarations, and structured error handling.</td></tr><tr><td>Name mangler</td><td>Rewrites BASIC identifiers (with <code>$</code>, <code>%</code>, <code>.</code>, and <code>::</code>) into legal C identifiers.</td></tr><tr><td>Preprocessor</td><td>Handles <code>%INCLUDE</code>, <code>%IF</code>/<code>%ELSE</code>, <code>%LET</code>, <code>%VARIANT</code>, and CDD <code>%FROM %CDD</code> extraction.</td></tr><tr><td>C runtime library</td><td>Descriptors, RMS record I/O, packed-decimal math, and the error stack, delivered as <code>libvxbas</code>.</td></tr><tr><td>Master runtime header</td><td>Type aliases, statement macros, and runtime prototypes that every generated C file includes.</td></tr></tbody></table></div><div class="callout"><strong>C as the intermediate language.</strong> Because the translator emits readable C rather than opaque binary, the migrated application is portable to any platform with a C compiler and can be inspected, built, and debugged with standard tools. The current line is VX/BASIC V8.6. </div><h2 id="source-language">Source language coverage</h2><p> VX/BASIC targets the full VAX and OpenVMS BASIC language. Every keyword maps to a code-generation action in the translator's reserved-word tables. The groups below summarise the statement coverage; the one documented exclusion is the graphics family (see Compatibility). </p><h6>Statement groups</h6><div class="table-wrapper"><table><thead><tr><th>Group</th><th>Constructs</th></tr></thead><tbody><tr><td>Flow control</td><td><code>IF/THEN/ELSE/END IF</code>, <code>FOR/NEXT/STEP</code>, <code>WHILE</code>, <code>UNTIL</code>, <code>SELECT CASE/CASE/OTHERWISE/END SELECT</code>, <code>GOTO</code>/<code>GO TO</code>, <code>GOSUB</code>/<code>RETURN</code>, <code>ON n GOTO/GOSUB</code>, <code>STOP</code>, <code>END</code>, <code>EXIT</code>, <code>ITERATE</code>, <code>CONTINUE</code>.</td></tr><tr><td>Subprograms</td><td><code>SUB/SUBEND/SUBEXIT</code>, <code>FUNCTION/FUNCTIONEND/FUNCTIONEXIT</code>, <code>DEF</code>/<code>DEF*</code>/<code>FNEND</code>, <code>CALL</code>, <code>PROGRAM</code>.</td></tr><tr><td>Error handling</td><td><code>ON ERROR GOTO</code>, <code>ON ERROR GOTO 0</code>, <code>ON ERROR GO BACK</code>, <code>WHEN ERROR IN / END WHEN</code>, <code>WHEN ERROR USE</code>, <code>HANDLER / END HANDLER</code>, <code>RESUME</code>, <code>RETRY</code>, <code>CAUSE ERROR n</code>.</td></tr><tr><td>RMS file I/O</td><td><code>OPEN</code>, <code>CLOSE</code>, <code>GET</code>, <code>PUT</code>, <code>FIND</code>, <code>DELETE</code>, <code>UPDATE</code>, <code>READ</code>, <code>WRITE</code>, <code>FIELD</code>, <code>LSET</code>, <code>RSET</code>, <code>RESTORE</code>, <code>UNLOCK</code>, <code>NAME AS</code>, <code>KILL</code>. <code>OPEN ... ORGANIZATION</code> supports sequential, relative, indexed, and virtual.</td></tr><tr><td>Terminal I/O</td><td><code>PRINT</code>, <code>INPUT</code>, <code>LINPUT</code>, <code>INPUT LINE</code>, <code>INKEY$</code>, <code>MARGIN</code>, <code>NOMARGIN</code>, <code>EXTEND</code>, <code>NOEXTEND</code>, <code>SET</code>.</td></tr><tr><td>Storage</td><td><code>MAP</code>, <code>REMAP</code>, <code>COMMON</code>/<code>COM</code>, <code>DIM</code>/<code>DIMENSION</code>, <code>FREE</code>, <code>RECORD</code>, <code>RANDOMIZE</code>, <code>WAIT</code>, <code>SLEEP</code>.</td></tr><tr><td>Program control</td><td><code>CHAIN</code>, <code>CHANGE</code>, <code>RUN</code>, <code>MAT</code>, <code>MID$</code> (statement form).</td></tr></tbody></table></div><h6>Operator precedence</h6><p> The expression evaluator preserves BASIC precedence, from highest to lowest: </p><div class="kw-grid"><div>** / ^ (power)</div><div>unary - / + / NOT</div><div>* / /</div><div>+ / - / &amp; (concat)</div><div>relational (= &lt;&gt; &lt; &gt; &lt;= &gt;= EQ NE LT GT LE GE)</div><div>AND</div><div>OR / XOR</div><div>IMP / EQV</div></div><p> Integer bitwise operators (<code>EQV</code>, <code>DIV</code>, <code>IMP</code>, <code>MOD</code>, <code>XOR</code>) are emitted as runtime macros that cast their operands to 32-bit integers, matching VAX BASIC semantics. </p><h6>Intrinsic functions</h6><p> Around ninety intrinsic functions are supported, each mapped to a runtime entry point. Representative coverage: </p><div class="kw-grid"><div>LEFT$</div><div>RIGHT$</div><div>MID$</div><div>SEG$</div><div>LEN</div><div>INSTR</div><div>POS</div><div>EDIT$</div><div>FORMAT$</div><div>NUM$</div><div>STR$</div><div>STRING$</div><div>SPACE$</div><div>CHR$</div><div>ASCII</div><div>VAL</div><div>TRM$</div><div>XLATE$</div><div>DATE$</div><div>TIME$</div><div>INKEY$</div><div>ABS</div><div>SIN</div><div>COS</div><div>TAN</div><div>ATN</div><div>LOG</div><div>LOG10</div><div>EXP</div><div>SQR</div><div>INT</div><div>FIX</div><div>MOD</div><div>MAX</div><div>MIN</div><div>SGN</div><div>RND</div><div>ERR</div><div>ERL</div><div>ERT$</div><div>RMSSTATUS</div><div>VMSSTATUS</div><div>STATUS</div><div>LBOUND</div><div>UBOUND</div><div>LOC</div><div>SWAP%</div><div>GETRFA</div></div><h2 id="data-types">Data types and C mapping</h2><p> Every VAX and OpenVMS BASIC data type maps to a fixed-width ANSI C type sized to match the original storage, so record layouts and arithmetic keep their exact widths after conversion. The table below is the accurate mapping used by the translator. </p><div class="table-wrapper"><table><thead><tr><th>BASIC type</th><th>Bits</th><th>C type emitted</th></tr></thead><tbody><tr><td><code>BYTE</code></td><td>8</td><td><code>int8_t</code></td></tr><tr><td><code>WORD</code></td><td>16</td><td><code>int16_t</code></td></tr><tr><td><code>LONG</code></td><td>32</td><td><code>int32_t</code></td></tr><tr><td><code>INTEGER</code> (default width)</td><td>32</td><td><code>int32_t</code></td></tr><tr><td><code>QUAD</code></td><td>64</td><td><code>int64_t</code></td></tr><tr><td><code>SINGLE</code></td><td>32</td><td><code>float</code></td></tr><tr><td><code>DOUBLE</code></td><td>64</td><td><code>double</code></td></tr><tr><td><code>GFLOAT</code></td><td>64</td><td><code>double</code></td></tr><tr><td><code>HFLOAT</code></td><td>128</td><td><code>double</code> (converted, with a warning)</td></tr><tr><td><code>DECIMAL(p,q)</code></td><td>up to 31 digits</td><td>packed-BCD decimal struct</td></tr><tr><td><code>STRING</code> (dynamic)</td><td>descriptor + data</td><td>VMS string descriptor pointer</td></tr><tr><td><code>STRING(n)</code> (fixed)</td><td>descriptor + n</td><td>VMS string descriptor pointer (fixed class)</td></tr><tr><td><code>RFA</code></td><td>48</td><td><code>unsigned char[6]</code> (RMS Record File Address)</td></tr><tr><td><code>ADDRESS</code></td><td>64</td><td><code>void *</code></td></tr></tbody></table></div><div class="callout warn"><strong>LONG is 32-bit.</strong> <code>LONG</code> maps to <code>int32_t</code>, and <code>INTEGER</code> defaults to the same 32-bit width (settable with <code>-ib</code>, <code>-iw</code>, <code>-il</code> or <code>OPTION SIZE</code>). The 64-bit integer type is <code>QUAD</code>, which maps to <code>int64_t</code>. Integer types are signed, matching VAX BASIC integer semantics. </div><h6>Strings and descriptors</h6><p> Every BASIC string lives in a VMS string descriptor rather than a bare C buffer. The translator emits a descriptor pointer for each string parameter, and the runtime owns the descriptor pool and performs allocation and release. Both dynamic strings and fixed-length <code>STRING(n)</code> strings are supported, which is what keeps migrated code compatible with VAX BASIC string memory management. </p><div class="callout"><strong>Floating-point note.</strong> <code>GFLOAT</code> and <code>HFLOAT</code> are converted to 64-bit <code>double</code> on non-VMS hosts. <code>HFLOAT</code> loses precision in this conversion and the translator emits a warning; there is no native 128-bit H-float on the target platforms. </div><h2 id="records-maps">Records, maps, and parameter passing</h2><p> RECORD, VARIANT, and MAP are the constructs that make VAX BASIC memory layout exact, and they are the most load-bearing part of a faithful translation. VX/BASIC lowers each of them into C types that reproduce the original byte layout, so a migrated program sees the same bytes at the same offsets as it did on OpenVMS. </p><h6>RECORD and VARIANT become struct and union</h6><p> A <code>RECORD</code> becomes a typedef'd C struct. A <code>VARIANT</code> becomes a <code>union</code>, and each <code>CASE</code> within it becomes a nested <code>struct</code>, so overlapping fields share storage exactly as they do under VAX BASIC. </p> <pre><span class="kw">RECORD</span> ACCOUNT_REC <span class="kw">VARIANT</span> <span class="kw">CASE</span> <span class="kw">WORD</span> BRANCH_ID, PRODUCT_CODE <span class="kw">LONG</span> ACCOUNT_NUMBER, BALANCE_CENTS <span class="kw">CASE</span> <span class="kw">LONG</span> RAW_KEY <span class="kw">END VARIANT</span> <span class="kw">END RECORD</span></pre> <p>translates to:</p> <pre>typedef struct { union { struct { _Word BRANCH_ID; _Word PRODUCT_CODE; _Long ACCOUNT_NUMBER; _Long BALANCE_CENTS; } case1; struct { _Long RAW_KEY; } case2; } var1; } ACCOUNT_REC_2__;</pre> <p> Note that <code>_Word</code> is 2 bytes and <code>_Long</code> is 4 bytes, so the layout is byte-for-byte what VAX BASIC produced. </p><h6>MAP overlays share one backing store</h6><p> Several <code>MAP</code> declarations that name the same program section describe the same storage viewed different ways. VX/BASIC emits one struct per declaration and binds all of them to a single backing block, so a write through one view is visible through the others. </p> <pre><span class="kw">MAP</span> (STMT) <span class="kw">STRING</span> STMT_RAW = 80 <span class="kw">MAP</span> (STMT) <span class="kw">LONG</span> STMT_ACCT, STMT_SEQ, STMT_AMOUNT</pre> <pre>static struct Tag_MAP_STMT { char sSTMT_RAW[80]; } *MAP_STMT; static struct Tag_MAP_STMT_1 { _Long pSTMT_ACCT; _Long pSTMT_SEQ; _Long pSTMT_AMOUNT; } *MAP_STMT_1; union { char data[sizeof(struct Tag_MAP_STMT)]; long align; } STMT; Vxb_PsectInit_1((void *)&amp;MAP_STMT, "STMT", &amp; STMT); Vxb_PsectInit_1((void *)&amp;MAP_STMT_1, "STMT", &amp; STMT);</pre> <p> Both struct pointers are bound to the same <code>STMT</code> byte block, so writing through <code>MAP_STMT_1</code> and reading through <code>MAP_STMT</code> sees the same bytes. This reproduces VAX BASIC's MAP-overlay memory model exactly. <code>MAP DYNAMIC</code> allocates on first reference and keeps the largest requested size; <code>MAP STATIC</code> becomes a BSS allocation. </p><h6>Parameter passing</h6><p> BASIC defaults to calling by reference. VX/BASIC preserves the <code>BY VALUE</code> and <code>BY REF</code> mechanism, passing literals by reference through a constant pool where needed. </p> <pre><span class="kw">EXTERNAL LONG FUNCTION</span> CALC_INTEREST(<span class="kw">LONG BY VALUE</span>) <span class="kw">EXTERNAL LONG FUNCTION</span> POST_LEDGER(<span class="kw">LONG BY REF</span>) <span class="kw">EXTERNAL LONG FUNCTION</span> AUDIT_ENTRY(<span class="kw">LONG</span>) <span class="cmt">! unmarked LONG defaults to BY REF</span></pre> <pre>result = calc_interest(1); /* BY VALUE: pass the int directly */ result = post_ledger(LCon(1, 1)); /* BY REF: pass address of pooled constant */ result = audit_entry(LCon(1, 1)); /* unmarked LONG defaults to BY REF */</pre> <p><code>LCon(n, v)</code> writes value <code>v</code> into constant-pool slot <code>n</code> and returns its address, which is how a literal is passed by reference. The <code>LONG</code> result field is a 32-bit integer, consistent with the type mapping above. </p><h2 id="runtime">Runtime model and error handling</h2><p> The generated C calls into <code>libvxbas</code>, the VX/BASIC runtime. The runtime provides descriptor-based strings, RMS record I/O, packed-decimal arithmetic, and the error stack. Each BASIC intrinsic maps to a runtime entry point named <code>Vxb_</code> followed by the routine name; translator-emitted helpers that never appear in user code use the <code>Vxbf_</code> prefix. </p><div class="table-wrapper"><table><thead><tr><th>Runtime entry</th><th>Purpose</th></tr></thead><tbody><tr><td><code>Vxbf_initialize</code></td><td>Master program initialisation: dialect selection, argument vector, decimal scale and rounding, and signal handlers.</td></tr><tr><td><code>Vxb_Open</code> / <code>Vxb_Close</code> / <code>Vxb_CloseAll</code></td><td>RMS channel open and close.</td></tr><tr><td><code>Vxb_Get</code> / <code>Vxb_Put</code> / <code>Vxb_Find</code> / <code>Vxb_Update</code> / <code>Vxb_Delete</code></td><td>RMS record operations.</td></tr><tr><td><code>Vxb_Print</code> / <code>Vxb_Input</code> / <code>Vxb_LineInput</code> / <code>Vxb_Inkey</code></td><td>Terminal I/O.</td></tr><tr><td><code>Vxb_Left</code> / <code>Vxb_Right</code> / <code>Vxb_Mid</code> / <code>Vxb_Instr</code> / <code>Vxb_Format</code></td><td>String intrinsics.</td></tr><tr><td><code>Vxb_Decimal</code> / <code>Vxb_Sum</code> / <code>Vxb_Prod</code> / <code>Vxb_Quo</code></td><td>Packed-decimal arithmetic.</td></tr><tr><td><code>Vxb_Err</code> / <code>Vxb_Erl</code> / <code>Vxb_Ert</code> / <code>Vxb_RmsStatus</code> / <code>Vxb_VmsStatus</code></td><td>Error introspection.</td></tr><tr><td><code>Vxbf_OnError_GoTo</code> / <code>Vxbf_Resume</code> / <code>Vxbf_Retry</code></td><td><code>ON ERROR</code> and <code>RESUME</code> machinery.</td></tr></tbody></table></div><p> The runtime exposes just under two hundred distinct entry points. It is ordinary C, so the same runtime call a translated program makes can be called directly from hand-written C that includes the runtime header. </p><h6>Error handling</h6><p> VX/BASIC preserves VAX BASIC error semantics so a migrated program behaves identically when it hits an error. <code>ON ERROR GOTO</code> is lowered onto the runtime's error machinery, which maintains a parent-linked context chain, sets a jump target per subprogram, and unwinds to the handler when an error is signalled. <code>RESUME</code> and <code>RETRY</code> resume execution, and the structured <code>WHEN ERROR USE</code> and <code>HANDLER</code> forms are supported with up to ten levels of nesting. </p><p> Programs introspect errors with <code>ERR</code>, <code>ERL</code>, <code>ERT$</code>, <code>RMSSTATUS</code>, and <code>VMSSTATUS</code>, exactly as on OpenVMS. RMS status values carry through as well: for example, the OpenVMS "file not found" condition (value <code>98962</code>) is the same value a migrated program sees, so return-value checks on system routines keep working without modification. </p><div class="callout"><strong>System services.</strong> When a program calls OpenVMS system routines and checks their return values, VX/BASIC backs that behaviour with the VX/RT runtime libraries and the bundled system-symbol headers, so status codes and RMS behaviour match the original. </div><h2 id="cli">Command-line reference</h2><p> The translator is driven from the command line. Each short option has a DCL-style long form for developers used to OpenVMS qualifier syntax. The highest-value flags are listed below. </p><div class="table-wrapper"><table><thead><tr><th>Short</th><th>DCL form</th><th>Effect</th></tr></thead><tbody><tr><td><code>-a</code></td><td><code>/NOPREPROCESS</code></td><td>Do not run the preprocessor as a separate pass; the translator handles <code>%</code> directives directly.</td></tr><tr><td><code>-c</code></td><td><code>/BOUNDS</code></td><td>Emit runtime array-bounds checks.</td></tr><tr><td><code>-C</code></td><td><code>/CDD</code></td><td>Treat the data definition as new-CDD format.</td></tr><tr><td><code>-ds</code> / <code>-dd</code></td><td><code>/REAL_SIZE=SINGLE|DOUBLE</code></td><td>Set the default real type.</td></tr><tr><td><code>-ib</code> / <code>-iw</code> / <code>-il</code></td><td><code>/INT_SIZE=BYTE|WORD|LONG</code></td><td>Set the default integer width.</td></tr><tr><td><code>-g</code> / <code>-G</code></td><td><code>/GO</code> / <code>/EXEC_GO</code></td><td>After translating, run the bundled host-compile script.</td></tr><tr><td><code>-k</code></td><td><code>/NATIVE_ACCESS</code></td><td>Emit direct C MAP-field access instead of descriptor accessors.</td></tr><tr><td><code>-L</code></td><td><code>/LIST</code></td><td>Emit a <code>.lst</code> listing.</td></tr><tr><td><code>-nN</code></td><td><code>/SCALE=N</code></td><td>Set the DECIMAL scale factor.</td></tr><tr><td><code>-q</code></td><td><code>/SQL</code></td><td>Treat the module as embedded SQL and emit Pro*C / ESQL-C output.</td></tr><tr><td><code>-R</code></td><td><code>/ROUND_DECIMALS</code></td><td>Round rather than truncate DECIMAL values.</td></tr><tr><td><code>-T</code></td><td><code>/LARGE_KEYS</code></td><td>Allow RMS index keys longer than 255 bytes.</td></tr><tr><td><code>-u</code></td><td><code>/PREPROCESS_ONLY</code></td><td>Run only the preprocessor and exit.</td></tr><tr><td><code>-vN</code></td><td><code>/VARIANT=N</code></td><td>Set the preprocessor <code>%VARIANT</code> value.</td></tr><tr><td><code>-x</code></td><td><code>/EXTERNAL</code></td><td>Force an EXTERNAL module with no <code>main()</code> emitted.</td></tr></tbody></table></div><h6>Environment controls</h6><p> Translator behaviour can also be set through environment variables, including <code>VXB_OPT</code>, <code>VXB_GO</code>, <code>VXB_LOWER_FUNCTION_NAMES</code>, <code>VXB_LOWER_MAP_NAMES</code>, <code>VXB_USE_RECORD_COPY</code>, and <code>VXB_USE_DESCR64</code>. </p><h6>Exit codes</h6><div class="table-wrapper"><table><thead><tr><th>Code</th><th>Meaning</th></tr></thead><tbody><tr><td><code>0</code></td><td>Success.</td></tr><tr><td><code>1</code></td><td>Completed with errors.</td></tr><tr><td><code>98962</code></td><td>Could not open the input source (the OpenVMS RMS "file not found" condition value).</td></tr></tbody></table></div><h2 id="compatibility">Compatibility and status</h2><p> The core language, RMS I/O, descriptor strings, packed DECIMAL, and structured error handling are fully supported. A small number of features are converted with a documented trade-off, and a few OpenVMS-specific constructs are out of scope. The matrix below records exactly one status per feature. </p><div class="table-wrapper"><table><thead><tr><th>Feature</th><th>Status</th><th>Detail</th></tr></thead><tbody><tr><td>Core language: types, flow control, SUB/FUNCTION, DEF, RECORD, MAP, COMMON</td><td><span class="ac-live">live</span></td><td>Full coverage with byte-exact layout.</td></tr><tr><td>RMS I/O: sequential, relative, indexed, virtual</td><td><span class="ac-live">live</span></td><td>All four file organizations supported.</td></tr><tr><td>Descriptor strings (dynamic and fixed)</td><td><span class="ac-live">live</span></td><td>VMS string descriptors preserved.</td></tr><tr><td>Packed DECIMAL arithmetic (up to 31 digits)</td><td><span class="ac-live">live</span></td><td>Scale and rounding controllable with <code>OPTION SCALE</code>, <code>-nN</code>, and <code>-R</code>.</td></tr><tr><td>Structured <code>WHEN ERROR USE</code> / <code>HANDLER</code></td><td><span class="ac-live">live</span></td><td>Up to ten levels of nesting.</td></tr><tr><td><code>HFLOAT</code> (128-bit H-float)</td><td><span class="ac-v1">partial</span></td><td>Converted to 64-bit <code>double</code> with a warning; precision is lost.</td></tr><tr><td><code>GFLOAT</code> on non-VMS hosts</td><td><span class="ac-v1">partial</span></td><td>Emitted as <code>double</code>; there is no native G-float on the target platforms.</td></tr><tr><td><code>MAT</code> matrix family</td><td><span class="ac-v1">partial</span></td><td>Basic forms work; mixed integer and decimal operands are rejected.</td></tr><tr><td>Embedded SQL (<code>-q</code>, Pro*C / ESQL-C)</td><td><span class="ac-v1">partial</span></td><td>Requires an Oracle Pro*C toolchain on the host.</td></tr><tr><td><code>OPTION ANGLE = DEGREES / RADIANS / GRADS</code></td><td><span class="ac-no">out of scope</span></td><td>Trigonometry is always in radians.</td></tr><tr><td>GRAPHICS family (<code>DRAW</code>, <code>PLOT</code>, <code>ROTATE</code>, ...)</td><td><span class="ac-no">out of scope</span></td><td>Recognised but not translated; this is the documented "all commands except graphics" exclusion.</td></tr></tbody></table></div><div class="callout"><strong>Maintain in place, or convert.</strong> Because the translation preserves structure, variable names, and comments, an application can continue to be developed in OpenVMS BASIC and re-translated on each release, or converted once and maintained as C. Either path keeps the same behaviour on Linux and Windows. </div><h2 id="quickref">Quick reference</h2><h6>Invoke</h6> <pre>vxbasic file.bas [-options] <span class="cmt">! produces file.c</span> vxbasic file.bas -g <span class="cmt">! translate then auto-compile</span> vxbasic file.bas -L <span class="cmt">! also emit a .lst listing</span> vxbasic file.bas -u <span class="cmt">! preprocess only</span></pre> <h6>Type map</h6><div class="kw-grid"><div>BYTE = int8_t</div><div>WORD = int16_t</div><div>LONG = int32_t</div><div>QUAD = int64_t</div><div>SINGLE = float</div><div>DOUBLE = double</div><div>GFLOAT = double</div><div>HFLOAT = double</div><div>DECIMAL = packed BCD</div><div>STRING = descriptor</div><div>RFA = uchar[6]</div><div>ADDRESS = void *</div></div><h6>Translation rules</h6><div class="kw-grid"><div>RECORD -&gt; typedef struct</div><div>VARIANT / CASE -&gt; union / struct</div><div>MAP overlays -&gt; parallel structs on one store</div><div>intrinsic X -&gt; Vxb_Xxx</div><div>helper -&gt; Vxbf_*</div><div>ON ERROR GOTO -&gt; Vxbf_OnError_GoTo</div></div><h6>Error introspection</h6><div class="kw-grid"><div>ERR</div><div>ERL</div><div>ERT$</div><div>RMSSTATUS</div><div>VMSSTATUS</div><div>STATUS</div></div><h6>Link line</h6> <pre>cc file.c -lvxbas -ll -lm -o file</pre> </div>

Frequently Asked Questions

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

Our OpenVMS application makes use of multiple System Services. How do you support that?

Sector7 has an extensive library of these services running on Linux to perform the same functions as on OpenVMS.  our language conversion tools will adapt the arguments in the procedure API to work with this library.  For instance, VX/COBOL or VX/FORTRAN etc will transform the argument passed BY DESCRIPTOR to a method that will work on Linux.

When the code is compiled on Linux, the compiled objects are linked with the Sector7 library of system services and the program executes on Linux just as it does on OpenVMS, making full use of the various system services.

The critical point here is that there is no need to touch the original source code, the Sector7 tools do all the work.

What is VX/BASIC and how does it help with legacy system migration?

VX/BASIC is a full-featured OpenVMS BASIC to ANSI C transpiler that enables legacy BASIC applications to run natively on Linux. It provides 100% conversion of VMS BASIC syntax to working C code while maintaining the original structure, variable names, and comments for easy maintenance.

For organizations ready to modernize their legacy systems, contact us to discuss your specific migration requirements.

What VMS BASIC data types does VX/BASIC support?

VX/BASIC supports all VMS BASIC data types including INTEGER, BYTE, WORD, LONG, DOUBLE, and STRING. The only exceptions are GFLOAT and HFLOAT, which are automatically converted to DOUBLE for compatibility.

How does VX/BASIC handle VMS error codes?

VX/BASIC preserves all VMS error codes and handling mechanisms, ensuring programs behave identically to their OpenVMS versions with the same error numbers and responses.

What I/O operations are supported in the migration?

VX/BASIC supports all standard VMS BASIC I/O operations including PRINT, INPUT, and file operations like OPEN FOR INPUT, converting them to equivalent ANSI C functions.

How many VMS system service calls does VX/BASIC support?

VX/BASIC supports over 900+ VMS system service calls, providing comprehensive integration with VMS functionality including RMS file handling and other system services.

What makes VX/BASIC different from rewriting applications from scratch?

VX/BASIC maintains the original BASIC source code structure while generating optimized C code, allowing developers to continue working with familiar BASIC syntax. The transpiled C code retains the original program flow and variable names, making debugging and maintenance much easier than completely rewritten applications.

What are the key benefits of using VX/BASIC for migration?

VX/BASIC delivers 100% compatibility with VMS BASIC while providing seamless portability to Linux. Organizations retain their valuable software investments while gaining modern infrastructure benefits including reduced hardware costs, improved performance, and enhanced scalability.

Companies like HH Gregg have successfully migrated millions of lines of legacy code using similar approaches. The generated ANSI C code ensures long-term portability and future-proofing for legacy applications.

Ready to explore migration options for your BASIC applications? Contact us to discuss your specific requirements.

What does VX/BASIC produce, an interpreter or C source?

It translates VMS and OpenVMS BASIC into ANSI C source and then compiles that C. C is used as an intermediate language, which is what gives the output long-term portability and lets it build, run, and be debugged directly on modern systems with standard tools.

Which platforms can the converted application run on?

VX/BASIC converts OpenVMS BASIC into C that compiles and runs on Linux and Windows, while still allowing the application to be maintained in its original OpenVMS BASIC form and re-translated on each release.

Which VMS BASIC data types are supported, and how do they map to C?

All VMS and OpenVMS BASIC data types are supported. Integer types map to fixed-width C types (BYTE to int8_t, WORD to int16_t, LONG to int32_t, QUAD to int64_t), SINGLE and DOUBLE map to float and double, DECIMAL maps to a packed decimal type, and STRING maps to a VMS string descriptor. GFLOAT and HFLOAT are converted to double.

Are strings handled the same way as under VMS BASIC?

Yes. VX/BASIC keeps both dynamic and fixed-length string handling and uses VMS string descriptors for STRING data, so migrated code stays compatible with VMS BASIC string memory management.

Will my error-handling logic still work after conversion?

VX/BASIC preserves VMS error codes and the ON ERROR handling model, so programs behave the same as they did on OpenVMS, including RMS errors such as the file-not-found condition. Return-value checks on system routines are backed by the VX/RT runtime libraries, and ERR, ERL, ERT$, RMSSTATUS, and VMSSTATUS are all available for introspection.

How are VMS BASIC records and maps converted?

VX/BASIC fully translates RECORD and MAP declarations into C. A RECORD becomes a typedef'd struct, a VARIANT becomes a union with one struct per CASE, and several MAP declarations that name the same storage become parallel structs bound to one backing block, preserving the exact VAX BASIC byte layout.

Which BASIC statements and I/O operations are covered?

All VMS BASIC statements are supported, including IF...THEN, FOR...NEXT, WHILE, SELECT CASE, GOTO, and ON ERROR GOTO. Terminal I/O such as PRINT, INPUT, and LINPUT is covered, and RMS file I/O supports sequential, relative, indexed, and virtual organizations.

Are there any limitations, for example graphics?

The one documented exclusion is the graphics command family, which is recognised but not translated. GFLOAT and HFLOAT are converted to double rather than preserved exactly (HFLOAT loses precision), and a few OpenVMS-specific options such as OPTION ANGLE are out of scope. Everything else in the core language is supported.

Transform Your Legacy Software Today!

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