close-circle
Close
0%
0%

Amstrad CPC 464 Next-Generation Speech Synthesizer

A modern speech-synthesizer for the Amstrad / Schneider CPC 464 based on the fabulous Emic 2!

Similar projects worth following
My first attempt of creating a "real" piece of retro-computer hardware - a speech synthesizer for the Amstrad / Schneider CPC 464 from 1984, featuring the Emic 2 speech synthesizer. The PCB was created with KiCad. An Atmega 328 microcontroller serves as a parallel-to-serial converter / interface to the Emic, which requires serial data @ 9600 baud. No driver on the CPC side is required - a simple sequence of "OUT" BASIC commands suffices to send ASCII characters to the Emic. The Atmega 328 acts as an intelligent driver, converter, and buffer.

The Amstrad CPC 464 - or: Schneider CPC 464, as it was branded in Germany - was my first real home computer (I started with a Busch 2090 Microtronic in 1984, which was a CPU trainer, similar to the KIM-1). I got the CPC from my parents in April 1985. I was always fascinated with speech synthesis - and so I had to get the DK'tronics speech synthesizer for the CPC in June 1985. I must say that I was disappointed with the quality of the speech that was delivered by the SPO256-AL2. The voice was very unnatural and hard to understand; by then, the software-based speech synthesizers that I had seen on the Commodore 64 and Atari 800 (e.g., "S.A.M."), and especially on the TI 99/4a, were so much better... Back then I thought - well, maybe one day I can create a more natural-sounding speech synthesizer for the CPC on my own. Then, when the Emic 2 was released, the idea of creating a "next-generation speech synthesizer" for the CPC popped up again - and here it is!

Some details: I used Lattice / Atmel G16V8 PLD's for the address decoding, and WinCUPL for programming them. WinCUPL does not work currently on Windows 10, so I had to use an old Windows XP installation - I guess there are other tools (Atmel Studio?) for programming them, but WinCUPL is easy to use and a good start for beginners. I got the idea of using the G16V8's for address decoding by looking at similar designs. For the Atmega 328, I did some Arduino projects before and wanted to go one step farther, so I read a book about AVR Programming and used the AVR gcc toolchain instead of the Arduino IDE this time. The other "glue logic" components are standard 74LSxx chips.

After a longer learning period about the CPC 464 expansion port, the way the Z80 handles IO requests, address decoding, and Z80 data bus latching, a first breadboard prototype as shown in this video was working, but the microcontroller software was not optimal yet:

To take it to the next level, I decided I wanted a real PCB, and started to learn KiCad. Some great tutorials on the web (such as "Getting to Blinky 2.0") helped me tremendously. Finalizing the microcontroller programming turned out to be challenging; it had some severe synchronization and timing difficulties between the Atmega 328 (16 MhZ) and the Z80 in the CPC (4 Mhz). But the hard work finally paid off - the PCB worked out of the box:

In the following days, I will add some more details about the challenges I faced during realization of this project, and how I overcame them. For now, just a couple of pictures, stay tuned ;-)

speech-polling-final-reset-button-ready-wait.c

Atmega 328 C-code. Uses Polling, READY / WAIT line of Z80, supports Reset button.

C Source File - 2.71 kB - 05/29/2017 at 02:11

download-circle
Download

EX-ENCODER.PLD

WinCUPL File

pld - 1.41 kB - 05/28/2017 at 15:36

download-circle
Download

EX-ENCODER.jed

JED Programmer / Image File

jed - 856.00 bytes - 05/28/2017 at 15:36

download-circle
Download

F9-ENCODER.PLD

WinCUPL File

pld - 1.06 kB - 05/28/2017 at 15:36

download-circle
Download

F9-ENCODER.jed

JED Programmer / Image File

jed - 562.00 bytes - 05/28/2017 at 15:36

download-circle
Download

View all 11 files

  • 1 × Emic 2 Speech Module The actual speech synhesizer
  • 2 × Atmel / Lattice GAL16V8-B PLDs Used for address decoding
  • 1 × Atmel AVR Atmega 328 Microcontroller Serves as intelligent driver, buffer, and parallel-to-serial converter
  • 1 × 74LS02 Quad 2-Input NOR Gate Used for glue logic - generation of select signal in combination with PLDs
  • 1 × 74LS08 Quad 2-Input Positive AND Gate Used for glue logic - generatoin of select signal in combination with PLDs

View all 19 components

  • Step 8 - KiCad Board Revision 2

    Michael Wessel05/25/2017 at 16:59 0 comments

    To be written.

  • Step 7 - Amstrad SSA-1 Compatibility? Roland in Space?

    Michael Wessel05/25/2017 at 16:58 0 comments

    To be written. Investigate how to make it compatible with SSA-1, and existing software / games that utilize the SSA-1, such as Roland In Time / Space.

  • Step 6 - PCB Soldering & One More Fix

    Michael Wessel05/24/2017 at 16:57 0 comments

    to be written

  • Step 5 - Atmega 328 Programming & Synchronization Issues

    Michael Wessel05/24/2017 at 16:56 0 comments

    After a week of wait time, I got an envelope from OshPark:

    By default, a pack of 3 is produced by OshPark. Due to the size of the PCB's, they were not inexpensive - about 85 $. However, the quality of the PCB's is superb!

    I had used the week of wait time to order the required components, and started assembling it right away. Also, I had still be playing with the synchronization issues and was still trying to get the ISR-based solution to work. When I had submitted the job to OshPark, the PCB's didn't have the track for the READY / WAIT signla yet, so I had to solder one wire manually by hand. The next PCB version will have that fixed - nobody is perfect ;-)

    One can see the extra-wire for the READY / WAIT signal. Looking at the PCB, I am noticing that I should order to brushed and Flux remover to clean it up a bit.

    Other than that, I plugged it into the CPC - and it worked!

  • Step 4 - PCB Design with KiCad

    Michael Wessel05/24/2017 at 16:54 0 comments

    Well, what can I say - this wasn't easy for a newbie! I must say "Getting to Blinky 4.0" from Contextual Electronics helped tremendously - thank you guys so much for putting this together!!

    Getting to Blinky 4.0 from Contextual Electronics

    Being an absolute beginner with KiCad and PCB production, I wanted to summarize some of the errors I made in that process such that other newbies can benefit from it - I actually had to generate the Gerbers 5 times, because I was finding errors in the PCBs again and again - it was quite a humbling experience, but great fun also! Here are some tips / hints, solutions to obstacles that I faced, and errors I made, hope it is helpful to others:

    A. ESchema:

    • When you connect PINs to a bus, make sure that all your pins have labels, otherwise the bus doesn't know which line / track is going from where to where. The pin numbers and names on the chips / components are not sufficient for that.
    • Many components have invisible GND and VCC. during electrical check, KiCad always complained about non-connected VCC and GND pins, even though I had global VCC and GND labels on the corresponding CPC expansion port pins! I tried to put global labels manually to these hidden pins, but to no avail. The right solution is the following, which required quite a bit of web research: First, make sure that KiCad shows the invisible pins. Then, for a bus connector such as the CPC's expansion port, attach a global GND label to GND pins (there can be more than one such pin which gets that label - they will be connected then), and attach a global VCC label to the VCC pin. THis is still not enough, even though the hidden pins have global labels VCC and GND. Next, create and place a VCC and a GND label somehwere on the board, and find the "gnd" and "vcc" components from the component library. Connect them to the labels. This is still not enough to satisfy KiCad's electrical checker. Next, find the "powerflag" component from the library and attach one to each of VCC and GND just just placed on the board. Finally, KiCad thinks the 74x chips are powered and connected correctly
    • Global labels are extremely helpful if you don't want connection all over the board. Just attach the GND or VCC global label and you are done.
    • Don't forget the rule check!
    • It is ok for KiCad to complaint about non-connected components, but carefully check all the errors and make sure that you did not forget any important connections.
    • KiCad is frequently updated it seems. Sometimes, they forget to include certain components. Believe it or not, but my installation didn't have the SW_PUSH little momentary push button in the library! I had to install and download the latest version of the KiCad component library. Likewise, other components might be missing from the standard libraries - in my case, the AMega328p and the Lattice GAL G18V8's. However, with a little bit of Web search I was able to find libraries for these. For the CPC edge connector, I didn't find anything on the Web, but had to create my own symbol and footprint. I could adapt and Apple 2 connector for this.

    B. PCBNew and Gerber generation

    • Really make sure that that you set up the design rules (global and connection-specific!) correctly, before you start with the (auto) routing! Especially if you are using the metric system (mm), and have to convert OSHPark's inches-based design rules, make sure that you convert inches correctly into mm's... that should be straight forward, but in my case, I messed up by an order of magnitude, and the tracks where only one tenth in width of what they should have been.
    • When you import the Netlist, do the "Spread Out". This is only available if you Switch the View to "Standard View" in my version, not "Open GL". And also only as right mouse button context menu with Arrow Tools selected, and the Place Components mode is enabled. Took a while to find this...
    Read more »

  • Step 3 - Breadboard Prototype with Atmega 328 and Emic 2

    Michael Wessel05/24/2017 at 16:53 0 comments

      Previously I had learned how to do the address decoding by connecting two G16V8 PLDs to the CPC's address bus, one PLD for the higher, one for the lower address byte. I chose &F9E0 - &F9E7 as the supported address range, and the second PLD would also act as an 8-line decoder, given HIGH on output <x> for &F9E<x>. Combined with the ~IOREQ and ~WRITE signal, the idea was to simply feed this into the Atmega 328, to trigger an interrupt, which would then read the data byte from the Z80's data bus. So much for the idea.

      Even though the 328 was clocked at 16 Mhz, and the CPC at 4 Mhz, it turned out to be impossible to reliably latch the correct data byte from the bus that way. The Interrupt Service Routine (ISR) was simply not trigger fast enough, and the data byte already gone from the Z80 bus. To overcome these challenges, I added a 74HCX373 transparent D flip flop, which would latch the byte from the data bus using the same select signal that the 328 was getting. Now, that the data byte was latched and available at the flip flop output for as long as I wanted it (after all, it now depended on the rate at which byte were sent by the CPC BASIC program, and this could be as slow as I wanted it), it should be possible for the AVR to read the data byte with the ISR reliably, right? And somehow, this still wasn't the case. Somehow, it appeared that the parallel reading of a whole bytes with the AVR input pins had a hysteresis. I even tried replacing the flip flop with a parallel to serial-converter, a 74HC595, since I then would not need to read a full byte in parallel with the 328, but only one bit at a time, but still I got similar problems. The only solution to these timing and synchronization problem I found were:

      1. don't use an ISR, use a busy loop and polling: loop_until_bit_is_set / loop_until_bit_is_clear
      2. pull down the Z80's WAIT / READY signal as soon as the select signal arrives. This will halt the CPC until the byte has been read from the data bus.

      Generating this signal with the 328 turned out to be an interesting challenge as well, since AVR pins / outputs cannot go in tri-state {not connected} mode. The Z80 needs a LOW (0) in order to be halted, but I cannot set it HIGH (1) otherwise, as this will of course interfere with the other hardware in the CPC. Rather, I need the two states LOW and "not connected". This is possible by toggling the 328 pin between input and output mode.

      With all the synchronization issues solved, and a working breadboard prototype, I could continue with the PCB design using KiCad.

      This video shows an interim version of the breadboard prototype, which still had some synchronization issues. It did not have the READY / WAIT signal synchronization line, hence the BASIC program requires some larger delays in order to slow down sending of bytes, and some other oddities in order to overcome byte reading challenges, i.e., I am using the 8th bit of the payload data and zero bytes in between payload bytes for synchronization. The latest version does not require these tricks anymore:

      And a picture {the 7segment LED display and decoder chip at the bottom are left-overs from my previous experiment):


  • Step 2 - Learning How To Do Address Decoding

    Michael Wessel05/24/2017 at 14:00 0 comments

    Address decoding is an essential part of any hardware extension. In a Z80 computer, every piece of hardware connected to the (address, and data) bus has an IO address which is used by the Z80 to talk to the extension. To send a byte of data to the hardware extension on the bus, the Z80 puts the address of that piece of hardware on the bus, and then signals using the ~IOREQ (IO-Request) and ~WR (Write) or ~RD (Read) signals whether he wants to write to or read from the extension. In the former case, the byte for the extension appears on the data bus.

    Hence, the extension needs to recognize a) its address on the address bus, and b) figure out when ~IOREG & ~WR are "low" to indicate the "hey hardware extension, there is a byte for you waiting on the data bus!" signal. Also, this byte of data is only available very shortly on the data bus (about 2,5 micro seconds in case of the CPC, where the Z80 is clocked at 4 MHz), so one might have to use a flip flop to "hold on" to that byte on the databus for longer. However, since the plan was to use a microcontroller (e.g., the Atmega 328), maybe it would be fast enough and could "latch" the byte without requiring a flip flop? Let's see.

    The plan was to not use a ton of 74LSxx gates for the address decoding and "glue logic", but rather a Programmable Logic Device such as the GAL G16V8B. It had seen similar designs in other hacker's Z80 computers, and hence liked the idea, especially since it would allow to change the IO address of the extension by means of re-programming / re-flashing the PLD.

    In order to practice this, I wanted a small test circuit - I decided to connect a 7segment LED display to the CPC such that an "OUT &F9E0, <byte>" issued from the BASIC would make it show the lower nibble of of <byte>. I hence need two PLDs for address decoding of high and low byte, a NOR 75LS02 NOT gate which would give me a "HIGH" when both IOREQ and WR lines go "LOW", and another 74LS08 which would get the output of the LS02 and some of the address bits and the output of first, low byte address decoder PLD, to feed into the second, high byte address decoder PLD. Then, I used a CD4543BE 7segment decoder/driver to drive the 7segment LED display (I know, I should use more than one resistor for the command Anode of the LED display);

    This worked out fine:

    After these preliminaries, I should be good to go to try to connect an AVR Atmega 328 microcontroler to the CPC data bus. So I thought ;-)

  • Step 1 - Making the CPC 464 Expansion Port Prototyping-Friendly

    Michael Wessel05/24/2017 at 06:49 0 comments

    Clearly, having not designed any CPC hardware before, I wanted to start this project with some simple experiments on a breadboard - too many unknowns. How would the address decoding work out? Which Z80 signals would be required to generated the "Select" signal? Would I need a flip flop? Will the Atmega 328 be fast enough? And so on. Breadboard prototyping was the way to go.

    However, in order to minimize error and crazy wires all over the breadboard, I wanted a clean solution for accessing the CPC expansion port. Inspired by the GPIO breakout boards / cobblers for the Raspberry PI, I designed a "CPC Expansion Port Connector" and a "CPC Expansion Port Breadboard Cobbler". I used KiCad for the PCB's:

    Now, with the CPC 464 expansion port being neatly and cleanly accessible from my breadboard, I was good to go!

View all 8 project logs

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates