In terms of hardware design, I realised that there are not really that many parameters one can play with when using off the shelf components. The point is that things like the 6502, 6526, 6522, 9918 etc. are by design expecting certain inputs and outputs, so they set out some fundamental design constaints.
The general input, output and interfacing capabilities provided by the 6522s (two of them) and the memory map design are the two most significant areas for creativity.
NEW Memory Map
For the old memory map, see that heading.
The reason for this new section is that I had the old memory map running since basically the beginning, and has served well for more than a couple of years. So why change it? Well, a number of reasons:
- I have a 128K RAM chip and 64K ROM chip
- Old memory map only addresses 48K of RAM and then minus 4K window for memory mapped IO
- Old memory map only addresses 16K of ROM
- Less than 1K of ROM remaining, limiting further ideas for functionality such as extending the interpreter (more hi-res commands, floating point support) and OS (better support for FAT, bigger cards etc.)
So I went ahead and came up with a new memory map with the following features:
- Smaller IO window of 1K still supporting 8 devices in 128 byte sections
- Any write to ROM space will write to the shadow RAM at the same address
- Ability to disable ROM to get full read and write access to RAM across whole 64K (except IO window of course)
- Ability to map top 32K of RAM to any 4 positions in RAM, so having access to all 128K
- Ability to map top 16K ROM access to any 4 positions in ROM, so having access to all 16K
The way I have done this is to take spare lines from the two 6522s I have, one will be ROM disable, two will be to select the RAM bank and the other two to select the ROM bank. The actual decode logic is in the downloads.
I now have plenty of opportunity to expand my project, especially so with the additional ROM space.
However although the hardware is done, I need to reorganise the software radically for example:
- One cannot simply change from one ROM bank to another, it would be pulling the rug from underneath the CPU. It has to be organised so that the CPU is still executing expected code after a switch. The easiest way to do this is have common switching routines across all ROM banks
The new decoding logic is available to view in the downloads section. It will take a while to reorganise the software though e.g.:
- Create a new common section
- Determine what banks will have what responsibilities e.g. BIOS, File System, Interpreter, Graphics, Sound, Utilities
- Rebuild the make file to assemble images in to a 64K ROM (at the moment I am simply copying the same 16K four times, so I have no extra usable ROM space)
It will take a little thinking through but once implemented, I will have loads of available ROM to fill - should keep me busy with the project for a many more months!
OLD Memory Map
The memory map was one of the first and most interesting areas of investigation. The 6502 has a 64KB address range - so all my RAM, ROM and memory-mapped devices need to fit in to that. Being old-school, I wanted to try and put together a map which maximised RAM without overly compromising ROM and memory-mapped device access.
In the very early incarnations of the homebrew, copied the simplest address decoding approaches I found in other designs:
- If A15 is zero, then select RAM
- If A15 is one and A14 is one then select ROM
- if A15 is one and A14 is zero then select devices
So this gives 32KB RAM, 16KB ROM and 16KB devices space. I had a board working with this configuration, but it was just to allow me to prove I had wired things up.
I went for an addressing scheme which would give me 44KB RAM, 4KB devices (8 512 byte IO blocks) and 16KB ROM. It was fun coming up with the decoding logic for this and then working out how to implement this using some NAND and NOT gates.
Section | Type | Address Range | Decode rule A15-A12 (binary) |
A | ROM | 0xc000 - 0xffff | Between 1111 - 1100 |
B | IO Blocks | 0xb000 - 0xbfff | 1011 only |
C | RAM | 0x0000 - 0xafff | Between 1010 - 0000 |
The decoding of these address line (A15 to A12 for the section select, A11 to A09 for the IO block select) needs also to take in to account the chip select lines available. Doing this would hopefully reduce the complexity of the decoding a little. I am using a W27C512 for ROM, 628128 for RAM and a 74xx138 3 to 8 decoder for the IO block select. These chips have the following select lines:
- ROM : /CE, /OE
- IO BLOCK (74xx138) : /E1, /E2, E3
- RAM : /CS1, CS2
In addition to the A15-A12 lines, the additional signals are also needed from the 6502:
- PHI2 (this is low when the 6502 has control of the data and address lines)
- R/W (this is high when performing a read, else it is a write)
The analysis I did resulted in the following table, which showed that I need 3 NOT and 6 NAND gates to implement the decode (plus the direct use of A14 which doesn't need a gate). I am sure someone can do better than, this but hey this is is my first custom memory map decoder, and it seems to work, so I'm happy - but any suggestions would be interesting. Note that Gate #1 Input A did have Phi2, but this is wrong, I don't need Phi2 to supply the RMO and IO decoder select - this is why my 6522 didn't work as I was not allowing devices to be selected until Phi2 was already high, whereas most 65xx family devices need the chip select to be activated before Phi2 goes high. Gate #6 is still correct - RAM must not be selected when Phi2 is low incase of inadvertent writes before address lines have become valid.
Gate # | Type | Input A | Input B | Output |
1 | NAND | +5V | A15 | ROM /OE, IO /E1 |
2 | NOT | A14 | ||
3 | NAND | A13 | A12 | |
4 | NOT | Gate #3 | IO E3 | |
5 | NAND | A15 | A14 | |
6 | NAND | Phi2 | Gate #5 | RAM /CS1 |
7 | NAND | A15 | Gate #2 | |
8 | NOT | Gate #7 | ||
9 | NAND | Gate #8 | Gate #4 | RAM CS2 |
10 | NAND | A14 | R/W | ROM /CE |
In addition, A14 feeds directly to /E2 for the IO decoder, and in addition to the chip select lines for RAM, the 6502 R/W line feeds directly to the /WE line (i.e. low for write, high for read).
Device Selection
As mentioned, the /E1, E2 and E3 outputs enable the 74xx138 decoder. This basically then takes A11, A10, A09 as inputs and selects one of 8 outputs (active low). So therefore I have 8 addressable devices I can select. This should be plenty, as I can't think of more than 8 different devices:
- Block 0 (0xb000) : 1st 6522 VIA
- Block 1 (0xb200) : 2nd 6522 VIA
- Block 2 (0xb400) : TMS9918 VDP
- Block 3 (0xb600) : 6551 ACIA (planned - low priority as I already have functional serial I/O)
I had hoped to make the AY-3-8910 a memory mapped device, but this is not going to be directly accessible due to the very long strobe and hold times that the datasheet is specifying. So this will be through the 2nd 6522 Port A for data transfer, two lines from port B to control access).
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.