1-Sep-80 02:29:00,415;000000000000
Date: Monday, 1 September 1980 02:29-MDT
From: MCTESQ at MIT-MC (Michael Toy)
To: INFO-CPM at MIT-MC
Subject: Well I just started using CP/M today...
And its not that bad. I'd rather have unix or even
tops (bottoms?) 10, but its better than i thought.
Is there some reason for the stat command to not work?
It just starts printing garbage over and over and
i don't understand why.
michael
1-Sep-80 02:39:00,683;000000000000
Date: Monday, 1 September 1980 02:39-MDT
From: MCTESQ at MIT-MC (Michael Toy)
To: INFO-CPM at MIT-MC
Subject: How do you ...
I have a lot of programs on cassete that sit at location 0. Is
there any way other than writing a program that loads them from disk to
run them under cp/m?
FJW, yes I exit programs in a way other than jmp 0, i pop h
at the beginning of my program and shld it someplace, then to quit
i lhld it back and do a pchl. Or I'll just save the
CCP stack pointer, set up my own stack, then restore the CCP stack
before I quit so I can go back with a ret. This keeps the disk
from clicking on at then end of every program i write.
michael
1-Sep-80 03:01:00,999;000000000000
Date: Monday, 1 September 1980 03:01-MDT
From: Frank J. Wancho <FJW at MIT-MC>
To: MCTESQ at MIT-MC
cc: INFO-CPM at MIT-MC
Subject: How do you ...
Loading from disk would solve the problem that CP/M wants the program
to start at 0100H. You will need to reassemble with ORG 0100H and
write a program which relocates itself to high memory and reads the
cassette programs into 0100H and then JMP 0100H.
I also save the CCP SP and end up with a RET as follows:
However, I don't save on the click because I may have done some disk
I/O from the time I left, and thus I do a RESET and SELDSK of the
saved default drive. So, in my case, I could probably cut all that
out and just do a JP 0 to get the same effect, right?
If your disk I/O works then the STAT should also work. What is your
configuration? Did you have to do anything special to bring up your
CP/M in the first place?
--Frank
1-Sep-80 03:04:00,223;000000000000
Date: Monday, 1 September 1980 03:04-MDT
From: Frank J. Wancho <FJW at MIT-MC>
To: INFO-CPM at MIT-MC
Subject: Another typo
Last message should read:
Loading the program from disk will NOT solve the problem...
2-Sep-80 00:34:00,711;000000000000
Date: Tuesday, 2 September 1980 00:34-MDT
From: Frank J. Wancho <FJW at MIT-MC>
To: INFO-CPM at MIT-MC
Subject: A MENU Program for CP/M
I transcribed and debugged the MENU program described in the Creative
Computing article previously cited and the source is now in
MC:FJW;MENU MAC in Z80 format (easily reverted to 8080 code) and
suitable for input to MACRO-80.
(I dumped it at a painful 300 baud for reliability, and read it back
in and assembled, linked, and ran it with no errors.)
Suggested enhancements:
1. Should list all COM files from all available disks.
2. Should be able to run from any drive.
You are welcome to it. (FTP from MC does not require an account.)
--Frank
2-Sep-80 15:20:00,218;000000000000
Date: Tuesday, 2 September 1980 15:20-MDT
From: MAZE at MIT-DMS (James Mazer)
To: INFO-CPM at MIT-MC
Does anyone have the documentation on cp/m stored on-line
anywhere? Replies to Maze at MC pls.
/Jamie
3-Sep-80 21:51:00,1149;000000000000
Date: Wednesday, 3 September 1980 21:51-MDT
From: Frank J. Wancho <FJW at MIT-MC>
To: INFO-CPM at MIT-MC
Subject: List Repaired
Apparently the list got reverted to some previous version and has now
been brought back up-to-date. Some of you may have missed some
correspondence since I do not know how long the list was broken. Here
is a summary of the current contents of MC:FJW;CPM ARCHIV (FTPable
from MC - no account required):
No. Lines From->To Subject or Text
1: 9 MAZE->INFO-CPM Does anyone have the documentation on cp/m
2- 22 FJW->INFO-CPM A MENU Program for CP/M
3- 9 FJW->INFO-CPM Another typo
4- 32 FJW->MCTESQ How do you ...
5- 16 MCTESQ->INFO-CPM How do you ...
6- 12 MCTESQ->INFO-CPM Well I just started using CP/M today...
7- 18 Lauren@UCLA-SECURITY- Kick-off
8- 10 FJW->INFO-CPM Typo
9- 30 FJW->INFO-CPM STAT in 2.2
10- 118 FJW->INFO-CPM Kick-off
11- 10 LEAVITT@ISI->INFO-CPM CP/M WITH APPLE
12- 16 FJW->INFO-CPM CP/M Mailing List
--Frank
10-Sep-80 03:53:00,11844;000000000000
Date: Wednesday, 10 September 1980 03:53-MDT
From: BLUE at MIT-AI
To: info-cpm at MIT-MC
Frank -- following is a two part article that may be of some interest
to some members of this list. It may be a little general for a few, but
is fairly informative. Do with it as you see fit.
--Bill <Blue@AI>
CPM/TIPS Part 1
CP/M, A VIEW FROM INSIDE BY CARL ADLER
Reprinted from NORTHWEST COMPUTER SOCIETY/July 1979
Although CP/M is not the ultimate in operating systems it is an
extremely useful software devlopment tool which is implementable
on a wide variety of 8080 and Z80 based computers. As a result it
has become, by default i not by design, the standard of users of
these microprocessors.
CP/M's implementability lays mainly in the portability afforded to
it through the use of the BIOS (Basic I/O System). This concept is a
simple but very effective solution to the problem of hardware vari-
ability inherent in microprocessor based computer systems. The S-
100 buss not withstanding there seem to be as many hardware config-
urations as there are designers(as an example, my own computer has
a 56 pin backplane and uses memory-mapped I/O). It is a surprising
and welcome gesture for a software manufacturer to supply the exact
procedures necessary to bring up its software. By the way of
contrast, I know a very compulsive programmer who spent the better
part of a month bringing up ISIS on a non-MDS hardware, whereas it
took less than a week for this same programmer to bring up CP/M on
the same hardware.
The other major fctor in CP/M's success must be the configur-
ability. How many microcomputer users start th 65K (wch ISIS
requires to do anything useful)? With the ability to turn a 16K toy
into a "real" computer, CP/M has ensured a very large following of
devotees.
As mentioned in thers manual, CP/M is divided into four
logically distinct but interacting parts:
BDOS (Basic Disk Operating System)
BIOS (Basic Input/Output System)
CCP (Console Command Processor)
TPA (Transient Program Area)
As there isn't a whole lot to say about the TPA other than that is
where the transient programs are executed, and the users manual is
very explicit on the use of the CCP, I will deal mainly with the
BDOS and BIOS, Which I consider to be the heart of the system.
THE BASIC DISK OPERATING SYSTEM (BDOS)
The BDOS is the file manager of CP/M Though an application program
can:
.Open a file
.Close a file
.Search for th first and subsequent entries in the
directory for a file
.Erase a file from the directory
.Read and Write logical rerds
.Create entries in the diry
.Rename entries in the directory
as well as various support functions. These functions mainly add
a level of abstraction to the disk hardware as implemented in the
BIOS allowing the application program(mer) to deal with the stored
data without having to know where it is physically located. The
BDOS also acts as a conduit betweehe application program and the
character I/O entry points in the BIOS as well as providing some
macro-functions for string I/O to and from the console. Thus through
the use of the BDOS, CP/M is able to transform a computer and its
peripherals into a generalized sstem with no particular hardware
characteristics machine language itself. (and through the use of
one of the many "hi-level" languages which have been implemented to
run under CP/M, it is possible to solve problems with out even
knowing or caring what kind of CPU is running the show). To my way
of thinking , this is a rather significant development in micro-
computing.
The BDOS functions are listed and explained quite throughly in the
programmer's guide so I'll not bore the reader (and make myself type
anyone than I have to) by reiterating them here. However I would
like to address myself to a cuple of points which are associated
with the BDOS. First, while monkeying around one day, I discovered
that there are some strange locations at the beginning of the BDOS
which can be (and most definitely are) used by transient programs
even though they are supposedly secure inside the operating system.
Here is an outline of these locations:
ORG BDOS
SERNUM: DS 6
ENTRY: JMP COMMAND
DW BADIO
DW SELERROR
DW ROERRORMAP: LXI H,MAPTABLE ;POINT HL AT LOG TO PHYS MAPPING
MVI B,0 ;SET BC TO LOICAL SECTOR (IN C)
DAD B ;INDEX INTO TABLE
MOV C,M ;CONVERT LOGICAL TO PHYSICAL SECTOR
JMP SETSECTOR ;DO BIOS SET SECTOR FUNCTION
NOP
MAPTABLE:
DB 1,7,13,19
DB 25,5,11,7 ;THIS TABLE MAPS LOGICAL ONTO
DB 23,3,9,15 ; PHYSICAL DISK SECTORS
DB 21,2,8,14
DB 20,26,6,12
DB 18,24,4,10
DB 16,22,,0
DB 0,0,0,0 ;THE NULLS ARE ADDRESS ALINGMENT
CONFIG:
DB SECPT ;SECTORS PER TRACK(26 ON IBM DISKS)
DB LASTDIR ;NUMBERS OF LAST DIRECTORY ENTRY (63)
DB RPB ;2**RPB=RECORDS PER BLOCK (3)
DB LASTSEC ;LAST SECTOR IN BLOCK (7)
DB LASTBLOCK ;LAST BLOCK ON THE DISK (242)
DB DIRALLOC ;DIRECTORY ALLOCATION MASK (C0 HEX)
DB DIRTACK ;TRACK THAT DIRECTORY BEGINS ON (2)
The first 6 bytes of the BDOS contain the serial numbers which
supposedly prevent copyright infringments. The next 3 contain the
actual entry point to the BDOS command decoder. The next 9 bytes
are the addresses of routines inside the BDOS which are executed
when one of the three fatal errors occur. Following the error
addresses is the routine which the BDOS uses to stagger its data on
a track followed by the mapping table. After the mapping table are
some constants which define the particular implementation of CP/M on
specific size disks.
The error address can be modified by an application program to
recover somewhat more gracefully from a fatal error (I ran across
this in a screen-oriented text editor which made possible to save
the memory image of the program after an I/O error). The config-
uration table data can be used (and is used by STAT in version 1.4)
to find out what's going on with disk space.
As far as I can tell it is not serendipitious that this infor-
mation exists and is located where it is. It seems that the people
at Digital Research left room for certain easy modifications and
enhancements to CP/M.
The second point I would like o address is the technique CP/M
uses to do housekeeping on its files. All of the file management
functions in the BDOS use the address of an FCB (file control block)
as their parameter. Like the concept of the I/O byte the FCB is a
rather elegant solution to the housekeeping problem in a file
management system. It accomplishes two things for the operations
system. First it "decentralizes" the process of maintaining "open
files", and second, it gives application programs access to the same
information about a file that the BDOS uses. These two factors
together allow an application to get as close as it requires to the
file management process. There are some problems with the FCB, as
implemented. First there is the question of whether or not an
application OUGHT to have access to housekeeping information as it
is possible to louse things up pretty badly if things are not done
correctly. This I think is a matter of taste. Since CP/M, as
implemented, is a single-user system there isn't the problem of
messing other people's files.
The other problem is a bit more serious (rumor has it that Digital
Research is dealing with . Since the largest number that an
allocated block can have (see below) is 255(D) and with a block size
of an even 2k, the maximum number of bytes of bytes of storage CP/M
can address on one disk is approximately 510 kilobytes. This
presents serious impediments (due to program standardization) to
implementing the operating system on the larger disk systems
becoming available.
Be that as it may, within the scope of single (or perhaps double)
density floppy disks, the FCB is in my opinion, a stroke of genius.
Because an application, through the use of the FCB, has such flex-
ibility it behooves the assembly language programmer to understand
as best he or she can how to use it. The programmer's guide
describes the format and I'll elaborate a bit on it.
The FCB consists of seven fields of information each having a
mnemonic associated with it. They are:
FIELD FCB POSIONS
ET 0
FN 1-8
FT 9-11
EX 12
NOT USED 13-14
ZC 15
DM 16-31
NR 32
The FN and FT fields are only logically distinct. The BDOS uses
all eleven bytes as a fundamental unit of information when opening,
closing, creating, erasing, searching and renaming files (The EX
field is included during OPEN, CLOSE, CREATE and SEARCH opera-
tions). It is the CCP and transient programs which make a
distinction between file ame and file type.
Since a file can be of any length up to the capacity of the disk
and since a single FCB describes only 16k bytes of a file, there
must be a way to link multiple sections of a file each described own
by its. This is done via the EX field. The first extent of a file
has an EX value of 0, the second a value of 1 and so on.
The ET field is an interesting mixture of usefulness and ambi-
guity. When the FCB is stored in the directory the ET filed may
contain a 0, indicating that the entry is used, or, an E5(H) indi-
cating that the entry has been deleted (or never used). However when
the FCB is used as a parmeter for one of the file management
functions, the ET field serves an entirely different purpose. If
ET=0 then then the BDOS will assume that the command pertains to the
currently selected disk. If the ET field is not zero then the BDOS
assumes that it contains the disk number+1 to which the function
pertains. In this case the BDOS will temporarily select disk number
ET-1 and then clear ET to zero before proceeding with the requested
function. When the file operation is complete, the BDOS will re-
store ET to its original value. Thus, an application program need
never concern itself with remembering or selecting specific disks as
these values are retained throughout processing from the time that
the CCP sets them up in the default FCBs.
The RC field is essentially an "end of extent" pointer. It is
"pushed" along by the NR field when writing and is used to limit the
NR field during reading to prevent the reading of unwritten data.
The DM field is an array of 16 bytes, each representing a logical
block of data within the extent. The value of each of these bytes
represents the physical area of disk space allocated to the logical
block unless the value is 0, in which case the block has not been
allocated any disk space.
Together, the RC and DM fields form a "current" description of the
locations on the dik used by the data contained with the extent.
The NR field is used to specify which record, relative to the
beginning of the extent, is to be read or written. The BDOS will
automatically increment th number during read and write
operations, making sequential file acess virtually automatic.
10-Sep-80 04:10:00,11916;000000000000
Date: Wednesday, 10 September 1980 04:10-MDT
From: BLUE at MIT-AI
To: info-cpm at MIT-MC
<part two of previous message>
CPM/TIPS Part 2
CP/M, A VIEW
FROM INSIDE BY CARL ADLER
Reprinted from NORTHWEST COMPUTER SOCIETY/July 1979
The BASI I/O SYSTEM (BIOS)
This section of the system concerns itself with the hardwar
dependent aspects of I/O. There are wo types: 1) Disk I/O, which is block oriented
2) Character I/O, which is byte oriented
It is convenient to consider these two aspects seperately as they
do not interact directly.
Looking at the BIOS jump table (as described in the CP/M docu-
mentation), the first two entries are paths to system initialization
routines. The next six are entry points to the character I/O rou-
tines and the rest are entry points to disk I/O and disk support
routines. The section called "BIOS Entry Points" in the System
Alteration Guide describes the function of each of these 15 entry
points better than I could. However, what the guide does not do (as
it is only a manual) is point out the importance of the I/O byte. I
consider this to be deserving of special attention.
Experience (mostly my own) has shown that until one makes concrete
use of the I/O byte concept, it is difficult to appreciate the ele-
gance of this technique. It does have its limits, but it is very
simple and effective solution to CP/M's character I/O device
standardization problems.
I first ran across this concept on the MDS MOD-80 development
system which did not have disk drives and used paper tape for its
off-line storage. The Intel Monitor used a standard jump table
which allowed programs to do character I/O without necessarily
having to worry about the actual hardware devices. Perhaps you've
seen it, but in case you haven't here it is.
ORG MOINTOR
JMP MAINLINE
JMP CI ;COSOLE INPUT
JMP RI ;READER INPUT
JMP CO ;CONSOLE OUTPUT
JMP PO ;PUNCH OUTPUT
JMP LO ;LIST OUTPUT
JMP CSTS ;RETURN CONSOLE STATUS JMP IOCHK ;RETURN I/O BYTE
JMP IOSET ;CHANGE I/O BYTE (NEW VALUE IN C)
As you can see thes basically the same that the BIOS jump
table does. IOCHK, IOSET and MEMCHK art needed in the BIOS
since the information returned by these rones are located in the
zero page of CP/M's memory.
As the alteration guide is not explicit on the bject of imple-
menting an I/O byte, I'll outline in assembly language code the
techniques I've found useful for a generized implation.
But first notice that each of the six character I/O routines must
decode out the path to the specific I/O device "currently assign-
ed". The way this is done (in English) is as follows. The I/O byte
contains four fields, each as consisting of low bits. Each field is
associated with one of the fo"logical" I/O devices (Lnch,
Reader and Console) and may take on the value of (in binary) 00,
01,10, or 11. Thus up to four different physical I/O devices may be
associated with each of the four "logical" devices. For example, the
logical device "List." By manipulating the value of these 2 bits
(presumably) without affecting the rest of the byte) one may
"assign" a specific hardware driver (and the device itself) to a
specific hardware driver (and the device itself) to the list
device. In PASCALese this is the decoder:
VAR IOBYTE(4): PACD ARRAY OF (0..3)
DO CASE IOBYTE(4)
0: TTYOUT;
1: LPTOUT;
2: CRTOUT;
3: USERLIST;
END
The alteration guide and documentation on PIP and STAT describe
what these physical device might be. The command:
STAT VAL:
produces essentially a menu of the nominal phyiscal devices
assignable in the CP/M system.
Here is some software:
IOBYTE EQU 3 ;LOCATION OF IOBYTE
CMASK EQU 03H ;CONSOLE MASK
RMASK EQU 0CH ;READER MASK
PMASK EQU 30H ;PUNCH MASK
LMASK EQU 0C0H ;LIST MASK
RD2 EQU 08H ;READER DEVICE 2 MASK
PD2 EQU 40H ;PUNCH DEVICE 2 MASK
CONTIN:
LDA IOBYTE ;GET IOBYTE
ANI CMASK ;LOOK AT CONSOLE FIELD
JZ TTYN ;CONSOLE 0:
JPE UCIN1 ;CONSOLE 3:
RAR ;CONSOLE1:
JC KBDIN ;CONSOLE 1:
JMP BACHIN ;CONSOLE 2
CONOUT:
LDA IOBYTE ;GET IO BYTE
ANI CMASK ;LOOK AT CONSOLE FIELD
JZ TTYOUT ;CONSOLE 0:
JPE UCOUT1 ;CONSOLE 3:
RAR ;LOOK FOR CONSOLE 1
JC CRTOUT ;CONSOLE 1:
JMP BACHOUT ;CONSOLE 2
CONSTAT:
LDA IOBY UL1 ;LIST 3: JM LPTOUT ;LIST 2:
JMP CRT ;LST 1;
MISCELLANEOUS STUFF
To conclude, there are some interesting tid-bits which the reader
may (or may not for that matter) find useful.
The SUBMIT processor in the CCP(Console Command Processor) uses a
very interesting programming technique which I found worthwhile
understanding. Recall that the transient program "SUBMIT" uses as
its input a text file of CP/M commands and produces a file called
$$$.SUB which the CCP will use as a command file. For example
consider the following submit file:
The Submit program turns this file into a series of 128 byte
records arranged such that the first line of the orginal file is the
last record of the new file, the second line the second to the last
record and so on. Each record has the following form:
The above file would look like this when converted to $$$.SUB (the
numbers in decimal and brackets are included here just for clarity):
record 1: (28)A:PIP LST:=B:PROG,PRN(T8P50)
record 2: (13)A:LOAD B:PROG
record 3: (14)A:ASM PROG.BBB
record 4: (33)A:PIP B:PROG.ASM=B:PO.SRC,B:P1.SRC
Why, you ask, is $$$.SUB backwards? Well that's part of the
trick. Remember that the RC field in the FCB is an end of extent
pointer. If the $$$.SUB file is on the disk the CCP will open it
and set the NR field of the FCB to RC-1 and read the file. What
this does is read in the last record of the file (as determined by
the RC field). After the read the CCP will decrement the RC field
and close the file, which will cause the FCB and specifically the RC
field command as if it had been typed in from the console. The next
time around, the CCP will do the same thing except that the RC field
is now pointing at the record whose number is one less than that of
the previous operation. In other words, the RC field is used as an
implicit record pointer. Very neat and it works too!
Sometimes it is desirable to bypass the BDOS and communicated
directly with certain BIOS functions. For example MICROSOFT's BASIC
interpreter does not use the BDOS character I/O functions as it does
its own line editing and the USCD Pascal system completely overlays
the BDOS. There is a technique for accessing the BIOS that is
general enough so as not to be consedered a kludge. An application
can always find the page boundary on which the BIOS begins by
examining the high order address byte of the warm boot entry point.
Using that as the high order byte of the address the low order byte
is set to an offset into that page as determined by:
OFFSET=FUNCTION * 3
as each entry is three bytes. For example here is a short routine
which causes data to be written to the list device:
LOFF EQU 0FH ; BIOS+LOFF=LIST ENTRY
LIST:
PUSH H ; SAVE HL
LHLD 1 ; GET ADDRESS OF WARM BOOT
MVI L,LOFF ; SET LOW ORDER BYTE TO LIST OFFSET
XTHL ; RESTORE HL, LIST ADDRESS ON STACK
RET ; EXECUTE LIST ROUTINE IN BIOS
This technique works as long as the BIOS begins on a page
boundary. The more general technique would be:
LOFF EQU 0FH-3 ; OFFSET FROM WARM BOOT ENTRY
LIST: PUSH H ; SAVE HL
LHLD 1 ; GET ADDRESS OF WARM BOOT
PUSH D ; SAVE DE
LXI D,LOFF
DAD D ; GET TO LIST ENRY POINT
POP D ; RESTORE DE
XTHL ; RESTORE HL
RET : EXECUTE LIST ROUTINE
There is one more thing and then I'll quit. If you remember from
above, I mentioned that while poking around inside a screen oriented
text editor, I found that it modified the error address fields at
the beginning of the BDOS. It also does another curious thing. In
the editor there is a command to save the rest of the file, exit the
editor and automatically process it with an entirely different
program, such as an assembler or text formatter. There is an
interesting technique here which could be generally useful. What
happens is this: First, modify the address of the console input
routine in the BIOS jump table to cause a routine inside the
application program to supply data to the CCP. This is done as
follows:
LHLD 1 ; GET THE BIOS PAGE ADDRESS IN H
MVI L,CI+1 ; HL IS THE ADDRESS OF THE ADDRESS
; OF CONSOLE INPUT ROUTINE
MOV E,M ; GET THE DEVICE ADDRESS IN DE
INX H
MOV D,M
XCHG
SHLD SAVE ; SAVE IT FOR LATER
LXI D,ALT ; DE IS ADDRESS OF ALTERNATE ROUTINE
MOV M,D ; POKE JUMP ADDRESS
; IN BIOS JUMP TABLE
DCX H
MOV M,E
JMP 0 ; AND RE-BOOT
SAVE: DS 2 ; LOCATION USED TO SAVE
; CONSOLE INPUT DEVICE ADDRESS
After doing this, everytime the CCP request a character from what
it thinks is the console input device, it will be handed a character
from inside the original application program. Just before handing
the CCP a carriage return the application will restore the original
address of the console input routine:
LHLD SAVE ; GET ORIGINAL DEVICE ADDRESS
XCHG ; PUT IN DE
LHLD 1 ; GET ADDRESS OF ADDRESS FIELD OF
; CONSOLE INPUT ENTRY
MVI L,CI+1
MOV M,D ; RESTORE ORIGINAL ADDRESS
INX H
MOV M,E
; ... AND CONTIN
The routine that does the character handling is essentially
this:
LHLD POINTER ; GET ADDRESS OF NEXT CHARACT MOV A,M ; GET NEXT CHARACTER
INX H ; ADVANCE POINTER
SHLD POINTER ; SAVE POINTER
CPI CR ; END OF DATA?
RNZ ; IF NOT THEN JUST RETURN IT
; ELSE RESTORE CONSOLE
; INPUT ADDRESSES
This works for two reasons: obviously the BIOS jump table can be
considered data as well as code (hail to John Von Neumann) and since
the original program will remain intact until the next program is
actually loaded on top of it, the routine simulating the console
will function normally. This technique suggests a viable method for
chaining a series of programs together without having to
specifically build a submit file for each chain.
14-Sep-80 00:07:00,700;000000000000
Date: Sunday, 14 September 1980 00:07-MDT
From: Frank J. Wancho <FJW at MIT-MC>
To: INFO-CPM at MIT-MC
cc: FJW at MIT-MC
Subject: CP/M Article (Updated)
In the last pair of messages you received a copy of an article on the
insides of CP/M. I have now placed online an updated version of that
article (as of July 1, 1980), courtesy of Bruce Tompson (WB3ETS@AI),
which was uploaded off of a disk file and re-edited. Due to its
length, I will not retransmit it, but will send it to you by net mail
upon specific request, or you can FTP the file yourself (MC:FJW;CPM
DOC).
Please keep in mind that the article was written before CP/M2, i.e.,
it applies to CP/M 1.4...