ZX Spectrum on FPGA

Introduction

The Speccy was the first computer I ever used. We had the 48K rubber key version, and it probably only had to share the living room TV with 3 channels when we first got it, although I was a bit too young to remember its actual arrival! I still have it in my possession and it is still fully functional, although it has few original parts – I repaired it a couple of years ago, requiring the replacement of the CPU, ROM and most of the DRAM. Luckily the ULA was still alive!

Manic Miner in-game screenshotSeveral years ago I implemented most of the 48K ULA in VHDL on a MAX7000 CPLD, mainly as an exercise to brush up on my HDL. I didn’t have enough space to fit the keyboard interface so it was pretty useless, but it did boot! The complete system used a pair of SRAMs, real Z80 and the Sinclair ROM in flash.

Back at the beginning of last year I decided to brush up on my HDL again and I bought myself an Altera DE1 development kit. The heart of this board is a Cyclone II FPGA with a range of support hardware including 512 KB SRAM, 8 MB SDRAM, 4 MB Flash, an I2S audio codec, VGA port and the usual switches, LEDs and buttons. I would highly recommend it for anyone looking for a relatively cheap way to play with FPGAs.

This is an implementation of the complete ZX Spectrum for the DE1 board. It can be configured to synthesise a 48K, 128K or +2A model, and it has a ZXMMC+ compatible interface. ZXMMC+ allows programs to be loaded from SD card, and it also allows extra RAM and Flash to be paged in over the top of the ROM. This feature makes it possible to run ResiDOS.

Architecture

All of the Spectrums used a Zilog Z80A CPU running at roughly 3.5 MHz (there is some variation between the different models). The display was generated by a custom Ferranti ULA, which was also responsible for the cassette interface and beeper. In the 128K models and later there was additional logic to handle bank switching, as well as the familiar AY-3-8912 sound generator.

16K/48K

In the earliest Spectrum model the memory map is completely linear. The first 16 KB is the ROM and the second 16 KB is a bank of DRAM which is shared with the ULA. The ULA takes priority, so CPU accesses to this region are slower than to the uncontended parts of the memory map. The 48K model has a further 32 KB of RAM in the upper half of the address space.

The ULA also presents a single IO port, nominally on port 0xFE, but actually visible at all even IO addresses (the Z80 has separate IO and memory address space). Writes to the port control the beeper, tape output and the colour of the screen border. Reads provide access to the keyboard and to the tape input.

128K

The 128K Spectrum adds a simple write-only paging register at port 0x7FFD, again incompletely decoded and actually appearing on all odd ports with A1 and A15 clear. Writes to this register select one of eight 16 KB banks of RAM to appear in the top quarter of the address space, with two of the banks also permanently accessible at 0x4000 and 0x8000. A further bit controls which of two 16 KB ROMs appears at the bottom of the memory map.

Proper sound hardware is also present – a General Instrument AY-3-8912, which was also found in many other machines at the time.

Spectrum +2A boot menu

Spectrum +2A boot menu

+2A/+3

The last of the Spectrums, the +2A and +3 were essentially the same machine but with the +3 adding a 3” disk drive. These were Amstrad machines, being made after the 1986 sale of the rights to Sugar’s company, and were styled similarly to the Amstrad CPC range of machines.

Hardware was comparable with that of the 128K Spectrum, with additional IO ports decoded for access to the floppy disk controller (where fitted) and to another paging register giving access to special paging modes. Two more ROM banks, four in total, were needed for the DOS and other extra features, although these are implemented as a pair of 32 KB ROMs in the real hardware.

Display

In all the machines the display is bitmapped 256×192 with an 32×24 attribute plane. The bitmap plane selects whether each pixel is displayed as background or foreground colour, and the attribute plane allows foreground and background colour, bright and flashing modes to be selected for all pixels within the corresponding 8×8 square.

The design allows for display of up to 15 different colours on screen at once for less than 7 KB of display memory, but results in the blocky “two colours per 8×8 square” style that makes Spectrum screenshots so easy to spot.

FPGA

The original DE1 implementation that I published last year supported only the 48K Spectrum. This has now been extended to support the 128K models as well, and SD card support has been added using logic compatible with the ZXMMC interface. The particular model to be synthesised can be selected using VHDL generics.

The design fits into 3252 Cyclone II logic elements in its most complex configuration (compiled on Quartus 9.1), and requires 32 Kbits of on-chip RAM for lookup tables if the 128K sound is included. The ROMs are now accessed out of the external Flash device and must be programmed using the DE1 tools prior to loading the Spectrum design.

CPU

The CPU is included on the FPGA and uses the T80 from OpenCores with fixes from Pace Dev.

Clocks

A 28 MHz master clock is used to allow the video to run out of phase with the CPU and other peripherals. This makes it possible to time-slice access to the SRAM, eliminating the contended memory present in the real Spectrum, but also necessary because the RAM here is in a single chip connected over a single bus.

Memory

The external SRAM is accessed over a 16-bit interface to enable the full 512 KB to be addressed. The first 128 KB is used for the Spectrum’s internal RAM, and the upper 256 KB is made available through the ZXMMC+ interface. The remaining 128 KB is not mapped.

Flash is accessed over a separate 8-bit interface and mapped in to the bottom 16 KB of address space. Different pages are selected depending on the type of Spectrum that was synthesised, and the condition of the ROM select bits in the paging registers, where applicable. Extra banks can also be used via the ZXMMC+ feature.

Display

The video logic generates an address for the SRAM on alternate clock phases, enabling it to fetch display data without interrupting the CPU. Attribute and bitmap data are assembled to produce the necessary RGB bitstream to drive the monitor, along with the related timing signals. In areas of active video that lie outside of the bitmapped screen area the logic generates a constant “border” colour, the particular colour of which is held in a 3-bit latch accessed through the single ULA register.

DE1 Spectrum

ZX Spectrum on Altera DE1

IO

Bus routing and address decoding are handled at the top level along with the paging registers present if synthesising one of the higher-end models. All the ports are incompletely decoded in the same way as in the real Spectrum.

Sound

The codec I2S signals are converted internally to 16-bit parallel buses for both input and output. A simple state machine drives the I2C bus necessary to program the required register settings into the codec when the board is first reset.

The beeper, present in all versions of the Spectrum, is driven by toggling a bit in the ULA register in software. The value of this bit drives the sign bit of the output to the Wolfson audio codec. Sound from the AY-3-8912 (actually a YM2149 core from FPGA Arcade) is mixed in as well.

The sign of the I2S input value is used to drive the EAR input in the ULA register to enable loading from tapes. A simple comparator and latch arrangement is used to provide a small amount of hysteresis to improve noise immunity.

Keyboard

The Spectrum keyboard is row-scanned by the 8 upper address bits, with 5 column outputs fed into the ULA and appearing as the bottom 5 bits in its only register. This topology is mimicked here by using an array of 5-bit registers, the appropriate one presented to the ULA depending on the value on the address bus. Further logic is used to set or clear bits in the registers according to press/release codes from the PS/2 keyboard interface.

Some key combinations (caps lock, backspace, escape and the cursor keys) are decoded into the required combination of keys automatically. The SHIFT keys operate CAPS SHIFT, and the CTRL keys operate SYMBOL SHIFT.

ZXMMC+

The latest release of the design supports access to the DE1’s SD card interface using logic compatible with the ZXMMC+. This also allows an additional 256 KB of both RAM and Flash to be paged in. Software support for SD cards is available through modified ROMs for the +2A and +3 computers (+3e by Garry Lancaster), and through an operating system extension called ResiDOS. More information about ZXMMC and ResiDOS (which is also supported) can be found on their respective websites.

Interfacing

The ULA implementation includes extensions to drive a VGA monitor directly (not through a scan doubler). Alternatively, a PAL TV can be connected through a SCART lead connected to the DE1’s VGA port. In this mode the HSYNC pin generates PAL compatible CSYNC and a small wire-mod is required in order to connect +5V to pin 9 of the VGA connector, which is in turn connected to pin 16 of the SCART plug. The need for the wire mod is historical and will be removed in a future release.

Beeper, tape and AY sound are all available on the green line-out connector, and the blue line-in connector can be used to load programs from a tape player (or from a PC running something like PlayTZX).

The PS/2 port needs to be connected to a PC keyboard.

ROMs

The Spectrum ROMs can be obtained from an emulator package and need to be loaded into the DE1’s Flash at the following addresses:

Offset ROM
0x00000 48K (Sinclair Research)
0x04000 Blank
0x08000 128K 0 (Sinclair Research)
0x0C000 128K 1 (Sinclair Research)
0x10000 +3 0 (Amstrad), or +3e 0 (Garry Lancaster)
0x14000 +3 1 (Amstrad), or +3e 1 (Garry Lancaster)
0x18000 +3 2 (Amstrad), or +3e 2 (Garry Lancaster)
0x1C000 +3 3 (Amstrad), or +3e 3 (Garry Lancaster)

A further 16 banks of 16 KB starting at 0x40000 can be paged in over the normal ROM using the ZXMMC+ feature.

If you want to use the +3e ROMs to access the SD card then you can download the bundle from World of Spectrum.  The mmcen3eE.rom image can be written to 0x10000 in one go, as it contains all four of the +3 ROMs.

Switches

Some of the switches on the DE1 affect the operation of the design:

  • SW9 controls system reset (up to run)
  • SW8 is used with the debugger, which is normally disabled
  • SW7 selects PAL mode (down) or VGA mode (up)
  • SW1 selects boot from normal ROM (down) or ZXMMC+ banks (up)
  • SW0 selects ZXMMC+ boot from RAM (down) or Flash (up)

Known Issues

Cycle accuracy – there is no memory contention and the CPU core doesn’t match the timing of the real Z80. This causes problems with games (and particularly demos) that do high-resolution colour by changing the attribute values at precise times. Multi-colour border effects also suffer.

Running “ulatest3” results in an immediate reset. This is suspected to be a CPU issue but has not been investigated extensively at this stage. Fixing this needs to happen before contention emulation can be considered.

The design currently fails timing, apparently due to the way in which the YM2149 is implemented. It seems to work alright, though.

Credits and Links

The following VHDL cores were used in the design:

These pages were of use during development and may be of interest:

Downloads

Update 2016-01-13: The files are now also available on GitHub

85 thoughts on “ZX Spectrum on FPGA

  1. Ash

    Just also confirmed – a lot of projects compiled with later versions of Quartus definitely still run unreliably if the “Power-Up Don’t Care” option is checked.

    I’m running Quartus 12.1, and the Speccy core still has random crashes / garbage on screen unless that tick box is unchecked before compilation.

    Just a warning there guys, that option is EVIL – turn it off! lol

    It’s obviously doing some other random naughty stuff?

    Ash.

  2. Laurent

    Hi Mike,

    This is a great project. I have a DE2-70 and would like to adapt the project to my board. The trouble is that there is SSRAM on the board instead of asynchronous SRAM. How could I manage to make your project work with SSRAM ?
    PS : Sorry if the question is silly but I am a beginner in VHDL.

    Thanks.

    Laurent

  3. Mike Post author

    Hi Laurent. I’m afraid I don’t have time to work on this at the moment, but I assume you just need to provide the clock to the SRAM as well as the rest of the bus signals. Since it’s all synchronous internally this should be fairly easy. I’ll take a quick look at the datasheet when I get a moment to see if I can spot any potential problems.
    Mike

  4. Laurent

    Hi Mike,

    This is a great day. I managed to adapt your source code in order to work with my DE2-70 board. It was a bit touchy (at least for me) as there are many differences between the DE1 and my board : SSRAM, Flash, VGA, clocks… But now the ZX is alive !

    Thanks for this great project.

    Laurent

  5. Mark Watson

    Great work and thanks for releasing the code.

    I’m trying to get this set up for loading from the SD card – exclusively using a 256MB card. I gather I need to type ‘format 0,16’ or similar from the +3 basic command prompt. Here I’m a bit stumped since comma is not mapped in keyboard.vhd! Is there another way of generating a comma on the spectrum that I’m missing?

  6. Mark Watson

    Ignore my previous comment! Realise I need the 48k spectrum layout. Ctrl+N for comma, Ctrl+P for quotes etc.

  7. Vlad

    Hi Laurent,

    Also have DE2 to play with, would it be possible to share your changes to save me a bit of time?

  8. Mike Post author

    Hi Vlad,

    I have Laurent’s DE2-70 port. I’m going to host it for him on here when I get a minute – hopefully this evening. Keep watching…

    Mike

  9. Vlad

    Hi Mike, will wait for it!
    At the moment I’ve have it running in 48 mode
    but in the other modes it looks like the RAM issue – I guess it is some timing violations on the routing of the signals as no restrictions in the design.
    The screenshots are in the same gallery.

  10. Pingback: ZX Spectrum on Altera DE2-70 | Mike's Lab Notes

  11. Mark Kohler

    Ok, I’m stumped! How do I prep the sd card?
    On boot up I have physical drives: 0 floppy, 1 MMC Logical drives: M

    Thanks,
    Mark

  12. Mike Post author

    I can’t actually remember! Isn’t the 1 MMC logic drive it? I seem to remember it just needed to be FAT formatted (possibly FAT16). It’s also unlikely to work with SDHC cards, so make sure the card is 2GB or less.

    The hardware is just an implementation of ZXMMC, so anything in the ResiDOS/+3e/ZXMMC docs would be applicable.

  13. Xavier Belanche Alonso

    Hi Mike,

    Thanks a lot for your amazing work. I have a DE1 and I tried to make it work but I have an issue: the screen says that signal is out of range (see the picture: https://i.imgur.com/g7UBg4n.jpg). I tested it on another screen and throws me the same message. I compiled the source code with QII 13.0.0. Any advice?

    Thanks in advance!

    Xavier

  14. Mike Post author

    Make sure SW7 is “up” (for VGA mode). If that is the case then you are just unlucky with the monitors you’ve tried. It seems that few modern monitors support the required 50 Hz mode now, so you may need to try to wire it up to a PAL TV instead (with SW7 in the other position).

  15. Mike Post author

    No, you have to put the ROM image into the DE1’s flash chip using the Windows tool that came with the board. You first need to load the demo logic that was on the board to begin with, then run the utility, program the flash, then load the Spectrum back in.

  16. Mike Post author

    Make sure SW1 is set to DOWN so that you are booting from ROM rather than the ZXMMC+ banks. The other thing to check (and I think this is probably the problem) is the MODEL setting near the top of spectrum_de1.vhd. I think in the archive it is set to 2, which is for the Spectrum +3. If you only loaded the 48K ROM at the bottom of flash then you will need to change it to 0 and rebuild the logic.

  17. Xavier Belanche Alonso

    Hi Mike,

    Bad news. I modified the MODEL variable in “spectrum_de1.vhd” and set to 0 (48K) and ROM_OFFSET set to “00000000” and rebuilded the logic without any error. I downloaded a 48k rom from this site: http://www.shadowmagic.org.uk/spectrum/roms.html and put on to flash with the DE1 utility but still doesnt work properly 🙁 I’m lost but I don’t wanna to annoying you more. Thanks for your help and time!

  18. Bill

    Hi Mike,
    This is a great project and I had a lot of fun getting it to work on my newer DE1 board. Could I bother you to ask how you were able to load all of the software on to your SD card and get ResiDOS installed? I’ve read through the various websites, but it wasn’t clear to me how to do this easily. Thanks!

    –Bill

  19. Mike Post author

    Hi Bill,

    It’s been a long time since I last looked at this! The ResiDOS ROMs need to be put in the on-board NOR flash starting at location 0x10000 – you should be able to use the demo application that came with the DE1 to do this (but I guess you already got this far). If I recall correctly the disk/tape images just needed to go on a normal FAT formatted SD card, possibly FAT16.

    Mike

  20. Reinaldo

    What should be done for porting the code from DE2-70 to DE2-35? someone has any ideas? Thanks!

    Reinaldo.

  21. Till

    BTW: The fix for first eight pixels top left flickering is to include the hcounter into the equation resetting the vcounter. This allows the last line to finish correctly and to begin the first line from the beginning. Without taking hcounter into account the last line was becoming the first line after one cycle so the first data fetch in that line was missing and the whole display had one line less than it should.


    -- Wrap vertical counter at line 312-1,
    -- Top counter value is 623 for VGA, 622 for PAL
    if vcounter(9 downto 1) = "100110111" then
    if ((VGA = '1' and vcounter(0) = '1' and hcounter = "1101111110") or
    (VGA = '0' and hcounter = "1101111111")) then
    -- Start of picture area
    vcounter '0');
    -- Increment the flash counter once per frame
    flashcounter <= flashcounter + '1';
    end if;
    end if;

  22. Davie

    The screenshot at the top isn’t from “Subterranean Nightmare”, is it? Nobody I know can remember it. It was hardcore difficult if I remember correctly.

  23. Greenaum

    Pardon me for bothering you, I’m sure you’re a busy man, but any chance you could do a PCB for this project? Allowing people to hook up maybe one of those tiny console keyboards they use on joypads? That, and composite out, might give you a wallet-sized fold-up Speccy, complete with micro-SD card, it could be pretty thin. Comparable to the real thing! Would be amazing to have an actual hardware-based Speccy in your pocket. Even if it’s not the original hardware, still better than emulation.

  24. Mike

    It’s a nice idea, although would it really be any better than the Vega+? The big advantage of a hardware implementation is the possibility of hooking up to real add-on hardware. For a handheld, the lowest cost, lowest power way to do this would undoubtedly be a software emulation on an ARM Cortex M4 or so.

  25. domenico

    Ciao a tutti,
    ho fatto dei test su altera DE1 e sono riuscito ad aggiungere i tasti speciali dai sorgenti di Goran al ns tastiera,
    non riesco ad implementare la DivMMC con Exdos al posto della ZXmmc, qualcuno ha fetto delle prove?

    Grazie per l’attenzione

    Domenico

  26. Domenico

    Ciao Mike,
    ho una FPGA Mcc216 che simula Amiga , Atari, e anche spectrum, all’avvio carica automaticamente una immagine “residos225.bin” direttamente dal una SD formattata Fat16, quindi con il comando “%roms” mi visualizza
    FatFs, Tapeio, Taskman preinstallati.
    Si protrebbe adattare una simile procedura alla Altera DE1 ?
    Es copiare l’immagine nella moemoria Flash , dopo le eprom e caricarla in ram all’avvio ?
    oppure un boot loader da scheda SD Fat16 pero’ il problema e’ che necessita una Sd formattata IDEDOS altrimenti non la riconosce subito.

Leave a Reply

Your email address will not be published. Required fields are marked *