Tip of the day, #35
Debugging. Part I. Hardware. You can use some very simple tools to debug your new home-made computer without expensive logic analyzers, emulators, simulators, or debuggers. Starting with a longish routine to actually do something useful will usually disappoint, as it probably won't run the first time and you won't have the means to debug it yet. Start with a very simple routine to just toggle an output bit, for example. If the computer doesn't do anything at all, first use your oscilloscope to see if you get a good reset. (See the Reset section.) Then make sure you have a clean Φ2 toggling at the expected frequency. (Use the scope probes' x10 setting for all but the lowest frequency work.) Are address and data lines toggling? If they sometimes don't go as high or as low as expected, maybe there's bus contention (meaning two things are trying to output data on the bus at the same time). If an IC's output voltage never gets very close to power or ground, did you forget to hook one of these up to it? Is IRQ or NMI going down unexpectedly? Is the power supply current approximately what you expected? Don't forget also the single-cycler clock source shown about 3/4 of the way down the clock-generation page. If the hardware seems fine, move on to software. (continued)
Added 6/26/22: 6502.org forum members frequently post that they seem to have gotten a bad IC.
However, my experience says you never will, as long as you buy from legitimate distributors. (See the
"Where to Buy 65-Family Parts" page, the shortest page in
this primer.) Good handling precautions are still necessary of course to prevent damage from static discharge. I
have been responsible for millions of ICs at work, all bought from legitimate manufacturers' distributors, and we have never
gotten a DOA one AFAIK. All I can think of is one R65C22 from Rockwell many years ago that seemed to have a bondwire
that let go after it was subjected to a lot of vibration in the field. Manufacturers' thorough testing and packing makes
sure only good ones reach the market.
Tip of the day, #36
Debugging. Part II. Software. A simple effective debugging tool is a piezoelectric beeper, the kind that's .010"-.020" thick and needs an AC signal in order to sound.
The one at the top is in a black plastic housing that's about 3/8" thick and improves the volume if there's no sounding board. The small one is 3/4" diameter and .012" thick. The bigger one that's tarnished from skin oils and soldering at four points around the edge (but would still work perfectly fine) is 1-3/8" diameter and about .020" thick.
They can be taped or soldered onto the breaboard, so the entire breadboard acts as a sounding board. I've even used scissors to cut them down to fit a tight space, like this one on the back of a mostly analog breadboard with a microcontroller on it:
Very simple beep and delay routines should run on the first try. Write about 3 beep routines of different frequencies and maybe different durations so you can tell them apart. These should save the processor registers like an ISR does. Once these work, call them at appropriate places in your next routine. Put delays between the beeps to make them easier to separate when you hear them. Does it make it to point A before crashing? How about point B? Does the loop that point C is in get run the right number of times? Keep re-assembling and trying your code, each time narrowing down the problem area. LEDs can also be used to monitor the progress; but the software-driven beeper's sound "signature" assures you that the output is not a random effect of a crash. As code development advances, you might incorporate other debugging routines; but in the absence of expensive debugging tools, the lowly beep is indispensable as a "crash-finder."
Without a digital-storage oscilloscope (DSO), you can still study a pulse train output by making the software repeat in
a loop for an analog 'scope. For real-time, things that cannot be slowed down to deliver a beep can be watched on the scope
with probes on various I/O lines. Sometimes it can be a challenge to get the triggering and sweep rate just right, but it's
usually doable. Added 11/29/21: See my post here
about using as little as a single I/O pin to output a set of status bytes to view on an oscilloscope for troubleshooting. Added
8/28/22: The prices of worthy-looking DSOs have come down nearly to $300; so even if you're on a semi-tight budget, you might not need
to be limited to an analog 'scope. See Rigol's 'scopes on Jameco's site. I don't know how user-friendly they are, so look
into the owner reviews. (I say that because my work gave me an $1800 Agilent 'scope that's user-hostile! I
mentioned that on a forum, and someone said, "Yeah, that's the way Agilent 'scopes are." (continued)
Tip of the day, #37
Debugging. Part III. An ounce of prevention... Take a building-block approach. Start with the really simple stuff, then use those things as components to build or debug more advanced components, and so on. The job is only complicated if you don't break it down into manageable chunks, conquer them, and then put them together in steps. This includes thinking ahead, right from the start, about how you're going to test various routines and modules.
Working for small, low-budget outfits (which I enjoy), I have found that the lack of expensive debugging tools actually teaches you to write better code. (Many of the development tools are free today, but it wasn't always that way.) You cannot have the attitude that "I'll whip out this code in record time and debug it later." Out of necessity, I've become more structured and neat in my programming, documenting everything thoroughly, making the code as readable as I know how, and proofreading.
And speaking of structure, after you get a little experience, I would encourage you to make good use of your assembler's macro capabilities. Around 2012 I started using macros to make program structures like high-level languages have, like FOR...NEXT, the CASE structure, IF...ELSE...ENDIF, BEGIN...WHILE...REPEAT, etc.. In most cases there is absolutely zero penalty in assembled program size or run speed, because the macros are assembling exactly what I would do by hand, just without showing the ugly internal details every time. I have an article about it here, and a couple of my best examples of usage are shown near the end of my article on simple methods of multitasking without a multitasking OS.
Large companies have been starting to see the value in the proofreading, and the industry magazines started publishing articles around the year 2000 on code inspection and having committees of the programmers' peers proofread the code. I sometimes catch bugs when further commenting code that's already working but not exhaustively tested yet. I comment as if trying to explain it to someone else who hasn't been following my train of thought on it. (If I come back to change it a year later, I'll need the comments anyway.) As a result of this madness, no user has ever found a software bug in any product or automated test equipment I programmed. The projects have had from 700 to 10,500 lines of code.
BTW, using a lines-of-code-per-day benchmark of programming performance is a sure way to end up with inefficient, buggy code that's hard to figure out and fix or modify later. I once worked with a programmer who typed non-stop. I always wondered what he was typing. After he left the company, I had to fix a 4-page routine he wrote. When I was done, it was down to a half page, bug-free, clearer, faster, and did more with less memory. (Eventually most of what he wrote had to be redone.)
Here's a debugging tip that doesn't seem to fit anywhere else: Mind your number bases! You can waste a lot of time debugging problems caused by specifying for example 56 when you meant $56 (ie, in hexadecimal).
And another: Don't read and use a variable you haven't written to first. RAM will definitely not contain all 00's or all
$FF's at power-up. However, there's also no need to zero all variables or RAM before starting something. We've heard it
said many times, "Uninitialized variables are a big source of bugs." Well, true, but initializing them to zero is usually not any
better than not initializing them at all. The correct initial value probably probably won't be zero; and zero can cause additional
problems, for example if you try to divide by it. I've had iterative functions where the number you start with almost didn't
matter because you'll be converging on an answer, and starting with a good estimate just reduces the number of iterations needed; but
starting with zero, you never would finish. It might just crash, or get stuck in an infinite loop, or it might cause some sort of
interrupt. I never do automatic initializations. Never. All my initializations are deliberate and appropriate for the
situation. Yours should be too.
Tip #40 is not directly related to debugging, but gives a way to recover more quickly from crashes during development.
It is common for the beginner to want all the fancy tools too soon, and these tools insulate him too much from really learning what goes on at the heart of the machine, giving him a disadvantage that takes much longer to overcome. A sure result down the road is more bugs, some of which he won't be aware of until after the bugs have been a secret cause of a lot of inconvenience, equipment damage, or worse. It's kind of like giving young kids a calculator to multiply and divide with before they really have an understanding of what these procedures even are. The appropriate thing is to get them well acquainted with, and practiced at, doing it with paper and pencil, before moving on. A very smart engineer I work with marvels at how I can quickly calculate logarithms (for decibels) in my head. He's just slightly too young to have used a slide rule, having started with calculators. Same kind of thing.
The last half of section 17 (on general steps for a successful project) is about how to more effectively
get help on the 6502.org forum if you need it.
Program-writing <--Previous | Next--> Programming tips
last updated Nov 28, 2023