• Please review our updated Terms and Rules here

RAMdisk under CP/M 2.2

I keep whittling away at reducing the circuitry down to the bare minimum. I read through the Z-80 manuals for the 1000th time and noticed how the address is stable for much longer than the /IORQ and the /Read lines, so I configured the address decode '138 to only look at address lines. The /IORQ and either the /Read or /Write controls lines run through an OR gate, and then to either the RAM chips of to the latch pins on the three '374 chips. I'm also going to get rid of the defensive circuitry before the '138 chip select decoder. I've also been looking at a few other things, that in retrospect I think a better design would have been to use an 8255 for the three address latches. oh well, there is always the S-100 version of this at some point.
If you keep the chip selected, as per that schematic, and read and write are not gated via a select line as per your original design, you will end up with valid write signals while a RAM is selected, when it should not be, resulting in corrupting the RAM chip not only with other I/O writes, but with memory writes as well.

Hence I/O request and an appropriate decode line really does need to feed into the enables on U10.

Regards
David
 
If you keep the chip selected, as per that schematic, and read and write are not gated via a select line as per your original design, you will end up with valid write signals while a RAM is selected, when it should not be, resulting in corrupting the RAM chip not only with other I/O writes, but with memory writes as well.

Maybe I'm just getting confused by the wording, but... are you saying that the chip receiving a read or write signal when it's not selected will corrupt it? That shouldn't happen. If your address select circuitry only pulls down CE when the chip should be active then it doesn't matter if read and write go low when the chip is not selected, it's going to ignore it. Conversely, even if the chip is enabled (which it would be if you don't qualify on /IORQ for the decode and you access a RAM address that has the lower 8 bits matching the port address) it's going to keep its bus tristated unless either /WE or /OE go low...

Oh. NM, I see the problem here. You're right, the way this is set up the CE of the RAM chip that is being addressed by the U07 latch is *always* on so it will indeed get clobbered; not on memory accesses because the chip's WE/OE lines *are* qualified on /IORQ, but it will get wasted during other I/O operations.

The easy fix here is to attach one of G2A/G2B of U10 to that enable line that's running between U12 and U17, so the RAM chip is only active when the buffer is. That should work fine; the chip will only be *awake* to handle /WE or /OE when you're accessing the 8 bit address for Port 44, and it won't trigger on memory accesses because /READ and /WRITE are qualified on /IORQ.
 
@Eudimorphodon , that is correct. Myke's earlier designs originally either gated RD and WR via IORQ and the Select lines, or used the Select decode to power the chip select.

The earlier designs also had a race condition, in which the decode and latch operations for the bus and for the RAM were decoupled, so that it was likely the bus would tristate before the RAM latched. That may or may not have worked, but it's always a good thing to avoid.

The problems seem to stem from an oscillation originating in the address and write lines on I/O write operations. I think most recently Myke was moving the I/O and other items to something that should work and I think the assumption was that the fault was originating elsewhere in the architecture.
 
We have a winner! This design seems to be working. Granted I have not hooked a scope to the enable or direction lines on the '245, but i haven't seen one erratic bit yet. So, after a gazillion design mods, this might be a golden design. Now I have to run some tests to put it through it's paces and then start writing code to actually use it....... I never thought I would see this day arrive. And this is due in no small part to all the help you all have given me. Thank you all.

1683334909194.png
 

Attachments

  • MemoryDiskv7.pdf
    86.2 KB · Views: 3
Awesome ! Most of what you were doing was pretty close to correct anyway, and probably would have worked under other circumstances... though hopefully you can find the original cause at some point also .

Nice work - Looking forward to see you getting it running. Have you thought about how to interface it to the BIOS? BIOS hooks should be relatively easy due to the jump table and all you need to do is locate a safe piece of memory near the CCP and redirect the hooks for reset, sector, track, read and write there, then just fill the disk the E5 bytes and it's formatted and ready to go with the OS. The code itself should be fairly tight since you only need to translater sectors and tracks and set up the latches, then READ and WRITE are basically just INIR and OTIR instructions before exiting. And hook SELDSK so that your routines know when to take over, and when to hand the result back to the BIOS for the original drives.

David
 
Thanks, but you all deserve a lot of the credit as well. After I get done putting the board through a good and complete memory test and call that test program 'golden', it is going to be off to start writing the MDFRMT program to format the thing. Then write the MDSAVE program to put a file on the thing; correctly! Then MDDIR to list the contents, then MDLOAD to put the contents in memory to execute, and finally MDERA to delete.

As for integrating into CP/M; my plan with that is to do that on the bigger S-100 system I am working on next. I want to put CP/M 3.0 on that one. S-100 boards have more real estate that STD boards, so I can add more memory to it, and I'll used NVRAM chips since I can get them with the same pinout as the SRAM I'm using now and they are inexpensive and can retain what is stored in them.
 
One suggestion on "formatting" the ramdisk, all you really need to do is put a 0xe5 every 32 bytes in the directory area. There is no need to fill every byte of all of it with 0xe5. For my CP/M 3 ramdisk, I check for a "signature" at the start of the directory area, that being a CP/M 3 directory label, which tells me whether the ramdisk needs formatting, then I copy the label data into the first location of the directory, then go through and place a 0xe5 at every other +32-byte location until the end of the directory (i.e. counting DPB.DRM+1 locations, including the label).
 
I don't think Myke's planning on using a CP/M format Doug - I think he's using the RAM disk like an archive - a bit like a single file space or a LBR file that writes to memory rather than a disk file.

It's a lot more work than making it a real RAMDisk using CP/M, since then he could use the built in CP/M commands to do it if he hooks the BIOS. Functionally, he's rewriting the BDOS and CCP himself like I did, except perhaps might make it proprietary while I went for compatible. But just the operations he describes are no less than an entire operating system within an operating system even if they are only archival in nature, since they not only move files, but can also execute them.

Just hooking into the BIOS jumps would be the easiest way, since all the hard work is done by the OS then - all he would need to do is translate the details for sector/track and write the transfer routines to and from the DMA areas. But I recall he did mention uncertainty about which way he would take his project way back at the start of it, and I get the feeling he intends to do it both ways in the long run.

Though if Myke changes his mind and goes with a CP/M RAMDisk to test it initially, it would save him time. And Myke, if you're not familiar with writing the Disk Peramater Block and Disk Perameter Header tables, I would be happy to assist with those since I'm currently reasonably current and familiar with them - :)

Either way, the next step in his project would be to start coding up the drivers, whether they work through CP/M or aside from it like LBR.

Oh, and what is the "Directory Label" in CP/M 3? I've seen some labels, but must have missed that since I focussed mainly on 2.2? I've seen some files in the Amstrad disks like that, but I thought it was something proprietary to Amstrad?
 
I don't think Myke's planning on using a CP/M format Doug - I think he's using the RAM disk like an archive - a bit like a single file space or a LBR file that writes to memory rather than a disk file.

...

Oh, and what is the "Directory Label" in CP/M 3? I've seen some labels, but must have missed that since I focussed mainly on 2.2? I've seen some files in the Amstrad disks like that, but I thought it was something proprietary to Amstrad?
Everything so far indicates this is intended to be a storage device in CP/M, with the discussions about organizing it as tracks and sectors, etc. So the point being that the initialization of the directory is all that's required, and only the first byte of each directory entry need be set to 0xe5. That significantly reduces the time and complexity of the disk initialization ("formatting").

The CP/M 3 manuals describe the directory structure. A disk label is a special directory entry that contains a name and optional configuration data for the rest of the directory (such as enabling time stamps, which are kept in their own special directory entries). The label was a convenient way to put a "signature" on the disk that could be checked to give some indication of whether the directory was already initialized. I believe there has been talk of battery backup or NVRAM, but even if not one does not want to wipe out the disk contents every time you reboot.

If it is just being used as bulk storage, then of course none of this matters.
 
I'll also add that the CP/M 3 directory label entry was designed so that it is ignored by CP/M 2.2. So, that can be used regardless if whether one is using CP/M 3.
 
Well, eventually, this design concept would get integrated into the CP/M 3 system I'm putting together next. That is going to be a 4MHz Z-80. My current STD system is more a development system to re-educate me, learn how CP/M works, and spawn ideas. Granted, this proprietary file system I'm putting together will probably end up in an archive folder somewhere, it will be a great help along the way. One idea I had today was to use that RAMdisk (w/NVRAM) as a primary boot device. Why not? Address lines aren't much different than track, sector, and byte locations on a hard disk or floppy. The only thing I don't use are the other bits that the disk drive or controller card uses.
 
Hi Doug, is there a convenient reference to the format of the directory label you can point me to? I would like to better understand it.

Myke -That would most certainly work - I have done the same - and I boot off of mine also. I call mine L: originally for Local, but since it's called LokiOS it seems L: is a good choice. Likewise M: is my memory map drive.

And real CP/M has no trouble reading and using both as disk space, though in the long run, L: is an EPROM based drive, and N: will likely be a RAM mapped NVM. So your idea is very parallel to what I already did, except mine is running in an emulator while you've finished your hardware already.

If you did want to use the CP/M system to run your RAMDisk, you don't really need to understand CP/M to do it - hooking the jumps in the BIOS is pretty easy.

SELDSK is the first one you need to hook, since it is responsible for Disk Selection and will know whether to pass the system back to the BIOS or handle the call locally.

The code to hook it goes something like this;

HOOK_SELDSK: SELJMP: DW 0000 ; reserve space to store the new jump vector. BIOS EQU $FB00 ; Wherever the BIOS starts. BIOS-SELJMP EQU $FB00+24+4 ; The Seldsk offset is often called 24, +3 since the bios counting system offsets by 3 due to the WBOOT hook at 3 being considered zero, and 1 for the jump location. LD HL,(BIOS-SELJMP) ; Get the current seldsk vector. LD (SELJMP),HL ; store it to jump back to later. LD HL, MYROUTINE LD (BIOS-SELJMP),HL ; Put your own vector into the BIOS. ret ; And it's hooked. MYROUTINE: (Save the stack, relocate it, don't corrupt C or E registers) LD A,C CP $MYDISK ; Are they selecting my disk? JR Z,MYDISK LD HL,(SELJMP) ; Retrieve the return vector to continue on to the BIOS. PUSH HL RET ; And jump there from the stack because I can't remember the Jump (HL) syntax. MYDISK: (Set some flags so my other hooks know to process this directly ) (Initialise anything I need to... ) (Store which of my disks is being accessed ) LD HL,MYDPH ; Return HL with address of new DPH for RAMDisk. ret MYDPH: Disk Parameter Header here... MYDPB: Disk Parameter Block here...

You would hook all of the disk related jumps in the same way. Most of their routines would just take the values of registers, maybe manipulate them a little, and send them to your address latches for the RAMDisk. There would be one to read 128 bytes and transfer it to wherever was specified in the DMA call, and one to take 128 bytes from the DMA location and transfer it to your card.

That's about it -

David
 
Last edited:
The CP/M 3 Programmer's Guide is a good source for information on the disk layout, particularly section 2.3 BDOS File System. Be aware, this document describes both the in-memory objects (as used by the BDOS calls) as well as the on-disk objects (directory entries). The most notable place those differ is the FCB (36 bytes) vs. directory entry (32 bytes).

As a possible example, here's what I do on a Z180 (1M RAM) system, using a directory label to determine whether the ramdisk has been initialized. These systems do have battery backup. Of course, this RAM is regular memory to the CPU. But, the idea of using a directory label applies in any case where the storage might not be initialized prior to use, regardless of whether it is being automatically initialized or just detecting uninitialized data.


As usual for all my work, this code uses Intel mnemonics.
 
Note that there are several obstacles faced when trying to add a BIOS extension dynamically, particularly on a CP/M 2.2 system. Because the CP/M 2.2 system components CCP, BDOS, and BIOS are all in fixed locations, and (CCP and BDOS) are typically reloaded on warm boot, you must deal with the problem of how to maintain your add-on module through warm boots. You also have issue with loss of TPA, especially if you try and use the CCP in it's original location (instead of replace it with one relocated to a new address). You also should be altering the JMP BDOSE at location 0005h so that programs are not confused by a BIOS address that is *below* the BDOS.

One example of a similar "system add-on" was CP/NET. CP/NET adds two modules below the current BDOS (SNIOS and NDOS in this case), adjusts the JMP at location 0005h and also intercepts many BIOS functions including warm boot (by altering the BIOS JMP table). CP/NET also comes with a replacement CCP devilered as CCP.SPR which is relocatable, and the new warm boot code loads that into whatever location is appropriate. The NDOS also becomes the new address at location 0005h, so that programs know where the (smaller) TPA ends. In the case of CP/NET, this intercept is part of the active NDOS, but in some cases you may need to simply use it as a springboard to the real BDOS as a way to make certain location 0005h reflects the smaller TPA.

If you have source to your CP/M, none of these (questionable) tactics are need, you just modify the BIOS to handle the new device(s).
 
I never thought about changing the BDOS hook to protect the driver code.... Thanks for mentioning that - I've learned something useful. :) That's a great idea.

Rewriting the BIOS to simply support the RAMDisk is the best idea though as you say.

@MykeLawson , you never mentioned whether you wrote your own BIOS or whether you reused another? Doug makes a good point that if you can rewrite your BIOS and reassemble your BDOS and CCP for new locations, then you don't need to hook the BDOS or play games with the OS.
 
I am using the CPUville (http://www.cpuville.com/index.html) CPU card and BIOS. His stuff is very well documented and he provides all the code right from his site, except for the actual CP/M 2.2 OS. But, after building the card, and going through the steps in his documentation, I was up and running without any issues. I will probably use the same Monitor and CP/M switching architecture he uses when I build out my S-100 version of things. I will probably change some ports around, but keep the same concept. I will certainly decode the ports more thoroughly to make sure the upper four bits of each port are decoded and not left empty.

From what I have seen in his code and instructions, I should be able to change from a IDE based CF Card storage to a port mapped NVRAMdisk based storage fairly easily. But I still have a ways to go with my RAMdisk thing for now. I'll do bits and pieces on my S-100 as I get in the mood to switch off fiddling with code.... Variety is the spice of homebrew computing after all!
 
In case anyone is using CompuPro M-DRIVE/H cards I have created a GitHub repo with drivers and test programs that I wrote, including a self-installing CP/M driver.
The URL is https://github.com/menkelis/CompuPro
Thank for your posting this... It was what got me thinking about z80 vs 8080 in the other thread... I really want to convert this to z80 at some point and examine the code more carefully. It looks like a fully integrated RAMDISK routine and isn't very big.
 
Back
Top