Build and Install CP/M Apps¶
Host-compiled dcc applications can be built and installed directly into a
CP/M disk image. The Apple container and Apps/build-all.ps1 use the same
writable 512 KiB base images in disks/512kb/.
See Apps/README.md
for the complete build and disk selection.
The per-app Altair disk workflow is:
pwsh ./Apps/ATTN/build-app.ps1
The script is cross-platform and runs under PowerShell 7 on macOS, Linux, and Windows.
Prerequisites¶
The default 512 KiB Apps/build-all.ps1 workflow uses Apple container on
Apple silicon macOS for disk installation, not host cpmtools. Start the
container service and run python3 scripts/z80_container.py prepare before
building. See local container setup.
The per-app Altair disk workflow requires the following host tools.
Install these host tools and make sure they are available on PATH:
- PowerShell 7
as
pwsh. - The dcc toolchain, including
dccmake. - cpmtools, including
cpmrmandcpmcp.
On macOS with Homebrew:
brew install cpmtools
On Debian and Ubuntu:
sudo apt install cpmtools
On Windows, install a Windows build of cpmtools and add the directory
containing cpmrm.exe and cpmcp.exe to PATH.
Verify the required commands from PowerShell:
Get-Command pwsh
Get-Command dccmake
Get-Command cpmrm
Get-Command cpmcp
Stop the emulator first
Do not update a disk image while the Apple container, the ESP32 emulator, or another process has that image mounted. Stop the emulator before running an app build script, then restart it after installation.
Build and install flow¶
Each app's build-app.ps1 delegates to scripts/build-dcc-app.ps1, which
performs these steps:
- Reads the last active
dcc-outputanddcc-output-formatassignments fromdccmake.txt. The default format is.COM;libselects.LIB. Fordcc-output=ATTNC11, the expected file isATTNC11.COM. - Deletes the previous host output and temporary
builddirectory. This prevents a failed build from installing a stale binary. - Runs
dccmakeand checks its native exit code. - Moves only the expected file from the configured build directory into the application directory. A missing output is a terminating error. COM build directories are removed; library intermediates are retained because a host can consume generated sources.
- Calls
scripts/cpm-install.ps1with the disk image, host file, and CP/M destination such as0:ATTNC11.COM. - Deletes the old CP/M file with
cpmrm, copies the new file withcpmcp, rebuilds the physical sectors, and atomically replaces the disk image.
PowerShell stops at the first failed command, so the disk installation only runs after a successful compile, assemble, and link.
Build all applications and composite projects¶
Build and install the complete dcc application cohort with:
pwsh ./Apps/build-all.ps1 -DccDirectory /path/to/dcc `
-LibraryLinker /path/to/dcc/l80c
The script scans Apps/disk-{all,a,b,c,d}-apps recursively. Folder placement
selects target drives; generated/build/test folders and disk-ignore are
excluded. It discovers C configurations, standalone C sources, assembly and
BASIC applications without requiring per-app build-app.ps1 wrappers.
Duplicate CP/M destinations are rejected before building or changing images.
For LIBDEMO, one root configuration defines the unit:
dcc-projects=lib,host
The root is scheduled once; the referenced child folders retain their
individual configurations but are not scheduled separately. One dccmake
invocation builds the library, generates typed wrappers, then builds the host.
Only after all child builds succeed are their .LIB and .COM outputs
collected and installed. Failure stops before installing any output of that
unit. Earlier successful units remain installed; installation is still
file-by-file, not a transaction covering all drives or outputs.
-LibraryLinker (defaulting to DCC_L80CC) supplies the special fixed-address
library linker only to LIB children. COM children keep their normal linker.
Without an override, child configurations select their own linkers.
The default targets are disks/512kb/drive_{a,b,c,d}_*.img. The SBC installer
copies into a temporary image, verifies the file and preserves the reserved
tracks before replacing the image. Use -DiskFormat Altair to select the
ESP32 88-DCDD images described below. The Apple container uses the 512 KiB images.
Preview with -ListOnly without invoking tools or writing disks. The summary
counts build units; a composite unit can produce more than one installed file.
See Apps/README.md
for assembly-only builds, image overrides and isolated regression commands.
Edit base disks and copy to the Z80 SBC¶
- Run Run interactive CP/M emulator from Tasks: Run Task, or
python3 scripts/z80_container.py startfrom this repository root. - Edit, create or delete files in CP/M. Changes persist directly in
disks/512kb/; there is no session-disk save step in this project. - Finish the CP/M application and stop the container with Ctrl+] in the task terminal. Stop the SBC emulator too before copying its disk sources.
- From this project's
Appsdirectory, choose one of these commands:
# Build/install applications, then copy all four disks.
pwsh ./build-all.ps1 -c /path/to/Z80ROMlessSBC/src/disks/native
# Copy current disks, including CP/M edits, without building anything.
pwsh ./build-all.ps1 -CloneOnly -c /path/to/Z80ROMlessSBC/src/disks/native
The destination directory must exist. Both commands overwrite all four SBC native images, including D's database. Build-and-copy replaces each built application's destination file but preserves unrelated files. Use copy-only when you want to keep an edited file that the application build would replace. Copying only occurs after a successful build; a failed build can leave earlier successful application installations in the ESP32 base disks.
VS Code tasks¶
Open Tasks: Run Task:
| Task | Function |
|---|---|
| Run interactive CP/M emulator | Check all four base disks with read-only fsck.cpm before refreshing A and starting CP/M; abort without disk changes on failure. CP/M edits persist. Ctrl+] in the task terminal stops it; Ctrl+C goes to CP/M. |
| Build Apple container | Build the container image and host audio client without starting CP/M or copying disks. |
| Altair: Build apps and copy disks to Z80 SBC | Prepare the container, build/install applications, then copy A-D to the chosen SBC native folder. |
| Altair: Copy current disks to Z80 SBC | Copy A-D without rebuilding applications. |
Both copy tasks prompt for the existing SBC src/disks/native/ directory.
The suggested relative path is for sibling checkouts under
GitHub/esp32/esp32-altair-8800 and GitHub/Z80ROMlessSBC; enter an absolute
path for another layout. These tasks do not flash either board.
For C debugging, choose DCC CP/M: Debug active C file in Apple container and press F5. It automatically builds the active application with debug metadata and prepares a private disk snapshot. Debug-session file changes do not update the base disks. The preparation helpers are hidden from the task picker; use the normal start task when editing persistent disk contents.
Use the copies in the SBC project¶
The SBC's Run interactive CP/M emulator, Refresh CP/M container and
session disks, and Pico disk tasks all read src/disks/native/.
They check exact image sizes and validate source and prepared filesystems with
the container's fsck.cpm -n. Drive A is patched with the SBC checkout's current
CP/M, BDOS, BIOS and AUTORUN; B-D are unchanged copies.
No import task is required.
SBC emulator writes go to its build/cpm-session/ copies, not back to the ESP32
base disks. Copying is one-way; SBC edits do not automatically synchronize to
this project. See the
SBC disk workflow
and follow its power/confirmation requirements before any Pico flashing.
Reusing the installer¶
Other application build scripts can call the shared installer directly:
& ./scripts/cpm-install.ps1 `
-Image ./disks/escape-posix.dsk `
-HostFile ./Apps/FOO/FOO.COM `
-CpmFile 0:FOO.COM
The CP/M destination starts with a user number, not a drive letter. The disk
image passed to -Image determines the emulated drive. In the ESP32 CP/M 2.2 configuration,
disks/escape-posix.dsk is drive C:.
Why the shared installer is required¶
The tracked .dsk files are raw MITS Altair 88-DCDD images, not flat arrays
of 128-byte CP/M records. Each image contains 77 tracks, 32 sectors per track,
and 137 bytes per physical sector, for a total of 337,568 bytes.
The Burcon CP/M 2.2 BIOS uses two physical sector layouts:
| Tracks | CP/M payload | Checksum | Trailer |
|---|---|---|---|
| 0-5 | Bytes 3-130 | Byte 132, sum of payload | Byte 131 is 0xFF |
| 6-76 | Bytes 7-134 | Byte 4, sum of bytes 2-134 excluding byte 4 | Bytes 135-136 are 0xFF, 0x00 |
Tracks 6-76 also apply a second physical-sector mapping after the CP/M skew table:
$$ \text{physical sector} = (17 \times \text{translated sector}) \bmod 32 $$
The shared installer therefore:
- Extracts each 128-byte payload into a temporary packed image while applying the track-dependent sector mapping.
- Runs cpmtools against that packed image using the repository-level
diskdefsentry namedaltair88. - Merges the modified payloads back into their physical sectors.
- Regenerates the checksum and stop bytes required by the Burcon BIOS.
The central altair88 definition records the CP/M disk parameter block and
skew table. It also sets logicalextents 1, matching the BIOS EXM=0 layout.
Without that setting, files larger than 16 KiB receive incompatible directory
extent numbers and may appear in cpmtools but not in CP/M.
Do not run cpmcp directly against the raw 137-byte image. It does not account
for both Burcon sector layouts, the second track-dependent skew, or the physical
checksums.
Validate an installation¶
Start the local SBC emulator after a default build:
python3 scripts/z80_container.py start
Then select the target drive, list the installed file, and run its help command:
A>C:
C>DIR
For ESP32, the authoritative BIOS implementation and disk
parameter block are in disks/cpm64_bios.asm.