Watching a Z80 Bus from an RP2350 – Part 2

Having gone through the basics in my previous post: Watching a Z80 from an RP2350, I’m now turning to the memory of the Z80 to see what I can do there.

As I mentioned last time, there is nothing particularly unique about doing this, and a number of projects have been here before.

Once again, I’m using my PGA2350 breakout to monitor my RC2014.

Z80 Bus Memory Writes

As described in part 1, there are a number of different bus control signals, creating different bus cycles. What I need to watch out for are memory writes. The Z80 CPU User Manual shows the timing diagram:

This appears relatively straight forward – we can see that a valid write will have the following:

/MREQ = 0 (active)
/RD = 1 (inactive)
/WR = 0 (active)
/IORQ = 1 (inactive)
/M1 = 1 (inactive)

At this point in the cycle, the address and data buses will have valid data. There is one additional complication however – memory refresh. But I believe that during a memory refresh cycle, as well as /RFSH = 0 (active), I don’t think either of /RD or /WR will be active, so I think it’s probably ok.

Memory accesses are generally 3 T-states (clock cycles) long, so at 10MHz, that will be around 300nS for one write, which isn’t very long. But if a RP2350 is running at 150MHz, that would mean there are around 45 clock cycles within which to detect it.

The Z80 CPU card I’m using in my RC2014 for this is the SC149 which has a built-in clock module running with a 7.3728MHz crystal.

RP2350 Updated Code

In order to be able to see what is going on, I want some means of feeding back what has been pulled off the bus. So the plan is as follows:

  • Create a 64K memory buffer on the RP2350.
  • Watch for /WR and /MREQ and pull the address and data off the bus.
  • Use the address as the index into the 64K buffer and store the byte of data.
  • Use both cores:
    • core 1 will be running just the watching and storing code, nothing more.
    • core 0 will be running some IO code that allows the memory buffer to be interrogated.

With the unofficial Arduino RP2040 core, the simplest form of multicore processing is to define two setup() and loop() functions, as described here: https://arduino-pico.readthedocs.io/en/latest/multicore.html

I’ve implemented a simple memory dump command. The serial monitor can be used to enter a 4-digit hex address, between 0 and FFFF and 16 bytes of memory will be dumped to the serial console.

The basic logic is as follows:

ram [65536]

setup()
Initialise serial port

loop()
Wait for serial input
Read characters as a hex string
print out the line of memory

setup1()
Clear ram[] buffer
Initialise the GPIO for address and data reading

loop1()
IF /WR and /MREQ
Read address and data lines
ram[address] = data

First use

The RC2014 SC monitor has memory dump, edit and fill commands. The RC2014 memory map is as follows:

  • 0000-7FFF ROM
  • 8000-FFFF RAM

When running against simple writes, the memory writes are successfully detected and the data stored in the correct places.

When performing a memory fill command however, it would appear that some writes are missed.

RC2014 Console:

*help
Small Computer Monitor by Stephen C Cousins (www.scc.me.uk)
Configuration S8 20221216, Monitor 1.3.1, SCZ80 BIOS 1.3.1

Monitor commands:
A [<address>] = Assemble | D [<address>] = Disassemble
M [<address>] = Memory display | E [<address>] = Edit memory
R [<name>] = Registers/edit | F [<name>] = Flags/edit
B [<address>] = Breakpoint | S [<address>] = Single step
I <port> = Input from port | O <port> <data> = Output to port
G [<address>] = Go to program
BAUD <device> <rate> | CONSOLE <device>
FILL <start> <end> <byte> | API <function> [<A>] [<DE>]
DEVICES, DIR, HELP, RESET
BASIC Grant Searle's adaptation of Microsoft BASIC
WBASIC Warm start BASIC (retains BASIC program)
CPM Load CP/M from Compact Flash (requires prepared CF card)
*fill 8000 8100 a5
*m 8000
8000: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8010: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8020: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8030: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8040: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8050: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8060: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8070: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8080: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8090: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
80A0: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
80B0: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
80C0: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
80D0: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
80E0: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
80F0: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8100: A5 D9 BA E8 B2 C3 FA 0B 20 0A AC F3 38 0A 89 A6 ........ ...8...
8110: CA 8B C0 92 A0 34 AE 67 2B 82 AB 8E EB A3 F8 DE .....4.g+.......

Arduino serial monitor:

8000: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8010: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8020: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8030: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8040: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8050: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8060: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8070: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8080: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8090: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
80A0: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
80B0: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
80C0: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
80D0: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
80E0: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
80F0: A5 A5 A5 A5 00 00 00 00 A5 A5 A5 A5 00 00 00 00 ................
8100: A5 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
8110: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

As part of the address reading code I was still updating some debug LEDs to represent the data being accessed. Removing the updating of the LEDs, fared better, but still missed the odd byte:

8000: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 00 ................
8010: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8020: A5 A5 A5 00 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8030: A5 A5 A5 A5 A5 A5 A5 00 A5 A5 A5 A5 A5 A5 A5 A5 ................
8040: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 00 A5 A5 A5 A5 ................
8050: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 00 ................
8060: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8070: A5 A5 A5 00 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8080: A5 A5 A5 A5 A5 A5 A5 00 A5 A5 A5 A5 A5 A5 A5 A5 ................
8090: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 00 A5 A5 A5 A5 ................

I also never seem able to see any writes that happen to memory as part of the SC monitor starting up and running. The additional data that is seen above is probably just random data kicking around in the SRAM from a previous session.

So the basic principle seems sound, but there are naturally performance issues to consider. Some options include:

  • Overclocking the RP2350.
  • Slowing down the Z80.
  • Using hand-optimised assembler in the loop.
  • Switch to using the PIO and DMA to automatically respond to memory writes.

It turns out that overclocking the RP2350 is trivial when using the unofficial Arduino core – just select the CPU speed as one of the Arduino board options:

When running at 200MHz I seem to be able to capture all the writes from the SC monitor “fill” command running on a Z80 clocked at 7.3728MHz:

*fill 8000 8fff a5
*m 8000
8000: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8010: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8020: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8030: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8040: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8050: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8060: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................
8070: A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 A5 ................

Paging through on the Arduino, all RAM in the range 8000-8FFF is indeed set to A5. It remains to be seen if this will be quick enough for some actual code running on the Z80, but this is very promising.

There are options to overclock up to 300MHz, but I don’t want to push things. Yet.

But really, the way to do it will be PIO.

Conclusion

This feels like some significant progress has now been made. It is really great to know I can grab writes off the Z80 bus and mirror them in memory.

I will need to perform some kind of extended test, but really that requires some running code on the Z80 now rather than commands in the SC monitor.

And for robustness I really do need to implement a PIO and DMA autonomous memory grabber such as that used on OneROM.

I am also thinking I’d like a jumper-configurable clock module for my RC2014. Something along the lines of that used with a Z80 CPU Tester where a LS193 binary counter or similar allows for subdivided clocks. This would be really useful for experimenting.

But for now, this will do nicely and might even be enough to allow me to go to the next stage of what I’d like to do.

Kevin

#pga2350 #rc2014 #rp2350 #z80
File:RC2014 Zed Pro Pride Front quarter.jpg - Wikimedia Commons

Turned out nice! And no shorts according to my measurements.

On to soldering the modules!

#RC2014 #Electronics
[!upload-IMG_20260601_162023.jpg]

I had a fantastic time at #RetroFest2026 this weekend. It was great seeing so many old machines there, as well as so many old faces too. And new ones!

Thanks everybody that stopped by the #RC2014 stand to chat, especially those of you that meant I had less stock to bring home :-)

The Captain Zilog comics proved popular. Everybody seemed to like the ZBot80 buggy (RC-Mobile?) too. Hopefully that will be available by the end of the month.

It's kind of cool that we have a modern de facto standard equivalent to the S-100 in the #RC2014 bus.

Let's get this show on the road.

#RC2014 #electronic

RE: https://mastodonapp.uk/@quazarsamcoupe/116662682108007384

It's been great seeing all the photos on various socials about the RetroFest 2026 show which I wasn't able to make this year to show what I do for #SAMCoupe, #RC2014 and #ZXSpectrum

The 10% off offer across the entire range of peripherals I make for RC2014 / RCbus / Compatibles are running until Friday. All the info at: https://2014.samcoupe.com

#retrocomputing #retrogaming #computing #hardware #electronics
#soundchip #chiptune #synth

#SAMCoupe music playing on a #RC2014 Mini setup!

SAM's E-Tracker music (and also Borik's online SAA1099Tracker that has a compile to Z80 code output) give a code blob with an Init routine to call on startup, and a Frame routine to be called 50 times a second. Perfect for my SAA1099P Soundchip Interface for RC2014 & Compatibles. (While you can use a delay loop for crude 50Hz timing for the Frame routine, the interrupt controller on my SID Soundchip Interface can be used for accurate timing - it can generate interrupts 50, 60 or 100 times a second!)

10% off my range of peripherals for RC2014/RCbus systems weekend!
https://2014.samcoupe.com
(UK / Europe / Worldwide shipping options available).

(These peripherals can also be used on the ZX Spectrum via my ZX-RC Bus Adapter)

#retrocomputing #retrogaming #chiptune

+++ SYSOP NOTICE +++ SYSOP NOTICE +++

On #rc2014bbs you can use CTL-S to pause text output and CTL-K to cancel text ouptut (e.g. when a bulletin or textfile is being displayed).

A short message indicating this feature is shown each time an ASCII file is displayed. Previously it read:

"Hit CTL-S or s to Pause; any key to Resume. Hit CTL-K or k to Abort."

However, some people seem to misinterpret this message as the way to abort message entry when entering a message.

This won't work. To abort message text entry hit <Enter> on an empty text line. A command prompt will be shown. There, press 'A' (without the quotes) to abort and return to the main menu.

So now I have changed the info message to read:

"CTL-S or s: pause screen output; any key to resume. CTL-K or k: cancel output."

I hope this helps clarify things a little.

Thank you.

#rc2014
#bbs
#rcbox