• Please review our updated Terms and Rules here

Installing CP/M 3 (Plus?) on a home-built Z80 computer

Same to you.

We don't have anything planned for tonight - we don't go in for New Year much. TV film and some nibbles probably.

I'll go back to modifying NAS-DOS for my NASCOM 2 to use an SD card...

Must remember that drives are numbered 0... under NAS-DOS and not A... as per CP/M!

Dave
 
Interesting that it seems to have ended up in the loader after jumping to CCP... perhaps it is still in Bank 0 (where the remnants of the loader are probably still).

Also, if you are using a location of memory to store C that makes the routine such that it cannot be re-entrant anymore. That may not affect this code, but just a warning. Using the stack would eliminate that, but also uses more stack and changes the way you have to write the routine.
 
I was thinking whilst I was doing the washing up...

I was just about to mention myself about not using static variables to hold values because of re-entrancy problems. As stated, it shouldn't cause a problem in what you are doing...

I think the bit of code you were looking for was:

Code:
phex3:
           PUSH  BC
           LD    C,A
           CALL  conout
           POP   BC

The other thing is that you may be running out of stack space itself. You have what looks to be 64 bytes of stack space = 32 stack elements. You might like to increase this a bit to be safe. We are using quite a lot of stack for diagnostic purposes after all... We can always reduce the stack space later when the BIOS is working. I also like to use a stack check mechanism just to make sure we aren't running out of stack space. In your case you will crash through the SIO stuff which I don't think we are using yet?

My own stack check code stores a constant just below the stack area which I then routinely check for being corrupt. I also put a few stack elements below that so I have some valid stack elements to print out my 'last will and testament' to the fact that we have run out of stack space before HALting...

The other usual trick I do (!) is to PUSH more element than I POP off the stack (i.e. I forget about something). The stack gets lower and lower and ... lower and lower and ... crash!

Dave
 
Last edited:
Same to you.

We don't have anything planned for tonight - we don't go in for New Year much. TV film and some nibbles probably.

I'll go back to modifying NAS-DOS for my NASCOM 2 to use an SD card...

Must remember that drives are numbered 0... under NAS-DOS and not A... as per CP/M!

Dave

Oh we don't bother that much either - just spent the evening with the boss's sister and her hubby and the kids.

The NASCOM is an intriguing machine - I didn't even know they existed until about a year go when I embarked on building this Z80-based SBC myself. Mind you, I'd have been a little too young when they first came out - had to learn to walk and talk before I developed an interest in computers! ;)

The percent sign is interesting in your last post!!! Where did that come from I ask myself?

Dave

I think that can be safely ignored. I've upped the stack size to 128 bytes, made the PUSH/PULL alteration to PHEX and I've just run through the process of launching CPMLDR from power-on 6 times, to see if the console log output is consistent. The results aren't great.

Out of 6 attempts to run CPMLDR, 4 result in the following output:

Code:
CP/M Plus Copyright 1982 (c) by Digital Research
Loading CCP...
Bank 1 selected...
seldsk called...
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
First bdose call completed...
Second bdose call completed...
Third bdose call completed...
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00

After BDOS 20, A = 01
HL = 1901
CCP loaded...

One did the above, with this on the end:

Code:
Loading CCP...
Bank 1 selected...
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
First bdose call completed...
Second bdose call completed...
Third bdose call completed...
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00

After BDOS 20, A = 01
HL = 1901
CCP loaded...

And the last one did this - almost appearing to work, with the appearance of the prompt at the end, but the system was frozen:

Code:
CP/M Plus Copyright 1982 (c) by Digital Research
Loading CCP...
Bank 1 selected...
seldsk called...
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
First bdose call completed...
Second bdose call completed...
Third bdose call completed...
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00

After BDOS 20, A = 01
HL = 1901
CCP loaded...
L!d
After BDOS 20, A = 01
HL = 0001
CCP loaded...
Bank 1 selected...
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
First bdose call completed...
Second bdose call completed...
Third bdose call completed...
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00

After BDOS 20, A = 01
HL = 1901
CCP loaded...
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00
read called...
read completed, erflag = 00

No space
A>

So, with the lack of consistency in the results, I'm worried that this is an issue we'll never get to the bottom of. :/ I've included the full BIOS3.ASM file in this post as it exists currently, so you've got the latest version of it and the code that produces the above output. File is here: View attachment BIOS3.ASM.txt

Interesting that it seems to have ended up in the loader after jumping to CCP... perhaps it is still in Bank 0 (where the remnants of the loader are probably still).

Also, if you are using a location of memory to store C that makes the routine such that it cannot be re-entrant anymore. That may not affect this code, but just a warning. Using the stack would eliminate that, but also uses more stack and changes the way you have to write the routine.

Yes, I've doubled the stack size and switched back to using the stack to protect the contents of BC when using PHEX. Again, the bank issue is interesting and perhaps is a light at the end of the tunnel - if the wrong bank is selected when the system tries to execute a piece of code in it or retrieve data from it, that would explain the inconsistency in the results of the console outputs...

I also like to use a stack check mechanism just to make sure we aren't running out of stack space. In your case you will crash through the SIO stuff which I don't think we are using yet?

The SIO stuff will have been used by the time the stack overflows into it (in theory) as the SIO is set up from those tables almost at the start of cold boot.

My own stack check code stores a constant just below the stack area which I then routinely check for being corrupt. I also put a few stack elements below that so I have some valid stack elements to print out my 'last will and testament' to the fact that we have run out of stack space before HALting...

Haha! Last will and testament... love it. :) That's not a bad idea if stack problems are suspected, will pop that onto a shelf in my memory somewhere and hopefully not forget it. ;)

The other usual trick I do (!) is to PUSH more element than I POP off the stack (i.e. I forget about something). The stack gets lower and lower and ... lower and lower and ... crash!

Dave

Oh yes, I've done that a few times when writing the monitor program for my SBC. I borrowed from the NASCOM BASIC source code to get a full command-line interpreter up and running and whilst writing various commands and utilities for the monitor program, I was having issues with some commands not handling the stack synchronously depending on how they were called (internally or by the user via the CLI) - took a while and a few paracetamol to solve those issues. ;)
 
That's interesting...

You did get the prompt at one attempt!

So, assuming you never enabled the interrupts like I suggested, this would have been expected behaviour!

The "No space" message implies that something is still wrong with the disk geometry or something somewhere.

Can I suggest the following?

Modify the console status and input/output operation of the BIOS for the time being to be polled rather than interrupt driven. This should allow you to enter CP/M commands (on the odd occasions you do get the prompt) and this may allow us to get a bit further. We can then update to use interrupts later. That will inevitably produce its own problems...

I know it will be a 'ball ache' if it only gets to the prompt 1 in every 6 ish times - but it will allow progress.

In the meantime, we can check the current BIOS source and see if we can track down the problem with a consistent boot.

I notice that it still read the CCP twice - once into bank 0 with a 7-sector read and then into bank 1 with a 6-sector read???

It may be useful to know which track, sector, bank and transfer address is being used for each disk transfer (i.e. expand the diagnostic information at the beginning of every sector read).

Dave
 
That's interesting...

You did get the prompt at one attempt!

So, assuming you never enabled the interrupts like I suggested, this would have been expected behaviour!

The "No space" message implies that something is still wrong with the disk geometry or something somewhere.

Can I suggest the following?

Modify the console status and input/output operation of the BIOS for the time being to be polled rather than interrupt driven. This should allow you to enter CP/M commands (on the odd occasions you do get the prompt) and this may allow us to get a bit further. We can then update to use interrupts later. That will inevitably produce its own problems...

I know it will be a 'ball ache' if it only gets to the prompt 1 in every 6 ish times - but it will allow progress.

In the meantime, we can check the current BIOS source and see if we can track down the problem with a consistent boot.

I notice that it still read the CCP twice - once into bank 0 with a 7-sector read and then into bank 1 with a 6-sector read???

It may be useful to know which track, sector, bank and transfer address is being used for each disk transfer (i.e. expand the diagnostic information at the beginning of every sector read).

Dave

Righto - re: changing the console IO to be polled instead of interrupt-driven... Where do I start with that? If I keep the interrupts disabled, data will still stream into the SIO - so how do I tell CP/M that it needs to poll const: regularly and call conin: if there's anything in the buffer? Not sure where to start with that.

I'm adding extra debug info to the rest of the BIOS at the moment - where would I get the extra info from that you require? setrk:, setsec:, setdma: and setbnk: look like good candidates?
 
Right, well, unfortunately due to the above accident (clearing my A drive) I now have no way of getting files into CP/M. This is a problem almost as bad as bricking a phone or tablet. The original method I used to get the file into CP/M (DOWNLOAD.COM) that is used to transfer all subsequent files to CP/M unfortunately no longer works, as I have modified and improved the hardware of the SBC to the extent that the old method will no longer work. I will have to have a think about this and write something in the monitor program to transfer an Intel Hex file to CP/M in the RAM.

Yes, I could have (and should have) backed up all the A: drive files into another drive (B: and C: are fine, but there's nothing in them that will me with this problem.)

So further development of the CP/M 3 BIOS will have to go on hold until I can sort this issue out. I'm just off to bang my head against a wall for a bit...
 
Sorry to hear about the accident :-(.

CP/M will poll the BIOS for characters anyhow - so there is nothing further that you need to do to to persuade it to do it!

Yes, all the existing entry points into the BIOS provide the necessary debug information. If you think about it, they are the same data points you need within the READ routine to actually read a sector in the first place.

Dave
 
Ah, that's a shame, I am following this thread to see if I could manage to get CP/M running on my homebrew Z80 VGA computer.. I have placed a DiskOnChip 2000 with a simple bootstrap that redirects to the (Z80) boot/monitor rom on the VGA card.

I would love to get CP/M on that: http://kgelabs.nl/?p=147

Hope you can manage to get it up and running again
 
Last edited:
Sorry to hear about the accident :-(.

CP/M will poll the BIOS for characters anyhow - so there is nothing further that you need to do to to persuade it to do it!

Yes, all the existing entry points into the BIOS provide the necessary debug information. If you think about it, they are the same data points you need within the READ routine to actually read a sector in the first place.

Dave

Hmm - well the accident was as result of the most recent CPMLDR attempts - not something I did in error. :/

Anyhow, I've got a workable solution (I think) - I would just like to pick your brains first, Dave, before I try it?

The code snippet below is a new routine I'm working on in my SBC's monitor ROM code. The notes say it all, really, but what I'm trying to achieve is to copy a block of code from 4100H-42BFH to 0100H in RAM Bank 0 (which is 0000-3FFFH for CP/M). The DMI (monitor ROM program) runs in Area 0 (0000-3FFFH), so I've had to copy a small program to 5000H which will swap Area 0 with the DMI ROM to RAM Bank 0, then copy 4100-4300H to 0100-0300H.

This routine is run from the monitor ROM, AFTER CP/M has been successfully loaded and the system soft-reset. So CP/M is still in memory, but the monitor code takes an Intel Hex stream (the DOWNLOAD.COM file I mentioned earlier) and puts it into RAM at 4100H. My new routine will then switch out the ROM and map the low memory for CP/M back in, whilst the program running from 5000H relocates the Intel Hex data from 4100 to 0100h. This is essentially identical to the method Grant Searle uses in his minimal SBC design - but mine uses banked RAM which is why his method will no longer work on my system.

When this is done, I can follow the old method to get DOWNLOAD.COM onto the drive - basically just by typing 'SAVE 2 DOWNLOAD.COM'.

That's the plan, anyway. But as my new bit of code requires a bit of shuffling of execution code, stacks and address maths, I thought I'd run it past you first for your opinion. :)

Code:
;------------------------------------------------------------------------------
; INSTALL
; Copies install_prog to 5000H and passes execution to it.
; install_prog sets up its own stack, maps Bank 0 to Area 0 (switching out the
; ROM) and copies RAM from 4100H-4300H to Bank 0, 0100H-0300H before passing
; execution over to CP/M (which needs to be resident in memory already).
;------------------------------------------------------------------------------
INSTALL:		
				POP			HL						; Dump CLI pointer from stack
				LD			HL,install_prog			; Source address
				LD			DE,5000H				; Destination address in RAM
				LD			BC,[install_stack-install_prog]
				LDIR								; Copy
				JP			5000H					; Jump to install_prog in RAM
				
install_prog:	LD			SP,5064H+[inst_stack-install_prog]
				LD			A,$38
				LD			C,A
				XOR			A
				OUT			(C),A					; Map Area 0 -> Bank 0
				LD			HL,4100H				; Set source address
				LD			DE,0100H				; Set destination address
				LD			BC,0200H				; Set copy size
				LDIR								; Copy
				
inst_stack:		.EQU		$
 
Forgot to mention - haven't added the last part where it jumps to E600, which hopefully will restore control to CP/M and allow me to access the CCP and save the DOWNLOAD.COM file.
 
Ah, that's a shame, I am following this thread to see if I could manage to get CP/M running on my homebrew Z80 VGA computer.. I have placed a DiskOnChip 2000 with a simple bootstrap that redirects to the (Z80) boot/monitor rom on the VGA card.

I would love to get CP/M on that: http://kgelabs.nl/?p=147

Hope you can manage to get it up and running again

Don't worry gertk - will have it up and running soon (with a little luck). I've got a plan to get the key file I need onto my CP/M drive. Once that's working, I'll have it up and running again in no time.
 
Just a word of caution, if you are doing what I think you said. By loading a program into 4100H and then mapping that into area 0000H, you are no longer operating with the standard memory map. You will have to work out a way to restore the original memory or communicate that "non standard" memory map to CP/M so that it knows what it is. Too bad the hardware doesn't support "write under ROM" or "direct bank-to-bank copy" so that you can write something into memory at 0000H while the ROM is still running there.
 
On a related subject, can anyone tell me what the advantage of CP/M 3.0/Plus is over CP/M 2.2? You're still restricted to a single task; you get a few more features that few programs take advantage of, and your TPA memory footprint can be somewhat larger. Have I missed anything?

I've got a couple of machinea that run 3.0 and, for the life of me, I can't figure out what makes it worth the trouble. You don't need CP/M to manage the extra memory used for a RAMDisk...

MP/M is a different kettle of fish--mutli-tasking, multi-user.
 
Just a word of caution, if you are doing what I think you said. By loading a program into 4100H and then mapping that into area 0000H, you are no longer operating with the standard memory map. You will have to work out a way to restore the original memory or communicate that "non standard" memory map to CP/M so that it knows what it is. Too bad the hardware doesn't support "write under ROM" or "direct bank-to-bank copy" so that you can write something into memory at 0000H while the ROM is still running there.

It shouldn't be an issue - hopefully. Here's how it works, step by step:

1) SBC powers up into monitor ROM. Memory map at power-on is Area 0 = Bank 0F, Areas 1-3 = Banks 1-3. The ROM monitor uses 4000-40FF for stack and various static vars.
2) User runs CPM. This switches the ROM out by mapping Area 0 to Bank 0 (RAM) for 64K of contiguous SRAM.
3) CPM 2.2 loads to CCP.
4) User soft-resets the computer (memory is intact) and as step 1 above, the monitor ROM loads up (as Bank 0F in Area 0.)
5) User uploads the Intel Hex format data for the CP/M file DOWNLOAD.COM. This is written to RAM (Bank 1) from 4100-42BFH (this is defined in the Intel Hex file, not of my making.)
6) User runs the new routine I've created in the ROM with the appropriate command.
7) As per the code above - the routine copies the main program to 5000H and executes it (this is in RAM Bank 1).
8) Running from 5000H, the ROM is switched out and Bank 0 mapped to Area 0 again.
9) The CP/M memory map is now intact again - with the only difference that the code for DOWNLOAD.COM is still at 4100H, the currently executing code is at 5000H.
10) Currently executing code moves/copies DOWNLOAD.COM from 4100H to 0100H, and then passes execution to CCP with a jump to E600H.
11) User creates the DOWNLOAD.COM file by typing "SAVE 2 DOWNLOAD.COM".

This is a modified version of the instructions provided by Grant Searle on his website to get the initial DOWNLOAD.COM file into CP/M on his SBC (which I was able to use initially as my SBC didn't start out with an MMU). His SBC doesn't have bank switching, so I've had to modify the process to work with my SBC.

The only things I'm not 100% sure about are the code I've written - I'm hoping I've not made any silly mistakes - and the jump to E600H. As far as I can tell, it's what Grant's code did.

If I can get this working and get DOWNLOAD.COM onto A: in CP/M, I can easily restore the filesystem that was erased. Then I'll back it all up to D: drive so if this happens again, I won't have this problem! ;)
 
Okay, all sorted. Will get CPMLDR and CPM3.SYS compiled tomorrow. In the meantime:

CP/M will poll the BIOS for characters anyhow - so there is nothing further that you need to do to to persuade it to do it!

Soooo... CP/M should continue polling the BIOS anyway and I should be able to type chars into the CCP even with interrupts disabled?
 
On a related subject, can anyone tell me what the advantage of CP/M 3.0/Plus is over CP/M 2.2? You're still restricted to a single task; you get a few more features that few programs take advantage of, and your TPA memory footprint can be somewhat larger. Have I missed anything?

I've got a couple of machinea that run 3.0 and, for the life of me, I can't figure out what makes it worth the trouble. You don't need CP/M to manage the extra memory used for a RAMDisk...

MP/M is a different kettle of fish--mutli-tasking, multi-user.

Err... good question. For me it's as much to do with the challenge as anything else. That and the fact I spent a week designing and building an MMU for my SBC to use the entire memory space available, so it's natural to want an operating system that can make use of it too.

EDIT: System is back to how it was now, with CPM3.SYS and CPMLDR.COM assembled and ready to go. I've also copied the entire A: drive to D: now, so if the same bug occurs and wipes A: drive, I won't have this issue again (not that it will be an issue next time as it's forced me to solve a potential thorn in the project anyway!)
 
On a related subject, can anyone tell me what the advantage of CP/M 3.0/Plus is over CP/M 2.2? You're still restricted to a single task; you get a few more features that few programs take advantage of, and your TPA memory footprint can be somewhat larger. Have I missed anything?

I've got a couple of machinea that run 3.0 and, for the life of me, I can't figure out what makes it worth the trouble. You don't need CP/M to manage the extra memory used for a RAMDisk...

MP/M is a different kettle of fish--mutli-tasking, multi-user.

Having spent a lot of time doing code and document development in CP/M, I can say from first-hand experience that CP/M Plus is a much more pleasant environment. True, most applications are not specific to CP/M Plus, but the extra TPA is a big benefit for compilers and word processors. And the better suite of CP/M utilities is also a major benefit. Re-editing of the (previous) commandline is also a big help, although it takes awhile to get proficient with the edit commands (keys) - I find these days I am too ingrained in the bash command editing and thus have not re-learned CP/M Plus command editing. Being able to use timestamps on files is also handy, and better handling of large disks and files was nice.
 
Back
Top