PL/1 on CPM

485 views
Skip to first unread message

Paul Richards

unread,
May 31, 2022, 2:37:59 AMMay 31
to
I'm running Digital Research PL/1-80 compiler on CP/M 2.2 in the SIMH
Altair Z80 simulator. I have typed in a sample program from the DR PL/1
Language Programmers Guide. I've typed it in accurately - as far as I
can see - but I keep getting an error on the last line of the program.
Can anyone provide an explanation please? Thanks in advance.

....

Program code:

B>type sample.pli
sample:
procedure options(main);
declare
c character(10) varying;

do;
put skip list('Input: ');
get list(c);
c = upper(c); /* function reference */
put skip list('Output: ',c);
end;

begin;
declare
c float binary;

put skip list('Input ');
get list(c);
call output(c); /* subroutine invocation */
end;

upper:
procedure(c) returns(character(10) varying);
declare
c character(10) varying;

return(translate(c,'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'abcdefghijklmnopqrstuvwxyz'));
end upper;

output:
procedure(c);
declare
c float binary;

put skip edit(c) (column(20),e(10,2));
end output;

end sample;
B>

Compilation output:

B>pli sample.pli



PL/I-80 V1.0, COMPILATION OF: SAMPLE


39 b end sample;
SYNTAX ?
UNEXPECTED EOF
B>

Udo Munk

unread,
May 31, 2022, 4:16:57 AMMay 31
to
The usual reason for this is that the last line in the file is not CR/LF terminated.
Many parsers read a line until CR/LF and don't expect EOF before that.

Roger Hanscom

unread,
May 31, 2022, 1:05:54 PMMay 31
to
Here ya' go, Paul. This actually works with the compiler you mentioned. Please forgive the "style". I'm not much of a PL/I programmer. *grin* And, I'm sure that the indentation will be lost when the program is included in the post.

ascii:
proc options(main);
dcl
z (1:79) static char (1) varying,
x (1:17) static char (1) initial
('0','1','2','3','4','5','6','7','8',
'9','A','B','C','D','E','F',' '),
(i,j,k,l,m) fixed,
(ca,cb,a,b,t,w) float binary(15);

do i = 1 to 25;

do j = 1 to 79;
ca=0.0458*float(j-40);
a=ca;
cb=0.08333*float(i-13);
b=cb;

do k = 1 to 16;
t=(a*a)-(b*b)+ca;
b=(a*b*2.0)+cb;
a=t;
if (((a*a)+(b*b))>4.0) then
do;
z(j)=x(k);
goto last;
end;
end;

z(j)=x(17);
last: end;
put edit((z(l) do l = 1 to 79)) (skip,79a(1));
end;

end ascii;

Paul Richards

unread,
May 31, 2022, 9:05:32 PMMay 31
to
On 31/05/2022 6:16 pm, Udo Munk wrote:
> The usual reason for this is that the last line in the file is not CR/LF terminated.
> Many parsers read a line until CR/LF and don't expect EOF before that.

Udo

Thanks. Adding a couple of <enter>s after the 'end sample:' fixed the
problem.

One interesting issue is that the code shown in the DR Language manual
had the binaries defined as 'binary(24)' but this threw up an error 'NOT
IMP' so I redefined them as straight 'binary'.

Paul Richards

unread,
May 31, 2022, 9:06:29 PMMay 31
to
Thanks Roger. Udo's suggestion solved the problem.

Stephen Mitchell

unread,
Jun 2, 2022, 4:14:17 PMJun 2
to
This thread raises the interesting question of why PL/I-80 did not acquire any significant traction as a programming language under CP/M. When Gary Kildall's unpublished memoir, I was struck by how proud he was of the compiler, which he asserted produced code every bit as good as an experienced assembly language programmer would. This must have been partly true, since Jim Gilbreath, in his 1981 Byte article on the Sieve of Eratosthenes benchmark, reported that PL/I-80 turned in the fastest execution time of any 8-bit compiler.

My own experience is that compilation and linking times are about the same as the BDS C compiler, though not as fast as Turbo Pascal. Execution time was somewhat faster than either. (An program to find all amicable number pairs up to 20,000, for example, took 1 min 41 seconds to run on a emulated Z80 system far speedier than my old Kaypro 10, while the Turbo Pascal executable took 1 min 57 seconds.) File sizes were comparable to those produced by BDS C and Turbo Pascal. My major gripe was that a simple error, such as forgetting the closing single quote on a string literal, early in the program could trigger a cascade of error messages scrolling by so quickly that it was hard to tell where the actual mistake was made. Very different from Turbo Pascal!

Steve Mitchell

dxforth

unread,
Jun 2, 2022, 11:42:05 PMJun 2
to
On 3/06/2022 06:14, Stephen Mitchell wrote:
>
> This thread raises the interesting question of why PL/I-80 did not acquire any significant traction as a programming language under CP/M. When Gary Kildall's unpublished memoir, I was struck by how proud he was of the compiler, which he asserted produced code every bit as good as an experienced assembly language programmer would.

I know little about either but why not PL/M given it was aimed at targets
such as CP/M? It was also Kildall's baby.

Udo Munk

unread,
Jun 3, 2022, 2:26:29 AMJun 3
to
PL/1 was not used much by hobbiest, for various reasons. Compiler was a lot more expensive
than BDS C, Turbo C... There were very few publications in magazines about the language.

But obviously DRI used it them self for various stuff, some of the CP/M 3 tools were
written in PL/1 and not PL/M.

Then during the 80's I was writing software on IBM mainframes, also in PL/1. I used
the CP/M system with DRI's PL/1 compiler at home to test and try some stuff, which then
was moved to the mainframes.

In Germany we even had books explicite about DRI PL/1, I still have:

PL/1 fuer Microcomputer
Helmut Schliessmann
ISBN 3-411-01636-1

So from what I have seen and done the compiler had some significance.

Phil G

unread,
Jun 13, 2022, 10:54:17 AMJun 13
to
Back In The Day ⟨™⟩ :-) I used PL/I-80 extensively at work (BT) and for some hobby stuff at home.
All our reporting stuff for management was written in PL/I and at home I had an AX25 packet radio BBS
in PL/I-80. I probably still have some paper listings up in the loft. I'm an assembler dinosaur at heart
so I also wrote a library for PL/I-80 that gave directory access, extra bit-banged serial ports, peek, poke, in, out,
delays, terminal functions, etc - I still have that RMAC source. But I must admit, 40 years on, PL/I does look clunky!
Cheers
Phil

Dennis Boone

unread,
Jun 14, 2022, 12:01:36 AMJun 14
to
> I had an AX25 packet radio BBS in PL/I-80.

Now that'd be fun to see.

De

A2CPM

unread,
Jun 17, 2022, 6:15:39 AMJun 17
to
Investigation reveals that the following image file in the Apple ][ Asimov repository contains mostly corrupted files:
./images/cpm/programming/CPM_PLI_3.dsk
I'm working on creating a replacement iimage file.

Willi

Phil G

unread,
Jun 23, 2022, 5:09:21 PMJun 23
to
Andreas asked to see whats left of my old PL/I-80 stash so I zipped up what I have after 40 years, the philslib source is there and a cut-down minlib version and a brief doc, but I cant find my declares file or the library demo program. Shouldnt be too hard to recreate the philslib.dcl for someone with a better memory! IIRC the bbs had parts compiled into overlays but after all this time I cant really remember. I honestly never expected any interest in PL/I after we moved from Z80s & CP/M onto IBM PCs!
The zip is here: http://www.mccrash-racing.co.uk/philg/cpm/philg_pli.zip
I'd be interested to hear if anyone finds a use for philslib in 2022 :-)
Cheers
Phil

Dennis Boone

unread,
Jun 24, 2022, 11:51:29 AMJun 24
to
Neat! Thanks for sharing.

De

David Given

unread,
Jul 9, 2022, 3:06:54 PMJul 9
to
On Friday, 3 June 2022 at 08:26:29 UTC+2, Udo Munk wrote:
[...]
> But obviously DRI used it them self for various stuff, some of the CP/M 3 tools were
> written in PL/1 and not PL/M.

Did the source code for any CP/M hosted versions of the PL/1 or PL/M compilers ever make it out anywhere?

The context is: now that I can use DR source in cpmish, I want to build the DR CP/M tools from scratch --- and they're mostly written in PL/M. It'd also be really nice to be able to include a proper compiler with the package.

I'm aware of ogdenpm's reconstruction of the Intel PL/M-80 compiler, but I'm a bit wary of those as I don't believe these were ever formally released by Intel, so the licensing is suspect.

Randy McLaughlin

unread,
Jul 9, 2022, 6:14:29 PMJul 9
to
The short answer is no.

The original CP/M was built on 8080 ISIS, the last systems on a VAX.

So many were disassembled and well documented so they can be self-built with assemblers now.

It would be nice to find tools that run on x86 machines, should be doable.

The biggest problem is that real sources are piece-meal.


Randy

Phil G

unread,
Jul 9, 2022, 6:58:01 PMJul 9
to
Udo wrote:
> some of the CP/M 3 tools were written in PL/1 and not PL/M.
I'm most surprised to hear this Udo, with no memory or port access PL/I seems a most unlikely choice for O/S tools.
Do you have any more details?
Cheers
Phil

Udo Munk

unread,
Jul 10, 2022, 5:07:22 AMJul 10
to
udos-mbp:cpm3src udo$ ls *.pli *.dcl
diomod.dcl initdir.pli plibios.dcl sopt.dcl

initdir doesn't need access to ports and there are declaration files for low level I/O.

Udo Munk

unread,
Jul 10, 2022, 5:43:29 AMJul 10
to
david...@gmail.com schrieb am Samstag, 9. Juli 2022 um 21:06:54 UTC+2:
> On Friday, 3 June 2022 at 08:26:29 UTC+2, Udo Munk wrote:
> [...]
> > But obviously DRI used it them self for various stuff, some of the CP/M 3 tools were
> > written in PL/1 and not PL/M.
> Did the source code for any CP/M hosted versions of the PL/1 or PL/M compilers ever make it out anywhere?

No. Also DRI had no CP/M based PL/M compiler, it runs on Intel ISIS. One can run it on CP/M with
help of an ISIS emulation, some documentation about how to do that here:
https://www.autometer.de/unix4fun/z80pack/doc_isis_intro.html

Then DRI used the VAX as software repository, not as compile machine. You can see that in their
build scripts, which fetch sources from the VAX to the ISIS system, compile stuff and transfer
results back to the VAX.

> The context is: now that I can use DR source in cpmish, I want to build the DR CP/M tools from scratch --- and they're mostly written in PL/M. It'd also be really nice to be able to include a proper compiler with the package.

The z80pack repository has disk images with compiler versions that are able to build everything from source:
https://www.autometer.de/unix4fun/z80pack/doc_cpm22_src.html
https://www.autometer.de/unix4fun/z80pack/doc_cpm3_src.html
https://www.autometer.de/unix4fun/z80pack/doc_mpm_src.html

> I'm aware of ogdenpm's reconstruction of the Intel PL/M-80 compiler, but I'm a bit wary of those as I don't believe these were ever formally released by Intel, so the licensing is suspect.

Intel is aware that the stuff is out there since decades and they will tolerate this. No way you ever will
get some formal licesing from them, I tried. So use it or don't.

rwd...@gmail.com

unread,
Jul 10, 2022, 1:03:38 PMJul 10
to
==============================================================================================================================================================================
For anyone interested in PLI-80 I attach a sample simple hexdump program, it uses a lot of features of the language, including on-error blocks. It is not the best hexdump program, just something to practice with PLI.

enjoy
Richard

hexd-101.pli

/*
This program has been inspired by hexdump.pas in COMPLETE TURBO PASCAL 5.0
by Jeff Duntemann (1988)
Converted to PLI-80 by Richard Deane (2019) and openly offered to the public domain
I ported it across to PLI-80 as an exercise in relearning PLI
It is offered as is, with no claim to follow good programming practice, nor any guarantee
of correct behaviour.

The hex to display-text conversion is performed using an in-built PLI formatting
capability on the "put edit" statement; however this only works for bit string output.

I find PLI-80 to be a very powerful, flexible and complete language, especially with
respect to conversion of data types.

I converted hexdump.pas between several versions of pascal and attempted a conversion to
Modula2 but got blocked by the inability to convert from string to text. I did not have
these problems with PLI-80 (update FTL HISOFT Modula 2 is much better than the ones i tried back then)

Some comments on features used in the program:

1) $1.$1 and $2.$2 are very useful pseudo constants that can be passed from command line
to file open statements; you can't access them in between in other pli-80 language statements.

2) Any error in the command line parameters to select file handling can be picked up by
"on error" blocks activated by file open.
Initially I had these tidily at the end of the program but they didn't work as expected.
By placing them just before the code that might cause an "on error" condition,
I found that they did work, but I have not properly deduced the rules yet.
I experimented with containing "begin end" blocks but was not able to come to a firm conclusion.

I expected to enhance this program to detect the "space" key so that I could pause the display
but discovered that one cannot mix direct cp/m bios/bdos console commands (status, char-in
and char-out) with pli-80 useage of "List" stream, so I reverted to just using the "List" stream

If you wish to use PLI-80 there are pdf's available of the language reference and programmer's guide
and very relevant notes in the pli-80 v1.4 distribution. You may need pli-80 v1.3 for additional examples.
The Digital Research Link-80 guide is also essential.

If you are experienced in Pascal and using PLI-80 then be cautious of your "begin end" statements,
it is so easy to forget to put the ";" after "begin"

To build this program you need to run (under either CP/M 2.2 or CP/M 3):
rmac cpmdio
pli hexdump
link hexdump,cpmdio

*/
/********************************************************************************/
hexdump: proc options(main);
%replace
false by '0'b,
true by '1'b;

%replace max_args by 4;
%replace arg_length by 16;

dcl
EOF bit(1) static initial(false),
processdata bit(1),
(I,J,K) fixed binary,
ch char,
(dumpfile, listing) file,
diskdata(128) bit(8);

%include 'diomod.dcl';

dcl
c char(1),
blank char(16) static initial(''),
v char(254) varying;
dcl
(dfcb0v, dfcb1v, dbuffv) pointer,
command char(127) varying based (dbuffv),
1 fcb0 based(dfcb0v),
2 drive fixed(7),
2 name char(8),
2 type char(3),
2 extnt fixed(7),
2 space (19) bit(8),
2 cr fixed(7),
1 fcb1 based(dfcb1v),
2 drive fixed(7),
2 name char(8),
2 type char(3),
2 extnt fixed(7),
2 space (19) bit(8),
2 cr fixed(7);


disp128:proc (cpmfilerecord);
dcl
(I,J,K) fixed binary,
ch character,
cpmfilerecord(128) bit(8);
Do I=0 TO 7; /* Do a hexdump of 8 lines of 16 chars */
Do J=0 TO 15; /* Show hex values */
put file(listing) edit(cpmfilerecord((I*16)+J+1))(x(1),b4(2));
END;
put file(listing) edit(' |')(a); /* Bar to separate hex & ASCII */
Do J=0 TO 15; /* Show printable chars or '.' */
unspec(ch)=cpmfilerecord((I*16)+J+1);
IF ((RANK(ch)<127) & (RANK(ch)>31)) THEN
put file(listing) edit(ch)(a);
ELSE put file(listing) edit('.')(a);
END;
put file(listing) edit('|')(a);
put file(listing) skip;
END;
Do I=0 TO 1;
put file(listing) skip;
END;
end disp128;

/* Main Program */
dfcb0v = dfcb0();
dfcb1v = dfcb1();
dbuffv = dbuff();
/* ************************* debug ************************
put edit ('Command Tail: ',command) (a);
put edit ('First Default File:',
fcb0.name,'.',fcb0.type) (skip,4a);
put edit ('Second Default File:',
fcb1.name,'.',fcb1.type) (skip,4a);
put edit ('dfcb0 ',unspec(dfcb0v),
'dfcb1 ',unspec(dfcb1v),
'dbuff ',unspec(dbuffv))
(5(skip,a(7),b4),skip,a(7),f(6));
********************************************************** */

on undefinedfile(dumpfile)
begin;
put skip list('ERROR: Incorrect filename for sourcefile');
put skip list('usage: hexdump filename.typ');
put skip list(' or hexdump filename.typ /C for output to
console');
put skip list(' or hexdump filename.typ /P for output to
printer (LST: device)');
put skip list(' or hexdump filename.typ filename.lst');
stop;
end;

on endfile(dumpfile)
begin;
EOF = true;
goto next;
end;

open file (dumpfile) record input sequential env(b(8192)) title('$1.$1');

on undefinedfile(listing)
begin;
put skip list('ERROR: Incorrect filename for listing');
put skip list('usage: hexdump filename.typ');
put skip list(' or hexdump filename.typ /C for output to
console');
put skip list(' or hexdump filename.typ /P for output to
printer (LST: device)');
put skip list(' or hexdump filename.typ filename.lst');
stop;
end;
if (fcb1.name = '/C' | fcb1.name = '/c' | fcb1.name= blank) then
begin;
open file (listing) print env(Locked,Buff(128))
TITLE('$CON') LINESIZE(80) PAGESIZE(0);
goto readloop;
end;
if (fcb1.name = '/L' | fcb1.name = '/P' | fcb1.name = '/l' |
fcb1.name = '/p') then
begin;
open file (listing) print env(Locked,Buff(128))
TITLE('$LST') LINESIZE(80) PAGESIZE(0);
goto readloop;
end;
open file (listing) print env(Locked,Buff(128))
TITLE('$2.$2') LINESIZE(80) PAGESIZE(0);
readloop:
do while(EOF = false);
read file(dumpfile) into (diskdata);
call disp128(diskdata);
next:
end;
on endfile(listing)
begin;
put skip list('ERROR: disk is full');
stop;
end;
end hexdump;


Phil G

unread,
Jul 10, 2022, 5:04:02 PMJul 10
to
Interesting, by chance do you have the two files cpmdio.mac and diomod.dcl please? (I'd like to compare diomod with philslib!)
Udo, in years of programming in PL/I I never realised all those years ago that it had been used by DR to create CP/M tools!
We live and learn!
Phil

Udo Munk

unread,
Jul 10, 2022, 5:37:40 PMJul 10
to
I don't know what cpmdio.mac is, contents of diomode.dcl below.

dcl
memptr entry returns (ptr),
memsiz entry returns (fixed(15)),
memwds entry returns (fixed(15)),
dfcb0 entry returns (ptr),
dfcb1 entry returns (ptr),
dbuff entry returns (ptr),
reboot entry,
rdcon entry returns (char(1)),
wrcon entry (char(1)),
rdrdr entry returns (char(1)),
wrpun entry (char(1)),
wrlst entry (char(1)),
coninp entry returns (char(1)),
conout entry (char(1)),
rdstat entry returns (bit(1)),
getio entry returns (bit(8)),
setio entry (bit(8)),
wrstr entry (ptr),
rdbuf entry (ptr),
break entry returns (bit(1)),
vers entry returns (bit(16)),
reset entry,
select entry (fixed(7)) returns (bit(16)),
open entry (ptr) returns (bit(16)),
close entry (ptr) returns (bit(16)),
sear entry (ptr) returns (bit(16)),
searn entry returns (bit(16)),
delete entry (ptr) returns (bit(16)),
rdseq entry (ptr) returns (bit(16)),
wrseq entry (ptr) returns (bit(16)),
make entry (ptr) returns (bit(16)),
rename entry (ptr) returns (bit(16)),
logvec entry returns (bit(16)),
curdsk entry returns (fixed(7)),
setdma entry (ptr),
allvec entry returns (ptr),
wpdisk entry,
rovec entry returns (bit(16)),
filatt entry (ptr),
getdpb entry returns (ptr),
getusr entry returns (fixed(7)),
setusr entry (fixed(7)),
rdran entry (ptr) returns (bit(16)),
wrran entry (ptr) returns (bit(16)),
filsiz entry (ptr),
setrec entry (ptr),
resdrv entry (bit(16)) returns (bit(16)),
wrranz entry (ptr) returns (bit(16)),
testwr entry (ptr) returns (bit(16)),
lock entry (ptr) returns (fixed(7)),
unlock entry (ptr) returns (fixed(7)),
multis entry (fixed(7)) returns (fixed(7)),
ermode entry (bit(1)),
freesp entry (fixed(7)) returns (bit(16)),
chain entry returns (bit(16)),
flush entry returns (fixed(7)),
setlbl entry (ptr) returns (bit(16)),
getlbl entry (fixed(7)) returns (bit(8)),
rdxfcb entry (ptr) returns (bit(16)),
wrxfcb entry (ptr) returns (bit(16)),
settod entry (ptr),
gettod entry (ptr),
dfpswd entry (ptr),
sgscb entry (ptr) returns(bit(8));

rwd...@gmail.com

unread,
Jul 10, 2022, 5:53:41 PMJul 10
to
CPMDIO.ASM (not in MAC form, but assembles ok with rmac); it is on pli 1.4 disk 2
DIOMOD.DCL is further down this page:
note: please ensure that the data is terminated with a final CR or LF at end otherwise I find compilers often bork.
note: The doc for dr link80 is essential. Do not confuse with MS link-80
Cheers
Richard

<< CPMDIO.ASM >>
name 'CPMDIO'
title 'Direct CP/M Calls From PL/I-80'
;
;***********************************************************
;* *
;* CP/M calls from PL/I for direct i/o *
;* *
;***********************************************************
public memptr ;return pointer to base of free mem
public memsiz ;return size of memory in bytes
public memwds ;return size of memory in words
public dfcb0 ;return address of default fcb 0
public dfcb1 ;return address of default fcb 1
public dbuff ;return address of default buffer
public reboot ;system reboot (#0)
public rdcon ;read console character (#1)
public wrcon ;write console character(#2)
public rdrdr ;read reader character (#3)
public wrpun ;write punch character (#4)
public wrlst ;write list character (#5)
public coninp ;direct console input (#6a)
public conout ;direct console output (#6b)
public rdstat ;read console status (#6c)
public getio ;get io byte (#7)
public setio ;set i/o byte (#8)
public wrstr ;print string (#9)
public rdbuf ;read console buffer (#10)
public break ;get console status (#11)
public vers ;get version number (#12)
public reset ;reset disk system (#13)
public select ;select disk (#14)
public open ;open file (#15)
public close ;close file (#16)
public sear ;search for file (#17)
public searn ;search for next (#18)
public delete ;delete file (#19)
public rdseq ;read file sequential mode (#20)
public wrseq ;write file sequential mode (#21)
public make ;create file (#22)
public rename ;rename file (#23)
public logvec ;return login vector (#24)
public curdsk ;return current disk number (#25)
public setdma ;set DMA address (#26)
public allvec ;return address of alloc vector (#27)
public wpdisk ;write protect disk (#28)
public rovec ;return read/only vector (#29)
public filatt ;set file attributes (#30)
public getdpb ;get base of disk parm block (#31)
public getusr ;get user code (#32a)
public setusr ;set user code (#32b)
public rdran ;read random (#33)
public wrran ;write random (#34)
public filsiz ;random file size (#35)
public setrec ;set random record pos (#36)
public resdrv ;reset drive (#37)
public wrranz ;write random, zero fill (#40)
;
;
extrn ?begin ;beginning of free list
extrn ?boot ;system reboot entry point
extrn ?bdos ;bdos entry point
extrn ?dfcb0 ;default fcb 0
extrn ?dfcb1 ;default fcb 1
extrn ?dbuff ;default buffer
;
;***********************************************************
;* *
;* equates for interface to cp/m bdos *
;* *
;***********************************************************
cr equ 0dh ;carriage return
lf equ 0ah ;line feed
eof equ 1ah ;end of file
;
readc equ 1 ;read character from console
writc equ 2 ;write console character
rdrf equ 3 ;reader input
punf equ 4 ;punch output
listf equ 5 ;list output function
diof equ 6 ;direct i/o, version 2.0
getiof equ 7 ;get i/o byte
setiof equ 8 ;set i/o byte
printf equ 9 ;print string function
rdconf equ 10 ;read console buffer
statf equ 11 ;return console status
versf equ 12 ;get version number
resetf equ 13 ;system reset
seldf equ 14 ;select disk function
openf equ 15 ;open file function
closef equ 16 ;close file
serchf equ 17 ;search for file
serchn equ 18 ;search next
deletf equ 19 ;delete file
readf equ 20 ;read next record
writf equ 21 ;write next record
makef equ 22 ;make file
renamf equ 23 ;rename file
loginf equ 24 ;get login vector
cdiskf equ 25 ;get current disk number
setdmf equ 26 ;set dma function
getalf equ 27 ;get allocation base
wrprof equ 28 ;write protect disk
getrof equ 29 ;get r/o vector
setatf equ 30 ;set file attributes
getdpf equ 31 ;get disk parameter block
userf equ 32 ;set/get user code
rdranf equ 33 ;read random
wrranf equ 34 ;write random
filszf equ 35 ;compute file size
setrcf equ 36 ;set random record position
rsdrvf equ 37 ;reset drive function
wrrnzf equ 40 ;write random zero fill
;
; utility functions
;***********************************************************
;* *
;* general purpose routines used upon entry *
;* *
;***********************************************************
;
getp1: ;get single byte parameter to register e
mov e,m ;low (addr)
inx h
mov d,m ;high(addr)
xchg ;hl = .char
mov e,m ;to register e
ret
;
getp2: ;get single word value to DE
getp2i: ;(equivalent to getp2)
call getp1
inx h
mov d,m ;get high byte as well
ret
;
getver: ;get cp/m or mp/m version number
push h ;save possible data adr
mvi c,versf
call ?bdos
pop h ;recall data addr
ret
;
chkv20: ;check for version 2.0 or greater
call getver
cpi 20
rnc ;return if > 2.0
; error message and stop
jmp vererr ;version error
;
chkv22: ;check for version 2.2 or greater
call getver
cpi 22h
rnc ;return if >= 2.2
vererr:
;version error, report and terminate
lxi d,vermsg
mvi c,printf
call ?bdos ;write message
jmp ?boot ;and reboot
vermsg: db cr,lf,'Later CP/M or MP/M Version Required$'
;
;***********************************************************
;* *
;***********************************************************
memptr: ;return pointer to base of free storage
lhld ?begin
ret
;
;***********************************************************
;* *
;***********************************************************
memsiz: ;return size of free memory in bytes
lhld ?bdos+1 ;base of bdos
xchg ;de = .bdos
lhld ?begin ;beginning of free storage
mov a,e ;low(.bdos)
sub l ;-low(begin)
mov l,a ;back to l
mov a,d ;high(.bdos)
sbb h
mov h,a ;hl = mem size remaining
ret
;
;***********************************************************
;* *
;***********************************************************
memwds: ;return size of free memory in words
call memsiz ;hl = size in bytes
mov a,h ;high(size)
ora a ;cy = 0
rar ;cy = ls bit
mov h,a ;back to h
mov a,l ;low(size)
rar ;include ls bit
mov l,a ;back to l
ret ;with wds in hl
;
;***********************************************************
;* *
;***********************************************************
dfcb0: ;return address of default fcb 0
lxi h,?dfcb0
ret
;
;***********************************************************
;* *
;***********************************************************
dfcb1: ;return address of default fcb 1
lxi h,?dfcb1
ret
;
;***********************************************************
;* *
;***********************************************************
dbuff: ;return address of default buffer
lxi h,?dbuff
ret
;
;***********************************************************
;* *
;***********************************************************
reboot: ;system reboot (#0)
jmp ?boot
;
;***********************************************************
;* *
;***********************************************************
rdcon: ;read console character (#1)
;return character value to stack
mvi c,readc
jmp chrin ;common code to read char
;
;***********************************************************
;* *
;***********************************************************
wrcon: ;write console character(#2)
;1->char(1)
mvi c,writc ;console write function
jmp chrout ;to write the character
;
;***********************************************************
;* *
;***********************************************************
rdrdr: ;read reader character (#3)
mvi c,rdrf ;reader function
chrin:
;common code for character input
call ?bdos ;value returned to A
pop h ;return address
push psw ;character to stack
inx sp ;delete flags
mvi a,1 ;character length is 1
pchl ;back to calling routine
;
;***********************************************************
;* *
;***********************************************************
wrpun: ;write punch character (#4)
;1->char(1)
mvi c,punf ;punch output function
jmp chrout ;common code to write char
;
;***********************************************************
;* *
;***********************************************************
wrlst: ;write list character (#5)
;1->char(1)
mvi c,listf ;list output function
chrout:
;common code to write character
;1-> character to write
call getp1 ;output char to register e
jmp ?bdos ;to write and return
;
;***********************************************************
;* *
;***********************************************************
coninp: ;perform console input, char returned in stack (#6a)
lxi h,chrstr ;return address
push h ;to stack for return
lhld ?boot+1 ;base of bios jmp vector
lxi d,2*3 ;offset to jmp conin
dad d
pchl ;return to chrstr
;
chrstr: ;create character string, length 1
pop h ;recall return address
push psw ;save character
inx sp ;delete psw
mvi a,1 ;length to a
pchl ;return to caller
;
;***********************************************************
;* *
;***********************************************************
conout: ;direct console output (#6b)
;1->char(1)
call getp1 ;get parameter
mov c,e ;character to c
lhld ?boot+1 ;base of bios jmp
lxi d,3*3 ;console output offset
dad d ;hl = .jmp conout
pchl ;return through handler
;
;***********************************************************
;* *
;***********************************************************
rdstat: ;direct console status read (#6c)
lxi h,rdsret ;read status return
push h ;return to rdsret
lhld ?boot+1 ;base of jmp vector
lxi d,1*3 ;offset to .jmp const
dad d ;hl = .jmp const
pchl
;
;***********************************************************
;* *
;***********************************************************
getio: ;get io byte (#7)
mvi c,getiof
jmp ?bdos ;value returned to A
;
;***********************************************************
;* *
;***********************************************************
setio: ;set i/o byte (#8)
;1->i/o byte
call getp1 ;new i/o byte to E
mvi c,setiof
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
wrstr: ;write string (#9)
;1->addr(string)
call getp2 ;get parameter value to DE
mvi c,printf ;print string function
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
rdbuf: ;read console buffer (#10)
;1->addr(buff)
call getp2i ;DE = .buff
mvi c,rdconf ;read console function
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
break: ;get console status (#11)
mvi c,statf
call ?bdos ;return through bdos
;
rdsret: ;return clean true value
ora a ;zero?
rz ;return if so
mvi a,0ffh ;clean true value
ret
;
;***********************************************************
;* *
;***********************************************************
vers: ;get version number (#12)
mvi c,versf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
reset: ;reset disk system (#13)
mvi c,resetf
jmp ?bdos
;
;***********************************************************
;* *
;***********************************************************
select: ;select disk (#14)
;1->fixed(7) drive number
call getp1 ;disk number to E
mvi c,seldf
jmp ?bdos ;return through bdos
;***********************************************************
;* *
;***********************************************************
open: ;open file (#15)
;1-> addr(fcb)
call getp2i ;fcb address to de
mvi c,openf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
close: ;close file (#16)
;1-> addr(fcb)
call getp2i ;.fcb to DE
mvi c,closef
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
sear: ;search for file (#17)
;1-> addr(fcb)
call getp2i ;.fcb to DE
mvi c,serchf
jmp ?bdos
;
;***********************************************************
;* *
;***********************************************************
searn: ;search for next (#18)
mvi c,serchn ;search next function
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
delete: ;delete file (#19)
;1-> addr(fcb)
call getp2i ;.fcb to DE
mvi c,deletf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
rdseq: ;read file sequential mode (#20)
;1-> addr(fcb)
call getp2i ;.fcb to DE
mvi c,readf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
wrseq: ;write file sequential mode (#21)
;1-> addr(fcb)
call getp2i ;.fcb to DE
mvi c,writf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
make: ;create file (#22)
;1-> addr(fcb)
call getp2i ;.fcb to DE
mvi c,makef
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
rename: ;rename file (#23)
;1-> addr(fcb)
call getp2i ;.fcb to DE
mvi c,renamf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
logvec: ;return login vector (#24)
mvi c,loginf
jmp ?bdos ;return through BDOS
;
;***********************************************************
;* *
;***********************************************************
curdsk: ;return current disk number (#25)
mvi c,cdiskf
jmp ?bdos ;return value in A
;
;***********************************************************
;* *
;***********************************************************
setdma: ;set DMA address (#26)
;1-> pointer (dma address)
call getp2 ;dma address to DE
mvi c,setdmf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
allvec: ;return address of allocation vector (#27)
mvi c,getalf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
wpdisk: ;write protect disk (#28)
call chkv20 ;must be 2.0 or greater
mvi c,wrprof
jmp ?bdos
;
;***********************************************************
;* *
;***********************************************************
rovec: ;return read/only vector (#29)
call chkv20 ;must be 2.0 or greater
mvi c,getrof
jmp ?bdos ;value returned in HL
;
;***********************************************************
;* *
;***********************************************************
filatt: ;set file attributes (#30)
;1-> addr(fcb)
call chkv20 ;must be 2.0 or greater
call getp2i ;.fcb to DE
mvi c,setatf
jmp ?bdos
;
;***********************************************************
;* *
;***********************************************************
getdpb: ;get base of current disk parm block (#31)
call chkv20 ;check for 2.0 or greater
mvi c,getdpf
jmp ?bdos ;addr returned in HL
;
;***********************************************************
;* *
;***********************************************************
getusr: ;get user code to register A
call chkv20 ;check for 2.0 or greater
mvi e,0ffh ;to get user code
mvi c,userf
jmp ?bdos
;
;***********************************************************
;* *
;***********************************************************
setusr: ;set user code
call chkv20 ;check for 2.0 or greater
call getp1 ;code to E
mvi c,userf
jmp ?bdos
;
;***********************************************************
;* *
;***********************************************************
rdran: ;read random (#33)
;1-> addr(fcb)
call chkv20 ;check for 2.0 or greater
call getp2i ;.fcb to DE
mvi c,rdranf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
wrran: ;write random (#34)
;1-> addr(fcb)
call chkv20 ;check for 2.0 or greater
call getp2i ;.fcb to DE
mvi c,wrranf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
filsiz: ;compute file size (#35)
call chkv20 ;must be 2.0 or greater
call getp2 ;.fcb to DE
mvi c,filszf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
setrec: ;set random record position (#36)
call chkv20 ;must be 2.0 or greater
call getp2 ;.fcb to DE
mvi c,setrcf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
resdrv: ;reset drive function (#37)
;1->drive vector - bit(16)
call chkv22 ;must be 2.2 or greater
call getp2 ;drive reset vector to DE
mvi c,rsdrvf
jmp ?bdos ;return through bdos
;
;***********************************************************
;* *
;***********************************************************
wrranz: ;write random, zero fill function
;1-> addr(fcb)
call chkv22 ;must be 2.2 or greater
call getp2i ;.fcb to DE
mvi c,wrrnzf
jmp ?bdos
;
;***********************************************************
;* *
;***********************************************************
end




<<DIOMOD.DCL>>

declare
memptr entry returns (pointer),
memsiz entry returns (fixed(15)),
memwds entry returns (fixed(15)),
dfcb0 entry returns (pointer),
dfcb1 entry returns (pointer),
dbuff entry returns (pointer),
reboot entry,
rdcon entry returns (character(1)),
wrcon entry (character(1)),
rdrdr entry returns (character(1)),
wrpun entry (character(1)),
wrlst entry (character(1)),
coninp entry returns (character(1)),
conout entry (character(1)),
rdstat entry returns (bit(1)),
getio entry returns (bit(8)),
setio entry (bit(8)),
wrstr entry (pointer),
rdbuf entry (pointer),
break entry returns (bit(1)),
vers entry returns (bit(16)),
reset entry,
select entry (fixed(7)),
open entry (pointer) returns (fixed(7)),
close entry (pointer) returns (fixed(7)),
sear entry (pointer) returns (fixed(7)),
searn entry returns (fixed(7)),
delete entry (pointer),
rdseq entry (pointer) returns (fixed(7)),
wrseq entry (pointer) returns (fixed(7)),
make entry (pointer) returns (fixed(7)),
rename entry (pointer),
logvec entry returns (bit(16)),
curdsk entry returns (fixed(7)),
setdma entry (pointer),
wpdisk entry,
rovec entry returns (bit(16)),
filatt entry (pointer),
getusr entry returns (fixed(7)),
setusr entry (fixed(7)),
rdran entry (pointer) returns (fixed(7)),
wrran entry (pointer) returns (fixed(7)),
filsiz entry (pointer),
setrec entry (pointer),
resdrv entry (bit(16)),
wrranz entry (pointer) returns (fixed(7));

Phil G

unread,
Jul 10, 2022, 5:56:06 PMJul 10
to
On Sunday, July 10, 2022 at 10:37:40 PM UTC+1, Udo Munk wrote:
>I don't know what cpmdio.mac is...

cpmdio.mac is the assembler file that does all the low level stuff that PL/I cant, it will have an assembler routine for every entry in the cpmdio.dcl declare file.
Effectively, its doing all the work and all the PPL/I part does is fancy formatting - which it was very good at, its main use during my time at BT.
The build sequence:
rmac cpmdio
pli hexdump
link hexdump,cpmdio

rmac is the relocating macro assembler which produces cpmdio.obj
pli hexdump compiles hexdump.pli to hexdump.obj, and
link hexdump,cpmdio creates the .com file by linking the two obj modules and resolving 'real' addresses.

Cheers
Phil

Phil G

unread,
Jul 10, 2022, 6:00:19 PMJul 10
to
On Sunday, July 10, 2022 at 10:56:06 PM UTC+1, Phil G wrote:
>a reply...

Ah we crossed posts, I see Richard has posted the library. Its more comprehensive than my own library but uses similar code, albeit in 8080 rather than the Z80 i used in philslib...
Interesting stuff after being dormant in my mind for nearly 40 years!
Cheers
Phil

Phil G

unread,
Jul 10, 2022, 6:03:03 PMJul 10
to
On Sunday, July 10, 2022 at 11:00:19 PM UTC+1, Phil G wrote:
> On Sunday, July 10, 2022 at 10:56:06 PM UTC+1, Phil G wrote:
>rmac is the relocating macro assembler which produces cpmdio.obj
>pli hexdump compiles hexdump.pli to hexdump.obj, and
>link hexdump,cpmdio creates the .com file by linking the two obj modules and resolving 'real' addresses.

Actually, thinking about it, they may be .rel rather than .obj , I cant remember...

Phil G

unread,
Jul 10, 2022, 6:20:35 PMJul 10
to
Richard: > I keep getting an error on the last line of the program.
Another reason can be the 'padding' making up the file to a 128-byte boundary for CP/M if the control-Z EOF is missing, often happens when transferring between CP/M & a PC

Mark Ogden

unread,
Jul 11, 2022, 5:10:44 PMJul 11
to
The binaries for the Intel PL/M compiler were released along with a MSDOS based emulation environment nearly 30 years ago.
My decompilation is for purely historic and academic purposes, the same applies to my port to C to allow cross compilation. I originally did the work as an intellectual exercise.
For most of the time I use the released binaries with an emulator, as on a modern system the performance is more than adequate.

Of interest, the PLI-80 compiler is actually written in PL/M as can be determined with a little disassembly.

Mark
Reply all
Reply to author
Forward
0 new messages