5

So, I'm taking a MIPS course after returning to school; and we're approaching the point where we begin on our final project.

I've always been one for large, well-structured projects: lots of supporting tooling, well-segregated zones of control and concern within the project, extensive testing, so on and so forth. Unfortunately, I don't really know of much, or any, tooling for assembly-language code.

Hell, at the very most basic, I don't know how to compose multiple files (or to flip that on its head: how to decouple groups of procedures into files, and inter-reference amongst them.) Is there a standard(ish) practice for these things?

Barring other information, I'm suspecting I may have to use the C preprocessor (or m4) :P to compose components; but besides that, how would you suggest I structure a larger project?


Also welcome: Any advice on writing relatively maintainable / shareable, clean, assembly-language code, beyond “use 8-space hard-tabs.”

  • Did you … even read my question? I'm not looking for an instruction-level reference, nor a tutorial: I know how to write code for MIPS. (Or rather, learning that is the point of this class. :P) All of the examples I've found, including the ones in the document you just linked, are single files. – ELLIOTTCABLE Jun 9 '16 at 8:54
  • tl;dr: don't need help writing MIPS. need help organizing an assembly-language project, for any given assembler. – ELLIOTTCABLE Jun 9 '16 at 8:55
4

A book could be written on this topic. I bet some have . . .

It's hard to answer because each tool-chain has its own strengths, shortcomings and quirks. I must have structured different projects a half-dozen different ways, but I will mention the two that I've used the most.

For almost all techniques, you need to partition the components adequately. The goal is to make each module 1) a well defined and documented black box that can be easily used without needing to dig into the code, and 2) general enough that it can be reused as-is in subsequent projects. All of my projects use my "math", "dispatcher" and "timer" modules. Then there may be modules for the SPI, I2C, SCI, PWM etc. Most of the time, this separation of functionality is pretty intuitive.

The first technique is to assemble each module separately and then put them all in a object library ("object" as in machine code, not "object" as in object-oriented programming). Then, when you write the main program, you just make calls to your library routines, and the linker will automatically include the necessary object modules in your executable.

But some tool-chains either don't support object libraries well, or make it a pain-in-the-neck to create and maintain. In that situation, I "include" each necessary source module at the end of the main program, and the assembler creates one large object module (which makes it easy for the linker).

In both cases, the main program starts with a list on "includes" for the hardware definitions and the macro definitions (I use LOTS of macros, both for readability and portability). Then I add the main code. If I'm using an object library, then that's it. If I'm using a source library, then I end with a list of "includes" for the required source modules.

I could go on, but its late . . .

-1

I've written and maintained a reasonably-sized commercial package in assembly language (40,000 lines of 16-bit code and 35,000 lines of 8-bit). It worked well and had zero bugs.

The only inter-module communications possible were that a symbol (an address in program code or a data address in memory) could be declared public in one module and external in another. I didn't bother with libraries because assemblers and linkers are fast enough anyway, so compiling and linking everything each time worked just fine.

Header files were used to define constants, but could equally well have been used to declare external symbols as well.

That's the mechanics of it. As for the choice of what to divide up into what source module, that is programming as a literary art and you need to work out what works best for you. I had a layer of utility routines (even arithmetic ones) at the bottom, and then vertical divisions for different areas of functionality.

The freedom of assembler is both liberating and challenging, but it is an unparalleled opportunity to develop your own style.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.