Skip to main content

Using the Speccy BEEP routine and a beeper speaker on my Orton 3C

W

hen I first learned about the Orton 3C and its very clever serial routine which toggles A15 (meaning that the max 32k of ram appears twice in the address space) I've wanted to repurpose this for driving a beeper speaker, just as you might have done with the early PETs* 

I'm writing this post after successfully playing a tune. The video is embedded near the end of this blog post if you'd like to skip straight to that.

Yes, it certainly would be possible to toggle a pin on the digital i/o by 'out'ing to port zero. This would potentially allow for 'bit-banging' multiple voices, with a lot more work than I've put in so far. However, what I love about the Orton 3C is the 'bare bones'ness of it. The whole point is that it can work using only three chips and to that end I've unplugged my digital i/o for this. I didn't go to the trouble of removing the port zero protection shim (3C-PO) but its one logic chip isn't being used here since I'm not writing to port zero. 


You can see that I've hooked up a small speaker module to the FTDI port. I did try a speaker connected directly to the Tx line and ground. I believed this should work, or at least not damage anything, since it's buffered with a transistor:

However, this is a logic buffer, not a current amplifier. It worked but was very quiet. As I'm writing this, I realise it might work with the speaker connected to the Tx and 5V. I'll try that another day, but what I've done up until now is to use that small amplifier module with mini speaker that you can see in the picture. 

I recently watched Happy Little Diodes' video in which he takes an in-depth line-by-line look at the code for the ZX Spectrum's BEEP command and explains it very well. I believe this was written for Sinclair by Nine Tiles and it amazes me how much effort they went to to get the frequency and timing of the beeps just right. By the end of that video I felt that I understood it well enough to use it in this project and that seemed very appropriate. Plus that's a lot of very complicated work already done! 

The Spectrum ROM has a BEEPER subroutine, the BEEP basic command and a lookup table for note frequencies. This is a screenshot of that from The Complete Spectrum ROM Disassembly, which I was happy to find online, since I borrowed that from the local library as a teenager (but didn't totally understand). Beware of some errors, which are perhaps OCR issues. 

The BEEPER subroutine is the part that actually toggles the speaker back and forth with some careful timing. This is completely blocking while the beep is sounding. It even stops interrupts. This and some other cunning measures in the code help to make sure that the frequency is just right. 

The longer BEEP command parses the user input, does some heinous floating point maths and passes two 16-bit numbers to the beeper subroutine. They're not simply note frequency and length as you might expect, but are more 'processed' than that so that the subroutine can use them in its loops. (One is frequency x time, which is the total number of oscillations of the speaker. The other is related to the number of T states that the speaker spends in each state, calculated from the note's frequency and the computer's clock and a bit of compensation for other things.)  

If I used the BEEP command code (which would have the benefit of making my music data very simple to write) then I'd need to include that longer routine, the table of frequencies and a floating-point maths library. Fine for assembling and loading in over serial, but not for toggling manually!

So I'm using just the simple BEEP subroutine and 'pre-rendering' my music data (doing that floating-point maths beforehand).

Here's my Orton 3C port of that BEEPER subroutine. I'll tidy this up and publish it on the repository that Spencer has set up for 3C code. As you might be able to see from that screenshot, I've transcribed the code from the disassembly book and then rewritten the lines that actually toggle the speaker. We've been able to lose some code, which has to do with the border colour and mic jack, which are on the same port as the speaker on the Spectrum. 

Instead of writing to a port, we need to toggle A15 (which doesn't actually affect the program flow because of the mirroring mentioned earlier). The routine already uses IX as a jump for the main loop, so I just flip the high bit of that.

With that done and tested, we need to pre-process our music data. I won't go into the calculations here but I'll make sure it's well commented in the code which I'll publish and link to from this post. 

Rather than write a little program for my Mac to read some music notation and spit out these numbers (which I still may do) I took a shortcut and made this spreadsheet. 



The first image shows a table of frequencies for each note, and the second allows me to type the note name and duration into the first two columns. The third column does a lookup to find the frequency from the note name, and the fourth and fifth columns have formulae to calculate those numbers, formatted as hex integers. 


I can then easily copy and paste that into my code and add the defw and formatting. 

That's a pretty easy process.

The 'player' part of the code is something I've written from scratch. All it needs to do is to read two 16-bit numbers at a time into DE and HL from that data, test for FFFF (indicating end of data) and pass those values to the beeper subroutine. I've used some self-modifying code there to keep things as short as possible. 

The music data, player and beeper subroutine all amount to 190 bytes. That might sound like nothing in today's money, but believe me, when you have to toggle those in in binary (and then check / correct) it's a lot! 

After many rewrites of my player, and corrections to the music data, here's the payoff:

(I was working on this on St George's day and hoped to publish then. Hence the choice of tune!)

If you think the Es in that music sound a little off then I agree. I don't know why they're slightly flat. It may be related to 'equal temperament' reasons and/or a rounding error (I don't know why I decided to copy and paste the frequencies into my spreadsheet tool to two decimal places, there's no reason it couldn't be more.) I may try these same notes in a Spectrum BEEP basic program to see whether it sounds the same.

I'm fully intending to add the beeper subroutine to my ROM (I haven't yet fitted the ROM module to my Orton 3C but I feel that this is a perfect use case for it). I may add my player too, although it needs some work. For example, it currently doesn't have a way to write a rest, meaning no gaps between notes and every note has to last until the start of the next one. 

[link to code will go here when published]

* the excellent Space Invaders game contains on-screen instructions showing you how to connect up the speaker to the user port, which then plays the game's sound effects. The game cleverly uses some serial functionality for non-blocking sound which Dave Curran explains in this blog post.

Comments

Popular posts from this blog

RC2024/10 - my entry

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 catego...

IM53 8080 birthday cake

 Each year I've been trying to get more creative with ideas for Spencer's birthday cake. The plan this year was to incorporate LEDs in place of candles. I eventually settled on an Altair / IMSAI / PDP -style computer since those are the type of computers that inspired his RC2014. The IMSAI 8080 has the most colourful switches as well as a name that I could twist. The thought that it could show randomly flashing lights (as if the computer were running) and that it could also play a game of 'kill the bit' was very appealing. A plan formed to use a capacitive touch pad on the cake itself. The first job is to bake the fruitcake. I often use a 7" square tin and one of those cut in half and rearranged makes a cake of suitable proportions.  Even after taking a slice off the faces to make them nice and square, there are still some rounded corners, so after putting on the marzipan, I used more marzipan as a filler to flatten the whole thing. Even though I wanted to end up w...

New MSX graphics / sound / joystick module for RC2014 / RCBus

I'm impressed at what Les has packed onto this standard-sized module. It contains an FPGA replacement for the TMS9918A, a YM/AY sound module and joystick interface.  The project is open-source and is here . In MSX terms this is the VDP (vidio display processor) and PSG (programmable sound generator), thus being an alternative for both the J B Langston TMS9918A video module and Ed Brindley's YM/AY sound module and adds two joystick ports to boot. All on a single module for RC2014 or compatible computers. There's no room for the d-sub joystick ports, so headers are provided so that these ribbon cables can be used.  This is a neat solution for those wishing to take advantage of Les' MSX8 system , which loads most MSX rom files along with a modified MSX BIOS from CP/M on a ROMWBW RC2014.  It is hard-wired to the MSX ports for the sound and video, so it won't be suitable for those wanting to run Colecovision ROMs, for example. I'm torn myself between the real TMS ...