Programming the memory and I/O address decoder PLD

Update:  It has been decades since I worked with and programmed the Z80’s.  In my “original” design of the address decoder, I used address lines addr[15:10] for decoding into 1K “pages” when in fact, I should have decoded A15 for the RAM chip selects and addresses A7-A4 for the I/O addresses … because the Z80’s I/O instructions are 8-bit only.  I sort of recall that being the case but the Z84C0020 (Z80) datasheet doesn’t outright say “hey 8-bit addresses for I/O instructions and 16-bit address for memory instructions”.  You have to look at the IN and OUT instructions to see that they supply an 8-bit operand.  

I only need A15 to select between the two 32K RAM chips but need at least A7 and A6 to decode for 4 I/O devices but since I had the pins available, I choose to use A7 to A4 to decode for 16 I/O devices.

Anyhow, I corrected the CUPL code for the address decoder and the following CUPL code (below) is the corrected code.  I thought it best to replace the posted code here rather than add it to subsequent another post and possibly confuse folks that are using my CUPL code as a template for their own.

[Original Post follows]

One of the more difficult parts of the design of this Z80 computer was the programmable wait-state generator. With that out of the way, I was able to proceed back onto the address decoder SPLD.

Most Z80 “homebrew” designs use the typical 74HC138 and 74HC139 3-of-8 and 2-of-4 decoder IC’s to decode the upper 3 (or 2) address bits of the address bus A[15..A13]. These parts are readily available and MOUSER.COM has them for about US$0.45 ea in single-piece quantities. I would surmize that many “Z80 homebrew” designers do not have access to programmable logic and/or a programmer. Although the prices for a decent China-made USB programmer have come down substantially. I recently purchased an AUTOELECTRIC TL-866CS programmer off eBay for US$45.00, which also included some PLCC-to-DIP adapters. The web site for the manufacturer is http://www/

I happened to have some SPLD’s from years ago when I was a Distributor level Field Applications Engineer (FAE). LATTICE, XILINX and ATMEL were among my support lines and I received training on all three OEM’s products but that was nearly two decades ago. Anyhow, back to the address decoder… Since I had snuggled the programmable wait-state generator into the GAL22V10 with no output pins available, I had to use a separate SPLD for the address decoder. I only needed 4 chip-select outputs but I also wanted to replace the few inverters being used in the 74HC04 I had designed in. With 10 inputs and 8 outputs available, as either combinatorial or registered outputs (or a combination of both), I set out to define my desired pins. I needed to decode addresses for both memory and I/O devices, so the Z80 MREQ and IORQ lines were required. Technically, since my RAM chips are 32Kx8, I really only needed to use the high-order address bit A15 but I decided to use a finer granularity than that, so I used A10 to A15, which yielded a 1K granularity for memory and I/O. I chose to buffer the AVR’s OC0A output, which is being used to drive the Z80’s clock line and since some peripherals may need an active high RESET signal, I brought that in as well. The total number of inputs was 10 and I ended up with 6 outputs. I still have 2 I/O pins available in case I decide to add more logic functions or add another peripheral before I design and cut PC boards.

After developing the combinatorial logic, I developed the simulation input data. Running the simulation, I could see I made a few mistakes in the simulation input file. Once corrected, all seemed to function as anticipated.  I chose the following address mapping:

RAM1_cs = 0x0000 to 0x7FFF 
RAM2_cs = 0x8000 to 0xFFFF 
AVR_cs  = 0x0000 to 0x00FF 
PIO_cs  = 0x0100 to 0x01FF
IO0_cs  = 0x0200 to 0x02FF
IO1_cs  = 0x0300 to 0x03FF


Current “functional test board”. Z80, ATmega328P, AT28C256, GAL16V8 and GAL22V10.

Currently, I have a solder-less breadboard that contains a Z80 (CPU), an AT28C256 (EEPROM), an ATmega328P (AVR), a ATF16V8B (Address decoder) and a GAL22V10D (programmable wait-state generator). I have room to eventually add the TEENSY 2.00++ and the 74HC299 but the wiring of the address, data and control buses is going to get unwieldy so I may just jump into designing the PC Board. Since the main design issues have been resolved, I feel a bit better about dropping US$100 on 5 prototype boards.

I would however, like to write a test program to exercise and functional test the address decoder for the AVR and PIO access. Since the AT28C256 is an EEPROM, I can remove it, program it with native Z80 code and run the code from it.

I would like to get one of the Z80 development software environments running, which is the Z80 assembler, C-compiler and a simulator. I’ve been playing with Z88DK but I cannot get the CP/M simulator to build, which is likely because the library build scripts fail. The Z88DK forum registration seems to work but then no confirmation email is ever sent. Any email sent to the web site bounces back as “undelivered”. There is a GOOGLE group that the RC2014 users started and there seems to be some group activity going on since it was only started in May of 2016.  I will post and look for some input from those folks.  The web page is at!forum/rc2014-z80 if anyone is interested.

In looking at my programmable wait-state generator pinout, I see that I need to add the AVR chip-select into the logic equations. As of the moment, the current design code will insert wait-states (if needed) on ALL I/O accesses (read or write and all addresses), whereas it is only intended to be for the slower AVR “peripheral emulation”.  Although I do not see it being a problem for the 82C55A, I do not think it is necessary and it’s any easy fix.

Next up, developing Z80 test code to exercise the AVR and PIO chip-selects.

Here’s the (updated) CUPL code for the address decoder:

Name Z80_IOdecode-1V10;
PartNo Z80C;
Date 10/18/2016;
Revision 01;
Designer Quest, Johnny
Company JQ;
Assembly Z80 Computer;
Location U4;
Device g16v8a;

/***** Z80 Address Decoder ******************************
 *             ______________
 *            |  Z80_Decode  |
 *  rst_l x---|1 I         20|---x Vcc
 *   mreq x---|2 I       O 19|---x RST_H
 *   iorq x---|3 I       O 18|---x 
 *        x---|4 I       O 17|---x !IO1_cs
 *     a4 x---|5 I       O 16|---x !IO0_cs
 *     a5 x---|6 I       O 15|---x !RAM1_cs
 *     a6 x---|7 I       O 14|---x !RAM2_cs
 *     a7 x---|8 I       O 13|---x !PIO_cs
 *    a15 x---|9 I       O 12|---x !AVR_cs
 *    GND x---|10 I      I 11|---x 
 *            |______________|
/* This device generates chip select signals for two    */
/* 32Kx8 static RAMs, the 82C55A PIO, the AVR's INT0    */
/* line and two additional I/O chip selects.            */

/** Inputs **/
PIN 1 = rst_l; /* AVR Z80 RESET (active low) */
PIN 2 = mreq; /* Z80 MREQ (active low) */
PIN 3 = iorq; /* Z80 IORQ (active low) */
PIN [5..8] = [a4..a7]; /* addr[7:4] for I/O */
PIN 9 = a15; /* MSB of address for RAM select */

/** Outputs **/
PIN 18 = RST_H; /* SYS RST (active high) */
PIN 17 = IO0_cs; /* Auxiliary I/O Select 0 (active low) */
PIN 16 = IO1_cs; /* Auxiliary I/O Select 1 (active low) */ 
PIN 15 = RAM1_cs; /* 32Kx8 RAM (active low) */
PIN 14 = RAM2_cs; /* 32Kx8 RAM (active low) */
PIN 13 = PIO_cs; /* 82C55A PIO (active low) */
PIN 12 = AVR_cs; /* SDmemory interface (active low) */

/** Declarations and Intermediate Variable Definitions **/
FIELD IOaddress = [a7..4]; /* addr[7:4] */

/* I/O Addresses */
avr_eqn = !iorq & IOaddress:[00..0F];
pio_eqn = !iorq & IOaddress:[10..1F];
io0_eqn = !iorq & IOaddress:[20..2F]; 
io1_eqn = !iorq & IOaddress:[30..3F];

ram1_eqn = !mreq & !a15; 
ram2_eqn = !mreq & a15; 

/** Output Logic Equations **/
RST_H = !rst_l; /* Needed for INTEL peripherals */

AVR_cs = !avr_eqn; /* AVR - low for addresses 0000h-03FFh */
PIO_cs = !pio_eqn; /* PIO - low for addresses 0400h-07FFh */

IO0_cs = !io0_eqn; /* IOO - low for addresses 0800h-0AFFh */
IO1_cs = !io1_eqn; /* IO1 - low for addresses 0B00h-0FFFh */

/* RAM select (active low) */
RAM1_cs = !ram1_eqn; /* low for RAM addresses 0000h-7FFFh */
RAM2_cs = !ram2_eqn; /* low for RAM addresses 8000h-FFFFh */

Here is the logic compiler’s output:


CUPL(WM) 5.0a Serial# 60008009
Device g16v8as Library DLIB-h-40-2
Created Fri Oct 21 08:16:54 2016
Name Z80_IOdecode-1V10
Partno Z80C
Revision 01
Date 10/18/2016
Designer Quest, Johnny
Company JQ
Assembly Z80 Computer
Location U4

 Expanded Product Terms

AVR_cs => !a4 & !a5 & !a6 & !a7 & !iorq
IO0_cs => !a4 & a5 & !a6 & !a7 & !iorq
IO1_cs => a4 & a5 & !a6 & !a7 & !iorq
IOaddress => a7 , a6 , a5 , a4
PIO_cs => a4 & !a5 & !a6 & !a7 & !iorq
RAM1_cs => !a15 & !mreq
RAM2_cs => a15 & !mreq
RST_H => !rst_l
avr_eqn => !a4 & !a5 & !a6 & !a7 & !iorq
io0_eqn => !a4 & a5 & !a6 & !a7 & !iorq
io1_eqn => a4 & a5 & !a6 & !a7 & !iorq
pio_eqn => a4 & !a5 & !a6 & !a7 & !iorq
ram1_eqn => !a15 & !mreq
ram2_eqn => a15 & !mreq

 Symbol Table

Pin Variable Pterms Max Min 
Pol Name Ext Pin Type Used Pterms Level 
--- -------- --- --- ---- ------ ------ ----- 

 AVR_cs 12 V 1 8 2 
 IO0_cs 17 V 1 8 2 
 IO1_cs 16 V 1 8 2 
 IOaddress 0 F - - - 
 PIO_cs 13 V 1 8 2 
 RAM1_cs 15 V 1 8 2 
 RAM2_cs 14 V 1 8 2 
 RST_H 18 V 1 8 2 
 a4 5 V - - - 
 a5 6 V - - - 
 a6 7 V - - - 
 a7 8 V - - - 
 a15 9 V - - - 
 avr_eqn 0 I 1 - - 
 io0_eqn 0 I 1 - - 
 io1_eqn 0 I 1 - - 
 iorq 3 V - - - 
 mreq 2 V - - - 
 pio_eqn 0 I 1 - - 
 ram1_eqn 0 I 1 - - 
 ram2_eqn 0 I 1 - - 
 rst_l 1 V - - - 

LEGEND D : default variable F : field G : group
 I : intermediate variable N : node M : extended node
 U : undefined V : variable X : extended variable
 T : function


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s