Pascal is the most modern of the traditional VMS languages and relative high level (significant higher than C). And therefore a good choice for new business application code that has to be native (for whatever reason Java, Groovy, Python, PHP etc. are not an option).
VMS Pascal as other VMS compilers comes with extensive documentation:
And there are also extensive help:
$ help pas
$ help pas data
$ help pas stat
$ help pas pred
...
This article is not trying to redo those.
This article is targetting a very specific case:
Those that learned programming somewhere between late 70's and early 90's probably have learned Pascal as it was widely used for teaching programming back then. But later then curly bracket languages got in fashion.
The approach is practical. This article will not cover everything - it will cover a some of the common stuff that a C/Java/C# programmer will want to know about.
There will be no attempt to distinguish between what is standard and what is VMS specific.
Alle examples will be super simple.
The style is very brief. You are expected to know everything except VMS Pascal syntax.
For all the details use the documentation linked to above.
First a little bit about the Pascal language to set the context.
Pascal was created by Niklaus Wirth in 1970.
It was heavily inspired by ALGOL.
It did inspire later languages like Modula-2 and Ada.
Multiple flavors of the language exists:
VMS Pascal is the topic here.
Basic usage is very straigthforward:
Example:
$ edit foobar.pas
$ pas foobar
$ link foobar
$ run foobar
Any editor can be used.
EDT and EVE comes with VMS. LSE (Language Sensitive Editor) comes with DECset suite and knows about VMS Pascal.
If editing on a PC, then VSI supply VMS IDE which is based on VS Code and knows about VMS Pascal, but any editor like notepad++ or JEdit can be used.
A typical (and my personal preference) structure of a Pascal program is:
program header constant declarations type declarations procedure/function declarations variable declarations program body
with a procedure/function declaration like:
procedure/function header variable declarations procedure/function body
Example:
program outline(input,output);
const
ONE = 1;
type
myinteger = integer;
procedure add_one_and_print(v : myinteger);
var
temp : myinteger;
begin
temp := v + ONE;
writeln(temp:3);
end;
var
v : myinteger;
begin
v := 123;
add_one_and_print(v);
end.
Output:
124
The curly brackets { ... } are replaced by begin ... end.
Pascal is not case sensitive. FOOBAR, foobar and FooBar are the same.
Note that the last end (the program/module body end) is followed by a period not by a semicolon.
Comments are neither /* ... */ block or // ... single line comments but (* ... *) block or { ... } block comments.
Literals in VMS Pascal are different from typical curly bracket languages:
VMS Pascal | C (*) | Java | C# |
---|---|---|---|
123 | 123 | 123 | 123 |
16#1B | 0x1B | 0x1B | 0x1B |
'ABC' | "ABC" | "ABC" | "ABC" |
'X''X' | "X\"X" | "X\"X" | "X\"X" |
VMS Pascal has almost all the basic data types:
VMS Pascal | C (*) | Java | C# |
---|---|---|---|
integer8 | int8_t | byte | sbyte |
integer16 | int16_t | short | short |
integer (**)/integer32 | int32_t | int | int |
integer64 | int64_t | long | long |
unsigned8 | uint8_t | N/A | byte |
unsigned16 | uint16_t | N/A | ushort |
cardinal (**)/unsigned32 | uint32_t | N/A | uint |
unsigned64 | uint64_t | N/A | ulong |
single | float | float | float |
double | double | double | double |
quadruple | long double (***) | N/A | N/A |
boolean | bool | boolean | bool |
*) newer C with stdint.h, stdbool.h etc.. **) these are the ones almost alyways used in practice ***) on many platforms incl. VMS unless /L_DOUBLE=64
Besides the integer types it is also possible (and recommendable!) to use integer ranges.
var
v : integer;
tell that v is an integer in the interval –2147483648 to 2147483647. That interval makes good sense for CPU's, but it does not make sense in the typical business application.
var
v : 10000..99999;
tell that v is an integer in the interval 10000 to 99999 and that interval (5 digits) may make sense in the business application.
For text data there are 4 types:
Example:
program data(input,output);
type
fixstr10 = packed array [1..10] of char;
passtr = varying [255] of char;
var
s : integer;
u : cardinal;
s8 : integer8;
s16 : integer16;
s32 : integer32;
s64 : integer64;
u8 : unsigned8;
u16 : unsigned16;
u32 : unsigned32;
u64 : unsigned64;
x32 : single;
x64 : double;
x128 : quadruple;
b : boolean;
fstr : fixstr10;
vstr : passtr;
begin
s := -123;
writeln(s:4);
u := 123;
writeln(u:4);
s8 := -123;
writeln(s8:4);
s16 := -123;
writeln(s16:4);
s32 := -123;
writeln(s32:4);
s64 := -123;
writeln(s64:4);
u8 := 123;
writeln(u8:4);
u16 := 123;
writeln(u16:4);
u32 := 123;
writeln(u32:4);
u64 := 123;
writeln(u64:4);
x32 := 123.456;
writeln(x32:7:3);
x64 := 123.456;
writeln(x64:7:3);
x128 := 123.456;
writeln(x128:7:3);
b := true;
writeln(b);
fstr := 'ABC';
writeln('|' + fstr + '|');
vstr := 'ABC';
writeln('|' + vstr + '|');
end.
Output:
-123 123 -123 -123 -123 -123 123 123 123 123 123.456 123.456 TRUE |ABC | |ABC|
It is common to use type declaration to define an alias for types used similar to C typedef.
Note that quadruple does not work on VMS x86-64 up to at least 9.2-3.
VMS Pascal | C | Java | C# |
---|---|---|---|
foobar = (a,b,c,d); | enum foobar { a, b, c, d }; | enum Foobar { a, b, c, d }; | enum Foobar { a, b, c, d }; |
array [n1..n2] of something | something[n] | something[] | something[] |
typnam = record field1 : type1; ... fieldn : typen; end; |
struct typnam { type1 field1; ... typen fieldn; } |
N/A | public struct typnam { public type1 field1; ... public typen fieldn; } |
typnam = record case typ of: val1 : ( field1 : type1; ); ... valn : ( fieldn : typen; ); end; |
union typnam { type1 : field1; ... typen : fieldn; } |
N/A | [StructLayout(LayoutKind.Explicit)] public struct typnam { [FieldOffset(0)] public type1 field1; ... [FieldOffset(0)] public typen fieldn; } |
Example:
program data2(input,output);
type
langtyp = (procedural, objectoriented, functional, generic);
passtr = varying [255] of char;
intarr3 = array [1..3] of integer;
demostruct = record
iv : integer;
xv : double;
sv : passtr;
end;
demounion = record
case boolean of
true : ( v32 : integer32; );
false : ( v8 : packed array [1..4] of integer8; );
end;
var
lang : langtyp;
ia : intarr3;
demos : demostruct;
demou : demounion;
begin
lang := procedural;
writeln(lang);
ia[1] := 123;
ia[2] := 456;
ia[3] := 789;
writeln(ia[1]:3, ' ', ia[2]:3, ' ', ia[3]:3);
demos.iv := 123;
demos.xv := 123.456;
demos.sv := 'ABC';
writeln(demos.iv:3, ' ', demos.xv:7:3, ' ', demos.sv);
demou.v32 := 258;
writeln(demou.v8[1]:2, demou.v8[2]:2, demou.v8[3]:2, demou.v8[4]:2);
end.
Output:
PROCEDURAL 123 456 789 123 123.456 ABC 2 1 0 0
Pascal has pointers, but Pascal pointers are more like Java/C# references than C pointers.
Pascal pointers can be used to allocate, to reference the allocated and to free the allocated. No fancy pointer arithmetic.
Pascal uses nil instead of NULL/null for the pointer to nothing.
The syntaxes are:
Example:
program dyn(input,output);
type
rec = record
val : integer;
next : ^rec;
end;
var
first, curr, temp : ^rec;
begin
new(first);
curr := first;
curr^.val := 123;
new(curr^.next);
curr := curr^.next;
curr^.val := 456;
new(curr^.next);
curr := curr^.next;
curr^.val := 789;
curr^.next := nil;
curr := first;
while curr <> nil do begin
writeln(curr^.val);
curr := curr^.next;
end;
curr := first;
while curr <> nil do begin
temp := curr^.next;
dispose(curr);
curr := temp;
end;
end.
Output:
123 456 789
VMS Pascal has all the usual operators, but the symbol used are different for some of them:
VMS Pascal | C | Java | C# |
---|---|---|---|
***** the ones to look out for ***** | |||
:= | = | = | = |
= | == | == | == |
<> | != | != | != |
DIV | / (for integer args) | / (for integer args) | / (for integer args) |
/ | / (for floating point args) | / (for floating point args) | / (for floating point args) |
MOD | % | % | % |
NOT | ! | ! | ! |
AND | && | && | && |
OR | || | || | || |
***** as expected ***** | |||
+ | + | + | + |
- | - | - | - |
* | * | * | * |
< | < | < | < |
> | > | > | > |
<= | <= | <= | <= |
>= | >= | >= | >= |
The most important things to note are that:
VMS Pascal has a long list of builtin procedures/functions, but here is a list of some of the most relevant:
VMS Pascal | C | Java | C# |
---|---|---|---|
c := chr(iv); | c = (char)iv; | c = (char)iv; | c = (char)iv; |
iv := ord(c); | iv = (int)c; | iv = (int)c; | iv = (int)c; |
iv := trunc(xv); (* towards zero *) | iv = (int)floor(xv); /* down */ iv = (int)trunc(xv); /* towards zero */ iv = (int)xv; /* towards zero */ |
iv = (int)Math.floor(xv); // down | iv = (int)Math.Floor(xv); // down iv = (int)Math.Truncate(xv); // towards zero |
iv := round(xv); | iv = lround(xv); iv = (int)floor(xv + 0.5); iv = (int)(xv + 0.5); |
iv = (int)Math.round(xv); | iv = (int)Math.Round(xv); |
x**n | pow(x, n) | Math.pow(x, n) | Math.Pow(x, n) |
exp(x) | exp(x) | Math.exp(x) | Math.Exp(x) |
ln(x) | log(x) | Math.log(x) | Math.Log(x) |
sqrt(x) | sqrt(x) | Math.sqrt(x) | Math.Sqrt(x) |
len := length(s); | len = strlen(s); | len = s.length; | len = s.Length; |
ix := index(s, 'something'); | s2 = strstr(s, "something"); | ix = s.index("something); | ix = s.Index("something"); |
s2 := substr(s, ix, len); | strncpy(s2, s + ix, len); s2[len] = 0; |
s2 = s.substring(ix1, ix2); | s2 = s.Substring(ix, len); |
VMS Pascal has the usual control structures:
VMS Pascal | C | Java | C# |
---|---|---|---|
if cond then begin ... end; |
if(cond) { ... } |
if(cond) { ... } |
if(cond) { ... } |
case v of val1 : begin ... end; ... valn : begin ... end; otherwise begin ... end; end; |
switch(v) { case val1: ... break; ... case valn: ... break; default: ... break; } |
switch(v) { case val1: ... break; ... case valn: ... break; default: ... break; } |
switch(v) { case val1: ... break; ... case valn: ... break; default: ... break; } |
for i := n1 to n2 do begin ... end; |
for(i = n1; i <= n2; i++) { ... } |
for(i = n1; i <= n2; i++) { ... } |
for(i = n1; i <= n2; i++) { ... } |
while cond do begin ... end; |
while(cond) { ... } |
while(cond) { ... } |
while(cond) { ... } |
repeat ... until cond; |
do { ... } |
do { ... } |
do { ... } |
Example:
program ctl(input,output);
var
v, i : integer;
begin
v := 1;
if v = 0 then begin
writeln('zero');
end else begin
writeln('not zero');
end;
case(v) of
0 : begin
writeln('zero');
end;
1 : begin
writeln('one');
end;
otherwise begin
writeln('not zero or one');
end;
end;
for i := 1 to 3 do begin
write(i:2);
end;
writeln;
i := 0;
while i < 3 do begin
i := i + 1;
write(i:2);
end;
writeln;
i := 0;
repeat
i := i + 1;
write(i:2);
until i >= 3;
writeln;
end.
Output:
not zero one 1 2 3 1 2 3 1 2 3
Just like the curly brackets can be omitted for a single statement in C/Java/C#, then in Pascal begin end can be omitted for a single statement.
But both in C/Java/C# and in Pascal it is considered good practice to have them also for a single statement.
Pascal distinguish between functions returning a value and procedures not returning a value.
VMS Pascal | C | Java | C# |
---|---|---|---|
procedure name(arg1, ..., argn); ... begin ... end; |
void name(arg1, ..., argn) { ... } |
void name(arg1, ..., argn) { ... } |
void name(arg1, ..., argn) { ... } |
function name(arg1, ..., argn) : returntype; ... begin ... name := ...; end; |
returntype name(arg1, ..., argn) { ... return ...; } |
returntype name(arg1, ..., argn) { ... return ...; } |
returntype name(arg1, ..., argn) { ... return ...; } |
argname : argtype | argtype argname | argtype argname | argtype argname |
var argname : argtype | argtype *argname | N/A | ref argtype argname |
Example:
program rtn(input,output);
function addf(a, b : integer) : integer;
begin
addf := a + b;
end;
procedure addp(a, b : integer; var c : integer);
begin
c := a + b;
end;
procedure testfac;
function fac(n : integer) : integer;
begin
if n < 2 then begin
fac := 1;
end else begin
fac := n * fac(n - 1);
end;
end;
var
i : integer;
begin
for i := 1 to 5 do begin
writeln('fac(', i:1, ')=', fac(i):1);
end;
end;
var
a, b , c : integer;
begin
a := 123;
b := 456;
c := addf(a, b);
writeln(c:3);
addp(a, b, c);
writeln(c:3);
testfac;
end.
Output:
579 579 fac(1)=1 fac(2)=2 fac(3)=6 fac(4)=24 fac(5)=120
Note that Pascal allow procedures/functions inside procedures/functions to limit scope. In the above example fac is actually declared inside testfac.
VMS Pascal supports both default values and named arguments for calls.
Example:
program args(input,output);
procedure test(a : integer := 1; b : integer := 2; c : integer := 3; d : integer := 4);
begin
writeln(a:1, ' ', b:1, ' ', c:1, ' ', d:1);
end;
begin
test(11, c := 33);
end.
Output:
11 2 33 4
VMS Pascal supports variable number of arguments.
VMS Pascal | C | Java | C# |
---|---|---|---|
|
|
|
|
VMS Pascal has always supported calling code in other languages. Back in the days Macro-32, Fortran etc. code. In the last few decades a lot of C.
The syntax is:
[external] procedure name(arg1, ..., argn); external; [external] function name(arg1, ..., argn) : returntype; external;
where argx is in the form:
mechanism name : type
and mechanism is one of %immed, %ref or %stddescr.
Let us see an example.
subroutine f(iv,sv)
integer*4 iv
character*(*) sv
write(*,'(1x,a,2h: ,i3,1h ,a)') 'Fortran',iv,sv
return
end
#include <stdio.h>
void c(int iv, char *sv)
{
printf("C: %d %s\n", iv, sv);
}
program fc(input,output);
[external]
procedure f(%ref iv : integer; %stdescr sv : packed array [l..u:integer] of char); external;
[external]
procedure c(%immed iv : integer; %immed sv : c_str_t); external;
begin
f(123,'ABC');
c(123, malloc_c_str('ABC'));
end.
Build and run:
$ for f
$ cc c
$ pas fc
$ lin fc+f+c
$ r fc
Output:
Fortran: 123 ABC C: 123 ABC
The code as shown does not free the memory allocated by malloc_c_str. That should obvious be freed in real code.
VMS Pascal text IO is very simple.
VMS Pascal | C | Java | C# |
---|---|---|---|
readln(line); | fgets(line, sizeof(line), stdin); /* note line will include \n */ | line = br.readLine(); | line = sr.ReadLine(); |
writeln(iv, ' ', xv, ' ', sv); | printf("%d %f %s\n", iv, xv, sv); | System.out.printf("%d %f %s\n", iv, xv, sv); | Console.WriteLine("{0} {1} {2}", iv, xv, sv); |
writeln(iv:3, ' ', xv:7:3, ' ', sv); | printf("%3d %7.3f %s\n", iv, xv, sv); | System.out.printf("%3d %7.3f %s\n", iv, xv, sv); | Console.WriteLine("{0:3} {1:7F3} {2}", iv, xv, sv); |
open(f, fnm, old); reset(f); |
f = fopen(fnm "r"); | br = new BufferedReader(new FileReader(fnm)); | sr = new StreamReader(fnm) |
open(f, fnm, new); rewrite(f); |
f = fopen(fnm, "w"); | pw = new PrintWriter(new FileWriter(fnm)); | sw = new StreamWriter(fnm); |
close(f); | fclose(f); | f.close(); | f.Close(); |
while not(eof(f)) do while not eof(f) do |
while((c = fgetc(f)) != EOF) while(fgets(line, sizeof(line), f)) |
while((line = br.readLine()) != null) | while((line = sr.ReadLine()) != null) |
readln(f, line); | fgets(line, sizeof(line), f); /* note line will include \n */ | line = br.readLine(); | line = sr.ReadLine(); |
writeln(f, iv, ' ', xv, ' ', sv); | fprintf(f, "%d %f %s\n", iv, xv, sv); | pw.printf("%d %f %s\n", iv, xv, sv); | sw.WriteLine("{0} {1} {2}", iv, xv, sv); |
readv(s, v); | sscanf(s, "%d", &v); | v = Integer.parseInt(s); | v = int.Parse(s); |
writev(s, v:3); | sprintf(s, "%3d", v); | s = String.format("%3d", v); | s = string.Format("{0:3}", v); |
Example:
program copy(input,output);
var
f1, f2 : text;
line : varying [255] of char;
begin
open(f1, 'z1.txt', old);
reset(f1);
open(f2, 'z2.txt', new);
rewrite(f2);
while not(eof(f1)) do begin
readln(f1, line);
writeln(f2, line);
end;
close(f2);
close(f1);
end.
VMS Pascal binary IO is a bit more different.
Basically it is a file of records that is accessed via a buffer pointer and data is moved between the file and the buffer via get and put functions.
Easier to show with an example:
type
pstr = varying [255] of char;
rec = record
iv : integer;
xv : double;
sv : pstr;
end;
program binw(input,output);
%include 'bin.inc'
var
f : file of rec;
begin
open(f, 'z.dat', new);
rewrite(f);
f^.iv := 123;
f^.xv := 123.456;
f^.sv := 'ABC';
put(f);
f^.iv := 456;
f^.xv := 456.789;
f^.sv := 'DEF';
put(f);
f^.iv := 789;
f^.xv := 789.123;
f^.sv := 'GHI';
put(f);
close(f);
end.
program binr(input,output);
%include 'bin.inc'
var
f : file of rec;
begin
open(f, 'z.dat', old);
reset(f);
while not(eof(f)) do begin
writeln(f^.iv:3, ' ', f^.xv:7:3, ' ', f^.sv);
get(f);
end;
close(f);
end.
Output:
123 123.456 ABC 456 456.789 DEF 789 789.123 GHI
Note that we include the type definition to ensure consistency.
Also note that such a file of records are a RFM=FIX file.
VMS Pascal has builtin support for index-sequential files. That is really builtin support for a NoSQL database (Key Value Store).
Super cool feature!
Technically it is very similar to the binary IO in previous section. Just:
type
fixstr10 = packed array [1..10] of char;
pstr = varying [255] of char;
rec = record
key1 : [key(0)] integer;
key2 : [key(1)] fixstr10;
ival : integer;
sval : pstr;
end;
program isqw(input,output);
%include 'isq.inc'
var
f : file of rec;
begin
open(f, 'z.isq', new, organization := indexed, access_method := keyed);
rewrite(f);
f^.key1 := 1;
f^.key2 := 'A';
f^.ival := 123;
f^.sval := 'This is test #1';
put(f);
f^.key1 := 2;
f^.key2 := 'BB';
f^.ival := 456;
f^.sval := 'This is test #2';
put(f);
f^.key1 := 3;
f^.key2 := 'CCC';
f^.ival := 789;
f^.sval := 'This is test #3';
put(f);
close(f);
end.
program isqr(input,output);
%include 'isq.inc'
var
f : file of rec;
begin
open(f, 'z.isq', new, organization := indexed, access_method := keyed);
rewrite(f);
f^.key1 := 1;
f^.key2 := 'A';
f^.ival := 123;
f^.sval := 'This is test #1';
put(f);
f^.key1 := 2;
f^.key2 := 'BB';
f^.ival := 456;
f^.sval := 'This is test #2';
put(f);
f^.key1 := 3;
f^.key2 := 'CCC';
f^.ival := 789;
f^.sval := 'This is test #3';
put(f);
close(f);
end.
$ typ isqr.pas
program isqw(input,output);
%include 'isq.inc'
var
f : file of rec;
procedure find0(k : integer);
begin
findk(f, 0, k);
if not(ufb(f)) then begin
writeln(f^.ival:3, ' ', f^.sval);
end else begin
writeln('Not found: ', k:1);
end;
end;
procedure find1(k : fixstr10);
begin
findk(f, 1, k);
if not(ufb(f)) then begin
writeln(f^.ival:3, ' ', f^.sval);
end else begin
writeln('Not found: ', k);
end;
end;
begin
open(f, 'z.isq', old, organization := indexed, access_method := keyed);
reset(f);
find0(2);
find0(4);
find1('BB');
find1('DDDD');
close(f);
end.
Output:
456 This is test #2 Not found: 4 456 This is test #2 Not found: DDDD
It is very similar to the annotation/attribute base ORM frameworks that are common in non-native programming languages (mostly outside of VMS).
All the above examples show one Pascal source file being compiled to one EXE.
But it is possible to create libraries in VMS Pascal and reuse those from multiple programs.
This is done using modules.
VMS Pascal | C | Java | C# |
---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
The inherit attribute makes the program inherit all types, procedures and functions from the module.
VMS Pascal ships with some modules that provide interface to system services and various RTL's, including:
Example:
[inherit('sys$library:pascal$lib_routines')]
program sys(input,output);
type
pstr = varying [255] of char;
var
cmdlin, sym, log : pstr;
begin
lib$get_foreign(cmdlin.body,,cmdlin.length);
writeln(cmdlin);
lib$get_symbol('HOME', sym.body, sym.length);
writeln(sym);
lib$get_logical('JAVALIB', log.body, log.length);
writeln(log);
end.
These 3 LIB$ calls all expect a string and a return length in the arguments.
That is easy to provide by utilizing the fact that:
type
pstr = varying [255] of char;
is equivalent of:
type
pstr = record
length : unsigned16;
body : packed array [1..255] of char;
end;
which is why .body and .length in the above LIB$ call examples works!
VMS Pascal is a procedural only language. No classes, no lambdas etc..
And in general it is a bad thing to try and use a language for something else than it is intended for.
But VMS Pascal has certain features that can be used to implement OO-like encapsulation.
Example:
module oo(input,output);
type
clz = [hidden] record
counter : integer;
end;
clzptr = [hidden] ^clz;
function clz_new : pointer;
var
p : clzptr;
begin
new(p);
p^.counter := 0;
clz_new := p;
end;
procedure clz_increment(obj : pointer);
var
p : clzptr;
begin
p := obj;
p^.counter := p^.counter + 1;
end;
function clz_get(obj : pointer) : integer;
var
p : clzptr;
begin
p := obj;
clz_get := p^.counter;
end;
procedure clz_delete(obj : pointer);
var
p : clzptr;
begin
p := obj;
dispose(p);
end;
end.
[inherit('oo')]
program ootest(input,output);
var
o : pointer;
begin
o := clz_new;
clz_increment(o);
clz_increment(o);
clz_increment(o);
writeln(clz_get(o));
clz_delete(o);
end.
The content of the record clz is inaccessible for code outside the module.
The hidden attribute can also be put on procedures/functions to make them inaccessible for code outside the module.
Pascal also has the ability to pass functions as arguments in procedure/function calls.
Example:
program fp(input,output);
const
EPS = 0.000001;
function diff(x : double; function f(x : double) : double) : double;
begin
diff := (f(x + EPS) - f(x)) / EPS;
end;
function diff2(x : double; function f(x : double) : double) : double;
begin
diff2 := (diff(x + EPS, f) - diff(x, f)) / EPS;
end;
function newton(function f(x : double) : double) : double;
var
x, xprev : double;
begin
x := 1;
repeat
xprev := x;
x := xprev - diff(x, f) / diff2(x, f);
until abs(x - xprev) < EPS;
newton := x;
end;
function testf(x : double) : double;
begin
testf := (x - 2) * (x - 2);
end;
begin
writeln(newton(testf));
end.
VMS Pascal as of V6.3/6.4 on VMS 8.4/9.2 is mostly using 32 bit addresses/pointers.
...
type
charptr = ^char;
qcharptr = [quad] ^char;
var
loc : char;
sta : [static] char;
p : charptr;
qp : qcharptr;
begin
...
new(p);
...
new(qp);
...
here:
For more about this topic see VMS - a 64 bit OS with both 32 and 64 bit pointers.
Version | Date | Description |
---|---|---|
0.9 | January 1st 2025 | Pre-release |
1.0 | January 3rd 2025 | Updates (thanks to Chris, Dennis, Dan and Martin for comments) |
1.1 | January 7th 2025 | Updates (thanks to John for comments) |
See list of all articles here
Please send comments to Arne Vajhøj