When we wrote our assembler we made many design choices that helped us cut down on the complexity of the assembler itself. One of those decisions was to have the assembler directly output executables. That was a good choice in that it allowed us to quickly test as we were developing: once we implemented an opcode, we could write an assembly file with that opcode, run it through our assembler, and run the resulting program without needing to do any more processing.
One signficant downside to this approach however is that we are constrained to only writing single-file assembly programs. If we want to split our programs into multiple files, or incorporate already existing libraries into our code, our assembler cannot do that. If we want to do that we need to have our assembler produce relocatable object code: code that can live "on its own" in its own independent (though not directly executable) object file and later be combined with other such files to produce an executable.
In this blog post, we will write a linker for our own object code format that is capable of producing CP/M and MS-DOS COM file format executables, for the Intel 8080 and Zilog Z80 on CP/M and the Intel 8086 on MS-DOS.