Of Baby Steps and Binutils

Hello everyone!

It’s been quiet for a while again in the land of Propeddle, so here’s a short update.

A while ago, I wrote an in-depth article about Propeller programming in C, and because of this, I was invited by Parallax to write some documentation for the PropGCC official website. I agreed that I would contribute some articles, but first I want to get Propeddle in a more finalized state, so I can sell it as a kit, and hopefully give a presentation at the Propeller Expo next May.

I’ve been rewriting the software in C and inline assembler for a while, and currently the code for the control cog is code-complete, and I’m ready to start debugging it with my Logic Analyzer.

Unfortunately, however, I’m hitting a major snag. Although it compiles without warnings or errors, the machine language that gets generated by the assembler is wrong: the Gnu Assembler (GAS) for the Propeller apparently still has some major issues that apparently don’t affect the C compiler, but generates fixups that are miscalculated for all instructions with immediate source arguments except JMP/JMPRET.

I had a long look at the Gnu Assembler code today and learned a lot about the architecture of binutils (of which GAS is a part). But the longer I look at it, the more I wonder why it works at all.

The Propeller uses hub memory and cog memory. All data and program code is initially stored in the hub, and gets downloaded to the cogs to get executed. In the hub, every byte is addressable, but the cogs use 32-bit addressing (every address represents a 32-bit word). When an instruction in the code refers to another item, the reference might refer to the location where that item is stored in the hub, or the location in the cog where the item was copied when execution started.

When using the Spin language, a reference or pointer to an item always refers to the hub. In assembler, all references and pointers refer to cog memory, except hub instructions. So it makes sense for GAS to store cog addresses into those instructions, and that’s what it does in most cases, but it gets it wrong:

  • Whenever an instruction refers to one or two locations in cog memory and isn’t used in immediate mode, everything appears okay
  • When using immediate arguments with JMP/JMPRET, the assembler stores an immedate value that (apparently) corresponds to the location of the referred label within the module, which is not correct in certain situations because it doesn’t take into account that the actual code may not start at location 0
  • When using immediate arguments with other instructions such as MOV or MOVS or MOVD, the value of the source argument is stored as an immediate value but it’s converted to a cog address only if the label was preceded by .cog_ram.

I’m going to try a workaround for this (basically by putting the assembly code in a separate module) but I’m probably going to be working on fixing this problem in the Binutils too.


[Update: The problems in GAS are now fixed, thanks Eric Smith (maintainer of Propeller-GAS)]