• Please review our updated Terms and Rules here

RAMdisk under CP/M 2.2

MykeLawson

Experienced Member
Joined
Mar 21, 2014
Messages
396
This is where my initial thread about the 32K RAM chips (https://forum.vcfed.org/index.php?threads/32k-vs-32k-sram.1241802/) was leading. A RAMdisk with six 32K x 8 SRAM chips. The plan is to decode the track & sector info into addresses to the RAM. Each sector being 256 bytes of RAM. If my calculations are correct, six RAM chips will yield me 48 tracks of 16 sectors each and 256 bytes per sector. The 256 bytes of sectore data would be the lowest 8 bits of the address and the track/sector info would make up the upper/middle bits and chip selects of the RAM chips.

So, I think the first thing I need to figure out, since this could modify my hardware design somewhat, is where CP/M identifies the actual track & sector values as it handles the read and write process with the disk. If I can easily insert my 'device driver' into that, then the decode of track/sector to memory address should be easy. So my first stop will be digging through the DR CP/M manuals and the Programmers CP/M Handbook.....
 
This is where my initial thread about the 32K RAM chips (https://forum.vcfed.org/index.php?threads/32k-vs-32k-sram.1241802/) was leading. A RAMdisk with six 32K x 8 SRAM chips. The plan is to decode the track & sector info into addresses to the RAM. Each sector being 256 bytes of RAM. If my calculations are correct, six RAM chips will yield me 48 tracks of 16 sectors each and 256 bytes per sector. The 256 bytes of sectore data would be the lowest 8 bits of the address and the track/sector info would make up the upper/middle bits and chip selects of the RAM chips.

So, I think the first thing I need to figure out, since this could modify my hardware design somewhat, is where CP/M identifies the actual track & sector values as it handles the read and write process with the disk. If I can easily insert my 'device driver' into that, then the decode of track/sector to memory address should be easy. So my first stop will be digging through the DR CP/M manuals and the Programmers CP/M Handbook.....
It sounds like you want to design the RAM array to emulate the cylinder/head/sector format of a hard drive. In that case why not just use a CF card instead of RAM - though not quite as fast as a RAM drive, it is plenty fast for systems running CPM2.2 and has the advantage of being non-volatile. There are many simple designs out there to use as a guide; most use just an 8255 parallel interface plus a few additional chips.

If you really want to implement a RAM disk, I suggest you take a look at the approach taken back in the day by CompuPro with its MDrive-H S100 RAM disk boards. These were laid out as a large linear array of RAM with two input/output ports - one used to specify the starting address of a read/write operation, and another to pass the data through. Data sent to the read/write port would be sequentially read (or written) from the starting address. Everything else was handled within the CPM BIOS, since CPM does not use "device drivers" in the modern sense. You can see an example of how CompuPro coded its BIOS support for their RAM drive, including generation of the "disk parameter blocks" and the routines that managed reads/writes to the MDRIVE-H by looking at the file HMXBIOS1.ASM at:
 
Cool, I actually have DeRamp's website bookmarked and I subscribe to his YouTube channel. And I downloaded close to 400 old S-100 manuals over the years, of which the M-Drive is one of them. Since I already have the chips, and the board I'm putting them on, in hand, I'm probably gonna proceed down that path. I suspect what I'm planning is going to be very similar, just on different hardware. But I will be using any bit of knowledge and reference material I can get my grubby fingers on. Thanks.

As for the CF card idea, my CPU board already does have one. It is divided into four drives; A, B, C, & D. And well, since I have the hardware, and enjoy the challenge, and I did design and build on for my first S-100 back in the early 80's; why not do it again. Keeps my brain thinking and active.
 
I built a homebrew S-100 RAMDisk a few years ago. As I remember it, it used 4 Alliance 512 x 8 SRAM chips. It worked fine. The problem comes with trying to keep the contents alive once it is loaded up with software. I found a watchdog chip (sorry, forgot the chip vendor and number) that would sense power dropping, and switch over to a coin cell to maintain the multiple SRAMs, but I must have done it wrong because the coin cells didn't last very long. Also, I doubt that I got the bus interface right. I'm a rank amateur. But it worked and I used it for a while.

You might want to take a look at the Digital Research Computers RAMDisk cards (256k and 1MB). Schematics were published with the cards, along with some diagnostic and BIOS code.

I found it was much easier to go with compact flash. No worries about maintaining power. Maybe not as fast, but much less troublesome.

Roger
 
Yep, assuming it is the same one, the LightSpeed-100.... I have several chips I have the spec sheets for that do the switching. Once I get the thing working, I may well switch over to NVRAM so I don't have to worry about power off.
 
Using a 256 byte sector size may seem like it makes everything easier… but it doesn’t since a CP/M 2.2 BIOS would have to block/deblock those sectors… which would slow down a RAM disk. You’d probably be better off ether going with 128 byte sectors (and shifting the LSBit out of the track) or stepping up to CP/M 3.x which handles block/deblocking for you.
 
Well, that sorta throws a wrench in things. Frankly, digging into, and making those kinds of changes to CP/M was a bit daunting to me at this stage. And moving from 2.2 to 3.0 might be a bit more than I want to bite off. So, I'll probably revert to my original plan; separate RAMdisk programs to do that work. One for reading, one for writing, and one for getting a directory, and one for utility work (status, erasing, etc). Then Once I get those all hammered out, combine them. I work better when taking small steps. Since I'm still at the thinking stage, I have time to figure more stuff out.

Another option would be to go from 48 tracks to 96 tracks. That could work as well. That wouldn't require a change in hardware, just the software. A bit is a bit to the hardware. Still thinking though....
 
I found a watchdog chip (sorry, forgot the chip vendor and number) that would sense power dropping, and switch over to a coin cell to maintain the multiple SRAMs, but I must have done it wrong because the coin cells didn't last very long. Also, I doubt that I got the bus interface right. I'm a rank amateur. But it worked and I used it for a while.

I think the Dallas/Maxim DS12/1315 can do the job, and you'll get a clock/calendar for free if so desired, but it requires RAM chips that are happy running at ~3v in standby mode (5V only SRAMs need not apply) and I imagine that if you were running 4x 512K RAM chips off it you'd want to go with double-As instead of coin batteries if you wanted them to last at all.

I found it was much easier to go with compact flash. No worries about maintaining power. Maybe not as fast, but much less troublesome.

FWIW, I built an expansion board for a 7.16mhz XT-class PC that has both EMS RAM and an 8-bit XT-CF IDE subset with an SD->IDE adapter hanging off it, and I've actually benchmarked the XT-CF interface against a RAM disk running in EMS. On a machine that slow there's effectively no difference in performance, IDE/CF is basically as fast as RAM emulating a disk. (I'm sure RAM being RAM is faster than reading from CF, but not a lot. My bad mental math tells me the best the CPU could possibly do copying a byte of RAM from one place to the other would only be about 1MB/sec, and the CF adapter benchmarks at 500Kps. This *is* with a V20 CPU that has I/O block move instructions, it's only about 60% as fast if limited to 8088 code, but that's still not enough slower to worry about. There's nothing a machine with a CPU this slow *needs* a disk that fast for.)
 
Last edited:
Modifying CP/M (2.2 or 3.x) BIOS’s is relatively trivial. Probably the most complicated part is getting the Disk Parameter Blocks (DPB’s) correct. Chances are only the READ & WRITE routines would have to be modified beyond that.
There‘s no shortage of knowledge and skills here (including myself) that can assist you with that. ;-)
 
>> I think the Dallas/Maxim DS12/1315 can do the job, and you'll get a clock/calendar for free if so desired, but it requires RAM
>> chips that are happy running at ~3v in standby mode (5V only SRAMs need not apply) and I imagine that if you were running
>> 4x 512K RAM chips off it you'd want to go with double-As instead of coin batteries if you wanted them to last at all.

Ahh ... that's it! Thanks for jogging my memory. The Dallas 1210 can be used (I think) for one SRAM chip, and the DS1211 and DS1221 can be used for 8 and 4. But they are *very* pricey, and maybe hard to find these days? I think that my RAMDisk had 4 Alliance SRAM chips, and I tried to use the DS1221. Double-As might have been a better idea. A coin cell certainly was *NOT*! *grin*

No need for that rather involved schematic, Myke. Take a look at the DS1221.

Roger
 
Ahh ... that's it! Thanks for jogging my memory. The Dallas 1210 can be used (I think) for one SRAM chip, and the DS1211 and DS1221 can be used for 8 and 4. But they are *very* pricey, and maybe hard to find these days? I think that my RAMDisk had 4 Alliance SRAM chips, and I tried to use the DS1221. Double-As might have been a better idea. A coin cell certainly was *NOT*! *grin*

The DS1315 is available new (oddly expensive for some reason through places like Digikey, though) and you can get used DS1215s from AliExpress pretty cheap, but... yeah, I didn't think of the fact part of what it does during the move to standby mode is force CE high for the chip it's controlling so strictly speaking it wouldn't exactly be optimized to control more than one chip. I can think of some sneaky ways around this, maybe?, like powering pull-ups for all the memory chips from that one CE output it has? (Might need to power a CMOS gate from the battery output along with the RAMs to fan it out? Need to ponder that a bit.)

The datasheet for the 1211 is certainly interesting. It pretty much has a '138 decoder built into it, IE, it *generates* the CE lines, not just buffers them. Maybe that's how you could fan out a DS1215, stick a 74HC138 on the battery powered loop to decode your multiple RAM chips and run the common CE for the whole bank through the DS1215? Off the top of my head I can't think of a reason that *wouldn't* work as long as the total current draw wasn't more than the DS1215 could switch.

FWIW, the DS1211/12 have the same limitation as requiring RAM chips that are happy running on 3v during standby. Again, not a problem unless you're using old high power SRAMs, I suppose.

EDIT: Maxim does still make a 4 chip version, the DS1321+. (And they have the one-chip one as well.) The 8 chip and bigger versions seem to be extinct.

 
Last edited:
From writing a BIOS, I can't think of any way the BIOS directly supports additional drives - there are a few complexities, such as the vector tables that track drive usage and the space in the BIOS that is set aside for the Disk Parameter Headers and the Disk Parameter Block - so unless there's spaces in the BIOS for these already, you'll need to add them into the "Driver" and I imagine the easiest way to address an additional drive would be a "shim" that intercepts calls to the BDOS at 0005, or otherwise the jump table for the BIOS where the BIOS starts.
Once you intercept and shim the entry point, you can check if the current disk is one that you're responsible for, and add in your own handler, then you can process the disk calls however you want and write in your own DPB and DPH and Usage tables, etc. Keep in mind that if you intercept the BDOS calls, you only need to intercept one address, and if you intercept the BIOS you will have to intercept all of the relevant calls.
There should be some code for RAMDISKS that exists already - I recall seeing some examples in the Amstrad disks I have, and it might be easier to begin with that code, and to then write in your own access routines, rather than using their memory paged routines to existing RAM.

Given what you've already approached Myke, I think you'd find writing your own BIOS shim pretty achievable.

The main things you need at the Disk Parameter Header, which tells how to find the Disk Parameter Block.

The Disk Parameter Block addresses the issues of heads, sectors, block sizes etc. It's pretty well documented.

if you do that much, you might be able to use a bios directly. The main challenge is that things like the Allocation Vector Table are sized based on your disk, and can get pretty big if your allocations are too small, and your drive is fairly large - It uses a byte for every 8 allocations, and if you have a lot of allocations ( let's assume you have 128 byte sectors, and you use 8 of them in an allocation, that means the AV table is 64 bytes long and needs to be held somewhere and you need a gap for it in the BIOS or elsewhere, along with similar tables for every other drive on your machine ). Probably the easiest way is to include this stuff in gaps in your driver code.
Also, there are ways to avoid this entirely and use your own format too, especially if you intercept the call at 0005 instead of the BIOS jump table.

Also there are Sector Translation tables to add in.

Here's examples of mine.

DW TRANS0 ; Put in label for vector here if translation table. 00=no translate - Same as L: drive DW 00 ; Scratchpad. DW 00 ; Scratchpad. DW 00 ; Scratchpad. DW DIRBF ; 128 byte scratchpad buffer for file directory. ( used by BDOS ) DW DPBLOKI ; Disk Parameter Block. DW 00 ; DW ALL00 ; TRANS0: DB 0,1,2,3 DB 4,5,6,7 DB 8,9,10,11 DB 12,13,14,15 DB 16,17,18,19 DB 20,21,22,23 DB 24,25,26,27 DB 28,29,30,31 ; This gives us a clean sector number starting with 0. DPBLK: ; Standard to all disks. ( can be different ) This is where I remember what the DPB parameters are . DW $20 ; Sectors per track. ie, 0 to 31. DB 3 ; BSF Block Shift Factor - 3 bits over Bit6 ( Bit6 = 128 = allocation ). Bit7 = 256., Bit 8=512 and Bit9 is 1024 ( 0 to 1023 ). Number of bits past Bit6. DB 7 ; BLM Block Mask - Related directly to the above. =2^BSF-1... 7 is a 3 bit mask, so BSF must be 3. DB 0 ; EXM - Extent Mask - DW 242 ; DSM - Disk size -1 - ie, 242= 243 BLOCKS. if the block size=1024k then 248,832 bytes on disk. Block=Allocation Unit. DW 63 ; DRM - Directory Max - Directory Entries = 64 entries. If 1024K allocation holds 32 directory entries then this means 2 allocations for directory. DB %11000000 ;AL0 - Alloc 0 = 192, but WATCH THE BITS. Why the heck they did this I do not know. One bit for each allocation. DB %00000000 ;AL1 - Alloc 1 - If all 16 bits are set, then it means 16 directory allocations... MAX... Why did they not just use a number here? DW 16 ; CKS - Check size = (DRM+1) /4 for removable media. = 64/4 = 16... In this case, If fixed then DRM=0 = Do not check directory records - Not sure why. DW 02 ; OFF - Track Offset. Skipped tracks = Added to SETTRK when called. Can be used for partitioning a big disk into smaller disks. 2 = 2 system tracks. DPBLOKI: ; Two bytes per allocation disk DW $20 ; 00,01 ; L drive is 32 sectors per track - DISK 11! 128 byte "sectors" = records DB 4 ; 02 ; 4 and 15(next ) = 128 byte sectors, 2048K allocations (blocks) = 2^4 x 128 blocks. DB 15 ; 03 ; See above. 4 bit mask. DB 1 ; 04 ; Extent mask. Blocks are 2K, so should be 1. DW 316 ; 05,06 ; This has 316 2K blocks. ( Allocation = 1 K ). Would be a 720k disk. DW 127 ; 07,08 ; 4 x 2k directory allocation = 128 file extents max. (128 total ) I think my format might be a little off though. DB %11110000 ;09 ; Only set 4 bits so we know 4 allocation is directory DB %00000000 ;0A DW 0 ; 0B,0C ; Might be necessary to make as 0 for fixed. DW 1 ; 0D,0E ; 1 track offset. This gives 2K for boot of BDOS and to load the CCP and other files. Enough. MARKER1: DB '>' ALL00: BLOCK 64 ; Allocation Vector reservation for drive 0 64 bytes. I won't use all of this for a 720 drive with 2K allocation. MARKER2: DB '<'

edit: Noted my documentation was a bit incorrect so edited some and added in my notes section. This was because as I progressed with my emulator I wanted more disk space so I kept "expanding" my disk... Was original 32K, then 64K, now it's about 512K, so the DPBLOKI parameters are probably correct for your RAMDISK even though the comments are wrong. I need to pay more attention to my documentation.
 
Last edited:
Oh, and it's worth mentioning that if you use CF cards instead of RAM, then you can avoid deblocking by using Address Mode of the card, which basically lets you write part of a sector in a single operation and directly access the correct byte in a record directly with A0 to A6.
In drive emulator mode, they only support A0 to A2.

David
 
One major problem intercepting BDOS calls to add a new disk type is that the BDOS calls are *FILE* accesses and you end up having to re-implement the BDOS in order to get the sector I/O necessary to use the new storage device. In my opinion, that is a show-stopper.

If you don't have the source code to the BIOS, it may be possible to "shim" a BIOS layer between the BDOS and original BIOS, but even that has significant hurdles. You've got to "relocate" the BDOS and CCP in order to make room for the new code. Most vintage CP/M distributions had MOVCPM.COM which was a relocatable copy of the BIOS,BDOS, and CCP, which gives one a starting point. Many of the modern CP/Ms assume 64K and don't provide any thing like MOVCPM. But, most modern CP/Ms do include source code, in some form or other.

Either way, you need to study the CP/M 2.2 System Alteration Guide and be familiar with coding assembly language as well as using the software interfaces to the hardware. DPH and DPB (and ALV, CKS, DSM, ...) need to become familiar terms.
 
I've been rolling this around in my head a bit while working on my printer project. I think I have a first cut of a schematic. It has 3 latches for the address; one for the track number, one for the sector number, and one for the 256 bytes of data in each sector. and I think I have an idea of the code to use the board; MDFRMT, MDDIR, MDLOAD, MDSAVE, MDERA, and MDTEST. Given it is pretty much a bunch of I/O ports on the bus, I can do some rudimentary stuff for design testing through MBASIC. If this all works out, then I'll look at integrating it into one program, or into CP/M. It's all in the fun and learning anyway.
 
You might find it easier to go with 7 bits for sector address, and 5 bits for sector number and 7 bits for track. That slows down a little when you get to the higher tracks, but gives you 512Kb of disk space. It also aligns better with the BIOS and you won't have to do blocking and deblocking since your record and sector size are the same.

Also, you need to put aside space for the AV table and the DPB and DPH and other tables such as the sector translation table, which you can omit for a ramdisk.

Thinking about it, intercepting the jumps at the start of the BIOS would then make it easiest to implement. Check if your disk is selected, and if not, jump back to the BIOS.. Write the data in the C register to the external register for track and sector, and write your own read/write routines, and the DMA jump can just save the destination address in memory for the reads and writes to use.

That would seem the most likely to work with any CP/M implementation that I can think of. After all, if you rewrite the BIOS - which isn't that difficult, then any CP/M implementation will work with your RAMDISK :)

Regards
David
 
In case it helps, here's the architecture I'm using for my Loki project. This is the MAD architecture ( MEMORY AS DISK ) in that all memory, including the TPA, can be accessed as disk. It can also be accessed in 128 sets of 4K x 16 pages, so the TPA can be constructed from memory anywhere, pages can be shared between TPAs and the whole lot is managed as "files" on a disk with standard CP/M... So a dir of the M: drive should show files that represent blocks of executable memory, and any process can simply add in more blocks and either access them as disk or as paged memory. It's a pretty radical architecture, and I can't find any evidence of it being done previously. But it has the benefit that a normal CP/M program will still run, without being aware of the paging environment, and can still use the full RAM as RAMDISK even if it's not aware that it can page that same memory into any 4K page.

LOKI Architecture - Frame 1.jpg




The main difference however between this architecture and what you're doing is that I use the upper address lines as lower RAMDISK lines - eg, A8 to A14 map to the ram into A0 to A6. This means I don't need a latch for those address lines, and also I can simulate DMA with the following tight block code;

LD B,$7F LD C,$RAMDISKPORT LD HL,DMA+$7F INDR IND

That isn't only tight- it's FAST. And shifts the RAM selection to the DMA selection in just a few commands. ( The IND at the end has to work with B=0 ). Almost as good as Hardware DMA.
And I only need registers for Sector Number and Track Number...

You don't need the Memory/Disk address multiplexing, so your architecture would be even simpler than this since you don't need the MMU, or the MEM addresses on the left - Just the I/O addresses and then you wouldn't need the lower MUX, and would only need the Sector and Track number latches.

David.
 
Just in case anyone is wondering why the above architecture, it's like adding a data segment to the z80 -

Anyway, Myke, even if you don't like the idea of copying what I did, still give consideration to using 16 bit I/O address, because it means you don't need to use a latch when you already have access to the upper address byte :) And just use OUT(C) and IN(C) instead of the usual port access.
 
David, I guess I'm more of a one's & zero's schematic type as that loots a tad more complicated than I could take on. And more board space than I have on the smaller STD bus cards. I have thought of a future project with a Z-80 on the old Intel Multibus platform. The boards are sure big enough. And with all the hardware design changes I seem to need, wire-wrapping works well. Anyway, here is the latest 'in development' schematic for my board.....
 

Attachments

  • MemoryDiskv4.pdf
    84.5 KB · Views: 10
Back
Top