The GraphQL Way

26May22

TL;DR

From a short time using GraphQL APIs I sense that there’s a ‘GraphQL Way’ for how things should be. A set of promises that the technology makes to its users. But those promises are frequently being broken, or at least undermined, as people rush to create GraphQL end points without perhaps investing enough time and effort into how things should work. The causes of this woe are mostly things we’ve seen before, as previous waves of new stuff have swept through the industry; so of course the remedies are much the same as ever, and it starts with building a picture of what good looks like.

Background

I’ve been using a few GraphQL APIs over the past year or so, and many that I’ve encountered fall short of the expectations set by the Introduction to GraphQL. This leads me to think that there is a ‘GraphQL Way’, but maybe it’s not yet easy enough for API developers to follow it.

Recent conversations with James Governor, Fintan Ryan, Paul Johnston and others such as this Twitter thread make me think that there’s a need to better articulate what good looks like. This post is intended to get the conversation started. I’m not a GraphQL expert, just somebody who has to use APIs and often finds themselves thinking ‘this could be better, this should be better’.

YOU ARE IN A MAZE OF TWISTY LITTLE PASSAGES, ALL ALIKE.

Colossal Cave Adventure

That’s how I often feel when trying to navigate a GraphQL API. The thing that you want should be through the next door, but after going through 3-4 doors you’ve circled back in the graph to the place you were a few steps back, with no sign of the grail of enlightenment.

The promises

GraphQL makes a number of implicit promises:

1. Ask for just the data you need, get back just the data you need

GraphQL is pretty good at realising this promise, which makes it great for its original purpose of more efficient communication between mobile apps and their backend services. It’s less chatty, and more verbose, which are both good things when you’re worried about latency, data consumption and network flakiness.

Previous APIs developed a nasty habit of serving up giant JSON documents, expecting them to be cut down to size by the client; whilst GraphQL lets you just get the bits that are needed.

2. Self describing

GraphQL APIs can serve up their own schema, which should be enough to help users of the API find what they need. They have the map, so it should be easy to find the treasure.

That’s fine, provided that the map makes sense, to people other than those who made the map. If the schema is just another way of shipping your org chart then it could be dysfunctional. If it’s full of unexplained internal references then it might be incomprehensible. Self describing only works if the description makes sense (to a newbie with no other context to grab onto). James Scott explores this in ‘Is GraphQL really self-documenting?‘.

Michelle Garrett recently talked at QCon London (link to follow once they get around to publishing) about the lengths taken by the Twitter GraphQL team to curate a schema that makes sense. This stuff doesn’t just happen, it takes work, and care – governance.

The good bit is that we’ve come a LONG way from Web Services Description Language (WSDL) and Universal Description Discovery and Integration (UDDI), and this time it’s clear that self describing can work (beyond standards org slide decks and out in the real world).

3. Filtering

GraphQL APIs should allow a request to specify a filter. But many implementations don’t actually implement that capability. This ends up breaking some of the first promise, as you now need to ask for everything and do the filtering locally. At least (with promise 1) GraphQL queries are specifying which nodes/fields you want back, so you’re not having to filter through all of everything, just some of everything. It’s not pretty though, and can quickly run into needing pagination where previous REST based approaches spared that.

The causes

Why do the promises get broken?

New shiny

There are lots of GraphQL implementations popping up that are the first time doing this for the teams involved. They’re inexperienced, don’t necessarily know what good looks like, and easily walk into minefields without noticing the warning signs.

CV++

Resume Driven Development bedevils the entire industry, and because GraphQL is the new shiny there are an awful lot of new GraphQL end points getting created to bolster CVs and LinkedIn profiles.

REST inertia

Where there’s an existing REST API there’s a risk that a new(er) GraphQL API will try to repeat the same story in a slightly changed dialect. The promises of GraphQL don’t come true by adding a new end point, they have to be underpinned by some (re)thinking about design and purpose.

Not used internally

If you’re creating a new API, and it’s just for external customers, and not being used internally; then something is wrong. Badly wrong. And you likely won’t be getting the immediate feedback to make things right; just lots of whinging and moaning off in the distance.

Making it better

There are of course ways to help ensure GraphQL implementations live up to the promises:

Documentation, Samples and Examples

My colleagues hear me say those words almost every day, and I say them because good documentation, samples and examples are generally the difference between something that’s accessible, easy to use, and scores mass adoption, and something that’s none of those things.

Even if it’s obvious to you the creator of the new super smart self describing GraphQL end point how every last drop of valuable data can be wrung out of it on demand; some samples and examples of that brilliance will help some of us less sharp people get to our ‘aha’ moments more quickly and easily.

Pave the golden path

There’s a lot of good stuff out there already to help people implement their GraphQL services, and corresponding good stuff to help people consume those services. But maybe there’s scope for more help with design considerations, the questions of ongoing schema governance, and helping people keep the promises of GraphQL.

Tooling might help here. But also pausing for a moment, thinking, running some experiments and getting feedback. Asking what good looks like, and charting a path there. Proper software engineering.

Conclusion

GraphQL APIs are coming into the mainstream, and as they do so they’re often breaking the promises made by the underlying technology and its proponents. That’s happening for a slew of fairly predictable reasons; which is why the remedies also look so familiar. We can do better, we should do better.


Generally MAME emulations that would use a terminal make use of the built in terminal emulation. So you can just start MAME and get going with a screen like this:

But that has some limitations, such as you can’t copy from it, or paste into it. Also if you want to use the MAME display (layout) for some blinkenlights or similar, then there has to be some other way of connecting a terminal.

Of course, there’s a way to deal with that. Running:

rc2014 rc2014bp5 -bus:1 micro -bus:1:micro:rs232 null_modem -bitb socket.localhost:1234

Is telling MAME:

  • rc2014 is starting my cut down emulator that I compiled whilst waiting for the 0.244 release (after which it can just be mame).
  • rc2014bp5 selects the 5 slot backplane
  • -bus:1 micro puts an RC2014 Micro card into the first slot
  • -bus:1:micro:rs232 null_modem configures the RS232 port on the RC2014 Micro to use null modem connectivity (rather than the default terminal)
  • -bitb socket.localhost:1234 then connects that null modem (serial) port to (TCP) port 1234 on localhost.

MAME itself will show No screens attached to the system:

But now I can attached a Terminal Emulator such as TeraTerm or PuTTY using Other to port 1234:

MAME should be configured for serial at 115200 baud:

And the terminal settings might take some fiddling with local echo etc.

Click to get a clearer view

COM ports

It’s also possible to connect to a COM port using something like:

rc2014 rc2014bp5 -bus:1 micro -bus:1:micro:rs232 null_modem -bitb COM6

Here I’m using com0com to provide a pair of COM ports on Windows (COM4 and COM6). I’ve found that the terminal emulator should be connected first (to COM4) so that MAME doesn’t hang when trying to connect to COM6. Again make sure that you have matching (fastest) baud rates in MAME and the terminal emulator.

This approach seems to be more reliable for cursor movement within the terminal emulation :)


Miodrag Milanović has created a new RC2014 driver for MAME, and it’s very comprehensive, offering a full range of systems, backplanes and boards. This post is intended to be a quick tour of how to use it.

At the time of writing the new driver hasn’t yet made it to a MAME release, but it should show up in 0.244. For now I’ve built a standalone rc2014.exe so that I can try things out. The command line to do that was:

make SOURCES=src/mame/drivers/rc2014.cpp SUBTARGET=rc2014 -j

I can then see a list of systems:

[MINGW64] C:\Users\Chris\git\github.com\cpswan\mame>rc2014 -lb
Source file:         Name:            Parent:
rc2014.cpp           rc2014
rc2014.cpp           rc2014bp5        rc2014
rc2014.cpp           rc2014bp8        rc2014
rc2014.cpp           rc2014bppro      rc2014
rc2014.cpp           rc2014cl2        rc2014
rc2014.cpp           rc2014mini       rc2014
rc2014.cpp           rc2014minicpm    rc2014
rc2014.cpp           rc2014pro        rc2014
rc2014.cpp           rc2014zed        rc2014
rc2014.cpp           rc2014zedp       rc2014

There are a LOT of ROMs to ‘PokeROM’ for this, which can be listed with:

rc2014 -listcrc

With the ROMs in place, let’s fire it up…

RC2014 Mini

Since I started my real life RC2014 journey with the Mini, I’ll repeat that:

rc2014 rc2014mini

The system boots up into 32K BASIC:

Hitting Scroll Lock then Tab brings up the options, and from there Machine Configuration can be selected:

Switching the ROM to Small Computer Monitor (SCM) is simply a matter of hitting the right arrow until SCM is selected, then going to Reset Machine:

At this stage a program can be typed in, whether that’s in BASIC or using the Monitor.

RC2014 Mini with CP/M

In real life it’s not long until you start adding things to your RC2014, and MAME brings instant gratification with the ability to try out expansion options.

First I need the image from CPM 128MB in transient apps.zip, which I’ve renamed A128.IMG. I can then start the emulation with the disk image mounted:

rc2014 rc2014minicpm -hard A128.img

Typing cpm into the SCM prompt starts CP/M:

The essential utilities are there:

RC2014 Pro

It’s fairly simple to step up to one of the bigger RC2014s. The only major change needed is to grab a CP/M image that supports the SIO/2 serial board, which I’ve renamed to S128.IMG:

rc2014 rc2014pro -hard S128.IMG

The big difference here is that the Slot Devices menu can be played with:

This provides lots of opportunities to play with different boards, and fiddle with the config of them.

Whilst many of the official RC2014 boards are there already, there’s also lots of scope to replicated the huge variety of other boards out there (or create new functionality from scratch).

Running Zork

One of the points of retro hardware emulation is to enjoy retro software, particularly games, and Zork was one of the defining games of the era.

With the Zork binaries in hand CPMtools can be used to copy them (in this case to the G: drive):

cpmcp -f rc2014g S128.img ~/zork/Z. 0:

NB: I’m using a patched diskdefs file there to provide the definition of rc2014g

Then start up CP/M, switch to G: and run zork1:

RomWBW

The largest RC2014 systems such as the Zed and Zed Pro use the 512K ROM 512K RAM board with Wayne Warthen’s RomWBW.

Grabbing the hd_combo.img file from the binaries directory inside the v3.0.1 release package, and mounting it as a Compact Flash card:

rc2014 rc2014zedp -bus:5 cf -hard hd_combo.img

Allows the system to boot up with lots of goodies installed:

Conclusion

This has been a very brief introduction to the new MAME RC2014 driver, and there’s a lot to explore, loads more hardware configurations to try out, a ton of cards (and similar systems) yet to be emulated, and endless possibilities bringing together the variety of the RC2014 ecosystem with the breadth of emulated components in MAME. I’m looking forward to tinkering, and seeing what happens next.


April 2022

30Apr22

Pupdate

April started off cold and wet, but the back half has been a great improvement, with some T-shirt weather days, and a chance for the boys to meet their sausagey friends.

Milo, Martha and Max

Motorcyle

The fair weather has also meant some opportunities to get out on my bike.

Tyres

At its MOT a few weeks back the bike got advisories for both tyres being close to the legal limit, and the front valve perishing. My first thought was that I could probably get another season out of the tyres, but once I started looking for new ones I was persuaded to change sooner. One factor was learning about tyre manufacture dates. The rear was a little long in the tooth at 7y old, but the front was positively geriatric at almost 14y old. Also much as I love the bike, I can’t say that it’s ever felt sure footed with the Bridgestone BT23s I got it with.

The only trouble is that the rear tyre is an obscure fitting. A quick search online suggested that Dunlop Roadsmart IV was the only show in town, and when I emailed my local place they came back with “are you sure about 160/70ZR18? We can’t find them anywhere”. Of course by that point Murphy’s Law meant that they’d gone out of stock at the place I’d found them online. I resolved to buy wherever I could find stock, and a last minute search of eBay struck gold with Sticky Stuff offering a matched pair for my bike at a very reasonable price.

Once fitted I then had a tenuous 100 miles to do of ‘scrubbing in’, and it must be the first time I’ve done that in 25y. They immediately felt at least as good as the old Bridgestone’s, and now that they are scrubbed in the bike is great. I wouldn’t quite use ‘transformed’, but the handling is certainly more confidence inspiring.

Fuel additive

While ‘scrubbing in’ I noticed that the bike was running super smoothly. Nothing really to do with the tyres, but rather the engine being able to pull better at low revs. I’d previously had to drop a gear to ride smoothly at 30mph, but now – not so much.

I then remembered that I’d put some Techron into the tank in Autumn after reading some recommendations for it on Quora. I’ve generally been sceptical about fuel additives, but this one really does seem to have worked well. Not only is the engine running much more smoothly, but I can see the difference in economy/range too.

QCon

QCon London 2020 was the last event I attended before lockdown, so returning to QCon 2022 felt like a watershed. I’d love to say that it marked post Covid life, but the pandemic clearly isn’t over. It was great to catch up with friends, and do some ‘hallway track’ again.

Vineyard tour

To make up for the tour of Chapel Down my wife missed in February we gave Bolney Wine a go with their ‘Ultimate Wine and Food Tasting Tour‘. It was a fabulous day out, and we really enjoyed the wine, though it was the nibbles we brought home with us, along with a bottle of their amazing ‘Sussex Rosso Vermouth‘.

I can see us returning to their shop just for more Charlie’s Smoked Trout, though there are a bunch more local vineyards to try out for their tours.

Bungee

We use a bungee cord to hold the back door open, and it was getting a bit frayed. Also the cord we used to hold the mini greenhouse against the wall had snapped. So I bought a reel of cord from Amazon, along with some hog rings (and hog ring pliers) to fix them up. For sure I could have just bought some more bungee cords with hooks etc. already on, but the replacement cord does seem better quality, so hopefully it lasts better.


Dear Mims Davies,

It’s been three months since I last wrote to you about Alexander Boris de Pfeffel Johnson’s unsuitability, and nothing has happened in that time to exculpate him.

In fact it is clearer than ever that he broke the law, repeatedly lied about it to parliament, and has declared no intention to resign (as he should to comply with his own ministerial code).

It now falls upon you and your colleagues to act as the last line of defence for our democracy and push him out. You have a choice: do you actually stand for the rule of law, or are you willing to go along with ‘might is right’ with the democratic fig leaf of the 2019 election?

I would hope that despite conflicting loyalties it should be a simple choice. That you chose to became a lawmaker because you believe that people abiding by better laws will make for a better country. As Margaret Thatcher once said, “the first duty of government is to uphold the law – if it tries to bob and weave and duck around that duty when it is inconvenient then so will the governed”. Allowing the present situation to continue will further destroy trust in government and public institutions on which a functioning society depends.

Of course you might be thinking you can toe the party line, and maybe grab some of that might for yourself, and use it for purposes that seem right. But there lies the road to ruin, as you will make yourself a co-conspirator in a corrupt and criminal enterprise; and no good can come from that.

“But what about the war?” I see already trotted out as a talking point. There are far more capable, competent, and seemingly honest people within the ranks of The Conservative Party who can deal with us providing support to Ukraine. Tom Tungendhat immediately comes to mind.

It’s time to act. Don’t let us down. History will not be kind to those who appease and equivocate over this mortal hazard to the integrity of our nation.

Yours sincerely,

Chris Swan


March 2022

31Mar22

Pupdate

Although it’s looking ready to snow outside as I type, we’ve had some glorious summery weather in March, which means plenty of time outside running around for the boys.

But the picture that grabbed most attention was this one:

People thought we’d got another sausage dog, but it was just the boys hanging out with their friend Amber at the dog sitter’s.

On the Road Again

The dogs were at the dog sitter’s because I was in Barcelona for Mobile World Congress (MWC) showing off a demo of end-end encrypted Internet of Things IoT. It’s my first overseas trip since Jan 2020, and the first EU stamp in my passport since Brexit (after queueing in the ‘other passports’ lane when I’d have previously whisked through the automated gates) :(

eSIM

Three no longer do their ‘Feel at Home’ roaming, which mostly doesn’t matter to me as I’ve ditched them for the family mobiles. But I still have a Three data SIM in my iPad Mini. So I got a GigSky eSIM for the trip, which was £7.19 for ‘Data Plan Europe 1024 MB 15 Days’. The provisioning process was super easy. It worked right away in the UK (so I could leave the Three SIM behind and avoid any possibility of costly mistakes), and service was great in Spain. I kept it running for the remaining days once home, and it seemed good. Recommended – will use again.

Carry on speedy boarding

While we’ve all been not travelling it seems that all the short haul airlines (whether they claim to be ‘budget’ carriers or not) have changed their carry on policies. Where before you could carry on something that would reasonably hold a few days clothes and essentials, the cheapest sticker price now gets you a carry on that’s approximately a medium sized ladies handbag. I consider myself a master of travelling light, but I wouldn’t attempt anything more than a day trip with the reduced bag allowance.

To get an actual carry on bag included you have to buy a premium ticket (or just pony up for a bag, which oddly costs even more). The net effect is that the ‘speedy boarding’ line is now almost everybody.

MAME stuff

The RC2014 Mini pull request I wrote about at the end of Feb eventually got merged, and I’m thankful for the patience of the dev team shepherding me through the process. I’ve subsequently had two more PRs merged, one for TMS9995 and another for a C64 ‘turbo’ ROM. They should all appear in MAME 0.242, which I hope will be in the next few days..

As promised I wrote some posts about Decoding the MAME Rosetta Stone and my Journey to the RC2014 Mini Driver for MAME.

I’ve got more RC2014 related things planned, but some boards for TMS9995 just dropped through the letterbox from Alan Cox (@EtchedPixels), so they might come first (if I can find all the bits I need).


My original attempt at an RC2014 Mini driver started out as pretty much a copy of the gsz80 driver, with a find/replace of ‘gsz80’ with ‘rc2014mini’. But as the MAME team reviewed my pull request, they surfaced a bunch of problems.

Firstly I didn’t have the correct memory map. The RC2014 Mini only has 32K RAM (and of course it needs the corresponding 32K BASIC).

// RC2014 Mini only has 32K RAM
void rc2014mini_state::rc2014mini_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x8000, 0xffff).ram();
}

Then there was some discussion about how to handle ROMs. Initially I went for the MAME BIOS selection method:

// from https://github.com/RC2014Z80/RC2014/tree/master/ROMs/Factory
// `dd skip=56 count=8 if=R0000009.BIN of=rc2014mini_scm.bin bs=1024`
// `dd count=8 if=R0000009.BIN of=rc2014mini_32k.bin bs=1024`
ROM_START(rc2014mini)
	ROM_REGION(0x2000, "maincpu",0)
	ROM_SYSTEM_BIOS( 0, "scm", "Small Computer Monitor")
	ROMX_LOAD("rc2014mini_scm.bin",  0x0000, 0x2000, CRC(e8745176) SHA1(d71afa985c4dcc25536b6597a099dabc815a8eb2), ROM_BIOS(0))
	ROM_SYSTEM_BIOS( 1, "32k", "32K BASIC")
	ROMX_LOAD("rc2014mini_32k.bin",  0x0000, 0x2000, CRC(850e3ec7) SHA1(7c9613e160b324ee1ed42fc48d98bbc215791e81), ROM_BIOS(1))
ROM_END

But I was persuaded that it was better to implement jumpers for the ROMs, as that’s more authentic to the original hardware:

void rc2014mini_state::machine_start()
{
	m_rombank->configure_entries(0, 8, memregion("maincpu")->base(), 0x2000);
}

// Set ROM bank from machine CONF at Reset
void rc2014mini_state::machine_reset()
{
	m_rombank->set_entry(m_jump_rom->read() & 7);
}

//...

static INPUT_PORTS_START( rc2014mini )
	PORT_START("A13-15")   /* jumpers to select ROM region */
	PORT_CONFNAME( 0x7, 0x0, "ROM Bank" )
	PORT_CONFSETTING( 0x0, "BASIC" )
	PORT_CONFSETTING( 0x1, "EMPTY1" )
	PORT_CONFSETTING( 0x2, "EMPTY2" )
	PORT_CONFSETTING( 0x3, "EMPTY3" )
	PORT_CONFSETTING( 0x4, "EMPTY4" )
	PORT_CONFSETTING( 0x5, "EMPTY5" )
	PORT_CONFSETTING( 0x6, "EMPTY6" )
	PORT_CONFSETTING( 0x7, "SCM" )
INPUT_PORTS_END

//...

ROM_START(rc2014mini)
	ROM_REGION( 0x10000, "maincpu",0 )
	ROM_LOAD( "r0000009.bin",    0x0000, 0x10000, CRC(3fb1ced7) SHA1(40a030b931ebe6cca654ce056c228297f245b057))
ROM_END

Of course that now means defining an INPUT at the end block:

//    YEAR  NAME            PARENT    COMPAT    MACHINE        INPUT          CLASS             INIT           COMPANY           FULLNAME                FLAGS
COMP( 2007, gsz80,          0,        0,        gsz80,         0,             gsz80_state,      empty_init,    "Grant Searle",   "Simple Z-80 Machine",  MACHINE_NO_SOUND_HW )
COMP( 2015, rc2014mini,     gsz80,    0,        rc2014mini,    rc2014mini,    rc2014mini_state, empty_init,    "Z80Kits",        "RC2014 Mini",          MACHINE_NO_SOUND_HW )

Along the way the RC2014 Mini stuff was folded back into the gsz80.cpp driver rather than duplicating it. So it gets its own class:

class rc2014mini_state : public gsz80_state
{
public:
	rc2014mini_state(const machine_config &mconfig, device_type type, const char *tag)
		: gsz80_state(mconfig, type, tag)
		, m_rombank(*this, "rombank")
		, m_jump_rom(*this, "A13-15")
	{ }

	// Different machine config due to different RAM
	void rc2014mini(machine_config &config);

protected:
	// RC2014 Mini only has 32K RAM, so memory map is different
	void rc2014mini_mem(address_map &map);

	virtual void machine_start() override;
	virtual void machine_reset() override;

	required_memory_bank m_rombank;
	required_ioport m_jump_rom;
};

and that needs the implementation for state to make use of the different memory map and the ROM switched into it:

void rc2014mini_state::rc2014mini(machine_config &config)
{
	gsz80(config);
	m_maincpu->set_addrmap(AS_PROGRAM, &rc2014mini_state::rc2014mini_mem);
}

All of this rather stretched my very limited C++ knowledge. I did a Computer Based Training (CBT) course on C++ back in the mid nineties where absolutely nothing sank in (maybe because it spent too much time recapitulating C stuff that I though I already knew). When I started doing Java Enterprise stuff in 2000 classes and objects and inheritance and polymorphism were all new to me. To try to claw things back I ran through the W3 schools C++ Tutorial, which does a good job of covering the basics. If you’re new to C++ but want to try stuff in MAME then it’s probably a good enough place to start. Beyond that, Learn C++ looks pretty comprehensive.


I mentioned in my previous post Grant Searle Simple Z-80 Machine on MAME that the gsz80.cpp driver was like finding a Rosetta Stone for MAME. In this post I’m going to go through it block by block to explain how I parsed it, and how it enabled me to figure out how to write a driver for the RC2014 (Mini).

// license:BSD-3-Clause
// copyright-holders:Frank Palazzolo

Thanks Frank :)

// MAME Reference driver for Grant Searle's Simple Z80 Computer
// http://www.searle.wales/

And thanks Grant. A key point here is that I was familiar with Grant’s design from my RC2014 tinkering. It’s just about as simple as making a computer can get, using just 7 chips. I’d previously looked at drivers like the Imsai, but couldn’t figure it out. But Grant’s design is clean and simple, and Frank’s code is clean and simple.

For a hardware person (like me) it should be possible to relate the blocks of code to the blocks of hardware. So here goes…

We start off with the includes, which are like a bill of materials for the pieces being used.

// All the common emulator stuff is here
#include "emu.h"

This is the key include, providing all the stuff underlying the other parts. If I was making a Grant Searle system on a breadboard, then this is the bit that would give me the breadboard (and the laws of physics).

// Two member devices referenced in state class, a Z80 and a UART
#include "cpu/z80/z80.h"
#include "machine/6850acia.h"

This gives us two of the main chips, the Z80 CPU and the 6850 UART. The other two main chips are RAM and ROM, which we’ll get to later, and there are also 3 ‘glue’ chips, which we don’t actually need when doing things in software.

// Two more devices needed, a clock device for the UART, and RS-232 devices
#include "machine/clock.h"
#include "bus/rs232/rs232.h"

The system has a clock using a crystal and one of the glue chips, and input/output happens with a terminal attached to an RS232 port.

// State class - derives from driver_device
class gsz80_state : public driver_device

We’re creating a driver, which is done by creating a class based on driver_device (coming from that emu.h include earlier).

{
public:
	gsz80_state(const machine_config &mconfig, device_type type, const char *tag)
		: driver_device(mconfig, type, tag)
		, m_maincpu(*this, "maincpu")   // Tag name for Z80 is "maincpu"
		, m_acia(*this, "acia")         // Tag name for UART is "acia"
	{ }

	// This function sets up the machine configuration
	void gsz80(machine_config &config);

The public methods for the class are essentially plugging the CPU and UART into the emulation ‘breadboard’.

private:
	// address maps for program memory and io memory
	void gsz80_mem(address_map &map);
	void gsz80_io(address_map &map);

	// two member devices required here
	required_device<cpu_device> m_maincpu;
	required_device<acia6850_device> m_acia;
};

and with the private methods we’re declaring that there will be a memory map, an I/O map and getting specific about the CPU and UART that are being used.

// Trivial memory map for program memory
void gsz80_state::gsz80_mem(address_map &map)
{
	map(0x0000, 0x1fff).rom();
	map(0x2000, 0xffff).ram();
}

The memory map uses the first 8K for ROM and the remaining 56K for RAM.

void gsz80_state::gsz80_io(address_map &map)
{
	map.global_mask(0xff);  // use 8-bit ports
	map.unmap_value_high(); // unmapped addresses return 0xff
	map(0x80, 0xbf).rw("acia", FUNC(acia6850_device::read), FUNC(acia6850_device::write));
}

The 6850 is mapped into an I/O address (which in hardware would be done by those ‘glue’ chips).

// This is here only to configure our terminal for interactive use
static DEVICE_INPUT_DEFAULTS_START( terminal )
	DEVICE_INPUT_DEFAULTS( "RS232_RXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_TXBAUD", 0xff, RS232_BAUD_115200 )
	DEVICE_INPUT_DEFAULTS( "RS232_DATABITS", 0xff, RS232_DATABITS_8 )
	DEVICE_INPUT_DEFAULTS( "RS232_PARITY", 0xff, RS232_PARITY_NONE )
	DEVICE_INPUT_DEFAULTS( "RS232_STOPBITS", 0xff, RS232_STOPBITS_1 )
DEVICE_INPUT_DEFAULTS_END

Connection parameters for the emulated terminal being connected to the RS232 interface provided by the 6850 UART to give input and output.

Now we wire the parts together:

void gsz80_state::gsz80(machine_config &config)
{
	/* basic machine hardware */

	// Configure member Z80 (via m_maincpu)
	Z80(config, m_maincpu, XTAL(7'372'800));
	m_maincpu->set_addrmap(AS_PROGRAM, &gsz80_state::gsz80_mem);
	m_maincpu->set_addrmap(AS_IO, &gsz80_state::gsz80_io);

The clock speed is set for the Z80 CPU, and it’s connected to the address map, where it will find the ROM and RAM, and the I/O map, where it will find the 6850 UART.

	// Configure UART (via m_acia)
	ACIA6850(config, m_acia, 0);
	m_acia->txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
	m_acia->rts_handler().set("rs232", FUNC(rs232_port_device::write_rts));
	m_acia->irq_handler().set_inputline("maincpu", INPUT_LINE_IRQ0); // Connect interrupt pin to our Z80 INT line

The UART is wired up to the CPU and RS232 interface.

	// Create a clock device to connect to the transmit and receive clock on the 6850
	clock_device &acia_clock(CLOCK(config, "acia_clock", 7'372'800));
	acia_clock.signal_handler().set("acia", FUNC(acia6850_device::write_txc));
	acia_clock.signal_handler().append("acia", FUNC(acia6850_device::write_rxc));

The UART is also connected to a clock (at the same frequency as the CPU, because in hardware they’d use the same clock signal).

	// Configure a "default terminal" to connect to the 6850, so we have a console
	rs232_port_device &rs232(RS232_PORT(config, "rs232", default_rs232_devices, "terminal"));
	rs232.rxd_handler().set(m_acia, FUNC(acia6850_device::write_rxd));
	rs232.dcd_handler().set(m_acia, FUNC(acia6850_device::write_dcd));
	rs232.cts_handler().set(m_acia, FUNC(acia6850_device::write_cts));
	rs232.set_option_device_input_defaults("terminal", DEVICE_INPUT_DEFAULTS_NAME(terminal)); // must be below the DEVICE_INPUT_DEFAULTS_START block
}

Finally the terminal is connected to the RS232 interface.

// ROM mapping is trivial, this binary was created from the HEX file on Grant's website
ROM_START(gsz80)
	ROM_REGION(0x2000, "maincpu",0)
	ROM_LOAD("gsz80.bin",   0x0000, 0x2000, CRC(6f4bc7e5) SHA1(9008fe3b9754ec5537b3ad90f748096602ba008e))
ROM_END

We said earlier that there would be 8K of ROM at the start of the memory map, here’s where we describe which ROM will be plugged in.

// This ties everything together
//    YEAR  NAME         PARENT    COMPAT  MACHINE   INPUT    CLASS        INIT           COMPANY           FULLNAME                FLAGS
COMP( 201?, gsz80,       0,        0,      gsz80,    0,       gsz80_state, empty_init,    "Grant Searle",   "Simple Z-80 Machine",  MACHINE_NO_SOUND_HW )

The driver definition concludes with a line describing the machine being emulated in terms of its ROMs, machine definition, input (not used), state class, and initialisation along with some biographical details.

And that’s it. The 4 main ICs, and how they’re wired together can be fairly easily picked out of the code. In a follow up post I’ll run through what changed to make an RC2014 driver.


Black Sea ’93

12Mar22

The last few weeks have had me reflecting a lot on my time in Russia and Ukraine almost 30y ago…

When I joined the Royal Navy in ’89 we were definitely living in ‘interesting times’. The defence studies lecturers at Britannia Royal Naval College (BRNC) Dartmouth were tearing up talks they’d used for decades as the Iron Curtain fell, the Berlin Wall came down, and we shifted into a brave new world of after the Cold War.

As I finished my degree I found myself spending a few weeks with the Polish Navy on their sail training ship ORP Iskra, giving me my first experience of a former Warsaw Pact country. A week or so later I joined HMS Avenger to complete my ‘fleet’ training, and found myself heading back East for the first Royal Navy deployment to the Black Sea since the end of WW2.

Novorossiysk

After transiting the Bosphorus into the Black Sea our first port of call was Novorossiysk, a large naval port city[1]. We stayed there about a week, and it was quite an experience. My time ended up split between hosting people on the ship, and attending the variety of receptions we were invited to.

For me, the highlight was visiting the ‘Champanski’ factory at Abrau Durso. They had a proud tradition of making high quality ‘traditional method‘ wine going back to the time of the Tsars, which had somehow survived the Bolshevik revolution, the ‘Great Patriotic War’ and subsequent Stalinism.

We were hosted for dinners on Russian warships, giving the opportunity for a good look around, and leaving the impression of ‘what were we ever scared of?’ – yes they were armed to the teeth, but all the kit was garbage. A visit to a Sovremenny-class destroyer revealed an operations room with command and control capability that looked like a WW2 time capsule [and some of those ships are still in service]. Of course the real problem was strength of numbers rather than quality of equipment. We faced wave after wave of vodka toasts saturating our livers, just like in conflict we’d have faced wave after wave saturating our defences.

I had my first encounter with the Klept. The local mayor (or maybe it was his ‘fixer’) was a former KGB Colonel. His kids had Nike trainers and satellite TV. The ‘McMafia’ was taking root and tapping itself into the cashflows as commerce opened up.

One peculiarity was money. Normally we’d get local currency, but for this visit we got dollars, and it soon became clear that the locals preferred pristine notes rather than worn ones, as they had more life left in them. The corruption and petty crime were insane. People expected $bribes to be paid for all sorts of things, and many of our sailors were robbed (some at gun point) for the $ in their wallets. We had to shut off access to the ops room during ship open to visitors as people were pinching the button caps from the Computer Assisted Action Information System (CAAIS) consoles to take home as souvenirs.

The visit was a BIG DEAL for the locals, and to an extent we were treated like rock stars. People would grab anybody in uniform to have their photo taken with a visitor from the West. I met some Brits who were starting up an import/export business, and amongst younger people there was much excitement and enthusiasm for the opportunities that were opening up. But the older folk were more skeptical, with many morning the demise of the Communism, the USSR, and the certainty it had given them.

Another memorable moment was meeting some of the teachers from a local school. We’d stuffed the ship with donated clothes and books and medical equipment that had all been donated to local charities. But they still wanted more English language books, so I gave one teacher my copy of Terry Pratchet’s ‘The Colour of Magic’, which I’d read on the way.

I also recall a soldier proudly showing off to me his AK-74. I was a little bemused that he didn’t remove the magazine as part of showing me the weapon was safe, until it transpired that nobody had any bullets. There were soldiers with guns all over the place, but none of them were actually armed.

A pause in the action

Our next port of call was supposed to be to Georgia, but they were rather inconveniently having a civil war, so we spent a few extra days at sea. For me it was a welcome break. I’d got food poisoning or a stomach bug in Novorossiysk that led to me losing a stone of weight, the constant entertaining and receptions had led to borderline alcohol poisoning, and the busy schedule hadn’t been at all conducive to progressing through the ‘task book’ that officers under training needed to complete.

Odesa

Next up was Odesa, Ukraine’s third largest city, and a major port. We weren’t there long, so I didn’t get to see very much of the place. As usual we hosted a cocktail party on arrival for local dignitaries, but this one was different. The Minister for Defence Malcolm Rifkind[2] was guest of honour, and he brought with him an entourage of the great and the good from Kyiv. I remember speaking to a young lady who spoke remarkably good English, and was in the process of setting up DHL’s operations in the Ukraine.

I got to spend an hour or two ashore sightseeing the wonderful architecture, but there wasn’t much to do. Though street traders were sprouting up, mostly selling Matryoshka dolls in a mix of traditional styles and modern ones representing Gorbachev and Yeltsin.

From my conversations with Ukrainians they were not bemoaning the end of the USSR. They were relishing the opportunities of independence, and all the potential that it brought.

And thus the divergent paths that bring us back to the present were embarked upon – Russia mourning its decline of empire, and Ukraine embracing an independent future. I’ve seen observations that Putin just doesn’t get this, but rather has a mental model forged in the 70s and 80s, and is now so insulated by surrounding kiss-ups that there’s no opportunity for the truth to break through.

Contstanta

I didn’t get to see anything of Constanta, as I was assistant liaison officer for the visit, which tied me down in admin work all day every day. At least the young officer from the Romanian Navy assigned to work with us was a great chap[3]. I did however get to see some other parts of Romania by helping to organise a trip to Transylvania, which included sites like the former Royal Palace at Peles Castle, and Bran Castle.

Bucharest was still showing the scars of the revolution that had deposed Ceaușescu, though we were treated to some amazing food there before the journey home. At least the back row of seats on the plane didn’t fall off their mountings on the way back like they had on the way out.

Varna

Varna in ’93 felt very different to the other Black Sea ports. It was busy and vibrant and fun in the ways that a city used to tourists should be. It wasn’t just a commercial port, looking after sailors for the short time there bringing trade in and out; it was a place where people came to eat, drink and be merry. We were almost back in the West, and it showed.

Update

16 Mar 2022 – my former colleague Igor Ilyinsky wrote about Odesa as his birthplace, and his mixed sense of identity, which reminded me of conversations about (Ukrainian) language whilst I was there.

Notes

[1] As Putin annexed the Crimea in 2014 I recall seeing an argument that Russia needed to secure Sevastapol as their only viable Black Sea port. This is of course nonsense, they always had Novorossiysk as well (and Sevastapol had continued to serve as a port for the Russian Navy).
[2] It can’t go without saying that I’ve never met a more ill mannered, pompous twat in all my life than Malcolm Rifkind. It says a lot about our political system that such awful people find their way into ‘safe seats’ where they get voted for despite everything, and of course this only emboldens their behaviour.
[3] His English was of course much better than my Romanian, but he very much desired the little Collins Romanian-English Dictionary we had on board. It wasn’t mine to give him, so I took his address and picked one up for him at W H Smiths once back home.


In my RC2014 Mini on MAME post this was one of the things I was planning to try next, and once again there was an existing driver that had most of the pieces in place – evmbug.cpp implementing the TMS 9995 Evaluation Module (aka TMAM 6095).

My first job was getting the existing driver working, which meant getting the ROMs sorted out. I found that the ROM config didn’t quite match the board, so I made some changes and got myself a working EVMBUG monitor. From there it was a short step to HELLO WORLD:

For the TMS 9995 Breadboard System (which I’ve previously made using RC2014 modules) I needed to add a revised memory map for the larger RAM, and the selection of ROMs for EVMBUG, BASIC, Forth etc. My pull request for tms9995bb has been merged, so it will be in the next release of MAME.

At this stage the one thing I’d like to change is the serial driver support. At the moment the driver isn’t using the TMS9902 UART (because it doesn’t support the broader rs232 system in MAME), meaning that the terminal is directly connected into an I/O port. The consequence of that is that it’s not possible to run the emulation headless and connect another terminal emulator, which turns out to be essential if you want to paste code in – something I’ve been trying out getting CP/M working with the gscpm driver. But more on that in another post.