Sounds like you got to go the same way as any system manufacturer back then:
- Build your (virtual) Hardware.
- CPU, RAM, ROM emulation
- Some terminal connection
- And at least some (virtual) mass storage controller
- It needs to have some way to 'mount' (virtual) media.
- Write a Boot-ROM
Until here it's Hardware/Firmware development, after that it's porting CP/M, as described in DR's CP/M Operating System Manual, the original one stop shop for bringing CP/M to a new machine.
- Write your boot code
- Write your BIOS
- Combining CCP, BDOS and BIOS
- Create a disk
- Boot your pretty new CP/M
CPU/RAM/ROM should work by now
A terminal connection can be
- a detailed emulation of a serial port with all bells and whistles, or, as I would suggest, a
- simply a pair of ports for communication, acting like a pipe, build as
- forwards each character written to to some (terminal) window
- offers every character typed in that window to your (virtual) computer.
- port#2 delivers when read (at minimum) two flags
- one flag marking whenever port#1 is able to accept a character
- one flag telling whenever port#1 has a character (from keyboard) available
A (virtual) mass storage controller may be as easy as the terminal connection. After all, a virtual device does not need to emulate all quirks of real hardware. It can be as simple as a set of I/O 'registers' to store address/drive/track/sector/count numbers and a command 'register' which when written let the emulation do whatever is requested (read/write/verify) and return.
Such a simplified hardware interface does not only ease emulation development, but simplifies Boot-ROM as well as BIOS code to the extreme.
If you really intend to add more hardware emulation postpone it to later. Get the system working first, add bells and whistles later. If at all, I mean, the whole purpose of CP/M is to provide an abstract layer ultimately hiding all hardware.
- The boot ROM needs to do basic system initialization
- (Maybe offer some minimum monitor program)
- Do warm boot detect
- Initialize the mass storage controller
- Load a / the first block from a mounted media
- Execute it.
Usually it's a great idea to have some minimum monitor program within the Boot-ROM offering functions like
- inspect memory
- change memory
- start execution at an address
- set various parameters, like
- terminal port(s)
- (boot) drive
- load a memory section from mass storage
- save a memory section to mass storage
- a format function would come handy which fills the mounted 'media'with a default pattern.
optional it offers an interface for boot code to functions it already contains, like
- terminal I/O
- basic mass storage access
- read/write a block
- read/write multiple blocks.
Such a monitor ROM is a great way to feel your new system exactly the way it was back then - also the best debugging aid possible, as it can not be killed by software.
For x80 systems such ROM is usually
- located at 0000h
- mapped in by RESET
- offers a function to map itself out of memory.
The last one is imperative as CP/M (and next to any other Program) do love to change the vectors :)) Usually a one way feature, i.e. there#s no way, except by reset, to map it in again.
For it's boot function it depends if there is a boot sector, or if the ROM is directly made to boot CP/M. Personally I'd go with a boot sector, as CP/M reserves the first sector for it. This also enables some checks to see if the disk is bootable - having the system crash or do crap when a different media is 'inserted' is a real bad idea.
0800h may be a good default address to load the first sector to. I do not expect your boot ROM being larger than 2 KiB.
#3 Boot Code
Well, this step is kind of optional, as the boot ROM could already contain dedicated code to load CP/M. Starting at track 0 sector 1 There were machines like that, then again, for an open system it's better to pack the CP/M specific code in sector 0 track 0, which is reserved by CP/M for the Cold Start Loader.
The whole purpose of that code is to load the CP/M code starting at track 0 sector 1 to whatever the load address for CP/M will be on your 'system' usually some higher up value like 0E000h. All depending on the amount of space your BIOS needs. That value may need to be adapted when you're done generating a system.
This boot code should be fairly short. It may be based on the Boot-ROM functions, or be complete self contained - the later mandatory if the Boot-ROM is a very minimal version. Back in the days an approach based on a sufficient Boot-ROM was preferred as it not only helps a lot bringing up the system, but encapsulates much hardware related issues.
The default way to write a BIOS is to take the Skeletal CBIOS listing from Appendix A of the manual. BIOS consists of a few abstract calls to read/write devices - mostly console and disk. When using the simplified hardware emulation, as suggested, most parts will be quite short. Essentially just a few moves and it's done.
#5 Combining CCP, BDOS and BIOS
Well, back in the days, one would have used GETSYS and PUTSYS to patch an existing CP/M Disk (well, better a copy) with your new BIOS. If you want to go that way, you need an existing CP/M System and above tools. Which might be not at hand.
But nowadays all of CP/M is available in source, so one could create an image using BDOS and CCP sources combine with a new BIOS. Seems like a big task, but may go ahead rather quick as not much is to be done - heck, the whole idea of BIOS is to make BDOS and CCP machine independent. The only dependencies to care for is the address layout. Something usually handled by MOVECPM and SYSGEN. But when compiling it anyway, these adjustments can be done in source. All needed is adjusting a few symbols.
#6 Create a Disk
Or whatever you call your virtual media. Here you might want to go ahead and build some short tools to stitch boot sector, and the CCP/BDOS/BIOS file created in the last step into a single file ... plus maybe a bit more space for data :)) In fact, doing the 'data' area is quite simple: just fill the whole media with 0E5h. While filling with 0E5h does not matter for data sectors, all directory sectors will implicit seem to be formatted and empty :))
#7 Boot Your Pretty New CP/M
... or not.
While you now got a basic system booting into command prompt, it will miss any and all software. So unless you want to use the monitor program (remember, the one in Boot-ROM?) and a lot of manual action, you need to find a way to copy CP/M binaries onto you 'disks'. Of course, one could go ahead and build a set of tools to access your emulated disk files using whatever format parameters you invented, but it would feel like cheating, wouldn't it?
Well, why not inventing some 'binary interface'? Done simple enough would not require a lot of code hacked in manually. For example what about a 'secret' function of your mass storage interface. Like using sector number 255 to read data from some assigned binary file into memory and using BDOS calls to write it to 'real' disk? That's maybe less than 100 byte of code plus a hack within the emulator.
Or do the same with a simplified serial, much like the console one, handled as PUNCH/READER? As soon as you got PIP on your new machine, everything else will be standard CP/M ... Well, with a few tweeks, but that'll be lots of fun.
Of course, you may write external tools to convert files to your CP/M disk format, but as mentioned, that's cheating.
Resources for Serious CP/Ming
Beside John Elliott's Seasip.Info, mentioned in the question, one my want to take a look at Gaby Chaudry's Gaby.de and CPM.Z80.de, "The Unofficial CP/M Web site". All three containing tons of genuine source material.
If these sources do not assemble, it might be due DRI Assembler specific parts. Eric Smith got a cleaned up copy of BDOS and CCP at Github.
Especially interesting for the purpose to bring up CP/M might be Donn Stewart's CPUVille which got, beside lot's of information about building Z80 systems, a rather good description how he moved CP/M 2.2 to his homebrew Z80. This might be quite helpful despite focusing on porting to real disk drives. The also produced a few tools on the way that may inspire your own development.
And yes, there are other sites out there that will be helpful to bring up and run CP/M. Many other sites. These are just the core starting points.