ZX Spectrum Compatible Video for RC2014

As part of my tinkering with the RC2014 I wanted an easy way to transfer ZX Spectrum compatible assembly over to an RC2014. Naturally the language and code itself are pretty straight forward, but when it comes to what the assembly code is attempting to achieve this will be quite different.

There are a number of architectural features of the ZX Spectrum that don’t exist on an RC2014 based system. The big ones being:

So really, although the original aim of the RC2014 project was spurred on by the idea of recreating a ZX Spectrum, ZX80 or ZX81 or similar computer, in reality the idea of reproducing all of the above design constraints on an RC2014 compared to finding an old ZX spectrum to recondition or repair, running one of the many modern recreations (The Spectrum, Spectrum Next, Harlequin, and so on) or using software emulation removes much of the reason for doing so.

Still, some people have already come up with various ways to attempt to do it and deal with the issues. Some interesting ones related to the RC2014 are:

But far most the most admirable effort is the ZX128: https://github.com/ZXQuirkafleeg/ZX128. This provides everything apart from the Z80 itself in a RC2014 format to provide a pseudo ZX Spectrum with an RC2014 backplane. This uses a CPLD to implement the ULA, which is an approach used on the ZX MAX boards too (e.g. the MAX 128).

There is detailed discussion of the idea here: https://groups.google.com/g/rc2014-z80/c/SUhZvL308ug. In particular, at the end of that thread is a post from 2023 from “Samster” who spent a fair bit of effort finding replacements for all of the above that could be used with an RC2014 system, and managed the amazing feat of a ZX Spectrum in RC2014 format. It is awesome work.

At this point I should also acknowledge some of the projects that are recreating the functionality of the ZX Spectrum using modern microcontrollers. The key ones I know of are:

  • pico-zxspectrum – firmware for ZX Spectrum emulation on a Raspberry Pi Pico.
  • ESP32 rainbow – board and firmware for a ZX Spectrum recreation based on an ESP32.
  • Also emulators based on the Teensy, ESP8266, STM32F40, and I’m sure many others.

As I dug deeper into the idea of a ZX Spectrum compatible display card for the RC2014, I eventually stumbled across the BusRaider. This is another strong contender for “ZX Spectrum from an RC2014”. It provides memory mapped graphics that are ZX Spectrum compatible, among other things, for an RC2014 system:

This takes an interesting approach – it grabs the Z80 bus and then uses logic chips to quickly read the memory and then uses the read data to generate a display. It thus has to “play nice” with the Z80 CPU in terms of sharing the bus, i.e. it has to use the various bus control signals properly. In this system the Z80 is writing to the memory and the BusRaider is reading it. As I understand things, this isn’t dissimilar to how the ULA worked to build up the display from the memory and bus shared with the Z80 CPU.

So why do I mention all this? Well, I’m not interested in a full clone, but I was definitely wondering what it would take to create a ZX Spectrum-compatible “video card” for the RC2014. Because, well, why not?

It looks like BusRaider does exactly that using a Raspberry Pi Zero. But I wondered if rather than reading out of the memory directly, could something like a Raspberry Pi Pico monitor the bus “live” and watch the writes as they happen?

It turns out that this excellent use of the Specduino does pretty much exactly that, to watch the memory of the ZX Spectrum and use that to generate a “live” map of the game Atic Atac: https://www.youtube.com/watch?v=aWh8rTzOfH4.

So that is the concept I’m starting from.

ZX Spectrum Video

The ZX Spectrum supports a 256 x 192 pixel display using a palette of 15 fixed colours (more here). Pixel and colour data are stored independently. One of the best resources I’ve found for describing how the ZX Spectrum display works is: http://www.breakintoprogram.co.uk/hardware/computers/zx-spectrum/screen-memory-layout.

There is a whole chunk of memory from $4000 to $5AFF that maps onto display data as described in the above link. There are 192 blocks of 32 bytes (6144 in total) for pixel data and 768 bytes for attribute data. In binary, these addresses look like the following;

b0100 0000 0000 0000 = $4000 - Start of Pixel data
b0100 1111 1111 1111 = $4FFF
b0101 0000 0000 0000 = $5000
b0101 0111 1111 1111 = $57FF - End of Pixel data
b0101 1000 0000 0000 = $5800 - Start of Attribute data
b0101 1010 1111 1111 = $5AFF - End of Attribute data

The pixel data is relatively straight forward, if a little odd, which I’ll come on to in a moment. A bit set in the pixel memory means the pixel will be set to the foreground colour (INK). A bit cleared maps to the background colour (PAPER). As already mentioned there are 192 “lines” of pixel data of 32 bytes each, so 32 x 8 pixels (256).

The most famous quirk of the ZX Spectrum graphics surely has to be the infamous “attribute clash”. This is due to the fact that, to save memory each 8×8 pixel block (character cell) can only have one INK and one PAPER colour at a time. This allows for the colour information for the entire display to fit into a 32 x 24 character grid, totaling just 768 bytes.

The address decoding is slightly odd too. As per the above link, the mapping of X,Y coordinates onto an address in memory is as follows:

For:
X = X0..X4 (5 bits = 0 to 31) = X coordinate (in bytes) 0 to 31
Y = Y0..Y7 (8 bits = 0 to 255) = Y coordinate (in lines) 0 to 191

Pixel Memory Address (in binary):
0-1-0-Y7 Y6-Y2-Y1-Y0 Y5-Y4-Y3-X4 X3-X2-X1-X0

Attribute Memory Address (in binary):
0-1-0-1 -1--0-Y7-Y6 Y5-Y4-Y3-X4 X3-X2-X1-X0

One reason for the odd ordering of the Y values is that when working with characters (e.g. in BASIC) then moving down one line, assuming the address of the first line is in register HL, is simply a matter of INC H, which is a really quick operation. This is also the reason for the curious ordering of lines when loading a screen from tape. As the data or each address loads in sequentially, it loads one byte at a time (X4-X0), by taking each third of the display in turn (Y7,Y6), and working through all bytes in the same row (Y2,Y1,Y0) for all of the 8 rows of characters in that third of the display (Y5,Y4,Y3). This loads in monochrome until the attributes then load over the top.

As the attributes work on 8×8 characters, the Y coordinate required is actually Y / 8 or Y >> 3, hence discarding Y0,Y1,Y2 above and only using Y3-Y7 to decode the address. Note that in both the pixel and attribute maps Y6,Y7 will encode which third of the display is being targeted. For the 192 lines, this will only be using the binary values b00,b01,b10 (b1011111 = 191).

One quirk of the addressing is that when calculating positions of 8×8 character blocks, the bottom 8 bits of the address are the same for the pixel data and attribute data. For example:

Coordinate (x=4,y=25) = (b00100,b00-011-001):
Pixel address = b010[0 0[001] 011]0 0100 = $4162 = 16740
\--\ | | |
Attr address = b0101 10[00 011]0 0100 = $5864 = 22628

Just double checking that attribute value then: for coordinate (4,25) we need attribute cell (4, 3) as 25/8 rounds down to 3. So taking $5800 (22528) + (3 * 32) + 4 = 22528 + 96 + 4 = 22628.

But what is stored at each attribute address? Again, from various sources, we know that an attribute byte has the following format:

Attr = F B P2-P1-P0 I2-I1-I0
F = Flash
B = Bright
P = Paper
I = Ink

Ignoring flash, this means there are four bits associated with colours. But there is no concept of “bright black” so both bright and normal black are just black. This gives us the 15 colour palette we all know and love, although we have to note that BRIGHT will apply to both PAPER and INK.

Wikipedia has all the colours mapped onto HTML colour codes and notes that this is a variation of 4-bit RGBI used by CGA – the Color Graphics Adaptor standard which was introduced in 1981 by IBM, although CGA had slightly higher resolution in terms of pixels.

RC2014 Video Options

To get video onto an RC2014, there are some existing options:

  • Pi Zero Serial Terminal – a now retired product that used a Raspberry Pi Zero to support a serial terminal on an HDMI display.
  • Pico VGA Terminal – this uses a Raspberry Pi Pico to drive a VGA output using GPIO and resister DACs from a UART serial connection to the RC2014.
  • RP2040 VGA Terminal – this is a rebuild of the above using a RP2040 microcontroller directly, but it is essentially a similar device.
  • Marco’s VGA Serial Terminal Console – another board (no longer produced) based on the same idea but based on a Parallax Propeller microcontroller.
  • TMS9918A Video Card for RC2014 – composite graphics board using the popular (at the time) TMS9918A graphics processor.
  • TMSEMU3 graphics card designed for RC2014 – replacement for the above using a RP2040 based DVI TMS9918A emulator device to provide DVI (via a HDMI connector) output.
  • Stegosaur V9958 RGB – based on the more recent V9958 video display processor (VDP) to give a VGA output.
  • MSX V99x8 Video Module – similar to the above, but specifically aimed at MSX emulation.

The serial terminal options are just that – serial console output only. The TMS9918 based solutions are interesting – these are genuine graphics devices and the register interface to the TMS99x8 is pretty well understood. But as I understand things, it can sometimes be slow to get throughput of screen updates, one reason MSX games were not as highly performing as other systems (apparently).

Using any of these TMX99x8 solutions would also mean significantly re-writing anything that already uses a memory-mapped video interface such as that expected by code running on the ZX Spectrum.

As already mentioned, what I’d like to do is somehow snoop the RC2014 bus and pull out any writes to the graphics area of the memory map and deal with them appropriately. There has been a bit of discussion of this in some places (see here for example).

But really this has a lot more in common with some of the projects that use a microcontroller to replace RAM or ROM directly or projects that act as a bus logic analyser than any of the existing RC2014 video options that I’ve found so far.

Raspberry PI Pico and Vintage Computers

Following up on that last thought, in addition to the aforementioned serial terminal to VGA options, there are a number of other interesting Raspberry Pi/Pico projects that actually do something very similar to what I’m after.

  • TK-Pie – Uses a Raspberry Pi Zero on a ZX Spectrum edge connector add-on to snoop the ZX Spectrum (Z80) bus directly. This does exactly what I want to do – it watches for address access to the video graphics area of memory and processes it accordingly. To solve performance and voltage level issues it uses a CPLD as the interface between the Z80 bus and the RPi Zero.
  • OneROM – Uses a RP2350 or STM32 to act in real-time as a replacement for a wide range of E(E)PROMS and RAM used in vintage computers. A very comprehensive open source solution with highly optimised PIO and assembler code running on the RP2350. Can replace a range of 24, 28, 32, 40 pin 8-bit and 16-bit devices and has comprehensive tooling support to make it easy to use.
  • Picotari – Using a Raspberry Pi Pico as an Atari 2600 ROM replacement.
  • Picocomputer – a 6502 based single board computer using two Raspberry Pi Picos to support the peripheral devices required by the 6502, including providing a VGA graphics interface.
  • RP2350 Pico2 400MHz 24 Channel Logic Analyzer – Using a Raspberry Pi Pico 2 (RP2350) as a multi-channel logic analyser.
  • MCEBlaster – using a RPi Pico as a converter (and, to a degree, an upscaler) between MCA/CGA/EGA graphics standards and VGA output for a more modern monitor.
  • Scott Baker’s Bus Supervisor – using two MCP23017 and a PCF8574 under Raspberry Pi I2C control to grab control of the Z80 bus and allow writing to or reading from RAM.
  • picoZ80 – a RP2350 based full emulation of the Z80 microprocessor all within the same 40-pin DIP footprint.
  • Happy Little Diodes’ “Specduino” – I can’t find designs published online (yet), but it is described in a lot of detail in this video with more here. This uses an ATMega32U4 and some latches to grab chunks of the ZX Spectrum’s memory by processing the signals directly from the Spectrum’s edge connector.
  • Apple II VGA Card – This does exactly what I’m thinking – it has a Pico snoop the 6502 bus and creates an in-memory map of the video which is sent to a VGA compatible output.
  • RP2350 ZX Spectrum compatible – all the 1980s components for a ZX Spectrum but with a RP2350 replacing the ULA, including providing video output.

All of the Raspberry Pi Pico or RP2350/RP2040 based solutions will typically be using the PIO subsystem for optimum transfer, so that is where I’m going to be heading.

Raspberry Pi Pico Video

The Raspberry Pi Pico has been used to produce VGA signals using GPIO to output to a resistor DAC for each of the analog signals, alongside the H and V sync signals.

It has also been used to provide HDMI, or at least a compatible DVI HDMI variant. There are breakout boards available for both options for experimentation. The RPi Pico VGA output is one of the official RPi sample “playground” projects and in the RPi “Hardware Design with RP2040” manual.

The MCEBlaster is also really interesting as it provides a physical VGA output for a range of smaller resolution TTL video inputs, including CGA, which is effectively the Spectrums video pallet.

But as mentioned here (Simplified Pico VGA – Part 2) the closest thing I’ve found is the pico-zxspectrum, which has a range of options for ZX Spectrum video for VGA. In particularly, the RGBY1111 mode taken from the pico-zxspectrum repository: https://github.com/fruit-bat/pico-zxspectrum/blob/main/docs/ZxSpectrum4PinAudioVga1111Ps2.md.

RPi Pico ZX Spectrum Video

Given everything discussed so far, the key requirements I’m after in this system are as follows:

  • 16-bit address lines, 8-bit data lines, some (tbd) control lines.
  • Address decoding to be done within the Pico, possibly with the PIO.
  • Snoop the Z80 address and data lines, so does not need to be write to the actual Z80 bus.
  • Must be able to handle bus control logic to understand valid writes and distinguish from IO and refresh cycles.
  • Fast enough to support typical frame rates (e.g. 50Hz refresh rate for a PAL display).
  • Video output that is scaled down to the ZX Spectrums video capabilities.

One issue with aiming for a VGA output is the large number of GPIO required (17 for three colours and sync). HDMI requires less GPIO so would be another option.

Looking at the MCEBlaster again is very interesting as it uses a much lower resolution resistor DAC for a CGA compatible output on a VGA screen than is required for full VGA. When limited to the colours of CGA, this manages the required output using just 8 GPIO: 2 each for R, G, B; H sync; V sync.

But as explored in Simplified Pico VGA – Part 2 when I’m limiting things to ZX Spectrum video compatibility, with a single brightness control, the RGBY1111 mode can be implemented with just 6 GPIO.

So the final GPIO requirements:

  • Address bus decoding: 16 GPIO
  • Data bus decoding: 8 GPIO
  • Control bus decoding: up to 8 GPIO
  • CGA/VGA signal output: 6 GPIO

This is a total of up between 30 and 38 GPIO which is more than available on a standard Pico which has 23 digital IO pins plus 3 digital/ADC pins broken out. They will also need to be 5V tolerant.

Z80 Bus Memory Writes

As described in Watching a Z80 from an RP2350, there are a number of different bus control signals, creating different bus cycles. This was followed in Watching a Z80 Bus from an RP2350 – Part 2 by showing how a CPU memory write can be monitored and captured in the RP2350’s own memory.

The conclusion is that I need access to the following bus signals: /MREQ, /RD, /WR, /IORQ, /M1.

Bringing it all Together

At this point then, I believe I have every required to build a ZX Spectrum compatible video card for the RC2014, by combining:

Key design points:

  • I’ll be running on an overclocked (probably at least 200MHz) RP2350B with 48 GPIO using the Pimoroni PGA2350 at least in the first instance.
  • A shared ram[64*1024] structure, although only the area within the Spectrum memory map will really be used.
  • Core 0 – running the VGA output using GPIO 40-45 reading from the raw ram[] area.
  • Core 1 – running the memory monitor using GPIO 0-31 writing to the raw ram[] area.

These will be explored in the following sections.

Adjusting the Scanvideo Example for RP2350

In principle the code I used in Simplified Pico VGA – Part 2 should run as is on the RP2350, but there is a slight complication when attempting to use it with my RC2014 PGA2350. I want the video output to run on GPIOs 40 onwards.

From the hardware_pio API documentation:

  • On RP2040, pin numbers may always be specified from 0-31.
  • On RP2350A, pin numbers may always be specified from 0-31.
  • On RP2350B, there are 48 pins but each PIO instance can only address 32 pins (the PIO instance either addresses pins 0-31 or 16-47 based on pio_set_gpio_base). The pio_sm_ methods that directly affect the hardware always take real pin numbers in the full range.

pio_set_gpio_base takes either 0 or 16 as the base. This can be set per PIO instance, i.e. pio0, pio1 or pio2 (on RP2350). There is also the definition PICO_PIO_USE_GPIO_BASE which will be set within the SDK to 0 for the RP2040, with no adjustable PIO base, or 1 for the RP2350 where the base can be chosen.

But although there are routines to allow PIO to function with any GPIO pin, in the scanvideo library the scanvideo_setup_with_timing() function uses a pin_mask value that assumes GPIO pins will be less than 32:

uint pin_mask = 3u << PICO_SCANVIDEO_SYNC_PIN_BASE;

Actually, this is assuming the number of GPIO will be less than the size of the default integer, which on the RP2350 should be 32-bits, but this is not exactly portable code. This really should be uint32_t.

This is used in the function to figure out which GPIO need initialising and then doing so:

for(uint8_t i = 0; pin_mask; i++, pin_mask>>=1u) {
if (pin_mask & 1) gpio_set_function(i, GPIO_FUNC_PIO0);
}

I did wonder about just making pin_mask a 64-bit value, but this didn’t make the code happy. In the end I set some global macros in the vgamode.h file to adjust the mask processing as required:


// If using all GPIO > 32 then this will adjust scanvideo.c
#define PSVMASKOFFSET 32
// If using all GPIO < 32 then this will leave scanvideo.c as is
//#define PSVMASKOFFSET 0

#define PSVMASK(x) ((x)-PSVMASKOFFSET)

And then in scanvideo.c whenever it is setting a bit in pin_mask it uses the PSVMASK() macro to adjust the shift value. Then when setting up the GPIO pins it adds in PSVMASKOFFSET. Some examples below:

uint32_t pin_mask = 3u << PSVMASK(PICO_SCANVIDEO_SYNC_PIN_BASE);
...
pin_mask |= 4u << PSVMASK(PICO_SCANVIDEO_SYNC_PIN_BASE);
...
for(uint8_t i = 0; pin_mask; i++, pin_mask>>=1u) {
if (pin_mask & 1) {
gpio_set_function(i+PSVMASKOFFSET, GPIO_FUNC_PIO0);
}
}

#if PSVMASKOFFSET==32
// Using the higher GPIO for PIO so change the base
pio_set_gpio_base(video_pio, 16);
#endif

We also need to adjust the base itself so that has been added just after the gpio_set_function() calls prior to adding the PIO program.

That seems to have done the trick. Here is my demo code from Simplified Pico VGA – Part 2 now running on GPIO 40-45 on my RC2014 PGA2350 board.

The complete diff for scanvideo.c can be found, alongside the other main files and instructions for how to reproduce and build the sketch, in GitHub here: https://github.com/diyelectromusic/sdemp/tree/main/src/Misc/PicoScanVideo

Formatting for the Spectrum Display Memory

The demo uses a relatively simple “draw_color_bar” function to drop a block of colour onto the display. I’m going to need to process the Spectrum video memory to create each scanline.

My starting point for this is the excellent pico-zxspectrum project as that already does exactly this. This requires the use of the following key files and functions:

  • src/ZxSpectrumDisplay.h
  • src/ZxScanlineVgaRenderLoop.cpp::ZxScanlineVgaRenderLoop().
  • src/ZxSpectrumPrepareScanvideoScanline.cpp::zx_prepare_scanvideo_scanline().

Implementing a ZX Spectrum compatible display is largely a case of porting the above over to my sketch. But the first version had an issue – I just couldn’t get the right alignment going on for the pixels, so in the end I re-implemented it myself using the above as a guide.

zxs_scanline

My own implementation has the following simplifications:

  • Uses VGA 320×240 mode directly as per my previous tests. The original uses 640×480 with pixel doubling to drop back to the ZX Spectrum resolution.
  • Pre-define the sizes of the ZX Spectrum display in a file zxdisplay.h. The original has a range of configurable options to cope with many different circumstances.
  • Uses the same 16-bit versions of the tokens I used in my demo. The original used 32-bit “double pixel” values to support the pixel doubling.
  • The border is currently fixed. I’ll need to research how tto change it (I see it was either a ROM call or an OUT IO instruction – filed as a “todo” for later).

Now the main calling code is as follows:

void setup() {
scanvideo_setup(&vga_mode);
scanvideo_timing_enable(true);
zxs_scanline_init(&vga_mode);
}

void loop() {
scanvideo_scanline_buffer_t *buffer =
scanvideo_begin_scanline_generation(true);
uint32_t frame_num =
scanvideo_frame_number(buffer->scanline_id);
uint32_t y =
scanvideo_scanline_number(buffer->scanline_id);

zxs_scanline(buffer, y, frame_num,
&ram[ZXS_DISP_ADDR], &ram[ZXS_ATTR_ADDR],
borderColour);

scanvideo_end_scanline_generation(buffer);
}

I initially dropped the frame number from the call, but then I realised that the original used the least significant bit of the frame number to control the flash attribute – i.e. every other frame the colours were inverted – so I put it back in.

I’ve used the Spectrum screen definitions as described in “The ZX Spectrum ULA” by Chris Smith:

// ZX Spectrum pixels definitions
#define ZXS_DISP_WIDTH 256
#define ZXS_DISP_HEIGHT 192
#define ZXS_BORDER_TOP 24
#define ZXS_BORDER_BOTTOM 24
#define ZXS_BORDER_LEFT 32
#define ZXS_BORDER_RIGHT 32

// Attribute cells are 8x8 pixels
#define ZXS_ATTR_WIDTH 32
#define ZXS_ATTR_HEIGHT 24

The original border would have been stretched to support the 312 scanlines of a typical PAL TV, but here they are cut short to fit a 640×480 VGA display. As the Spectrum resolution is 256×192 we use pixel doubling in both directions. This means 1 Spectrum pixel is a 2×2 VGA pixel at 640×480. Of course, when using VGA at 320×240 it is a single (larger) pixel.

So, for example, the space for the top and bottom border would be 480 – (192*2) = 48, meaning 24 pixels each rather than the perhaps more expected 312 – (192) = 120, meaning 60 pixels each (although for some reason I see this often stated as 56 rather than 60).

The original code from pico_zxspectrum supports many more configurations including some that require a blanking period before the top or left borders are drawn. I’m not bothering with any of that here.

The algorithm for outputting a line of the display is as follows:

zx_scanline (y):
IF y is in TOP or BOTTOM border:
Draw solid scanline for whole width in border colour

ELSE:
Draw left border using COLOR_RUN in border colour
Find position in display and attribute memory for start of line y

FOREACH of the 32 8-bit attribute blocks:
Set brightness, paper and ink colours from attribute memory
Output RAW_RUN for 8 bits of pixel data in display memory

Draw right border using COLOR_RUN in border colour
Output single BLACK pixel to complete line

Much of the code that turns line numbers into actual Spectrum display parameters is left as it was from the pico_zxspectrum ZxSpectrumPrepareScanvideoScanline.cpp.

The resultant code can be found on GitHub here.

Here is an example that is just filling the display RAM sequentially, which of course means we get to see the characteristic Spectrum “loading screen” pattern fill.

By the way, the bottom block of colour is because the last two attribute rows are inverted to test the attribute handling (well actually they are flashing, but this has caught them inverted). The middle third of the display also has bright set, but you can’t really see that at this point.

Putting it all Together

Plugging the Pico board into an RC2014 does actually seem to work!

Here is a BASIC program to fill the attribute memory and then the video memory:


10 FOR N=0 TO 767
20 POKE 22528+N,58
30 NEXT N
40 FOR N=0 TO 6143
50 POKE 16384+N,255
60 NEXT N

This fills the attribute memory with 0x3A (58) = no flash, no bright, white paper, red ink and the fills all video memory with 0xFF (255) revealing the now expected display loading pattern.

It is a bit slow running in BASIC mind. I did try using the fill command in the SC monitor, which is a lot faster, but I seemed to crash things pretty regularly and I’m not convinced the Pico could keep up with the burst of bus activity. It may also be that the SCM was using part of the memory that corresponds to the video memory and I was overwriting it, so I’ll have to look into that at some point.

It also has to be remembered that this is also running on an 8MHz Z80 not the 3.5MHz of the original ZX Spectrum so the RP2350 may well struggle to keep up.

Here is the BASIC code in action:

ZX Loading Screens!

I have a short python script that will read a TAP file and find the block that is 6914 (6144+768+2 header bytes) long and print out a BASIC program that will POKE the data into the screen memory locations.

Once the resulting BASIC code is pasted into the RC2014 terminal window and run, it does quite a nice impression of the ZX Spectrum loading screen, but naturally without all the associated screaming.

Note that this results in a BASIC program that is around 440 lines long, the vast majority being DATA statements.

(Also, you may notice that I’m using RGB222 mode in the video below).

Conclusion and Next Steps

It has taken quite a long time to get this far and there have been lots of side quests along the way into Raspberry Pi Pico PIO, using VGA on the RP2040 and RP2350, monitoring a Z80 from the RP2350, building some helper PCBs and so on.

But I’m really pleased with how this has come together so far.

The next steps involve some of the following:

  • Looking at the performance. The bus monitor might need to switch to PIO, or I might be able to overclock a bit more. Of course, I need to try it with a more authentic 3.5MHz Z80 too.
  • Figure out how to code something directly for the RC2014 so I’m not relying on BASIC or the SCM, but also look into why the SCM seemed to crash when writing to the display area.
  • Write some kind of loading screen selection box thing. It would be fun to be able to watch some classic game loading screens build up.
  • Ponder supporting more than one video mode so I could potentially support higher resolution (or at least proper CGA without attribute clash) video output. I wanted it for the Spectrum compatible display, but there is no reason I couldn’t go a bit further. I have enough GPIO for RGB222 at least.
  • Create a new PCB with the RP2350 and video circuitry combined.

But this is a really good place to leave this post for the moment.

Kevin

添加评论
点赞收藏
点踩分享查看原文
评论
?
参与讨论