• Please review our updated Terms and Rules here

CP/M 2.2 Source Code

You have the opportunity to avoid the legacy artifact of using logical and physical sector numbers via that table. As the FDC you're using is capable of formatting with interleave sector numbers, you'd be doing yourself a favor to implement any unique format via that method. To be backward compatible with existing system formats, you'll have to use the table if they did.

Logical sectoring implemented interleave in a time when floppy controllers could only format tracks with sequential sector numbers. That practice should have been stamped out a lot sooner as the FDCs quickly allowed physical interleave. Made sense when they did it, didn't make sense a couple of years later and ever since.
 
...but if you're interested in keeping compatibility with standard SSSD 8" DRI 8" format, you'll want to keep that definition intact. Seeing as how your rig is sui generis that'd be very advisable. Lots of systems with all sorts of strange formats were equipped with the ability to use A1 format. As a matter of fact, at the time, Gary Kildall identified A1 as the only standard CP/M format.

If you do decide to invent your own format, but want to keep A1 as well, the easiest thing is to format with track with ID headers that are different in some way. For example, instead of using 0 for a head number, use 64; the 765 doesn't care--then you can determine the format type with a simple READ ID operation on the first track.
 
Thanks for the information regarding the Disk Parameter Block. I think, I understand each one of them now, after I read them a few times. I also found a book the "Programmers CP/M Handbook" which has a nice section on this also. After reading this and understanding it, I don't think that this is where my problem lays. As for keeping the A1 format. For now I want to keep as much standard as possible. I'm having enough trouble with getting it to work, much less souping things up even before I have the dumb thing working. Maybe after I have it working then I can look at some of the bells and whistles. The current problem is when CP/M calls my HOME routine it fails, yet when my CBIOS calls it, it works fine. I'm sure that the problem is in the CBIOS. I've corrected a number of errors in it so far and found some pretty neat programming in the body of the CP/M that I have never thought of before. I'm sure with more time, I'll find the problem and make some more progress. Thanks, Mike
 
Should the CBIOS home routine need to do anything other than call SETTRK with 0? And SETTRK doesn't do anything other than record the track number. You don't have to actually do any seeking until you're ready to issue the READ or WRITE.
 
Yeah.... it's really exasperating. HOME just sets c=0 then calls SETTRK which saves c to a word called TRACK, but then I call seek right away, so I don't forget. I thought that the physical track has to be correct before a read or write is done. I've got some ideas to check out in the next couple of days. Mike
 
Mike_Z said:
...HOME just sets c=0 then calls SETTRK...

CALL SUBR
RET

- - - - converted to
JUMP SUBR

While its good to use nested CALL SUBR & RET pairs when writing the code to avoid stack leakage or to learn to write stack balanced code, when its obvious or as a final step to modify working code, its useful to "flatten-out" stack usage by turning CALL SUBR & RET into a simple JUMP SUBR to avoid using unnecessary stack and speeding up the code by dropping 4 stack operations, and also saving one BYTE of code space.

Using the JUMP SUBR instead, the RET in the subroutine will serve the same purpose as the original RET that followed immediately after CALL SUBR.

A flatter stack is always more efficient. A flatter stack wastes less time doing pushes and pops of return addresses that are superfluous and thus contribute to faster code. It also lessens the amount of ram memory required for the stack to operate. And lastly it removes a useless RET opcode (1 byte) from the code.

Note that conditional returns wouldn't be the same automatic construct to replace. If there is code that has to follow the CALL SUBR, then the call is necessary unless its a case where pasting short inline code to replace the CALL, makes sense.

BIOS is not real-time code so its not terribly important, but its a good firmware practice to understand and put in your toolkit.
 
Last edited:
Dallas, funny you should mention this. I have, in the back of my mind, that maybe the stack was over running something. I agonized over where to put the initial stack and decided to use my Monitor stack area, because it is out of the way, but, CP/M reassigns the stack to different places. Since the trouble is not repeatable, what I mean by this is sometimes the CP/M Call home works and then I get a 'Bdos Err at Y: Select', others the home doesn't work and the head is moved to a higher maybe 2 or 3 track and the program gets stuck in a routine that is waiting for the FDC to be ready for output. I would think that unless the code sends the program out into the 'pucker brush', I would think the program end would be consistent. I'll look at what you suggested, thanks, Mike
 
Yeah.... it's really exasperating. HOME just sets c=0 then calls SETTRK which saves c to a word called TRACK, but then I call seek right away, so I don't forget. I thought that the physical track has to be correct before a read or write is done. I've got some ideas to check out in the next couple of days. Mike

It's always been unclear to me why the disk BIOS primitives were done the way they were. Perhaps there was a thought that some processing might be done while seeking and even when that turned out not to be true, it was kept for old times' sake.

In fact, it's not clear why physical disk geometry has any role in CP/M at all, when it's simply ignored for most things. It would seem to have been better to pass a relative block and let the disk driver figure out what needed to be done.

In any case, since CP/M isn't a multitasking OS, there's nothing to be gained by not deferring seeks until a read or write operation is being called for. Been there, tested that.
 
The one situation where I believe that physically seeking track zero in the HOME call is a good idea is for drive/controller combinations which do not store track meta-data in the sector headers. For example, the NorthStar hard sector format does not keep a track ID on the disk. After a seek operation is done, there is no way to know if the head is really on the correct track.

CP/M does a HOME operation prior to searching through the directory. If track IDs are not present on the media, then by physically seeking track zero during the HOME call, the BIOS provide a safe and frequent track recalibration mechanism.

With drive formats which do store track IDs, then seek retry logic in the BIOS typically does a hard restore to track zero whenever the track ID doesn't match the target track number. In this case, physically seeking track zero during a HOME call is not as important.

Mike
 
Yes, perhaps, but both the Intellec and the MDS used standard 3740 single-density disk formatting, in which the track (and head) are encoded in the address headers (as well as the IAM), so why would Kildall think that a HOME function was necessary, as this system was what he used for development?

I'm a bit too lazy to investigate, but did the original IMSAI 8080 2-card controller record track IDs in headers? Since CP/M 1.3 was one of the first versions distributed, that might explain it. I don't recall if 1.3 checksummed the first few directory entries to detect disk changes.

In any case, I wonder if doing anything but returning from a HOME call has any purpose. Given that the system boot area is on cylinder 0, there's no good reason for CP/M to read it--and I believe that any subsequent call for disk I/O would be accompanied by a SETTRK call in any case.

What I find even more surprising is that there was no "Veriy" API in CP/M. That is, there's no way to verify the integrity of a disk sector without allocating memory and reading it.
 
Last edited:
I've been doing a little reading. Point out any item that I may be mistaken on. The CP/M System is on tracks 0 & 1 of the disk. The Directory is on Track 2 Sectors 1 & 2. The rest of the disk is transient program file space. Each directory entry is called an extent and is 32 bytes long. It contains the user number, file name and extension. The extension has addition stuff in the seventh bit of each extension byte. It has the extent number, which is the number of the directory entry, records or sectors used in the last extent and the allocation blocks used. When CP/M works with a file, it creates a File Control Block FCB which comes from the file extent. But what parts of the extent are used, or is the entire extent copied, modified and replaced when the file is closed. Still reading, Mike
 
Only two sectors for directory? That makes it 2x 4entries (each 32bytes long), e.g. max. 8 files or less if they occupy more than one extent.
 
Well..... I'm still having trouble with CP/M calling my CBIOS HOME routine and getting it to work. So, I did this test, the last item in my CBIOS is to call BOOT. So I changed that to

LXIB 060 000 (OCTAL) 48 decimal
CALL SETTRK --- this is my CBIOS routine which sets TRACK to the number selected and then asks the FDC to SEEK (my CSEEK command in CBIOS) to that track.
CALL HOME --- this is my CBIOS routine which sets TRACK=0 and then asks the FDC to SEEK (my CSEEK command in CBIOS) to that track.

This works repeatedly, with no errors at all. Yet when CP/M calls HOME, the drive head will advance one track (at least it looks like it does) and everything stops after that.

Looking through the CP/M code, the BOOT will jump to the CCP, which first sets the user number. This works, the function call 32 does what it should and returns. Next there is a function call 13 which resets the disk system. This clears the write protect and login flags, selects A: as ACTIVE and sets the DMA Address to 000 100 (OCTAL). It next calls LOGINDRV, here a routine called SELECT runs, which call my CBIOS SELDSK routine, copies DPBase to BDOS storage, copies DPBlock to BDOS storage, then looks at the disk size in blocks to determine whether the disk is large or small, not sure about this, and returns. This works I have checked that the Parameter block data is transferred to the BDOS storage spots. Next there is a call on zero to a SLCTERR, this routine will print the message 'Bdos Err on Y: Select" error that I some times (not always) get. If the selected drive is not valid this error will occur. So, I figure at times the defined word ACTIVE maybe corrupted at this point. Next, if I get past the Select error, a SETBIT routine is called, which sets bit in BC depending on the slected drive number. This works. Next a routine called BITMAP is called. Here I think the directory is read and the bitmap is the location of the allocated blocks is made. In this routine, there is a call to HOMEDRV, which gets to my CBIOS Home routine. This has never worked. All I can think of is that either the DRIVE number or the TRACK number has been corrupted some how.

My Cold Start Loader uses a bunch of defined bytes and words, that at first I had stored in the WAITIO space of the CBIOS, but I moved them to another spot in memory so they would not or could not be overwritten. The only spots that my CSL uses now is TRACK (@4CE9H 114 351), SECTOR (@4CEBH 114 353), DMAAD (@4CEDH & 4CEDH 114 355 & 114 356), and DISKNO (@4CEFH 114 357). I do wonder what the CDISK at 0004H is for. The BOOT will write a zero here and then jump to GOCPM, but then doesn't update DISKNO. DISKNO will not change until CP/M calls my CBIOS SELDSK during the reset disk system function call. Sorry that I'm a little wordy here, but it's hard to explain otherwise. So far I'm stuck. Mike.
 
Last night I think I found an error in my copy of CP/M 2.2 code. In the GETUSER FUNCTION call code, the first item is to check the parameter to determine whether to 'Get User' or to 'Set User'. Register E should be set to 377 (FFH) to get user else it will set user. For some reason the code loads Register E into the Accumulator, then does a Compare Immediate, with what should be 377, but it's 061 (31H). I don't think that this is a direct cause of my problem, where the CBIOS HOME doesn't work, but now I'm suspect of my copy of the CP/M code. Could an error have been introduced while I converted the code from a file to my 8" disk, is my media bad, I don't think my CBIOS code is bad, I tested it a bunch of times for read/write accuracy. Anyway, now I'm concerned that maybe there is another bug somewhere in the code that is sending my machine off into the 'Pucker Brush' at some inappropriate time. I'll have to look at the code a little closer for correctness. Mike
 
Well.. I was led astray on the HOME routine not working, it does. I blocked off some of the code and the HOME works fine. What was actually happening was CP/M did the call to HOME (which worked) then began working on setting up to read the directory extents. A call to move to track #2 was made and this is actually where the failure occurs. The head moves to track #2 after it goes home. This happens so fast I could not see it. After the call to the CBIOS seek track #2, which works, I get, consistently now, the 'Bdos Err on Y: Select' error. Some how the Drive Number is being changed during the seek from 000 Drive A to 030, Drive Y.

Over the weekend I was concentrating on studying stack usage. It appears that there are multiple stacks. The CCP has a stack, CCPSTACK and the DBOS has a stack STKAREA. There is also a place to store the users stack pointer. Apparently, I'm guessing that when a user program calls a function call, maybe CP/M saves the userstack and returns it after the function call. I also noticed that the CCPSTACK is only 8 levels (2 bytes) where as the BDOS STKAREA is 24 levels (2 bytes). I'm working on the idea that the stack is overwriting something and causing the DRIVENO to change. But I'm a little confused as to which stack is being used at the time. My Cold Start Loader begins with it's own stack pointer to load the first 2 tracks, then branches to BOOT. I do not know which stack is being used when the directory seek call is made. I do know that the CBIOS calls are 6 levels deep, and there is a PUSH of B,D & H just prior to the call, besides all the other CP/M calls that were made to get to this program spot. I'm going to try temporarily removing some of the seek calls in my CBIOS just to see if the DRIVENO doesn't change. I know there will be other problems, but. Mike
 
You don't clobber location 0004 by any chance do you? That holds the current drive and user number. Clobbering that will produce the symptom you describe.
 
I didn't check that, next time. What I did do is remove the seek call in SETTRK and this prevents the change in DISKNO. So I think my options are 1. move the seek to occur just prior to a read or write, but that probably will only delay the problem until a read or write occurs. Or 2. look at reducing the calls in my CBIOS seek routine. Mike
 
Back
Top