C-Z80 Computer is officialy runing CP/M 2.2 for a few weeks now:
As soon as the hardware modifications described in the previous post have been done, I started more serious research about the CP/M itself. Despite my initial worries, it turned out, that adapting this operating system for a particular hardware wasn`t really that hard. It`s even more obvious, as the system was designed in a way to make it easy to adapt for any computer with 8080/Z80 CPU, with only one important requirement – RAM area starting from addres 0000.
When searching for useful documents, tutorials and examples, I came across this page:
Donn Steward describes his experiences with setting up CP/M on his Z80 system. The article is very well written, has many links to usefull resources, and includes sources of his own work. After printing the article, and reading it several times I was shure I have everything I need to put CP/M on my own system. I Initially used his source of system`s core, but I had to change some assembler directives in order to make it compatible with TASM. Eventually, I ended up using the source provided by Grant Searle on his Breadboard CP/M project site:
http://searle.hostei.com/grant/cpm/z80sbcFiles.zip (cpm22.asm in /source directory)
It is basically the same source Donn uses, but adapted for TASM. The source code is a work of Clark A. Calkins, who did a really great job in disassembling it from a working system. This source does not need any changes (except origin address – for me it`s D400h for 60K CP/M), and TASM assembles it without any problem. The main part of the work is of course the CP/M BIOS. Donn used the skeletal customizable BIOS code found as an appendix to the official CP/M Alteration Guide. I used the same one, but with Donn`s 8080 to Z80 mnemonic change which saved me a bit of work (except of adapting the assembler directives for TASM). I also used his way of bypassing the unneeded sector translation.The skeleton of the bios was ready to be adapted for my own system. For a quick start and debugging I decided to use serial port for console I/O operations (const,conin and conout subroutines) as it was just a few lines needed for complete implementation. Next big task was to implement the disk I/O operations. I also stick for now to the default 4x ~250kB disk drive setup in order to leave as much original code intact. Because I use single CF Card as a mass storage, all disk selecting and head-positioning subroutines (home, seldsk, settrk, setsec, setdma, sectran) are just variable settings operations. All the magic is in read and write subroutines.For CF card low-level operations I reused much of the code from my own monitor program. Before they can be used, some assumptions had to be made:
- To make it easier I`m not using sector deblocking algorithms – I only use 128 bytes of each 512byte sectors.
- Some translation scheme is needed for a standard 77 track / 26 sect/track 8″ floppy layout to LBA addressing. I arranged it this way:
LBA3 = 00h LBA2 = 00h LBA1 = (up to 4 bits of drive number)(4 upper bits of track number) LBA0 = (3 lower bits of track number)(5 bits for sector number)
It`s not the optimal soultion (still some space is wasted in addition to using on-quarter of each sector), but really it`s not a problem with today CF Card sizes: the whole 4-disk CP/M space (about 1MB) takes 14bits of LBA address, which makes: (16383sectors * 512bytes per sector) = 8191kB = 7MB.
- The CF card is divided into 2 regions: FAT16 partition, and remaining space: for CP/M`s exlusive use. For the particular 64MB card I`m using, I set the FAT16 partition to about 40MB. The rest of it – starting from LBA sector 00 01 4A 00 is for CP/M. This offset has to be added to every calculation mentoined in the previous point.
With these considerations, read and write procedures were implemented without much trouble.
Next two problems are:
- formatting the CP/M disks
- putting the CPM image into first two tracks on the first CP/M disk.
Donn described those two processes in detail, and also presented his code. As the code is rather straighforward and easy to understood, I didn`t bother to rewrite it from scratch, but rather I modified it for my system:
For “format” the idea is: the bios code contatining all disk-related subroutines should allready be loaded at it`s proper destination (for this I use fload command from my monitor program). Then, the assembled format.bin must be loaded to it`s proper destination (generally anywhere below BIOS area). then we must jump to this code. The program is hardcoded to format 4 CP/M disks and return to ROM Monitor.
The “putsys” program writes CP/M image to first two tracks (0 sect. 2-26 and 1 sect. 1-25) of first CP/M disk. Both system and bios images must be loaded into their respective memory locations first using ROM Monitor fload command. Then, the putsys program can be loaded at it`s destination and it`s code may be executed.
The last problem to be solved before the system may finally start was to write a bootstrap program. It`s task is to place both system and bios images into RAM, switch the memory configuration to all RAM, initialize all needed hardware (serial port, keyboard, video cards, CF card, etc..) and jump to BIOS starting point. Again, ROM Monitor FAT16 subroutines come in handy: binary images are read from FAT16 partition and placed into their RAM locations. Hardware is initialized, memory configuration changed, and jump to BIOS executed as a last step.
For the initial tests I used to manually load bootcpm.bin into memory and jumping to it every time I needed to reboot CP/M. Later, I add a very small modification to the ROM Monitor code: In first few bytes I check if any key is pressed. If it is – the ROM monitor starts as usual. If no key is pressed during hard reset or power on – another piece of code is executed: the FAT16 variable area is being initialized, bootcpm.bin is then loaded into RAM, and the control is being passed to it.
- CP/M is always automatically loaded without any additional effort.
- If ROM Monitor is needed for any purpose – it is easy to switch to it.
- Because ROM Code always loads the same file stored in FAT16 partition, it is very easy to modify any element of the CP/M boot process: I just need to rewrite any part needed, assemble it and put on CF card right from my PC.
The ROM Monitor modifications are here:
.org 0000h LD HL,0FFFFH LD SP,HL call start_skan cp 0ffh jp z,boot_cpm jp START .org 0100h START: .... boot_cpm: ; FAT16 variables clear ld hl,85aeh ;FAT16 variable block start ld bc,02e6h ;that many bytes to clear _FAT16_var_clear: ld a,0 ld (hl inc hl DEC bc LD A,b OR c JP NZ,_FAT16_var_clear call erase_screen ld a,kursor_on ld (znak_kursora),a call clear_line_buffer ld a,0 ld (bufor_flaga),a ld (x_kursora),a ld (y_kursora),a ld (timer_kursora),a ld (R_SHIFT_PRESSED),a ld (L_SHIFT_PRESSED),a ld bc,0 ld (index_bufora),bc ld hl,file_bootcpm ld de,bufor_linii call copy_fname call file_fload jp 0c000h copy_fname: ld a,(hl) ld (de),a inc hl inc de cp 0 jp nz,copy_fname ret file_bootcpm: .db "fload bootcpm.bin c000",0 .END
After all these steps are made, the computer boots into CP/M system without problem.
However, we start with completly clean system – no files, no external ( “transient” ) commands. We only have few internal CP/M commands. Direct writing of files from a PC to CF Card`s CP/M disks area is out of question – it would be too complicated: we wolud need to consider both disk/track/sector to LBA mapping, and also CP/M filesystem structure. A piece of software had to be written on the PC for this. Instead, as many people do, I used the CP/M`s built-in “SAVE” command to download binary files from RAM memory to disk files. For this purpose, I`ve added a little modification to previously mentoined bootcpm program – just before jumping to bios entry point, it executes a procedure for downloading binary file from PC to RAM via parallel port. It`s the same procedure as in ROM BIOS ( “load” command) , but now it has to work as a separate piece of code, because by the time it is started, the memoryis allready swapped to all-ram, and the destination address is 0100h. After the code has been downloaded to RAM, the rest of the boot process takes place. Afte CP/M has started, we ca issue “SAVE xx filename” command, where xx is the number of 256-byte pages of code (starting from 0100h) to be saved to a choosen filename. Using this procedure I`ve managed to place most important CP/M system files inside the system itself. However, this scheme works only for files that can fit into memory in one piece. For larger files, the proces is slightly more complex: We have to divide the file into smaller chunks (for this I`ve used HJSPLIT v3.0 but any simmmilar software will do.) then, we have to download the chunks into separate files inside CP/M using previous procedure. After all chunks are downloaded we can use “pip” transient command to merge all chunks into one file: the syntax is something like that:
The [O] parameter for each file chunk means that we are working on binary (non ASCII) files. Using this procedure, I`ve managed to download some useful software into the system: Games (Zork1, Sargon, Catchum, Ladder, Startrek), Basic interpreters (two versions of Microsoft Basic) and others.
I`ve tested the system and played around with it for some time, using serial console as input/output. Here are some screenshots of the aplications and system itself:
I`ve encountered lots of problems with terminal emulation standards. Some aplications didn`t look good or were completly unusable. Sometimes there was some configuration programs included for them, so most terminal problems could be solved. I`ve also noticed, that on PC`s side, the most compatible terminal emulation program was Minicom running on Linux. For debugging the console input / output a program “Realterm” for Windows, version 220.127.116.11 proved to be extremally helpful. Thanks to it, I was able to track the transmission of escape characters in both directions.
After I gain some skills in using the CP/M and understanding how an ANSI terminal works, I was ready to move on to the final stage: implementing real hardware keyboard and MDA monitor handling subroutines.
For the keybord it went quite shoothly – I`ve reused most of the code I allready wrote for the ROM Monitor. Only minor changes were needed. Now it works fine. For the display it was more complicated, as basically it reqired to write a complete ANSI terminal emulator. It now works good, and works for most applications. There were lots of issues with performance – scrolling, and cursor positioning were taking too much time, and the display was updating too slowly. Some optimisation were needed, and now it runs fine. Here are some screenshots from actual MDA monitor:
Here is the link to actual version of the BIOS:
CP/M adaptation for my computer system is almost done, and continues to give me a great deal of fun! There are however lots of tiny issues to be resolved and tweaks to be made, so I plan to work on them in near future. Here is the list of thinks I wolud like to correct:
- Keyboard readout: For this moment, the conin subroutine waits for the key to be released before it is returned to the system. This blocks the system itselffor a short time. I need to change it to non-blocking mechanism with proper debouncing and repeating algorithms.
- ANSI compatibility of the display – I`ve addes quite a few escape sequences so far, and many programs works fine. However, there are still some more complicated sequences that I need to write properly.
- Increase disk space: for now I only use standard, default 4x~250KB disk drives. It will be easy to expand it to 16 disks, but they will need formatting and another, larger CF Card with new partition layout. Some dull work. I`m not planning to change disk parameters – I find no joy in messing with this. I`m quite happy with what I allready have.
- Make more flexible boot proces: I plan to add some configuration file mechanisms, which will allow me to choose the BIOS operation for particular boot. Some text file could be stored along BIOS, CP/M and bootstrap program. During startup, bootstrap code would check this file, and set some BIOS parameters according to it`s content. This will also include LBA starting point for CP/M partition. This will be a great help when moving to another CF Card would beneeded.
- Find an easy way of transfering files into CP/M – most desirably by using serial port in some program inside CP/M.
- Maybe change the MDA mode to something more compatible with modern displays – at least a CGA mode with a converter to SCART RGB, but 8-bit VGA card would be perfect !