The source files from the last article are not a museum exhibit. They compile, with standard assemblers, into the exact bytes that make up the operating system. This article is the first half of the build: turning each source file into the binary it produces. The next one takes those binaries and lays them onto the disk.
The build recipe lives in the tree as CPMV220-44K/README.md; this is the narrated version.
Two assemblers, because two processors
There is no single tool, because the operating system is written for two processors and they use different assemblers.
The 6502 parts, the boot loader and its world, assemble with ca65 and link with ld65, the assembler and linker from the cc65 suite. ca65 turns the source into an object file; ld65 places that object at its load address and writes out a flat binary.
The Z-80 parts, the CCP, BDOS, and BIOS, assemble with sjasmplus, a modern Z-80 assembler. sjasmplus does the placing itself: a SAVEBIN directive at the bottom of each file says “write these bytes, from this address, this many of them,” and the assembler emits the binary directly.
Both are off-the-shelf and freely available. The recovered source is deliberately written to standard syntax so that nothing here depends on a custom tool to assemble.
One source, one binary
Each file produces exactly one binary, fixed in size, loaded at a fixed address:
| Source | Assembler | Produces | At | Size |
|---|---|---|---|---|
CPM_RPC6502_223.s (2.23 only) | ca65 + ld65 | the embedded 6502 service | $0DD0 | $0072 |
CPM_BIOS.asm | sjasmplus | CPM_BIOS.bin | $AA00 | $0600 |
CPM_CCP.asm | sjasmplus | CPM_CCP.bin | $9400 | $0800 |
CPM_BDOS.asm | sjasmplus | CPM_BDOS.bin | $9C00 | $0E00 |
CPM_BootLoader.s | ca65 + ld65 | the $0800-$13FF boot image | $0800 | $0C00 |
The sizes are not suggestions. The BIOS is exactly $0600 bytes, six 256-byte pages, because that is how much room the system area gives it. If a change made the BIOS one byte larger, it would no longer fit its slot, and the build would catch it. The fixed size is part of the contract.
Two complications the assembler needs help with
Compiling these is almost, but not quite, “run the assembler on the file.” Two things need arranging first, and they are the reason there is a thin layer of tooling rather than a one-line shell command.
The shared headers. The Z-80 files include cpm22.inc and cpm_system_220.inc, which live in a separate include/ directory. The assembler has to be told where to find them. Nothing exotic, but it has to be set up.
The foreign-CPU blocks. A file that embeds another processor’s code with INCBIN needs that block already assembled into a binary before it runs, because INCBIN pulls in a finished .bin. So the order matters: assemble the embedded block first with its own assembler, then assemble the host file that includes it. The 2.20 boot loader embeds two Z-80 fragments this way; the 2.23 BIOS embeds a 6502 one.
The pipeline knows all of this
Because each file’s load address, size, include path, and embedded dependencies are fixed and known, the project records them once, in a chunk map at cpm_pipeline/chunk_map.py, and a small assembler driver at cpm_pipeline/assemble.py does the staging. Compiling a single component to its binary is then one call:
source shared/toolchain/env.sh # puts ca65, ld65, and sjasmplus on PATH
python -c "from cpm_pipeline.chunk_map import SOURCES_220_44K; \
from cpm_pipeline.assemble import assemble_chunk; \
open('CPM_BIOS.bin','wb').write(assemble_chunk(SOURCES_220_44K['CPM220_44K_BIOS_Disk']))"
# -> CPM_BIOS.bin, 1536 bytes
assemble_chunk stages the includes and any embedded blocks into a temporary directory, runs the right assembler, and hands back the bytes. The same call with CPM220_44K_CCP, CPM220_44K_BDOS, or CPM220_44K_BootLoader produces the other three. You can verify the result the obvious way: each binary is exactly the size in the table, and it matches the corresponding stretch of the original disk.
That is the whole compile step. Four source files (five for 2.23, which has the extra embedded 6502 source), four binaries, each the right size, each at the right address. They are not yet a disk. The next article takes these binaries and writes them onto the boot tracks, which is where the sector interleave from article three comes back to be paid.

Comments
Nothing yet. Say the first thing.
Sign in to join the conversation.