If I attach a 16 KIB EEPROM to a 6502 or similar, and put some kind of operating system on it, it will run fine, but won't have access to any other chips. So, when a Commodore VIC20 had 5k of ram and 8+8k of ROM, how did it make the rom/ram chips know that its their turn and how did it translate the 16 bit adress signal into a signal in their range? And if it accomplishes that, that explains how memory mapping worked, because it can easily switch chips.
First note that in "real" retro computers the amount of address lines on a memory chip is generally much lower than the address lines the CPU has. This means that you won't be able to "fill" the CPU's address space without using some more logic, and, obviously, multiple memory chips.
Beyond its address and data lines, every memory chip has some more control lines that define its bus interface and are intended to connect to some address decoding logic.
- /CE - Chip Enable. This is the line that allows the CPU to signal "Yes, I'm talking to YOU" to the memory chip. If this line is not asserted, the chip won't do anything at all, just silently sit on the bus.
- /OE - Output Enable. This line allows the CPU to actually read from the memory chip. When asserted, the memory chip will evaluate the address lines and put the bits addressed by the address lines onto the data bus
- /WE - Write enable. This line, if present (on writable memory), will tell the chip to put the contents of the data lines to the adrerss as signalled
What you see here is the pinout of a 28C16, a 2K x 8 EEPROM. As you can see here, this guy has 11 address lines that connect directly to the CPU's address bus and 8 data lines intended for the data bus. If you connect only those, the chip won't do anything. The important bits are with the /W, /E, and /G lines (/W, /E, and /G are vendor-selected synonyms for the above /WE, /OE, and /CE signals).
/W is the /WE from the explanation above - It tells the chip the CPU wants to write
/E is the /CE from above, enabling the chip ("Yes, I'm talking to YOU")
and /G is the /OE from above, telling the chip to put data on the data bus.
Now let's assume you want to place 2 of these chips into the upper address range of the 6502, the first one covering the address range from 0xF800-0xFFFF, the second one directly below that, from 0xF000-0xF7FF. Obviously, we need to look at the CPU's address lines not connected to the memory, and generate some logic to provide the /CE signals of the chips (/WE and /OE can trivially be connected with some logic to the 6502's R/W signal).
The 5 upper address lines you cannot connect directly to the memory will have a value of 0b11111 for the upper ROM and 0b11110 for the lower one. A circuit like the following would provide the /CE lines for the upper and lower ROM (Note this is non-optimized and very probably non-practical, just explaining the concept):
If you put these two pictures together, you'll end up with a system that has two EEPROMs of 2kBytes each in the upper address space of the 6502.
I'm not really sure how this question is meant, so this is maybe less of an answer as an attempt to understand the question first and answer accordingly.
If I attach a 16 KiB EEPROM to a 6502 or similar, and put some kind of operating system on it, it will run fine, but won't have access to any other form of memory.
You may still need logic to assign the ROM to some memory location, as well as to access other memory or I/O (which is memory-mapped on a 6502 anyway). Also, a 6502 won't be of much use without at least a little bit of RAM. Similarly, some I/O to let it spread its message (*1) may be a good idea.
So, when a Commodore VIC20 had 5k of ram and 8+8k of ROM, how did it make the rom/ram chips know that it's their turn, and how did it translate the 16-bit address signal into a signal in their range?
Each of these needs only a part of the default address range on a 6502 (or other CPU with 16 bit address space). 5+8+8 KiB = 21 KiB, which fits nicely into 64 KiB even leaving much space unused. Each of these memory chunks (RAM, ROM, ROM) need only a maximum 13 bit wide address (2^13 = 8096). So the remaining 3 bits of an address (usually the highest value ones) can be used to generate a select signal to each of these areas.
Most common is the use of a 74138 1-of-8 decoder, which turns these 3 bits into 8 lines that could select either memory (or I/O) chip.
Keep in mind, on a 6502 ROM must go up (for Reset vector access) and RAM down (for ZP and Stack). So RAM would be the uppermost two. So the lowest output would be fine to select the RAM, while the ROM needs to be hooked to the uppermost. As for now, the use of 8 KiB may be well enough - and simplify the setup much - so the highest address bit will be unused and should be grounded (*3,4).
Next to add RAM, for example a 6264 which gives you convenient 8 KiB that could be directly hooked up to the lowest 74138 output without further logic.
The remaining 74138 output lines could be used as a very crude output logic by attaching six LEDs. Now every access between
DFFF would light one of these diodes for a little moment. Doing this in a loop will make them light continuously. Really is a simple 4 chip 6502 system able to tell you its existence with Blinkenlights :))
And if it accomplishes that, that explains how memory mapping worked, because it can easily switch chips.
No idea what you want to ask here.
*1 - Read some display, even if it's just a few led and inputs - or a keyboard.
*2 - Using the VIC20 as an example is a bit dangerous, as its specific memory decoding is a bit complicated, and its use even more.
*3 - Using a switch instead of grounding would even allow to have two 8 KiB ROM versions online and accessible in a flip of a switch.
And replacing the switches by some latch would make a first step to ROM or more general memory management. But that's a different story.
*4 - To assign the full 16 KiB EEPROM, two 74138 lines need to be joined as CS (chip select). Since they are low active, an AND gate will do the trick.
Common 8-bit CPUs have sixteen address lines and eight data lines appearing on their own dedicated pins on the chip; these are connected in various ways to other devices in the system to form the system address bus and data bus.
Memory and other devices have their own sets of address and data pins, which will be connected in various ways to the address and data buses, and also "chip select" lines that determine whether the chip will read the address bus and read or write the data bus, or just ignore both.
As an example here I'll use a common 27128 16K EPROM. This has:
- Eight data pins,
D7, which you would connect directly to the system data bus.
- 14 (not 16!) address pins,
A13, which you would connect directly to lines
A13of the system address bus.
- Active-low chip select
C̅S̅and ouptut enable
O̅E̅pins. The overbars indicate that the signal is active when low, i.e., bring it down to ground level to select/enable the chip and up to +5 V to disable the chip, making it ignore the buses.
C̅S̅makes the chip read the address bus and
O̅E̅makes it write the data bus; here I won't get into the reasons for separating these two functions.
P̅G̅M̅signal for programming the ROM which in this example we will assume is always disabled (high).
Now if you connect the data and address lines as above and hardwire
O̅E̅ to ground, the chip will always be enabled and will have
no knowledge of what's happening on lines
A15 of the
address bus. Any read by the CPU will return data from the EPROM.
Since the EPROM ignores the top two address lines, reads from any of
the four addresses
0xA000 will return
the data from location 0 of the ROM. (These are usually referred to as
"mirrored" locations since, though they are different addresses from
the point of view of the CPU, they all access the same location in the
If you want to add a second EPROM to the system, you need to have some
way of choosing, based on the address the CPU puts on to the bus, the
device from which you wish to read. You can do this by adding a bit of
extra logic, an inverter, to the board. Connect
A14 directly to
on EPROM #0 and run it through the inverter to
C̅S̅ on EPROM #1. Now:
- Reading from address 0x0000 (or address 0x8000) will make
C̅S̅low (enabled) on #0 and, because it's going through the inverter, high (disabled) on #1, and the data you read will come from EPROM #0.
- Conversely, reading from address 0x4000 (or address 0xA000) will
C̅S̅high (disabled) on EPROM #0 and low (enabled) on #1, and the data you read will come from EPROM #1.
This can be extended further with devices like a 74LS139 chip, which
can translate a pair of address lines that represent values 00, 01,
10, and 11 in binary (0, 1, 2 and 3 in decimal or hexadecimal) into
individual signals that can be sent to the
C̅S̅ pins on more chips.
The '139 has inputs
B for the binary input and active-low
Y3. Each output is enabled (brough low)
by a specific binary input on the
B, with the rest of the
outputs disabled (high), as represented in this table:
Inputs | Outputs
A B | Y0 Y1 Y2 Y3
0 0 | 1 0 0 0
0 1 | 0 1 0 0
1 0 | 0 0 1 0
1 1 | 0 0 0 1
(There are actually more pins on this chip, both for a second decoder that works just like the one above and for chip select, but for simplicity I ignore these in this example.)
So you can see that you can connect
A15 to inputs
B on the '139 and connect each output
Y3 to the
pin on each of four EEPROMs and now you have a unique 16-bit address
for each of the 14-bits with of address space in each of the four
memory devices, the appropriate one being selected by the values of
A15 address lines as set by the CPU.
And that's the basics of address decoding. Usually decoding will be
more sophisticated than this: you need to deal with the read vs. write
R/W̅) signal on systems with RAM, and you'll usually have I/O
devices such as keyboards or serial ports that need to be decoded too,
but all that works with simple expansion of the principles above.
There are plenty of online examples of address decoding systems for 8-bit microcomputers. Two good ones are:
- Grant Searl's 6502 computer is a fairly simple but complete
computer system that has address decode logic for a 16K ROM, 32K
static RAM and memory-mapped I/O for a serial interface. This uses
just four NAND gates to map
R/W̅to the three devices. (
φ2is also involved; this is a clock signal that indicates when valid data to be written is on the data bus.)
- The Wilson Mines Co. 6502 PRIMER: Building your own 6502 computer has a full page with extensive details of how to do address decoding.
One further note: here I've discussed only memory address decoding, which is all you need on CPUs like the 6502 where I/O devices are accessed via memory reads and writes. Other processors, such as the 8080/Z80 have special instructions and their own address space to do I/O (that is, "IO port 0" is separate from "memory address 0"); for these you need a bit of further decoding if you want to use this I/O space. But it works pretty much the same way as straight memory-only address decoding, except you need to look at an additional "I/O" pin on the chip to see if it's using the I/O address space instead of the memory address space, and build that into your decoding.
The most common solution was to have a bank switching register. The register would act like extra address lines, switching RAM and ROM and other chips in and out of the CPU's address space.
Some systems had the bank switch register mapped into memory somewhere, meaning they could only use part of the available address space for switching. Others used extra ports on the CPU designed for this kind of thing, effectively adding an extra bus separate from the main memory one that was used for bank switching and other important functions that the CPU might need to access often.
An example of this is the Commodore 64. It has a 16 bit address space covering 64k, and also 64k of RAM. A bank switch register allows part of that space to be switched between RAM and ROM/video registers.