Porting Arduboy2 library to SAMD

EDIT: Here’s my fork in case people can spare the synapses to get involved :wink:

@MLXXXp I didn’t want to necro your general Arduboy2 discussion thread and annoy people who aren’t interested in a SAMD port. If you think it’s better to keep discussions going in that thread, please merge.

I wanted to ask about some things regarding an attempt to port Arduboy2 to SAMD. I’m hopeful I won’t be the only contributor but I am getting started. I say SAMD, because I think we should be leaving the door open for anyone who may want to go crazy and build a SAMD51 based device, even though SAMD21 is what we probably all imagine this working on.

A general question to start with - naming/terms etc. Currently your ArduboyZ library uses:

#define ARDUBOY_Z

Which of the following or an alternative would be best for Arduboy2?

#define ARDUBOY2_Z
#define ARMDUBOY2

Just leave it as ARDUBOY_Z or change it to ARDUBOY_SAMD if you wish. It defines the target hardware, not the library, the same as ARDUBOY_10 and AB_DEVKIT are currently used by both Arduboy and Arduboy2. It would be changed to something that describes an “official” product name if one were ever to materialise. Note that if there are ever any hardware variations that can’t be determined by defines pre-defined by the Arduino environment, they would be passed to the library by the “boards” package, possibly using user settings for the selected board.

As you’ve surmised, my modifications of the Arduboy library were just “quick and dirty”, to prove the concept and get something going quickly. (I would have used the Arduboy2 library but I hadn’t created it yet.)

I don’t think using a SAMD51 is crazy. I’ve given my reasons for why it feel it would be an advantage for a gaming environment. That’s assuming the additional cost isn’t prohibitive, which I don’t think it would be.

The Tachyon Kickstarter campaign has suffered from delays. I got a refund and have purchased a SparkFun SAMD51 Thing Plus for almost the same cost. I just haven’t had any time to work with it as a base for a SAMD51 based Arduboy successor.

The good news is that the internal peripherals are highly compatible with the SAMD21 ones, so any library changes to account for the differences should be minimal.



…it will be.

Phew, I didn’t want to start out thinking I was the only one who’d end up looking down that route.

Yes. I have also recently had success building my own bootloaders for a custom SAMD51 board that I developed at work, so we could be fairly flexible on the pinouts and SERCOM configurations. For example, I wanted two SPI ports, so I made them. I want to get into SAMD interrupts next.

1 Like

@bateske has mentioned it as well.

Regardless of whether a SAMD21 or SAMD51 is used, assigning the pins properly is a priority. For instance:

  • You want to put the speaker on the DAC(s).
  • The RGB LED needs to have PWM capability.
  • I’d still like to have a Micro-SD slot. The SAMD51 has a SD/MMC Host Controller peripheral. It would be nice to use it to allow going beyond just a basic SPI interface.
  • There’s been some desire to have infrared wireless communication capability, so you may want to make sure USART pins capable of IrDA Modulation and Demodulation are left available for it.

Look at us two, veering quickly away from the OP about SW and right back into HW config! :grin:

I agree though. For now, I’m going to concentrate on making the existing pinout work. I do want to make IR comms a possibility for multiplayer. I’m sure it’s been thought of or even executed already but @bateske is a sucker for these inane ideas:

Arduremote - for all your TV switching needs. Or is it a small HDMI dongle that “casts” your Arduboy screen to the TV via IrDA?

1 Like

Well, the two sort of go hand in hand. :wink:

I put a lot of thought into the pinout for the SAMD21. I haven’t yet done the same for the SAMD51.

And SAMD timers. Will be key to getting sound working on the ported library.

Or is it a small HDMI dongle that “casts” your Arduboy screen to the TV via IrDA?

Come to think of it, a cheap linux SBC with IR receiver and HDMI output would easily allow you to play with “standard” IR equipped Arduboy as wireless remotes. Just needs integrating into the arduboy emulator to run on the SBC…

@MLXXXp first detailed question. This throws an error that doesn’t seem to be related to SAMD when I try to compile, because I can’t see where TXLED0 or TXLED1 are defined/declared/initialised.

bool Arduboy2Base::nextFrameDEV()
  bool ret = nextFrame();

  if (ret) {
    if (lastFrameDurationMs > eachFrameMillis)
  return ret;

I’m planning to make it conditional like this for now but is that likely to cause an issue? It looks like it was a debugging/development function. Ta.

bool Arduboy2Base::nextFrameDEV()
  bool ret = nextFrame();
  if (ret) {
    if (lastFrameDurationMs > eachFrameMillis)
  return ret;

They’re supposed to be defined in the pins_arduino.h file for each particular board. However, I could only find them defined for AVR boards.

TXLED0 and TXLED1 turn the USB TX LED off and on. For SAMD, you may have to do the same thing directly.

nextFrameDEV() is documented in the Arduboy2 library documentation, as are all the other functions exposed to the user API.

1 Like

I’ve been wanting to use the SAMD51 on a “new” Arduboy. But there are still too many Arduboys in stock.

If someone wants to buy several hundred or maybe a thousand units then for sure can start looking at a next version. :slight_smile:

How about a crowdfunding campaign to raise funds for the buyout/writeoff of old stock and pave the way for the next version. It’s like kickstarter meets groupon… groupkick.

1 Like

@MLXXXp I’ve had a look in the docs but I don’t think there’s enough there to help me over this hump. In both Arduboy2.cpp and Sprites.cpp there is assembly language. In Arduboy.cpp for the Z version you used a workaround in fillScreen() but to me there’s no obvious equivalent of the Sprite

  asm volatile(
    "push r28\n" // save Y
    "push r29\n"
    "movw r28, %[buffer_ofs]\n" // Y = buffer_ofs_2
    "adiw r28, 63\n" // buffer_ofs_2 = buffer_ofs + 128
    "adiw r28, 63\n"
    "adiw r28, 2\n"
    // load bitmap and mask data
    "lpm %A[bitmap_data], Z+\n"
    "lpm %A[mask_data], Z+\n"

    // shift mask and buffer data
    "tst %[yOffset]\n"
    "breq skip_shifting\n"
    "mul %A[bitmap_data], %[mul_amt]\n"
    "movw %[bitmap_data], r0\n"
    "mul %A[mask_data], %[mul_amt]\n"
    "movw %[mask_data], r0\n"

    // if yOffset != 0 && sRow < 7
    "cpi %[sRow], 7\n"
    "brge end_second_page\n"
    // then
    "ld %[data], Y\n"
    "com %B[mask_data]\n" // invert high byte of mask
    "and %[data], %B[mask_data]\n"
    "or %[data], %B[bitmap_data]\n"
    // update buffer, increment
    "st Y+, %[data]\n"


    // if sRow >= 0
    "tst %[sRow]\n"
    "brmi skip_first_page\n"
    "ld %[data], %a[buffer_ofs]\n"
    // then
    "com %A[mask_data]\n"
    "and %[data], %A[mask_data]\n"
    "or %[data], %A[bitmap_data]\n"
    // update buffer, increment
    "st %a[buffer_ofs]+, %[data]\n"
    "jmp end_first_page\n"

    // since no ST Z+ when skipped we need to do this manually
    "adiw %[buffer_ofs], 1\n"


    // "x_loop_next:\n"
    "dec %[xi]\n"
    "brne loop_x\n"

    // increment y
    "dec %[yi]\n"
    "breq finished\n"
    "mov %[xi], %[x_count]\n" // reset x counter
    // sRow++;
    "inc %[sRow]\n"
    "clr __zero_reg__\n"
    // sprite_ofs += (w - rendered_width) * 2;
    "add %A[sprite_ofs], %A[sprite_ofs_jump]\n"
    "adc %B[sprite_ofs], __zero_reg__\n"
    // buffer_ofs += WIDTH - rendered_width;
    "add %A[buffer_ofs], %A[buffer_ofs_jump]\n"
    "adc %B[buffer_ofs], __zero_reg__\n"
    // buffer_ofs_page_2 += WIDTH - rendered_width;
    "add r28, %A[buffer_ofs_jump]\n"
    "adc r29, __zero_reg__\n"

    "rjmp loop_y\n"
    // put the Y register back in place
    "pop r29\n"
    "pop r28\n"
    "clr __zero_reg__\n" // just in case
    : [xi] "+&a" (xi),
    [yi] "+&a" (loop_h),
    [sRow] "+&a" (sRow), // CPI requires an upper register (r16-r23)
    [data] "=&l" (data),
    [mask_data] "=&l" (mask_data),
    [bitmap_data] "=&l" (bitmap_data)
    [screen_width] "M" (WIDTH),
    [x_count] "l" (rendered_width), // lower register
    [sprite_ofs] "z" (bofs),
    [buffer_ofs] "x" (Arduboy2Base::sBuffer+ofs),
    [buffer_ofs_jump] "a" (WIDTH-rendered_width), // upper reg (r16-r23)
    [sprite_ofs_jump] "a" ((w-rendered_width)*2), // upper reg (r16-r23)

    // [sprite_ofs_jump] "r" (0),
    [yOffset] "l" (yOffset), // lower register
    [mul_amt] "l" (mul_amt) // lower register
    // NOTE: We also clobber r28 and r29 (y) but sometimes the compiler
    // won't allow us, so in order to make this work we don't tell it
    // that we clobber them. Instead, we push/pop to preserve them.
    // Then we need to guarantee that the the compiler doesn't put one of
    // our own variables into r28/r29.
    // We do that by specifying all the inputs and outputs use either
    // lower registers (l) or simple (r16-r23) upper registers (a).
    : // pushes/clobbers/pops r28 and r29 (y)

The docs say Sprites and SpritesB do the same role but with different optimisations (speed, code size, respectively). Does this mean I can put a preprocessor macro condition in front the assembly above such as

#ifdef ARDUBOY_SAMD // from void SpritesB::drawPlusMask(int16_t x, int16_t y, const uint8_t *bitmap, uint8_t frame)
  draw(x, y, bitmap, frame, NULL, 0, SPRITE_PLUS_MASK); // convert variables to work in Sprites

and then #endif after the assembly?

Why doesn’t Arduboy grow to the next stage yet? Why does it seem like nothing is happening?

Real Talk Contained Within

Kind of the plan with the Arduboy FX, to sell the modchip to people who already have an Arduboy, or rework the remaining inventory to include all the games. Had a plan to give a way 1,000 dev kits of the FX setup co-branded with hackster.io but Seeed totally screwed me and quoted me like $27 each to produce them, despite only being about $4 BOM. I didn’t have time, or wasn’t smart enough in advance to look for another manufacturer.

Also, the first kickstarter was a complete fluke, the market for these kinds of things is not that big. I think I’d struggle to even sell through what I’ve got even with the kickstarter.

Keep in mind the special edition only sold about 700 units on the campaign.

Without some kind of distribution model or financial partner to subsidize it going into bigger markets (i.e. investment to make enough units to really bring the price down to impulse purchase territory) , the idea for Arduboy as a finished good hardware is not a viable business model.

Kit form is still an attractive option and I’ve got some ideas for the emulator too… But frankly the only reason Arduboy made it farther than anyone else doing this kind of stuff (pokitto, gamebuino, etc) is that it had the advantage of going viral a year before the origonal campaign and generated nearly a billion impressions, and got day 0 coverage on huffington post and gizmodo. Those days are over my friends.

The long term vision for Arduboy is to both fully democratize the platform as much as possible and also one last hurrah at getting it into education markets. But also to be totally 100% transparent it doesn’t even make enough money for me to pay myself a living wage any more. The only reason the company didn’t go bankrupt 2 years ago is because I’ve been living at my parents house, or girlfriends house. #realtalk #business

I’m actually looking for a new job myself so that I can continue to do Arduboy without relying on it as a primary source of income.

I guess this is sort of “too much info” for this topic, but it’s a little heartbreaking every time someone brings this sort of thing up.

I’m actually considering making a “arduboy is dead, long live arduboy” video and pushing it out, but I’m trying to resolve another issue critical to the business first. I also want to finish the Arduboy FX mod chip because I see that being the best way to give back to the developers is let customers have the easiest access possible to the games they made.

Woof :dog:

EDIT: After writing this I realized I should also give equal credit the reason Arduboy has existed at all or so long is really to you, and all of the developers and everyone continuously believing in the platform and developing for it. But nothing lasts forever.


Have a look in my Pokitto port, I replaced almost all of the assembly from the original:

For Sprites.cpp you can just substitute the code from SpritesB.cpp.

1 Like

Hey, I didn’t mean to do that - sorry. We all know the “what ifs” outweigh the “got dones” by an immeasurable amount.

Although it won’t pay your wages forever, I think you’ve achieved something very few have - a conceptual “standard”. Yes, the SW and HW may well be fragmented but there’s a core concept of simple monochrome handheld games that seems to have coalesced here rather than anywhere else. I hope you are able to make enough to keep this community website going for many years to come.

I also applaud the move into education. I think your idea for a $5 Arduboy is great. I want my children to have stuff like Arduboy to learn and play with and I want other people’s children to as well.

If I could make a suggestion with no intention of making you feel bad: given your outlook as captured in that message above, I think what I have said before applies. Accepting the usual caveats about design by committee, I think you could afford to crowdsource some of your hardware engineering work from the community a bit more than perhaps you have done in the past. Maybe that will help make space to transition to whatever is next.

Long live Arduboy!

1 Like

Will do, thank you!

1 Like

Theoretically you could even just scrap Sprites.cpp and just make Sprites.h consist of:

#ifndef Sprites_h
#define Sprites_h

#include "SpritesB.h"
using Sprites = SpritesB;


I’m not sure how fast SAMD51 is, but I’m assuming it would be fast enough to match the pace that the assembly in Sprites.cpp managed on Arduboy.

Maybe something more original would be better in case someone else attempts to build something SAMD based?

It depends what your goal is though, whether you’re just trying to make something that works for your purposes or whether you’re trying to make something that could work for multiple SAMD boards/configurations.

From the way you’ve structured the code so far it looks as if you’re trying to make sure the code still compiles for Arduboy.
Is that the case?

I’ll hopefully be able to make some contributions at least.
I said I would back when this project was last discussed and I’ll stick to my work.

I still haven’t quite got round to finishing my Pokitto port of Arduboy2 for various reasons,
but I did spend a long time sifting through all the Arduboy2 functions so I know the API in quite good detail.

As I said a while back, I think your first priority should be making a list of all the functions you’ll need to reimplement so you can decide what to focus on and keep track of how far you have left to go.

Glad to see you remembered my tip about the // TODO: comments. They’ll come in handy.

1 Like