Nocturnal CP/M

*** Work in progress ***

This is a CP/M BDOS emulator which is meant to run CP/M programs from the host command line. That is no command processor is provided. Files are accessed through the host file system and thus no disk images need to be made.

The aim was to make running CP/M programs as seamless as possible.

1 Building

There is a Makefile. Additionally to the default all target there is an install target. Adjust $PREFIX in Makefile if you like. The code was tested on Linux/amd64, macos/amd64 and macos/arm64.

2 Invokation

ncpm [<comfile>] [<args>...] [-- <inject>]

A .com file named by comfile or comfile with ".com" added is searched in the following places:

Before comfile is considered the executable itself is tried with a ".com" extension. This symbolic links could be used to make CP/M .com files into regular host commands.

So a basic invokation looks like so:

ncpm contrib/mbasic/mbasic.com

To make mbasic available as a host command you can create a symlink:

$ cd contrib/mbasic
$ ln -s ../../ncpm mbasic
$ ./mbasic
BASIC-80 Rev. 5.21
[...]
Ok
_

2.1 Keyboard Injection

When inject is given, it is keyboard input to provide to the executed program prior to reading keyboard input from standard input. The usual C escapes are accepted.

This is handy to script interactive programs like Turbo Pascal. E.g. to make it load foo.pas set compilation options to .com file, compile and quit again, we can say:

cpm turbo.com -- 'YW' foo.pas '\rOCQCQ'

That string is all the needed keystrokes.

2.2 Console

SIGINT is blocked to make ^C available to the CP/M prorgamm. Use SIGQUIT if you need to bail out.

There is a very simple line editor available, if the program actually uses the BDOS call to read a line; muLISP does, but unfortunately MBASIC does not.

At the moment all keyboard input is case flipped. There is no option to turn that off. You would need to comment that out in console.c:con_thread_main.

2.3 Filesystem

All the files in the current working directory are available to the program. Additionally files in the very directory the .com file was found are available. That is handy if the program needs any auxiliary files. However, whenever a file is created it ends up in the working directory.

There is no provision to automatically translate between CRLF and bare LF. However, when the last block of a CP/M file written ends in a row of ^Zs those are stripped. Likewise if a file is not a multiple of 128 bytes the last block is padded by ^Zs.

2.4 Environment Variables

CPM_DEBUG

Debugging level

CPM_VERBOSE

Verbosity level

CPM_BDOS

Set the address of the BDOS to simulate limited memory.

-- 2025-06-21, Gilbert Baumann