A while ago I made this MIDI module for RC2014:
It works but a better design would have its own serial chip and port decoding.
As it is, it provides the MIDI interface and a clock signal for the second SIO2 serial port. This means that it requires a little setting up and will only work for RC2014s with an SIO2 (and port B not already used).
I think people might reasonably expect it to be plug-and-play and self-contained, ie do all the serial itself.
My challenge to myself is to:
- learn how to connect a serial chip (probably 68B50 ACIA) to receive the incoming MIDI and to serialise outgoing MIDI
- design the module, including the port decoding
- write a library so that it can easily be used on any RC2014. Potential applications include a MIDI sequencer and using incoming MIDI to trigger notes on the AY or SID sound chips.
Entering the Retro Challenge 2024 (aka RC2024/10) has given me an incentive to get on with this!
I'm happy to see several more entries in the RC2014 category, including another musical project.
Project logs:
- 1 Oct 68B50, ports, MIDI serial
- 4 Oct remarked chips, prototyping
- 7 Oct interrupts
- 8 Oct schematic and pcb design
- 9 Oct software refinement
- 16 Oct shiny new pcb
- 24 Oct 'AY Piano' example application working
- 25 Oct 32 steps forward!
- 27 Oct The finish line!
Checklist:
☑ Software framework in asm for 32k Classic
☑ Software framework in asm for CP/M Pro
☑ Software framework in asm for ROMWBW machines (has some special challenges)
☑ Make "AY piano" (plays notes using AY sound chip in response to notes played on a MIDI controller)
☑ Make a simple sequencer
☑ Write documentation
Tue 1 Oct
I've ordered a 68B50 aka ACIA. I think it could also correctly be described as a UART, which also seems to apply to the protocol. (I could have pinched one from a RC2014 classic but don't want to mess around on a breadboard with something that's part of a regularly-used machine.)While that chip isn't manufactured today and there's no new old stock (despite what some sellers claim) it is easy to buy them 'tested' (what 'tested' means depends on the seller). It does seem like the most sensible and most age-appropriate option
While waiting for that I set about reading and understanding the data sheet and starting to sketch my schematic.
This is more like revision, because I own a Datel MIDI cartridge for C64 and I've dabbled in this stuff when programming that.
ports and registers
The 6850 has four internal registers but you can think of them as two that can each be written to or read.
My C64 cartridge implements them as four separate addresses in the i/o space, two of which you can poke, two of which you can peek. The RC2014 classic serial module uses two hardware ports, each of which is readable and writable and that seems like the way to go here.
The data register is obviously where you write your data to be transmitted and read your data when there's a byte ready. (This chip doesn't seem to buffer any bytes, which would definitely be helpful here.)
The control register, when read, gives a status byte. Each bit is a flag, the most useful ones to us are the lowest two which indicate when the receive buffer is full (indicating that we can read a new byte of data) and when the transmit buffer is empty (indicating that we can write a new byte of data).
When written, the various bits allow us to specify whether we want interrupts, the flow control and the serial word length, parity and stop bits that we want*, and importantly the clock division that we want**.
The MIDI serial protocol
* I found a discrepancy here. The instructions for my C64 cartridge tell me to send the bytes 03 and then $12 or $92 (depending on whether interrupts are needed) to the control register. I now know that the 3 is a reset, followed by a byte that sets clock division to 64 (more of that in a second) and sets the serial to "8 Bits +2 Stop Bits". However, this page for the same interface says $16 or $96. That sets the serial to "8 Bits + 1 Stop Bit".
The internet generally agrees that MIDI is serial at 31,250 baud / bits per second, 8 bits plus a start bit and a stop bit.
I don't know how this matches up with the discrepancy above. Is a start bit + stop bit the same as two stop bits? Does it matter if there's an unnecessary stop bit? I guess all I can do is experiment and see what works.
[update] I found a note in the data sheet that a start bit is always sent anyway. So I guess that one stop bit is all that's needed, and maybe an extra stop bit makes no difference. It seems correct to use the "8 Bits + 1 Stop Bit" setting but we'll see when I try to make it work.
When making my Mk1 module I calculated that a 2Mhz clock would give me the required 31,250 baud. I based this on the clock speed of the RC2014 and the resulting baud rate (7.3Mhz giving 115200 baud).
This makes sense now (assuming that the SIO2 which I was taking advantage of before is also set to divide by 64) because 2Mhz divided by 64 is 31250.
So I can simply use the same clock circuit and components from my Mk1 module.
I think that's about it for now. When my 68B50 arrives I'll be able to breadboard it and know what I'm doing, and write some rudimentary code for it.
Over and out.
Fri 4 Oct
Postie has just brought my 68B50. As predicted, despite the seller claiming 'new old stock', of course it isn't. I don't think there is any NOS left of this part (I'll be happy to be proved wrong). There's a sheen to the surface that I recognise now (it fails the acetone test) and the legs are tinned and not perfectly straight. It's almost as if making old chips look new (and claiming that they are) is just acceptable and accepted now.
Anyway, it works which is the most important thing. I swopped it with the same chip in my RC2014 Classic and ran it. (Interestingly, the appearance and markings are almost identical, except for the date code, I half expected that to match.)
[some hours later]
It's very late but I've had some success.
First, for a quick and dirty way to show that my understanding of how to hook up the 6850 is correct, I built this:
I tapped the digital i/o module for a signal when port 0 is written, tapped my mk1 midi interface for its 2Mhz clock signal and used a dev board for the midi circuitry.Then I realised that this wouldn't work. I needed to send data to both ports; control and data. The register select on the 6850 is one line, high or low. I could have added extra logic for that but that would become even more sketchy than this is already. So I pulled that apart before even switching it on.
And took the time to build this instead:
I still didn't bother to put the clock circuit on there, or the MIDI ports, but at least this does its own port decoding* and is connected up according to the schematic that I've sketched out. I have more confidence in wire-wrap than breadboards anyway.
(I just went into MBASIC80 and sent the reset / setup bytes to the control register, currently on port zero, and then these three bytes, a MIDI message, to port 1)
.. and YAY! received at the other end.
The software should really read the status register and check that the data register is empty before sending each byte, but I (correctly) guessed that BASIC would be slow enough for that not to be necessary.
This setup will allow me to get on with some software.
Ports
*For this prototype I'm just using ports zero and one. That kept the wiring simple
After a conversation with Spencer about unused ports, I've settled on DE and DF with a solder pad to allow that to be switched to AE and AF, as with my recent paddle module.
Over and out.
Mon 7 Oct
Let me interrupt you
Real life has got in the way of this project over the last couple of days and I have other things that I should really have been giving priority to today. But curiosity got the better of me and I did a lot of reading about Z80 interrupts, which will be important when wanting to receive MIDI. The 6850 doesn't buffer any bytes other than the one it has received so you have to collect it pretty smartly before the next one is ready.
I now know all about the three interrupt modes. Since the 6850 just has a single pin for making an IRQ, with none of this fancy 'putting an instruction on the bus' (mode 0) or 'providing part of the interrupt routine address' (mode 2) we'll be using mode 1.
I've also read the manual for SCM, my 'operating system' on my Classic RC2014. I was delighted to discover the Mr Cousins has very helpfully provided a jump table in RAM, with the restart vectors (which are in ROM in that system and therefore not changeable by the user) pointing to that table. He's even provided API routines for registering routines, so that makes it even neater.
I haven't really established which interrupt mode CPM uses on my RC2014 Pro and Zed Pro (in the latter case it might depend which of the available systems I boot into) but this isn't so important at this point, because the RST at 0x38 (used by mode 1) is in RAM, so I can easily put a suitable jump there and set mode 1.
Receiving data
I'm getting ahead of myself. I'm looking at something that's very much a prototype and I'd like to get a real PCB ordered sooner rather than later. First I want to prove that my schematic is good. I've already sent MIDI data, so I just need to receive some.
Without worrying about interrupts just yet, I rattled out this simple loop (using C for speed - speed of development and speed of execution) which will print incoming MIDI messages.
Like buses, the bytes will usually come in threes (sometimes twos and sometimes ones) but if the top bit is set on any byte, then that's the first byte of a message.
So here's me playing a scale on a handy midi controller:
Yay!
Over and out.
Tue 8 Oct
Even though I've only had bits and pieces of time to spend on this, I've made a giant leap forward today, the boards are ordered!
A reminder that this is what I'm looking at on my desk
The guts of the operation are the three chips on the prototype board. I'm tapping the 2Mhz clock from my Mk1 MIDI module, there was no point building a copy of that circuit again, and I'm using a serial <-->MIDI interface which has the very standard MIDI in and out ciruits on it.
I've been gradually sketching the schematic as I've been going along. The MIDI circuitry and the clock circuit were a copy & paste from my Mk1. The port decoding is something I've done again and again. For my recent projects I've been using a '688 for this job which is a neat solution.
In a very recent project I used some additional logic with the '688 so that a single jumper could be moved to select one of two port options. This time I've tried something new. I used 'cut and shut' solder pads on the back of the board which can be used to reconfigure the ports to any range. It'll always be xE ad xF but it could potentially be 0e/0f through to fe/ff. I feel that method is appropriate here. You probably won't want to be switching it all the time. The default ports will probably be OK, but if not, you can reconfigure it for your setup fairly easily with a knife and soldering iron. And it's always reversible or reconfigurable.
The challenge
The part that has put the 'challenge' in my Retro Challenge is learning how to connect and use the 68B50. This is the barrier that has prevented me from tackling this project for a long time.
I've done a lot of reading, I think I understand it, I've made the prototype and it seems to work.
I've seen this send and receive MIDI messages flawlessly using very simple programs, so I feel confident about transferring all of that to a single new module design.
Last night and this afternoon, I've polished my schematic a bit and moved to the PCB layout.
I'd forgotten just how much I enjoy the process of cobbling something together and then steering that through to an actual solid piece of hardware.
Prototype boards are now ordered from my favourite Chinese manufacturer and should be with me in a week or so. In the mean time I'm going to enjoy working on some software using my prototype.
Over and out.
Wed 9 Oct
I've moved to my Classic II now and I've started developing the framework in assembly, even though my preferred language these days is C. Why?
The move to the Classic is because development is much faster. Using a paste to send a small hex file into SCM is almost instant. If I use assembly, in Textmate I can rebuild the program with a single keystroke and thus the turnaround time from code change to trying it on the computer happens in the blink of an eye.
So I've spent some time on my framework, and can still send and receive MIDI as well as user input / output.
"This result looks exactly the same as from two days ago?"
As is often the way with development, you spend time on something, refining it and improving it, and then the result is just the same as the quick-and-dirty get-something-working code.
But now we've got an interrupt service routine and we're configuring interrupt mode 1. When there's a new MIDI byte ready on the 6850, it generates an IRQ which we're responding to and queueing the data in a circular buffer.
The interrupt and buffering means that the main program can do whatever it needs to do and call the MIDI task when it's convenient. No bytes will be dropped.
We've got a 'send MIDI message' routine, which sends the three bytes of a message, with a proper check for the transmit register being empty/ready.
I'd like to make this same framework available in C, which I favour these days, but one of the reasons for getting this working in assembly first is that I checked out how to configure interrupts when using z88dk and that looked scary. OK, maybe not too scary but that's an additional challenge that I hadn't bargained on.
IRQ signals
I had a thought, which is that I now have two ACIAs on the RC2014 (the one on the regular serial module, and the new one on the MIDI module) and they both have a pin connected to the IRQ line on the bus.
They may both assert at the same time (both low) which shouldn't be a problem, but what happens when one is asserting (low) and the other is not (high)?
According to the data sheet, that pin on the 6850 is "open-drain (no internal pullup)" which, if my understanding is correct, means that when it's not pulling low, it's open, or hi-z, and that means you can safely connect as many as you like to the same line. Phew!
I'm learning things that I didn't originally know that I'd need to know. Unknown unknowns!
Over and out.
Wed 16 Oct
It's been a week since any progress, but since I was waiting for boards to arrive, I used that as an excuse to concentrate on other things, even though I could have got on with the software side.
Anyway, they've arrived this morning, looking great!
This allows me to move from this:
to this:
Designing the board, ordering and then building the first module is an absolute pleasure because I've done that so many times now. No challenge there. Here it is in place looking grand:
That's in my RC2014 Classic. As mentioned previously, software development is quicker on that machine and I still have a real soft spot for the simplicity of it. Bare-bones it's just CPU, serial, clock, ROM and RAM.
It looks amazing but does it work...?
No.
I had a heart-sinking moment, and mentally prepared myself for some troubleshooting. First thought was that maybe the port decoding was bad because that's the part that's different from the prototype boards I'd just been using. And with a slap to the head, I realised that yes, my prototype was on ports 0 and 1 because... lazy. I'd built proper port decoding onto this pcb but I'd loaded up my quick test program from before. A quick change to that:
.. and hurray! It works.
More about ports
As mentioned before, I consulted the various lists of known ports for RC2014 modules, and also consulted Spencer, and settled on DE and DF, with solder-pad options in case that didn't suit everyone.
It would be nice if there was already a 'standard' set of ports for MIDI on RC2014. It would be nice if that matched standard ports on, say MSX. However, there seem to be various hardware options for MSX, all with their own set of ports. Manufacturers like proprietary! But it's way better for users if all available software for MIDI works with all available hardware.
I'd like to see a standard emerge for MIDI on RC2014 and I'll revise my board if necessary in future. I'll be even happier if DE and DF becomes that standard now that I've used those ports.
I'm happy with the way this looks. Rather than use DIP switches or jumpers, I've gone for these 'cut and shut' solder pads. Documentation will be required for people to use these (though you might be able to guess how they work) and they're not designed for switching back and forth every day. But if the given ports clash with anything else on your system, you can change the ports with a knife and soldering iron.
All that remains....
I noticed that fellow Retro-Challenger Olav has begun his log with a checklist of tasks. That made me realise that I've been winging it a bit. Not having a clear list of tasks may be why I've not done much in the last week. I've written the checklist and moved it to the top of this post.
I think that's me up to date. Over and out.
Thu 24 Oct
There's a bit of a gap here in my logs but I have been working on this in the background. In particular, talking to Wayne Warthen about interrupts and ROMWBW booted into CP/M or Z-system and trying a few things with that. We may be limited to polling the MIDI IN on those systems.
I have got my library working for CP/M RC2014 'Pro' (It's possible to force interrupt mode 1 on that system, which we need to use with the 6850. This may not be possible with ROMWBW.)
In pursuit of a 'quick win' after messing around with a lot of things today that ultimately haven't worked, I turned to the 'AY Piano'.
It's a quick win because I'd already written this little application for the MIDI module mk1 and the new framework is compatible with the old one. Here it is running:
I'm not trying to demonstrate virtuosic playing, simply that the MIDI is being received from the controller, and the notes are being played on the AY, with 3-note polyphony and a software envelope control on each voice. (The chip has an envelope generator, but this can't be used for each voice independently).Here's another demo. I used a modern computer with a MIDI player application to play a .mid file and send the MIDI to the RC2014 via a USB -> MIDI lead.
You can see that I added a simple level meter here by lighting the LEDs on the digital i/o module according to the envelope generator (the three voices added together.)
That's another item on the 'to do' list checked off. Next is to make an example application that sends MIDI out, eg a simple sequencer.
Over and out.
Fri 25 Oct
Good progress today. I can cross off the 'Software framework in C' item on my checklist, and also the 'simple sequencer'.
One possible barrier was being able to set up the interrupts and an interrupt service routine (ISR) in C as easily as I could in assembly. This turned out not to be a problem. I found one or two examples online showing how to do it. The ability to easily include asm in C (at least with z88dk which I'm using) was a boon because I could copy most of my existing assembly ISR and serial setup.
I did find some instability when using printf while interrupts were happening, which I was expecting since I'd read something similar online. That's not a problem, I've added my own text output routines now and everything seems solid.
This is all on a 32k machine with ACIA serial (meaning that I could also configure that 6850 to send interrupts, and do my own keyboard buffering in the same ISR). I'll need to make some changes for CP/M (with ACIA and with SIO2) and also for ROMWBW. But I think those will be quick to do.
I've simplified this as much as possible, putting the serial and interrupt voodoo in a separate C library, leaving the main file (which myself and users will duplicate and then use) very simple and clean.
This is really it as far as the user is concerned. At the start of main you call init_serial(). Within the main loop you call midi_task(); which reads the input buffer and calls midi_message_received() when there's a new midi message. That's where the user puts their code to deal with incoming midi messages. There are easy functions for send_note_on() and send_note_off() as well as some useful utilities for receiving keyboard input and sending characters to the terminal.I've started to realise that offering hardware and a software development framework (which is my usual M.O.) will only suit a small number of like-minded people. I think that the majority of people are looking for hardware with a good library of finished software to run on it. This C framework is going to allow me and others to work on some (hopefully) fun and useful applications.
I have some big ideas for this drum sequencer (and possibly in addition to that, a bass sequencer). Today I've had to keep reminding myself that all I need to do at the moment is just focus on getting something working well enough to demonstrate the hardware and the software framework.
Nevertheless I'm very happy with what I've done here in one afternoon:
This has 32 steps, is limited to 3 instruments (though it could be expanded to have many more). It allows you to navigate and edit / enter notes using the keyboard. You enter a note by typing 0-F in the right place, that alphanumeric number represents the velocity (allowing for expression). There's tempo adjustment and a start/stop key.
This proves that the MIDI out is working well, and that sending notes programmatically is very easy (the majority of the work was the UI stuff).
The big thing that remains (for the Retro Challenge at least) is to write some documentation. Over the next few days I'll get that done and spend a little time working on the software side, and then call my Retro Challenge done!
Over and out.
Sun 27 Oct
I've just checked off the remaining items on my 'to do' list (scroll to the top of this post).
But is this kind of thing ever finished? The framework is written and working for various systems but there are more that I could add (there are many permutations of OS and serial i/o that have a bearing on the way the framework is configured and used). I've written some example apps (see the demo videos embedded further up this post) but they can be improved and I have many more ideas.
After all, being able to make and play with new applications is, for me, the whole point of making this module and software framework.
So today I've checked off 'documentation'. My document for the user is here:
https://peacockmedia.software/RC2014/MIDI/manual.pdf
and I've made a web page which is here:
https://peacockmedia.software/RC2014/MIDI/
I've also spent a huge amount of time getting the framework and examples ship-shape and ready for publication. It's such a PITA going through the variations for the various systems, in both C and asm.
The Retro Challenge has given me the incentive I needed to get started and get finished with this 'mark 2'. It did indeed have challenges for me. In particular I found the serial chip scary, and also using interrupts on the Z80 while co-habiting with the existing serial i/o. I'm particularly pleased that I've been able to get interrupts running within a C application.
I'll finish with another demo of the 'AY Piano' example application in action - this time one of my favourite .mid files. It's fast and furious, so I think the new hardware and software are doing a good job of receiving the incoming data and distributing the notes to the three channels of the AY chip.
I'm using my Classic for this (Z80 at 7.3Mhz, 32k RAM). I love the 'bare bones' nature of this machine and my previous version of this module wasn't able to work on this system.
Hi, Regarding to MIDI protocol, I suggest to check the official MIDI specification located at https://midi.org/midi-1-0-core-specifications . On page 1 of "MIDI 1.0 Detailed Specification 4.2.1", the required hardware specs are shown ,which include baud rate , start/stop bit, and even sample schematic for interface.
ReplyDelete-Yoshi
In an 8-bit connection, the second stop bit just gives the sides a little more time to process fast data streams. It slows down total throughput though. It will effectively be ignored as it is just processed as one twice-as-long-stop-bit rather than two discrete stop bits. Hope that makes sense.
ReplyDeleteGood to know, thank you. That sounds like it could be useful in this project, and it does clear up that discrepancy.
Delete