Alpha: Memory subsystem

Photo by Tyler Daviaux / Unsplash

The processor circuit itself, as we designed it in the last chapter, is of no use. Respectively - you can plug it in, connect the data bus via resistors (ca 10k) to ground and watch the 1 and 0 change on the highest bit of the address (A15), because the processor reads the whole memory over and over again from 0000 to FFFFh, finds the operation code 00 everywhere (that's the resistors connected to ground), it means "do nothing and load the next instruction", so the contents of the PC register (Program Counter) increases by 1 and the whole cycle repeats. If you connect an LED to A15 via a resistor, it should flash about 7 times per second. This means that the entire memory range can be traversed by the processor seven times per second at a given frequency.

You've just created an amazing 16-bit counter. But that's probably not what we want. We need to make sure the processor has something to perform. We have to give it memory to hold the program and the data.

The processor, once it's running, has no mechanism to, say, take a program from somewhere else and load it into memory. You have to take care of that yourself. And the most common way is that you use ROM (more likely EPROM or EEPROM/FLASH), which stores some basic control program.

This program was first a so-called bootstrap, i.e. a short program (512 bytes for example) that could read data from a tape, from a card reader, from a hard disk, from anywhere, load it into memory and run it. A similar approach can be seen with Arduino: here, for example, there is a bootstrap loader stored in the processor, which waits if the computer does not accidentally send some (exactly given) data. If it does, it switches to programming mode and loads the incoming data into memory.
Later, the bootstrap became a Monitor. The Monitor was a slightly larger program that already communicated with the operator and could execute simple commands, e.g. to view memory, change it, load a program from external memory and run it. If you remember the PMD-85 computer in its first version, it was the monitor that announced itself when it was switched on. You could use MGLD to load something from the tape recorder into memory, run it using JUMP, or download and run BASIC G from an external rompack
On some computers, mostly home computers, the monitor wasn't even used anymore, it just started the higher language (BASIC).
Professional computers, for example for CP/M work, slowly reverted to small service programs. They weren't called bootstrap, they were too big for that, and they weren't called Monitor, because they didn't communicate with the operator. The abbreviation BIOS took hold, after one of the basic parts of CP/M (Basic Input Output System). BIOS is more like a collection of routines and programs that control the hardware of a particular computer. The system does not have to deal with exactly where and how the disk is connected, the routines in the BIOS know that. After boot, the CP/M loader is started from the BIOS.
For single board computers, the "Monitor" model was used. Such a monitor could fit into a 1 kB ROM. To do this, the single board offered, for example, 1 kB of RAM, and there it was!

I wanted to keep to the spirit of the times, but I admit that the memories of that time, for example 2kB EPROM and 1kB SRAM, are nowadays partly unavailable, partly available, but they are miserable to work with... I really don't want to force you to find an ultraviolet EPROM eraser!

That's why we don't use EPROM, we use EEPROM, which can be erased and programmed more easily.

Bus exciter

But before that, it would be nice to make one adjustment. That is to strengthen and separate the data bus. Processors generally don't have very powerful output circuits, and if you connect more circuits to the data bus, you overload it. With two memory circuits, an ACIA circuit, and an address buffer, we're already on the edge. So before we add anything, we'll connect a booster.

By the way: the rule for low power and low bus load is: Use CMOS wherever possible. Avoid TTL circuits (74, 74LS, 74ALS, etc.) and look for CMOS versions (74HC, 74HCT, 74ACT). The same applies to peripheral circuits - for example, the PIO 8255 circuit discussed below can be found in the CMOS version 82C55, which has lower power consumption and hardly loads the bus. CMOS versions are usually fully compatible with their predecessor, sometimes they are slightly improved...

We will use the 74HCT245 circuit, which is an eight bidirectional buffers and exciters. The DIR input determines the direction of data flow (0= from B to A, 1= from A to B), the G input switches the circuit to the high impedance state (if the G=1 input, the circuit is disconnected and both A and B outputs are in the high impedance state).

I have connected the G input to the ALE signal, this way when the processor sends the lower part of the address on the bus, the circuit is disconnected and does not interfere. So if some peripheral circuit wanted to send data at the moment when the processor is latching the lower part of the address (ALE=1), the data bus is decoupled.

Due to this circuit, at most two circuits are connected to the data bus, and due to the ALE signal they are connected "opposite" each other, i.e. only one of them is always active. The whole "processor" part looks like this:

(EEP)ROM

I know, it's not exactly a simple thing to have lying around at home, but I recommend either buying an EEPROM memory programmer or building one from an Arduino MEGA. No need to reinvent the wheel, tutorials already exist and usually you just need to connect the pins from the MEGA to the memory pins: https://danceswithferrets.org/geekblog/?page_id=903 - I do it just like that, use a non-soldering contact array, connect that to the Arduino Mega, then plug the memory into the array and program like clockwork.

We will use EEPROM of 32 kB. These memories are manufactured under the 28C256 designation (e.g. AT28C256 according to the manufacturer). They are well available and have a suitable DIP housing.

Data pins D0-D7 need no introduction. A0 to A14 is our familiar address bus (15 bits = 32 kB) and the three signals are used to control the memory. The /WE enables writing (we hardwire it to log 1 in the system to disable writing - we program the memory in the programmer). /CE (Chip Enable) tells the memory to respond to instructions -if 0, the memory responds, if 1, the memory is disconnected. Finally, /OE (Output Enable) enables reading from the memory, i.e., a value stored in the memory at the address given by the address inputs is exposed to the female outputs IO0-IO7.

Yeah, but what if we only need 8 kB of ROM? In that case, you simply connect inputs A13 and A14 to the log. 0 (ground), and three quarters of the memory space will be unused. This way of wiring is sometimes used intentionally - for example, you have four alternative firmwares in one memory, and either by mechanical switches or by some internal logic, you can choose which firmware you want to use. (Some computers had this too - remember the ZXSpectrum 128 and its two BASICs?)

RAM

We will use RAM of type 62256. Its pins are distributed as follows:

A quick glance reveals that both memories are wired exactly the same. Except for some differences in the markings. Instead of /CE(Chip Enable) the signal is /CS (Chip Select), but the meaning is the same.

Memory subsystem and addressing

So, we have two pieces of memory, each with a size of 32 kB, and one processor with an address space of 64 kB. It is entirely possible to divide the available space into two halves, one going from 0 to 32767, the other from 32768 to 65535, with ROM in one and RAM in the other. Question for the sharp minds: where will be which memory?

The answer is hidden in the previous paragraphs: after RESET, the processor starts executing the program from address 0000h, so at this address should be the ROM. Ergo ROM will be at addresses 0000h – 7FFFh, RAM at addresses 8000h – FFFFh.

Now: how are we going to connect? We will connect both circuits in exactly the same way: the data bus to the processor data bus, the addressing signals A0 to A14 to the corresponding pins of the processor. We'll leave the A15 pin of the processor aside for now, we'll use that later.

Connect the /OE (Output Enable) signal to the /RD signal. Recall: The /RD signal tells the processor that it will read. Input/OE for memory is used to enable the output of stored information to the data bus. This is actually exactly what we need: when the processor wants to read, it sends the /RD signal, and that „opens&#8220the output of our memory.

Analogously, we connect the /WE (Write Enable) input of the RAM to the /WR signal of the processor. Only for RAM, not for EEPROM. We probably don't want a random error in the program to overwrite the contents of the EEPROM…

The last input left is Chip Select (/CS, marked as /CE for RAM, but the function is the same). This is the „universal enable input“. If it is inactive (log 1), the memory ignores everything that happens on the other pins. If it is active (log. 0), it performs read and write operations according to the signals described above.

Since we have connected the data outputs of both memories in parallel, it would be nice to somehow ensure that only one memory sends its information at a time. Which one? Well, that depends on the state of the A15 driver.

Wire A15 is in log. 0 when the processor accesses addresses 0000h – 7FFFH, in log. 1 if it accesses the upper half of the space. Thus, if A15 = 0, EEPROM should be selected, if A15 = 1, RAM should be selected.

But not always! Only when the processor accesses the memory! Remember – this is what the IO/M signal is for. 0.

Thus summarized:

/CE if
/CE for EEPROM (/ROMCS) active (0) A15 = 0 and IO/M = 0
/CE for RAM (/RAMCS) active (0) A15 = 1 and IO/M = 0

For EEPROM memory it is the logical function A15 OR IO/M, for RAM memory it is (NOT A15) OR IO/M.To avoid using OR and NOT gates unnecessarily, we use one 74HCT00 IC - 4 x NAND, as follows:

/ROMCS = (NOT A15) NAND (NOT IO/M)

/RAMCS = A15 NAND (NOT IO/M)

One gate will invert IO/M, the second will invert A15, the third will produce the /ROMCS signal, the fourth will produce the /RAMCS signal.

And there we have it! The complete schematic is here:

You can try to create the first program, load it into EEPROM and let the LED on the SOD output blink.

Comments powered by Talkyard.

Martin Maly

Martin Maly

Programmer, journalist, writer and electronic hobbyist. Vintage CPU lover. Creating new computers with the spirit of 80's.
Czechia