Skip to content

bin2cpmhex.js

Peter Wilson edited this page Jan 6, 2024 · 11 revisions

Getting applications onto a bare metal CP/M box is a challenge faced by anyone looking to bootstrap a new CP/M system. Grant Searle wrote a great little utility to fill the CP/M part of this equation. He seems to change websites fairly often but at the time of writing his download utility is described on this page. Included is a Windows 'packager'. I'm a Mac user so that's not wonderfully useful. Instead I wrote this node.js utility that should run from the command line on any system with a recent version of Node installed. Typical syntax would be:

node ../path/to/bin2cpmhex.js prog.com > dnld.cpm

Full synax

node bin2cpmhex.js [--minsize=nnn] [--maxsize=nnn] [--excltype=XXX]+ [--userdir] file|directory [file|dir]+

The file prog.com is read and a translated output produced suitable for consumption by Grant's download programme. Note output is sent to stdout so direct it where you want it.

This simple example translates a single file but it can happily generate output for whole directories. The generic syntax is:

bin2cpmhex.js file_or_directory [file_or_directory...]
  • Files
    • With .hex extension, assumed to be an intel hex file. The output filename is translated to .com and the hex content used as input to download.com.
    • Any other file is assumed to be binary and the individual bytes are converted to hex to be consumed by donwload.com
  • Directories - each entry in the directory is checked. Any that are standard files are converted as described above for files. And entries that are directories are recursively descended into and this directory process is repeated.

In this way bin2cpmhex.js can bulk convert files ready for CP/M.

Output format for an individual file is:

A:DOWNLOAD fname

U{X}
:{hexdata}
>LLCC

Where:

  • {X} is the CP/M user number (0-15) in hex (so single character 0-F). The default is 0. See command line options for changing this.
  • {hexdata} is a sequence of hex character pairs (one per byte)
  • LL is the lower 8 bits of a count of all the bytes in the hexdata set (size of binary)
  • CC is the checksum, calculated by adding every byte in the hexdata set and taking the least significant 8 bits.

Where more than one file is included in the package the above format is repeated for each file.

User number

CP/M has the concept of a user number, or user area. These are roughly analogous to 16 sub directories on each disk. Some CCP extensions allow these to be assigned aliases to make the analogy more natural for users of more modern systems.

The download.com programme allows a download to specify a user area in the form of Unn, where nn is the target user number. This value appears on the first line of the data sent to download. See the example format above, U{uu}.

By default bin2cpmhex will set the user number to zero, which is the default. This can be change in two ways.

Changing the default user number.

Specify --defuser=n on the command line to make all files load into that user area rather than area zero.

Setting the user number from the path

Some archives (those of the RunCPM maintainers for example) user a directory name as the user number. So you might find something like the following in an archive:

  + A
    + 0
      + prog1.com
      + prog2.com
      + data1.txt
      + ...
    + 1
      + progU1.com
      + ...
   + B
     ...

To make it easy to use archives in this format specify the --userdir command line parameter. With this switch ON a check is made of the name of the direct parent directory of each file. If it's a number and in the range 0-15 then this will be used as the CP/M user number. If not then the default user number will be used.

Filtering files

Some archives contain much larger files than would be expected in a retro 8 bit system. Examples include very large PDF copies of manuals. Generally these aren't useful and often won't fit on media available to retro machines. My system, for example, partitions an SD card into 4MB logical 'disks'.

Three command line filtering options are available:

Switch Description
--minsize=nnn exclude any file that is smaller than nnn KB ** - note kilobytes, not bytes. So 1MB would be 1024.
--maxsize=nnn exclude any file that is bigger than nnn KB ** - note kilobytes, not bytes.
--excltype=XXX exclude eny files with this file extension. For example --excltype=pdf would exclude a file called MANUAL.PDF. XXX is case insensitive. You can have multiple instances of this parameter for different extensions

Patching a single file

The following syntax allows an Intel hex formatted file to be used to patch an executable CP/M file (a .com file) before writing to the output file. Disclaimer I obviously added this facility for some specific problem I was having at one point, no idea what that was now and I don't currently use this!

The syntax is:

node ../path/to/bin2cpmhex.js --patch=patch.hex prog.com > dnld.cpm

An error is generated if the number of files to be processed is not exactly one.

The programme to be converted is loaded into memory. Each data record in the patch file (patch.hex) is processed. All data records are then applied to the loaded file. The patched file is then written out ready to be fed to download.com.