New Memory Map

Now that I have ROL working and have been doing some programming, I have decided to modify the memory map. For one thing, I don’t need two EEPROMs. As mentioned previously, I am going to have the EEPROM code only run long enough to load the actual system code from a storage device into faster RAM. Ideally, I should only need a little EEPROM code to do this. 2K maybe. But EEPROM chips don’t seem to come that small in a parallel configuration. 8K is about as small as I can find. So, I’m just going to stick with an 8K EEPROM. For now. Maybe. I could, of course, set up the memory mapping such that only 2K of the 8K chip is exposed, leaving 6K hidden by some other chip that would presumably use that space. But I’m not really short on memory addresses, so I might as well leave all of the 8K exposed.

Up to now, I have used a couple of 74ACT138 chips to decode the address lines and I didn’t really care about running it very fast. The first decoder handled the main memory map and then if the address was in the I/O range, it would enable the second decoder to go a little more fine-grained. That still left some pretty large holes in the memory map that I wasn’t really happy with, but I kept it around while I worked out stability issues. “I’ll just deal with it later.”

Well, now it’s later.

I’ve seen a few other people using GALs to implement the address decoding in similar projects. Initially, I didn’t want to mess with it… I didn’t know anything about programming GALs or whether I could even get any in these modern times. Buuuut, the subject kept coming up as I was investigating better implementations for address decoding. Aaaand I thought of some other things I might be able to implement with them: namely, the paged memory mechanism and a simple IRQ mapper. I went ahead and ordered some ATF22V10 PLDs from Mouser, which I confirmed are the modern incarnation of the GALs I’d seen others using.

Those arrived yesterday and a TL866II+ I ordered from Amazon came today (Sunday evening), so I decided to dig in. My first obstacle was downloading WinCupl from Microchip. Doing this all from my Windows 10 laptop, since that is what WinCupl needs, I went to the download link and hit the “download button”. Of course, that didn’t work. That would be too easy. “That’s what they’ll be expecting,” said the Microchip people, in hushed, conspiratorial tones. This was from the latest version of Chrome, so I don’t know. I found that if you right-click the link and then copy the URL, then paste that into another browser window, it will actually download WinCupl. Here is the direct link to the download: http://ww1.microchip.com/downloads/archive/awincupl.exe. Clicking that here, now, on a MacBook Pro, in Safari seems to work. You may have to copy it to the address bar manually. The serial number to use with WinCupl is given on the Microchip site: 60008009.

Fortunately, WinCupl comes with a directory of example code. Unfortunately, the help system doesn’t work because Windows 10 doesn’t support it anymore. Officially, WinCupl is only targeted at Windows versions up to XP, and even that appears to be debatable. I did find out you can convert .hlp files online, so I did that and browsed over the help files a bit. They did have some useful content, but not enough that I felt comfortable diving in yet. So, I went online hunting down more information. That’s when I ran into this video. It was an excellent, clear explanation of this whole PLD thing. I read the accompanying article and saw some stuff at the end about galasm and minipro. I decided to go ahead and try those out.

I cloned galasm and compiled it pretty effortlessly on my MacBook Pro. It had some examples and some html documentation included, so I went over that. After having watched the aforementioned video, the documentation made sense and I finally felt comfortable enough to break out the PLDs I bought and shoved one into the venerable TL866II+. I roughed out my memory decode logic (naively, as it turns out), presented here:

GAL22V10 ; The type of chip this is written to
roldec.1 ; Up to 8 characters of descriptive text

; These map the pins of the ATF22V10 to names.
; CLK is pin 1, going down then to the right,
; then up to VCC as pin 24. So, GND is pin 12.
; A space between each label.
CLK A15 A14 A13 A12 A11 A10 A9 A8 A7 A6 GND
 A5  S0  S1  S2  S3  S4  S5 S6 S7 S8 S9 VCC

; S0 - S9 are the 10 output pins provided by 
; the chip. Each one will act as the chip 
; select line for a different chip.
; / = NOT
; * = AND
; + = OR (not used here)

; With that, here is the actual decoding logic:

; S0 is low (the "/") when A15 is low and 
; the clock is high. This is main RAM at
; 0x0000 - 0x7fff.
/S0 = /A15 * CLK

; S1 is low when A15 is high, A14 is low
; and the clock is high. This is a 16K
; window for paging in extended RAM at
; 0x8000 - 0xbfff.
/S1 = A15 * /A14 * CLK

; S2 is low when A15 and A14 are high and
; A13 is low. This is the first block of
; I/O at 0xc000 - 0xc01f.
/S2 = A15 * A14 * /A13 * /A12 * /A11 * /A10 * /A9 * /A8 * /A7 * /A6 * /A5

; S3 is low when A15 and A14 are high and
; A13 is low, then all other pins are low
; and A5 is high. This is the 2nd block 
; of I/O at 0xc020 - 0xc03f.
/S3 =  A15 * A14 * /A13 * /A12 * /A11 * /A10 * /A9 * /A8 * /A7 * /A6 *  A5

; S4 is low when A15 and A14 are high and
; A13 is low, then all other pins are low
; except A6. This is the 3rd block of
; I/O at 0xc040 - 0xc05f.
/S4 = A15 * A14 * /A13 * /A12 * /A11 * /A10 * /A9 * /A8 * /A7 * A6 * /A5

; S5 is low when A15 and A14 are high and
; A13 is low, then all other pins are low
; except A6 and A5. This is the 4th block 
; of I/O at 0xc060 - 0xc07f.
/S5 = A15 * A14 * /A13 * /A12 * /A11 * /A10 * /A9 * /A8 * /A7 * A6 * A5

; S6 is low when A15 and A14 are high and
; A13 is low, then all other pins are low
; except A7. This is the 5th block of
; I/O at 0xc080 - 0xc09f.
/S6 = A15 * A14 * /A13 * /A12 * /A11 * /A10 * /A9 * /A8 * A7 * /A6 * /A5

; S7 is low when A15 and A14 are high and
; A13 is low, then all other pins except
; A7 and A5 are low. This is the 6th 
; block of I/O at 0xc0a0 - 0xc0bf. This
; is mapped to the UART.
/S7 = A15 * A14 * /A13 * /A12 * /A11 * /A10 * /A9 * /A8 * A7 * /A6 * A5

; S8 is low when A15 and A14 are high and
; A13 is low, then all other pins except
; A7 and A6 are low. This is the 7th 
; and final block of  I/O at 0xc0c0 - 0xc0df.
/S8 = A15 * A14 * /A13 * /A12 * /A11 * /A10 * /A9 * /A8 * A7 * A6 * /A5

; Finally, S9 is low when A15, A14 and A13 
; are all high. This is 8K eeprom at
; 0xe000 - 0xffff.
/S9 = A15 * A14 * A13

; Here is some descriptive, free-form text.
DESCRIPTION
Address decoding logic for ROL.

I compiled this with galasm, simply doing “galasm roldec1.pld”. It ran without issue and generated four output files. I copied those over to my Windows 10 laptop and started up “Xgpro”, which is the software supplied with the TL866II+. I clicked “Select”, clicked the “PLD/GAL/CPLD” checkbox, chose “ATMEL”, then selected “ATF22V10C from the list of devices. Once that was done, I went to “File”, loaded the “roldec1.jed” file and then “Device”/”Program”. The TL866II+ quickly erased and burned my definitions.

I took the chip out of the ZIF and plugged it into the new breadboard where I’m building the circuit. I hooked up +5V and GND and got some wire to test with and initially connected all the inputs to GND. I then methodically moved the pins to +5V in order, testing each memory range.

Everything went perfectly until I got to the I/O. I noticed that if the A5 pin was low, I would get the correct response. If I set it high, it would set the proper output line low, but also the next line!

I did some more reading and realized each output has a different maximum number of AND terms that can be used with it. I also realized that pins A12 through A8 didn’t actually contribute to the address decoding, so I was able to simplify the logic and bring it down to the term limits for the pins:

GAL22V10 ; The type of chip this is written to
roldec.1 ; Up to 8 chars of descriptive text

; These map the pins of the ATF22V10 to names.
; CLK is pin 1, going down then to the right,
; then up to VCC as pin 24. So, GND is pin 12.
; A space between each label. NC is "NO
; CONNECT".
CLK A15 A14 A13 A7 A6 A5 NC NC NC NC GND
 NC  S0  S1  S2 S3 S4 S5 S6 S7 S8 S9 VCC

; S0 - S9 are the 10 output pins provided by 
; the chip. Each one will act as the chip 
; select line for a different chip.
; / = NOT
; * = AND
; + = OR (not used here)

; With that, here is the actual decoding logic:

; S0 is low (the "/") when A15 is low and 
; the clock is high. This is main RAM at
; 0x0000 - 0x7fff.
/S0 = /A15 * CLK

; S1 is low when A15 is high, A14 is low
; and the clock is high. This is a 16K
; window for paging in extended RAM at
; 0x8000 - 0xbfff.
/S1 = A15 * /A14 * CLK

; S2 is low when A15 and A14 are high and
; A13 is low. This is the first block of
; I/O at 0xc000 - 0xc01f.
/S2 = A15 * A14 * /A13 * /A7 * /A6 * /A5

; S3 is low when A15 and A14 are high and
; A13, A7 and A6 are low and A5 is high. 
; This is the  2nd block of I/O at 
; 0xc020 - 0xc03f.
/S3 = A15 * A14 * /A13 * /A7 * /A6 * A5

; S4 is low when A15 and A14 are high and
; A13, A7 and A5 are low and A6 is high.
; This is the 3rd block of I/O at 
; 0xc040 - 0xc05f. 
/S4 = A15 * A14 * /A13 * /A7 * A6 * /A5

; S5 is low when A15 and A14 are high and
; A13 and A7 is low and A6 and A5 are 
; high. This is the 4th block of I/O at 
; 0xc060 - 0xc07f.
/S5 = A15 * A14 * /A13 * /A7 * A6 * A5

; S6 is low when A15 and A14 are high and
; A13, A6 and A5 are low and A7 is high.
; This is the 5th block of I/O at 
; 0xc080 - 0xc09f.
/S6 = A15 * A14 * /A13 * A7 * /A6 * /A5

; S7 is low when A15 and A14 are high and
; A13 and A6 are low and A7 and A5 are
; high. This is the 6th block of I/O at 
; 0xc0a0 - 0xc0bf. This is mapped to the 
; UART.
/S7 = A15 * A14 * /A13 * A7 * /A6 * A5

; S8 is low when A15 and A14 are high and
; A13 and A5 are low and A7 and A6 are
; high. This is the 7th block of I/O at 
; 0xc0c0 - 0xc0df.
/S8 = A15 * A14 * /A13 * A7 * A6 * /A5

; Finally, S9 is low when A15, A14 and A13 
; are all high. This is 8K eeprom at
; 0xe000 - 0xffff.
/S9 = A15 * A14 * A13

; Here is some descriptive, free-form text.
DESCRIPTION
Address decoding logic for ROL.

I compiled this and burned the resulting .jed file on the chip, put it back in the breadboard and tested again. This time it worked perfectly!

My next step was to implement the IRQ routing logic in a similar manner, along with some miscellaneous control signals, like the UART write enable/read enable, UART reset, and RTC output enable which are all derived from various 6502 outputs and were previously implemented in 4 logic chips (counting the IRQ logic).

After all of this, I had replaced five or six chips with two. Each I/O chip takes up 32 bytes of memory. As you can see in the map below, that leaves quite a bit of empty space from $c0df to $e000 currently. At least that gives me some room for expansion.

ROL memory map

Things are running quite well at 2MHz and almost, but not quite, well at 4MHz. I’m getting a few random garbled characters going out the UART at 4Mhz. Speaking of the UART, now that the SC28L92 is running stably, I find myself not using the Arduino monitor. I went ahead and took it out of the circuit completely.

Next, I’m working on getting I/O with an SD card working over SPI. For now, I’m bit-banging it with the 6502. Once I get that working, I can start concentrating a little more on the purely software side of things. I’m anxious to get a boot loader going and native development system that supports loading and running relocatable executables in RAM.$c

Once things get a bit more refined, I’ll put the KiCad files and code on GitHub.

Leave a comment

Your email address will not be published.