Recently I have been working on a research project at the NCSU OSL to design, construct and test a blackbody radiator for the calibration of IR cameras. It will essentially be device that holds a 4*4in surface at a constant temperature using thermistors to read the temperature and thermoelectric coolers to sustain the temperature. These components will be interfaced by a Technological Arts NanoCore12 prototyping board, which is built around a Freescale 68HCS12, a descendant of the old Motorola 6800.
My supervising professor said that assembly language is best suited for this project, so over the past several weeks I have been picking up this architecture's dialect. It's been my first exposure to assembly so I've hit a few speed bumps along the way, many due to my being stuck in a different frame of mind coming from higher level languages like C++ and Java. There is a stark difference in workflow coming from purely software-based environments with step-through debuggers and loads of example code to one where the only relevant documentation is buried in a maze of manuals. Here is a list of the most important things I've learned so far from working on a prototyping board:
1) Check and re-check all your bread-boarded circuits until your eyes are sore before you supply voltage to them, and go ask for help as soon as you get stuck.
At the beginning of last week I shorted some leads on a live board for several hours, but didn't cognize that I was doing so until much later. I spent the remainder of the week glazing over extremely basic code to blink an onboard LED on and off with respect to an input voltage. To my dismay, the processor started overheating to egg-frying temperatures under no load and the LED wouldn't turn off consistently. After way too much time of not getting anywhere I took the problem to my professor and he concluded in under an hour that the board's Analog to Digital module was fried, along with some other parts. This colossal time sink would never have occurred had I just installed some resistors near the voltage source (or had just went and asked my professor sooner).
2) Simulators are not debuggers.
One of the most indispensable tools I have used in this project is a processor simulator similar to the one found here. For a while I treated this tool like an on-board debugger that I so long for in the assembly environment. However, since the simulator only reproduces the architecture's processing instructions and not any processor modules, the simulator's response doesn't always perfectly correspond to what the board's actual behavior, especially in situations like #1. My workflow so far has been to get the code working in the simulator first, then start mucking around in module documentation when it doesn't work in real life.
3) Change only one thing per trial during experiments.
After I got a new board to work on, my blink code still didn't work. I somehow convinced myself that I wasn't properly implementing subroutine returns, and that the ATD module was misconfigured. I mangled these areas of my code in all kinds of different ways to try and figure out what was wrong. Out of desperation I decided to mentally step through every single line of code and think about the operation's definition in the architecture documentation, and the description in module documentation of the register the operation acted on. Only after grilling every relevant codeblock line-for-line in this manner did I realize that that it was because while the ATD output was unsigned, I was comparing values using a signed check (BLE/BHE instead of BLS/BHS). Virtually no abstraction happens within the syntax, so there is nothing to stop you from comparing apples to oranges since they are all basically just bits. In retrospect, I can't really think of how I would have found the problem without going through that process, but I would have arrived to it with much less grief if I had tried the systematic, controlled approach first.
4) Make your source code verbose.
6800 assembly is the ugliest language I have ever seen in real life, and I'm sure most other assembly dialects are about the same. It is difficult to write readable assembly for many reasons. Everything is in structured in columns, but consistent delimiters are not enforced (e.g. Python) so if you copy and paste your code anywhere there's a fairly high chance your lines will get stretched all over the place. The length constraint (around 6 chars) of labels, your only way of assigning names to things, is not long enough to allow for anything descriptive. I am still developing my style but so far I have settled on a few no-brainer guidelines:
- Keep your source code along with the processor's documentation files and for every line you write to a settings register, add a comment with the relevant documentation's filename, page number, and the options you meant to set.
- Every time you label an acronym, add a comment that expands the acronym.
- Keep everything in blocks with comments above them. You can't indent your code, but at least you can indent your comments.
- Try and structure things like a sane language. Encapsulate what would normally be a function using labeled subroutines. Describe each subroutine with a comment.
- If you run out of good names, start a name table in a separate file.
- Comment everywhere!
I have wasted a good deal of time trying to decipher my own code because I though it was obvious at the time of writing. You can save yourself by writing comments better comments than I did!
Learning to write assembly has been very rewarding so far. Hopefully my experience will help other beginners gain some insight on how to approach coding in assembly language.