Playing Chiptunes With A YM2149 And Optimizing An Arduino

[Oryx] grew up with the bleeps and bloops of an Atari ST, so it comes as no surprise he would want to relish in his nostalgia by playing with the YM2149 sound generator he recently picked up on eBay. Like most of us, [Oryx] went to his old standby, the Arduino, when it came to connect this bare chip to his computer. The first circuit didn’t work, so after a lot of poking around the firmware, [Oryx] discovered the benefits of hand-optimizing software.

There are a lot of sound files available for the YM2149 floating around on the Internet. These files are just dumps of the 16 registers at 50Hz, so it’s very easy to send these from a computer to an Arduino over a serial connection. Unfortunately, when [Oryx] got his breadboard set up nothing happened. After breaking out the ‘scope, he discovered the Arduino was switching pins 100 times slower than the YM2149 data sheet called for.

[Oryx] remembered seeing a great blog post going over the speed at which the digitalWrite() function changes pin states. We’ve seen this before, and the fastest way to change pin states on the Arduino is with the ugly bitwise manipulation. After changing a few lines of code, [Oryx] was switching two pins nearly simultaneously.

Now that the YM2149 chip is working correctly, [Oryx] is planning to make a MIDI synth out of his project. You can get an idea of how that will sound with the demo video he put up after the break.

[youtube=http://www.youtube.com/watch?v=8U6ZYENWb24&w=470]

19 thoughts on “Playing Chiptunes With A YM2149 And Optimizing An Arduino

  1. Although I like the Arduino boards the Arduino libraries are shockingly bad in many places – see this post and I’ve finally bitten the bullet, ditched the Arduino software and written my own. Being able to use printf() and having proper line-buffered interrupt-driven, backspace-aware serial IO alone has made the effort worthwhile.

  2. “Bitwise manipulation” is not ugly, calling a function that is 100 times slower than needed just to flip a bit is.

    I find it easier to visualize actually :

    R |= 0b10000000; // sets the MSB
    R &= 0b01111111; // resets the MSB
    R &= ~0b10000000; // resets the MSB (other method)
    R ^= 0b10000000; // flips the MSB

    // Or:
    R |= 1<<8; // sets the MSB

    etc.

    You can do that without any problem in the Arduino environment, it uses GCC to compile your code, so anything C++ is legal, even classes.

    1. All the examples you give result in unnecessarily inefficient 16-bit operations rather than 8-bit ones. That’s because C arithmetic operators promote their operands to int, i.e. 16 bits. See the avr-gcc FAQ for details. If you look through the avr-gcc header files you’ll see they are full of csats such as:

      #define FUSE_CKSEL0 (unsigned char)~_BV(0) /* Select Clock source */

      1. @Alan It’s an example. I agonized for a while over the promotion problem, but wanted to show how simple it could be (so as not to scare the crowd, @Aaron!).
        Besides, even with the promotion to int, it’s still way more efficient than the function call.

  3. It’s funny how “using the I/O instructions the way the designers intended” is considered an “optimization” in the Arduino world…

    This is also a case where a 30-year-old 8-bit CPU with a parallel bus and memory-mapped I/O will still beat an Arduino. No need to mess with bus lines with nanosecond accuracy—the CPU does that for you. Plus, on a 6809, which can do 16-bit writes, you can send both the register number and value to a YM2149 with *one* instruction if the lowest address line is connected to BC1.

  4. Again, wow, you mean there are other micros than the arduino? and you can like, actually write code for it instead of just using the library? WOW… I don’t know if I could do something like this without having someone hold my hand.
    Just think of how much more amzing projects would exisit if people, i dunno, learned how to code themselves, or *GASP* read a datasheet and tried to understand it…

    There is a reason we techs get a little nervous when an engineer picks up a tool…

  5. I love this project, and have a few AY-3-8910’s in my toolbox. I’ve been wanting to do something with them forever now. I will be following your efforts ;-)

    I have a suggestion for the Arduino code, and it might not be such a good one ;-)

    Instead of breaking down the address and data into bits for the digitalWrite() function to spread across two ports (because you are using the hardware UART pins 0,1 – and PortD (pins 0-7) is the only 8-bit port available), couldn’t you spread them across ports B and C like so with direct port manipulation?


    //Pin Mapping
    //
    //Arduino 9 (PORTC) -> YM Addr/Data 7
    //Arduino 8 (PORTC) -> YM Addr/Data 6
    //Arduino 7 (PORTD) -> YM Addr/Data 5
    //Arduino 6 (PORTD) -> YM Addr/Data 4
    //Arduino 5 (PORTD) -> YM Addr/Data 3
    //Arduino 4 (PORTD) -> YM Addr/Data 2
    //Arduino 3 (PORTD) -> YM Addr/Data 1
    //Arduino 2 (PORTD) -> YM Addr/Data 0

    //Pin Setup
    DDRD = DDRD | B11111100; // sets Arduino pins 2 to 7 as outputs, leaves 0 & 1 untouched.

    DDRB = DDRB | B00000011; // sets Arduino pins 8 and 9 as outputs, leaves 10-13 untouched.

    //Port Writing
    PORTD = (data <> 6) & B00000011; // Shifts data 6 bits right to set up upper 2 bits of data to occupy lower 2 bits of PORTC, masked and written out.

    It’s the same pins you are currently using, but would be a more efficient use of them… and tidier code.

    You could potentially also use a software UART on port B or C, and write address and data directly to port D without bit manipulation… however debugging via the Arduino IDE would be a pain. First solution seems more feasible for Arduino.

    What do you code gurus think? I probably messed something up… I know the masked off portions of the PORTs do get written to as 0’s … which probably has an ill effect on the UART… meh.

    Well, maybe just direct port manipulation Bit by Bit then… ?? Would be a lot faster but still require some counters to walk through all 8 bits.

  6. Nice! I’ve done the same thing with a YM3812 (an OPL2) I borrowed from an old PC sound card, which I can use to listen to Adlib music from DOS games, also through an Arduino. It’s a great learning project (if you can find the YM ICs!)

  7. For those who didn’t follow the second link to an old article on my blog, there is an Arduino library that maintains the ease-of-use of the Arduino function calls but compiles down to efficient direct port manipulation.

    http://code.google.com/p/digitalwritefast/downloads/list

    The trick is in all per-preprocessor macros. Has the limitation that the pin number must be known at compile time.

    I use it quite a lot when time is sensitive.

Leave a Reply to PhilippeCancel reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.