• Please review our updated Terms and Rules here

Question about the RAM for CP/M 3

Ruud

Veteran Member
Joined
Nov 30, 2009
Messages
1,369
Location
Heerlen, NL
I know that for the banked version of CP/M 3 there should be a common block of RAM and several blocks (up to 16) that can be swapped. The System Guide says the common block can be as small as 4 KB but doesn't state a maximum or best size. Looking at the schematics of the Bondwell 14 it appears that this machine has a common block sized 32 KB. I want to design some own hardware and a block of 32 KB keeps the hardware simple.

My question: what is the best size?

Think you in advance!
 
The common (resident) portion of CP/M 3 generally takes less than 8K (depends on the resident size of the BIOS), so an optimal common size would be 8K. The larger the bank size, the more memory available to CP/M 3 for banked data. The TPA size is not affected by the bank size, it is still the size of the resident OS.

32K common will work, but does not give you as much banked memory to use.
 
It's a tradeoff, particularly if you plan to bring up MP/M on the same hardware as well. In MP/M, each process has to go below the start of common memory so it wants to start at a relatively high address (say, 56k). In CP/M Plus, it can be useful to have the start of common memory overlap with the end of the TPA, so that user programs or RSXs can bank switch (for example, to access structures like the DPH, or a memory-mapped screen if your system has one).
 
When I did a CP/M 3 port to a Multibus system a few decades ago, the boot EPROM started at address 0 at power on and copied itself to a 4k bank 60k. The EPROM image contained the code to fiddle with the extra address bit. I used a single bit from the parallel port to add an address bit to swap the first 60k of RAM for the second 60k of RAM. The 4k RAM for the boot ROM image didn't decode the extra address bit so it was always resident. If I remember correctly the extra 56k of RAM was only used for disk buffers. The system also contained a memory mapped text video display that took another 8k. The code for the video board was also in the boot ROM image.
 
Another question: is there a max limit to the ROM size? At this moment the design is such that at start-up there is 32 KB of ROM and 32 KB of RAM. So far I didn't see anything that forbids this amount of ROM. What I read so far is that only a part of the ROM is copied to the common RAM and then the ROM is swapped with RAM. I studied the behavior of my Bondwell and mr. Thompson more or less confirms this behavior. So far I didn't see any need for RAM between 4 and 32 KB. A conformation would be appreciated. Thanks!
 
If I understand your design, you have a "ROM BIOS" that you copy into high RAM, and want to use that when running CP/M 3? In that case, you must make certain the GENCPM creates a CPM3.SYS that adjusts the "top page of memory" appropriately. You have to tell GENCPM to not touch anything where your ROM is copied. A normal "all RAM" system would set the top page at "00". If your "ROM BIOS" starts at F000H, you would set the top page to "F0".

I'm not sure what you mean by "I didn't see any need for RAM between 4 and 32 KB". CP/M requires RAM from 0000H to the top of the BDOS (RESBDOS3). The BIOS, even if running in ROM, needs RAM as well - which has to be outside of the BDOS or TPA. So, if your ROM code is F000-FFFF, then you probably also need to reserve RAM somewhere for that code to use. Your top page of memory may need to be lower than "F0". I'm assuming you still have a RESBIOS3 (usually in the same SPR file as BNKBIOS3) that provides stubs to call into the "ROM BIOS".
 
If I understand your design, you have a "ROM BIOS" that you copy into high RAM,
Maybe I should ask my question in another way: the systems I know have 4 KB of ROM and 60 KB of RAM at startup. Would it be a problem if it is 32 KB ROM and 32 KB RAM?

I'm not sure what you mean by "I didn't see any need for RAM between 4 and 32 KB".
The System Guide only mentioned what the size of the common area should be at least, nothing about any RAM needed below it. 32 KB of RAM should be more than enough and then IMHO 32 KB of ROM should not be a problem. But I wanted some conformation, just in case I overlooked something.

CP/M requires RAM from 0000H to the top of the BDOS (RESBDOS3). The BIOS, even if running in ROM, needs RAM as well - which has to be outside of the BDOS or TPA. So, if your ROM code is F000-FFFF, then you probably also need to reserve RAM somewhere for that code to use. Your top page of memory may need to be lower than "F0". I'm assuming you still have a RESBIOS3 (usually in the same SPR file as BNKBIOS3) that provides stubs to call into the "ROM BIOS".
Consider me as a simple user so far. Everything you mentioned here above is more or less new to me. I know how to build an engine, now I have to find out how to fit it in the car. But for the moment I have to be sure that the dimensions of the engine are right.

Thank you anyway for your help!
 
Ok, resetting.

So, there are three basic types/styles of ROM used for CP/M machines:

A) ROM that exists only on RESET/power-on and is mapped out once CP/M starts.
B) ROM that is always mapped in, at high memory, and CP/M fits below it.
C) ROM that is dynamically mapped in/out based on need, and CP/M is designed to work under those conditions.

"C" is the most complicated, and even more-so when adding banked RAM for CP/M 3.

I'm not sure if you are using a 32K ROM because that is convenient, or if you really need all (most) of the 32K. It is possible to, for example, map only 4K of the 32K ROM and thus not carve-out such a large chunk of the CPU address space.

So, one decision that needs to be made is where the ROM will be "ORGed" - i.e. at what CPU address does it "operate". Since the ROM needs to get control when the CPU is RESET or powered-on, something needs to provide at least one executable (meaningful) instruction at location 0000H.

Another consideration is that ROM code typically needs at least some RAM in order to operate, certainly it needs a stack. So, if using scheme B or C, that RAM needs to be protected from CP/M in some way. Typically, the RAM used for that is placed at high addresses, and CP/M is kept below that (CP/M expects to have continuous RAM from 0000H to the last byte in the BDOS).

As an example, the Kaypro uses an 4K/8K ROM ORGed and mapped at 0000H. This ROM uses several pages of RAM at the top of the address range, in order to maintain context, etc. The Kaypro CP/M is always created as a "63K" version, to reserve that high memory. The CP/M BIOS then makes the proper preparations and maps the ROM back in to perform various I/O functions. When I implemented CP/M 3 on the Kaypro, I was just not interested in trying to make that work on top of banked RAM, so I re-implemented all the ROM routines in the BNKBIOS3 and reclaimed all of memory, and also avoid yet-another bank switch tap-dance, since CP/M 3 is already bank-switching between the TPA (1) and system (0) banks.

So, probably the first decision/question is: what purpose will the ROM be serving once CP/M 3 is booted and running? I guess a related question is what will be in this 32K ROM? If you plan on having the "boot" image of CP/M 3 there, that affects a lot of the next decisions.
 
I don't know if this will help or just add to your confusion, but the S100 site has the manual for the Delta Products (also XOR Science) Z80 CPU board and on page 12 of the manual there's a description on how this board loads upper memory with the boot EPROM 2716 (16/8 = 2K for memory mapping), which is what I think your 32K ROM (2732 = 32/8 =4K or FFFF-4K = F000) is doing too. ROM's are sized by bits not bytes. Yours it is trying to load at F000 is seems to be acting somewhat the same as how the Delta Products EPROM is used. Page 15 provides a list of jump locations for various BIOS routine usage and most likely yours follows the same for jump routines to be used by CP/M and user programs.

See: http://s100computers.com/Hardware Manuals/Delta Products - XOR/DP-CPU Manual.pdf

PS - I've used a 2732, in lieu of the 2716) in the past when testing my own changes to the D.P. provided monitor program (1.83) assembly code. I just have to be sure to write to the correct 2K or write it twice, once to each half of the larger EPROM so the hardware finds the code and runs it properly. (A 2732 being used as a 2716 in this case) As stated elsewhere a true 32K program would need to load at F000 and eats 4K of the the maximum 64K TPA.
 
...

PS - I've used a 2732, in lieu of the 2716) in the past when testing my own changes to the D.P. provided monitor program (1.83) assembly code. I just have to be sure to write to the correct 2K or write it twice, once to each half of the larger EPROM so the hardware finds the code and runs it properly. (A 2732 being used as a 2716 in this case) As stated elsewhere a true 32K program would need to load at F000 and eats 4K of the the maximum 64K TPA.

I took the "32K ROM" to mean a modern 32Kx8 EEPROM.
 
Maybe it helps if I give you the schematic of my design:

cpm.png

The 174 latch serves as Memory Management Unit (MMU) selecting chunks of 32KB of RAM or ROM for the first 32 KB of the address range. When the range 8000..0FFFFH is chosen, RAM is always selected and always the same RAM (common area). In this case the very first 32 KB of the 62816. After a reset the first 32 KB of the 29F040 is selected in visible in the range 0000..7FFFh. So the Z80 has ROM available to start up from. It is not that I need it, it is just available due to its design.
After the ROM has copied what ever is needed from itself to the common area and the Z80 started to run from there, it is a matter of writing the correct value to the 174 latch to fill the first 32 KB of the range with RAM. Writing 41h to the 174 will select the second 32 KB block of the 62816. The 29F040 can serve as ROM drive. For example, writing 01h to the 174 will select the second 32 KB block of the EEPROM into the 0000..7FFFh range.

So my choice for going for a 32 KB ROM is not that I need it so big but it fits in the whole design. But if there is something wrong in doing it in this way I would like to know, please.

The remark on just having an 8 KB common area triggered me to create another design. Expanding the above design with extra MAxx address lines won't work so created one using a 74LS612 MMU, The MMU enables me to pick any chunk of 8 KB of RAM or ROM for any 8 KB in the range for the Z80. If anyone is interested, let me know.
 
Yes, a schematic helps a lot.

So, if I understand the intent of your design, you have a "bank" register where bits 0-3 select a 32K "bank", and bit 4 selects ROM ("0") or RAM ("1") in address range 0000-7FFF. Address range 8000-FFFF is intended to always select RAM address range 8000-FFFF. It would appear that you allow banked access to ROM as well as RAM, depending on D4, which I presume is to facilitate in-circuit flashing of the ROM as well as allowing access to all code in the ROM (provided the ROM code is organized into 32K banks).

NOTE: I'm not sure if that RAM/ROM select circuit is correct, it might be reversed/inverted. You may want to double-check that.

So, I think you can make this work for CP/M 3, you just have a somewhat-restricted bank size so the amount of code and buffers you can place in bank 0 is limited.

One thing that I believe is very important for CP/M 3 is the ability to do bank-to-bank memory copy operations. This has an impact on the resident size of CP/M 3 plus speeds up use of additional banks (e.g. directory hash tables). For your design, you'd need another 4 "bank register" bits to select the "write bank" differently from the "read bank". And then you need circuitry to switch the bank select based on /RD vs /WR operations. For normal CP/M 3 bank select operations, both (RD and WR) bank select registers would have the same value. But for XMOVE operations you would set the RD bank different from the WR bank and perform an LDIR.

One problem with making finer grained bank/page select, for example as with the 74LS612, is that it requires more I/O in order to do a bank select. Your (simpler) design requires only one OUT instruction to select the bank (two, if you support XMOVE). The 74LS612 requires many, potentially 15 depending on common memory size, OUT instructions in order to switch banks. As an aside, I believe you could adapt the 74LS612 to support XMOVE by feeding /WR to one of the MA inputs, and programming the bank registers accordingly.

Another approach to memory mapping is something like what was done in the Z180 (as well as many other MMUs). You set a register that defines the common memory boundary, then each bank select is a single OUT instruction to set the base physical page used for the banked memory. This allows common memory to be any size you want. You could have two (RD and WR) bank base registers, to support XMOVE, as well.
 
Just doing a little math, the standard DRI BNKBDOS3 takes up nearly 12K. It of course depends on the complexity and robustness of your BIOS, but a fairly-fully-function BIOS I use takes up 13K. So, bare minimum I'd need 25K in bank 0. I'd want to put some directory buffers there, and assuming all devices have 512B sectors, that's maybe 1K. With XMOVE you can put disk data buffers also in bank 0, so maybe another 1K+ (otherwise disk data buffers must be in common memory, reducing the TPA). It is common for people to store the CCP.COM in bank 0, which would be a little over 3K, although with fast flash-based disk it may not be as necessary to keep CCP.COM in memory, especially without XMOVE. I guess you could also put CCP.COM in a ROM bank.

Anyway, it sounds like it could be tight, but seems like there should be space in 32K for CP/M 3. With the ROM copied into high memory, you've got a bit of a tap-dance to boot CP/M 3, but I'm sure there are ways around the complications. Typically, CPM3LDR runs at 0100H so you can go that route. You might also make CPM3LDR another bank of the ROM. But, the banked portions of CP/M 3 will be loading into the upper parts of the 32K, as well as the upper parts of the common area, so you've got to keep the loader code small, and carefully-placed. The potential for CPM3LDR to overwrite itself while loading BNKBDOS3 may be the biggest challenge.

Just curious, is there any particular reason you are ORGing your entire "monitor" ROM to run in high memory? rather than just let it "naturally" run at 0000H and using a "boot loader" routine that you copy to high(er) memory? Either way you've got issues to work out when booting. Some platforms have/had a "write under ROM" feature so you could simply copy the ROM into RAM in-place. Of course, with FLASH/EEPROM it puts a kink in how you update the ROM in-system.
 
Hallo Doug,

You understood the schematic all right. The reason for choosing a 8KB bank size and not 4 KB as was possible, has to do with the number of OUT instructions that have to be performed. 8 KB means I only need seven.

I never ever hear about "read bank" or "write bank". I'm 100% sure that the C128 and Bondwell 14 cannot do that and these are the only machines I'm familiar with.

As an aside, I believe you could adapt the 74LS612 to support XMOVE by feeding /WR to one of the MA inputs, and programming the bank registers accordingly.
First, I haven't heard of XMOVE or LDIR either but I guess what XMOVE does. I know what you mean but I cannot translate it into a schematic so quickly. But it led to my own solution of using two 74LS612s: one for reading, the other for writing. Simple IMHO :)

One understanding I have of CP/M is that, except one or two files, it is completely machine independent. This means that handling XMOVE is done by the BIOS or one of these files (BDOS ?). So if you want to copy (parts of) on bank to another bank, IMHO it is just a matter of temporary popping up an 8 KB segment of the source bank into an 8 KB segment of the destination bank that is not targeted (yet). Not as fast as your RD/WR bank combination but it will work.

It seems I have a LOT to learn but as long as it is fun, I don't mind :)
 
"LDIR" is the Z80 "block move" instruction, basically it copies the number of bytes in the BC register pair from the address in HL to the address in DE. It is the fastest way to copy a block of memory using software (a DMA controller could do that much faster).

The XMOVE is a BIOS entry defined for banked CP/M 3 BIOSes, it sets up a cross-bank move operation. You should read the DRI CP/M 3 System manual carefully to fully understand what needs to be implemented. The XMOVE call simply tells the BIOS that the *next* call to MOVE should use the inter-bank data provided.

The banked version of CP/M 3 (BNKBDOS3) determines whether XMOVE is supported, and will use that to move data to/from system buffers when needed. The existence of this features allows disk buffers to be in bank 0 instead of requiring them to be in common memory.

Regarding the 74LS612, from what I see it is a DIP40 device. That takes up a lot of PCB real state, and using two of them takes up even more. I'm not sure what you space budget is, though. As you already mentioned, you are reducing the number of MMU registers you must update when switching banks, so not all 16 MMU registers are being used. It's a trade-off, but you may want to consider supporting XMOVE.

Also, if you are not locked-in to using the 74LS612, I think you could use a different approach that takes less space on the PCB and requires fewer OUT instructions to change banks. While reading through the Z180 datasheets in order to understand it's MMU is probably a long detour, you could basically implement that using a 4-bit output port to select the common memory boundary (or you could hard-code it with dipswitches), then use a 4-bit comparator like the 74LS85 to see if CPU address bits A12-A15 indicate common or banked memory. Then you could use an 8-bit port, or two, to select the bank address. You could also use a fast adder to allow more flexible selection of the physical memory used for each bank, and less wasted memory.

A very simple/crude example is here, http://sims.durgadas.com/kaypro/ram256k.pdf, that I did back in 1986 for a Kaypro machine. Because I needed to support DRAM refresh, and only had 256K RAM, that schematic is not what you'd want to use but it might demonstrate the general idea. There's a little description of that circuit at http://sims.durgadas.com/kaypro/kaypro84x.html under "Memory Modification". Note, that circuit would waste the "duplicate" common memory of each bank (extremely simplistic bank select), but using an adder to combine a base address would allow each bank to better utilize all of memory.
 
Just curious, is there any particular reason you are ORGing your entire "monitor" ROM to run in high memory? rather than just let it "naturally" run at 0000H and using a "boot loader" routine that you copy to high(er) memory?
The original idea still is to mimic (more or less) the Bondwell and C128. They happen to have 4 KB of ROM and boot from an external device, floppy drives in both cases. I want to start with doing about the same although the drive won't be a FDD but a PC. So I don't expect my ROM to be about the same size, the design just happens to give me 32 KB instead. Later, when I know how things work, I'll see if I can use these 32 KB ROM as a boot device. And I'm also thinking about starting with CP/M 2.2 first to keep it more simple.

But I gave it a good thought and I decided to go for the MMU design instead. Just because it is more flexible. Have a look at the schematic:

cpm2.png

As you see, just a few parts. The flip-flop made out of the two 3-input NAND gates makes sure that the MMU starts up in "Pass mode". If you want to know more, let me know. Just one thing: not a single bit of software has been written yet. I first have to revive all very old knowledge I have of CP/M, or better, what should come in the ROM and how should the first files look like.
 
"LDIR" is the Z80 "block move" ....
Thank you for the explanation.

Regarding the 74LS612, from what I see it is a DIP40 device. That takes up a lot of PCB real state, and using two of them takes up even more. I'm not sure what you space budget is, though.
I have no idea yet what the costs will be with the momentary design. So far I made everything on prototype board. But there must be a first for everything.

but you may want to consider supporting XMOVE.
IMHO it should be possible with even one MMU, so why not.

There's a little description of that circuit at http://sims.durgadas.com/kaypro/kaypro84x.html under "Memory Modification".
I did the same trick for my Commodore 64 and 128. And I learned a very important lesson: don't design hardware if there is no software support for it. But reading your site I realize I never have thought about using the extra RAM in the C128 for CP/M !!! Stupid, stupid, stupid. My only excuse: I simply didn't know that it was possible at all to use it for CP/M at that time.

And you have a very nice and interesting site!
 
Here is a dirty hack of the previous SCH, now having two MMUs:

cpm3.png

Main differences: the extra MMU of course and I had to add a 139 2-to-4 demultiplexer. The MAxx outputs are activated by the ME input. The idea is simple: one output is activated by the WR signal, the other by the RD signal. If you see any flaws, please shoot.

This won't be the final version. The idea is to replace the two 139s and the NAND IC with one GAL. It seems a 16V8 will do but I'm only human and could have overlooked something.
 

Attachments

  • cpm3.png
    cpm3.png
    45.7 KB · Views: 2
Last edited:
There are various ways to do bank-to-bank memory transfers (XMOVE / MOVE), depending on the system hardware. On the Amstrad PCW, for example, which has a 16k memory paging granularity and can map any 16k block into any of the four slots, the KL_MOVEMEM function is used. This maps the 'from' block at 4000h, the 'to' block at 8000h, copies up to 16k, and repeats until all bytes are copied. On the Spectrum +3, which doesn't have that degree of flexibility in its memory paging, bank-to-bank transfers are done using a 64-byte buffer in common memory (select source bank, copy to buffer, select destination bank, copy from buffer...)
 
Back
Top