What is E-REUTo add more RAM to a C64 or C128, one could buy the so called "RAM Expansion Unit" (REU). A REU can expand a system with up to 512KB of RAM. But these devices were expensive and therefore rare. It is still one of the most wanted items nowadays. Just for the fun I started to play with the idea if it was possible to build your own REU.
The REUThe original REU added RAM to the C64 or C128. It came in three sizes: 64, 256 or 512 KB. As the C64 only can address 64 KB, I can imagine that you are wondering how the C64, even the C128, suddenly can handle up to 512 KB. It can't. Stronger, the C64 and C128 cannot read or write one single bit of this memory! The only thing the C64/128 can do is telling the REU what it wants by filling some registers with data and then the REU takes over.
This "taking over" means that the REU stops all activities of the processor by halting AND tristating it. At this moment is has almost complete control of the system. Almost, because the VIC-II video IC still controls the system during the low half of the clock. The REU performs the requested action using a technique called "Direct Memory Access" (DMA) and then returns the control to the processor again.
There exist three kinds of actions:
- Copying data from the system into the REU
- Copying data from the REU into the system
- Swap the data from the system with the one in the REU
Copying means that the data in the destination is overwritten. Swapping means that the data is exchanged.
The REU cannot store data during power off. This simply means you have to save every bit of data to a floppy or another non-volatile medium before you shut down the computer.
The baseThe whole concept about the REU can be divided into three parts:
- the I/O-part
- the DMA-part
- the Server
The I/O-partThis is the part the C64 will see when programming the E-REU. There are 11 registers and they are mirrored throughout the $DFxx-page every 32 bytes. A brief description of the registers:
Register 0 Status register - read only Bit Function 7 Interrupt Pending (1=interrupt waiting to be serviced) 6 End of Block (1=transfer complete) 5 Fault (1=block verify error) 4 Size (tells if a jumper is cut in the REU, or not) 3-0 Version number (0 on the REU I tested) Bits 7-5 are cleared when this register is read. Register 1 Command Register Bit Function 7 Execute (1=initiate transfer per current config) 6 reserved (returns 0 upon reading) 5 Load (1=enable AUTOLOAD option) 4 FF00 (1=disable FF00 decode) 3-2 reserved (0 upon reading) 1-0 Transfer type: 00=C64->REU 01=REU->C64 10=swap 11=verify AUTOLOAD: Restore address- and count-registers. FF00 : wait for a write access to $FF00. Cleared each time it is used. Register 2 Bit Function 7-0 C64 start address (LSB) Register 3 Bit Function 7-0 C64 start address (MSB) Register 4 Bit Function 7-0 REU start address (LSB) Register 5 Bit Function 7-0 REU start address (More SB) Register 6 Bit Function 7-3 Not used 2-0 REU start address (most significant bits) Register 7 Bit Function 7-0 Transfer length (LSB) ($0000=64 KB) Register 8 Bit Function 7-0 Transfer length (MSB) Register 9 Interrupt Mask Register Bit Function 7 Interrupt enable (1=interrupts enabled) 6 End of Block mask (1=interrupt on end of block) 5 Verify error (1=interrupt on verify error) 4-0 unused (1 upon reading) Register A Bit Function 7-6 Address Control Register 00=increment both addresses 01=fix expansion address 10=fix C64 address 11=fix both addresses 5-0 unused (1 upon reading)For more and detailed info see: registers of the REU and how to program them.
Register 0 is a read-only register. A special feature is that bit 5, 6 and 7 are reset after reading the register.
Bit 4 is meant to determine the size of the REU but with just one bit and at least three models available this appeared to be impossible. It should be known that you have to use a program to determine the real size.
Bit 0..3 are used as version number. IMHO this means that the values of bit 0..4 probably don't matter so much.
Translating this concept to hardware we end up with a 74LS273 and 74LS541 as core. The inputs for the bits 5..7 of one IC are connected with the outputs of the other. The inputs for bit 0..4 of the 541 are connected to GND or +5V to emulated the output of a real REU. We could connect the other outputs of the 273 with the inputs of the 541 as well to create another source of information about our own E-REU. The decision is up to you.
Resetting bits 5..7 after reading the register involves some extra logic. My first idea is to generate a pulse for the CLR-line of the 273 after it has been read. To ensure a normal state after powering up the C64, the RESET-line has to be involved in the reset of the 273 as well.
Register 1 is a Read/Write-register but also one with a special reset option. Therefore we'll use again the combination of a 74LS273 and a 74LS541. Bit 2, 3 and 6 are not used and should be read as (H) by the C64; the inputs of the 541 for these bits should be tied to +5V. But again, this does not mean we cannot use them for our own purposes!
Bit 7 of this register is used to start the DMA-transfer. To make sure the C64 can start up after powering up, the RESET-line has to be involved in the reset of this 273 as well. As bit 4 is involved in starting the DMA as well, extra logic for this bit is needed as well.
Registers 2 to 5, 7 and 8
These registers are Read/Write-registers without any special reset options AFAIK. Therefore we'll use the combination of a 74ALS574 and a 74LS541.
This register is a Read/Write-registers without any special reset options AFAIK. Therefore we'll use again the combination of a 74ALS574 and a 74LS541. Bits 0..2 of this register represent the address lines A16 to A18. Bit 3..7 are not used. But as far as I know there is no reason to use these bits as well, creating a REU capable of containing up to 16 MB!
A lot of discussion is going on about this register. Any interrupt can only be handled AFTER the end of a DMA-session. The only advantage of letting the IRQ-routine servicing the two possible IRQ's is that you gain some time; in that case the normal program does not have to take care of checking the registers itself. But with every normal IRQ you loose some time checking if the IRQ was generated by the REU. I think I don't have to tell you which of both ends up as the most time consuming one :-).
IMHO We'll need a 273/541-combination with a reset-mechanism for the bit 5 and 6 after reading the register.
This register is a Read/Write-registers without any special reset options AFAIK. In this case only bits 6 and 7 are used.
The DMA-partI already produced a document about connecting an external system or CPU to the C64 by using DMA: X-DMA. The only thing that has to change IMHO is the way the C64 is put in DMA mode. In X-DMA the C64 takes care of it itself. In this case, the Server has to do this.
The ServerWhy did I call this circuit 'Server'? Simply because I don't know a better name one yet.
I have two ideas realizing this part:
- Using an existing system
- Building one from scratch
Using an existing system
With 'system' I mean everything from micro-controller to PC. Theoretically you even can use an old PC-XT with 640 KB memory onboard as server to emulate a 1750-REU equipped with 512 KB. Mentioning a PC-XT as server probably raised your eyebrows because the old baby lacks one thing: speed. But we (probably) don't have to worry about that because the old XT has something to compensate that: an 8237 DMA-chip. This means that the 8088, the CPU of a XT, doesn't perform the actual transfer, but that the 8237 does the job. The only problem at this moment is that I never worked with DMA in any way in my own programs so I cannot give you any ideas about using the 8237 for this purpose.
Extra remark: I don't think the 8237 can be used for swapping. This means we need an extra 64 KB of RAM. First the original contents is read in this extra block, then the data is written to the C= and the last action is copying the data from the extra block to the 512 KB-block. But the last action can be done without disturbing the C=.
But using an external system means that there is a big chance that our E-REU won't be as fast as the original REU, even when using DMA. The delay occurs in interpreting the registers. The system first has to read and to interpret the values and then to act on them. Later it has to update them. This will cost more time then with 'only-logic' as the REU (probably) uses.
Building one from scratch
Building one from scratch was my first idea but was soon left because I had no idea at all how to do it. One day, when I was working out the 'Build your own 6502', it occurred to me that I could use a similar mechanism for the Server of E-REU:
- an instruction decoder that only has to take care of a few 'instructions'
- the registers, already described above
- address counter
Nothing should happen at all until bit 7 of register 1 (R1-7) is set (the 1 bit wide instruction I mentioned before). IMHO the only thing this bit has to do is to release the counter. The first thing our decoder has to do is to disable the influence of R1-7.
The next step for the REU is to check bit 4 (R1-4) to see if it has to wait until data is written to $FF00 (Input). This means we need a mechanism to detect this moment. The output of this mechanism is used to block the counter or not. For this we need an output of the decoder. I also assume we need an output to reset the mechanism.
What type of transfer is wanted? R1-0 and R1-1 will tell us this. For swapping data we need a kind of temporary buffer. For comparing the data we need a mechanism as well. The output of this mechanism is used in the first place to stop the comparing-action. As so it must be an input as well. Further it is needed to set R0-5.
I don't think we need an output to signal that the comparing result is needed or not; we can use the two transfer type bits to do the job.
During the actual transfer counters represent the addresses of the REU and the C=. RA-7 determines whether the address for the C= is fixed or not. RA-6 does the same for the REU. For the moment I think we can use the bits directly to block the counters.
It is possible to restore some of the registers to their original values. For this reason we need a mechanism to do this task. This means we need an output to tell this mechanism when to perform its task. Combining this output with R1-5 determines whether this action really happens or not.
The REU is able to generate interrupts. IMHO you only have to combine the mask bits of register 9 with the results and to place the results on register 0. The only thing the decoder has to do is to start the actual process.
Other things of influenceOnce the REU has disabled the processor, it is allowed to use the bus any time it wants. But it also has to reckon with the VIC-chip. This video-IC sometimes needs some extra cycles to perform its task. It signals this to rest of the world by negating the line BA. IMHO the only thing we have to do is to stop the counter and to tristate the busses.
- x Counter bits
- R1-1 Transfer type
- R1-0 Transfer type
- Result compare
- Disable R1-7
- Use $FF00-mechanism
- Reset $FF00-mechanism
- Restore addresses, counters
- Generate IRQ
To be continued.....
You can email me here.