15

I recently finished writing an emulator for the Intel 8080, having passed all of the usual test suites, and wanted to try running an actual OS on it (such as CP/M). I'm aware that I would have to write my own BIOS for interfacing with "hardware", but I want to know how I would load CP/M itself -- or any other OS, for that matter.

In particular:

  • Do I need to compile from source? If so, where can I get source files and how do I compile? I did find resources here but I cannot figure out what to do with the files.
  • As much as possible, I want to run the OS with just the compiled programs in memory and my emulator, and no "high-level emulation", if that's the right term. That is, I don't want to be running a CP/M emulator, I want to be running CP/M on an Intel 8080 emulator.
1
  • 8
    By coincidence, The Register ran this story three days ago, which includes a bunch of links to original CP/M source code (in PL/M) and third-party replacements for each component (Z80-targeted, but in assembly so possibly easier to adapt). Checking out ZSDOS for the difficulty of eliminating Z80 extensions might be a starting place.
    – Tommy
    Jul 19 at 0:11

3 Answers 3

19

Sounds like you got to go the same way as any system manufacturer back then:

  1. 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.
  2. 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.

  1. Write your boot code
  2. Write your BIOS
  3. Combining CCP, BDOS and BIOS
  4. Create a disk
  5. Boot your pretty new CP/M

#1 Hardware:

  1. CPU/RAM/ROM should work by now

  2. 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
      • port#1
        • 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
  3. 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.

#2 Boot-ROM

  • 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
    • etc.
  • load a memory section from mass storage
  • save a memory section to mass storage

additionally

  • 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.

#4 BIOS

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

Enjoy!

... 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.

3
  • 2
    For an emulator that isn't simulating a specific real-life computer, it's not necessary to create an emulated boot ROM or boot sector. Just have the emulator populate the 8080's memory with the CCP, BDOS and BIOS, set the program counter to the BIOS 'cold boot' entry point, and launch. The DOS-hosted emulator MYZ80 behaves like this, for example.
    – john_e
    Jul 19 at 9:46
  • @john_e I guess that would work equally well for an emulator of a 'specific RL computer', wouldn't it? There are many ways to tackle this, enabling different aspects of fun. I'm pretty sure the OP will find his combination. (Not to mention that there were Computers with CP/M in ROM :))
    – Raffzahn
    Jul 19 at 11:25
  • A CP/M 2.2 bios is rather straight forward, except for the bit that handled 128-byte sectors on devices with a different sector size. I would therefore strongly recommend just emulating an original 8" SSSD disk for starters to keep this bit simple. Jul 20 at 16:13
11

You probably do not need to build CP/M from source. If you have a binary image you should be able to use that. But, you'll have to emulate the correct hardware for the build you have (since CP/M can be built for different machines with different hardware capabilities.) If you build from source you can control exactly what hardware your build expects.

In order to load CP/M, you'll have to figure out the boot sequence and emulate enough hardware to load CP/M from storage (since, I believe CP/M was not embedded in ROM). In order to run the actual CP/M code, your emulator will be responsible for emulating the hardware on which it is expecting to run.

(Source: I've written a more than one 6502 emulator which boots the Applesoft ROM.)

14
  • Would it be necessary to emulate hardware, or could one simply have the emulator respond to any attempt to fetch an instruction from address 5 as a request to have the emulator perform an operation selected by the contents of register C, manipulate registers suitably, and behave as though code fetched a RET instruction?
    – supercat
    Jul 18 at 23:20
  • 1
    @supercat: Yes, you can do that too. That technique requires that the emulator have intimate knowledge of the exact program it's going to run, but sometimes you might need it. It's more general if the emulator only has to emulate hardware and not know about the software. Jul 18 at 23:40
  • If the goal is to emulate a "generic" CP/M machine, I would think that having the emulator process BIOS calls directly would be simpler than trying to add an extra hardware emulation layer, though upon some consideration I think storing a JMP 0FFFFh to address 5, and then trapping execution at address 0FFFFh would be better than trying to trap address 5 directly, since CP/M might modify the vector at address 5 to point to its BDOS handler, and have that call the BIOS handler for functions it doesn't know how to handle.
    – supercat
    Jul 18 at 23:44
  • 4
    But wouldn’t catching PC=5 and doing the CP/M call via native code be exactly the high-level emulation the author doesn’t want? Regardless of ease.
    – Tommy
    Jul 19 at 1:29
  • 1
    @supercat: You're talking about BIOS functions (eg: 'read sector'), but intercepting the call at address 5 would catch BDOS functions (eg: 'open file').
    – john_e
    Jul 19 at 15:28
3

While the source code to CP/M is available, the more usual method of bootstrapping CP/M on a new system uses a binary image (also available from www.cpm.z80.de) of the BDOS and other parts of CPM that is relocated and combined with the assembled version of a CP/M BIOS written for your particular machine.

This process is described in detail in section 6 "CP/M Alteration" of Digital Research's CP/M Operating System Manual. (That's the second edition; there's also an OCR of the third edition here.) You'll want to use that as your primary reference during the process. However, you'll also need a disk image that has a valid directory on it; if you don't have one already Don Stewart's article Setting up CP/M 2.2 on a New Z80 Computer discusses how to do this (and probably provides useful hints to be read along with the manual above).

The process as described is oriented around doing it on an actual machine running CP/M, but if you want to do parts of it via directly accessing disk images on the machine hosting the emulator the changes you need to make should be clear.

You'll need to start by writing a BIOS; the BIOS routines and exactly what each must do are described in detail in section 6.6. There's a sample "skeleton" BIOS in Appendix B and the full source for the Intel MDS-800 system BIOS in Appendix A.

You'll then need a cold start loader. This can work in a variety of ways; the most common is to have a bootstrap stored on the first sector of the disk that's loaded in by a ROM or other automatic routine. (An example of such a boot sector is given in Appendix E; you'd need to add to that code to read a sector on your system.) The ROM itself might be in high memory and somehow started with a "GOTO" of some sort by the user, or more commonly ROM is in low memory at reset and the ROM bank switches itself out for RAM after it's loaded the disk boot sector.

In your case, since you're using an emulator, you might consider making the "loader" entirely external to the emulated machine by having the emulator load the entire BIOS and BDOS into its memory before starting execution at the BOOT entry point of the BIOS. (This is also a good way to do small manual tests of your BIOS; load it into memory along with a simple test program that calls the BIOS and checks the results and start execution at the entry point of that test program.)

The original version of CP/M was supplied to be used on a system with 20 KB of RAM, with the CCP at 3400h and the BIOS starting at ccp+1600h. This will obviously run on a system with more memory, but to make the extra memory available to user programs you need to use the MOVCPM program to relocate the CCP, BIOS and BDOS and build a new disk with this relocated version that you can then boot. The details of that are probably more appropriate for an answer to a question specifically about that, if the manual doesn't do the trick for you.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.