BillG wrote:
A good programmer will document things as they are understood instead of leaving the code unchanged.
Or even rewrite the code as he goes. That's a technique I often use when when trying to understand new code. Whether I keep the rewritten code or just throw it out depends on how good the test framework is, whether I actually improved it or not, and so on, but the mere act of trying to do what was already done in a different way can be very enlightening.
Quote:
A comment such as
Code:
dex ; Decrement X register
adds no useful information and may as well not be there.
In fact, it may have negative value: making a developer read a lot of useless (to her) verbiage can slow her down.
But on the other hand, even comments like the above may be valuable. The key is to understand your audience: who will be reading your code, and what do they likely need to understand it? If you're writing code you expect to be read mainly by people entirely unfamiliar with 6502 assembly language, a comment like the above might be exactly the right thing.
For example, here's a little routine with a fair amount of subtlety in it:
Code:
; Convert ASCII character to binary number in bases up to 41.
; This translates A-Z[\]^_ and a-z{|}~DEL as 10, 11...40.
; The caller must check the returned value if the intended base was
; less than 41.
;
; A: (written) ASCII character to convert; binary number on return
; N flag: (written) clear on success, set on error.
; X, Y: (preserved)
;
convascdigit
cmp #'9'+1
bcs .letter ; >'9', convert letter
sbc #'0'-1 ; convert digit; if <'0', N flag set for error
rts ; N clear, no error
.letter
and #%11011111 ; clear bit 5 to convert to upper-case
bmi .exit ; high bit set, error
sbc #'A'-$0A
cmp #$0A ; if <$0A, set N flag to indicate error
.exit rts
That version is what I'd normally use in my projects, or post here if I were seeking help or comments on it. But this code is also the example I use for explaining certain aspects of assembly language to people, such as careful use and tracking of flag values. So the
the actual source code is in fact a bloated 50-line monstrosity where you can hardly find the code amongst the comments. It's pretty horrible to read if you're a half-way decent 6502 programmer, but good for beginners, and doesn't really hurt the project too much since it's unlikely to be changed in the future anyway.
Code:
; Convert ASCII character to binary number in bases up to 41.
; This translates A-Z[\]^_ and a-z{|}~DEL as 10, 11...40.
; The caller must check the returned value if the intended base was
; less than 41.
;
; A: (w) ASCII character to convert; binary number on return
; N flag: (w) clear on success, set on error.
; X, Y: (p)
;
convascdigit
; This routine has extra explanation for 6502 beginners.
cmp #'9'+1
bcs .letter ; >'9', convert letter
; At this point we know that the carry is clear, better thought
; of as the borrow being set. Rather than use an extra instr
; to set the carry, we instead subtract 1 from the subtrahend
; because the set borrow will also be subtracted from the result.
sbc #'0'-1 ; convert digit; if <'0', N flag set for error
; Normally for an unsigned comparison we'd check to see if the
; carry is clear, i.e., the borrow was used, to determine
; whether our result was negative. We can't do this here to see
; if our char was < '0' because of the optimization above. But
; it's safe to check the N flag because we know from the check at
; the start that the char was ≤ $39 ('9') and so this will
; always produce a result between $39-$30=$09 and $00-$30=-$30
; ($D0 in two's complement), all values of which are negative.
; Since the N flag is our error code, we need not even BMI;
; just let the N flag pass through.
rts ; N clear, no error
.letter
and #%11011111 ; clear bit 5 to convert to upper-case
; Now the char is in one of two ranges:
; $3A-$40 chars between '9' and 'A'
; $40-$5F A-Z and punctuation after ([\]^_)
; $80-$FF chars above DEL
; We check the N flag here to see if the high bit is set, which
; means the character is invalid. We have to do the AND first
; because the N bit from the CMP test is based on the result
; of the CMP subtraction.
bmi .exit ; high bit set, error
; We subtract 'A'-$0A to bring it down to the $00-$28 range,
; No SEC is needed before this SBC; we branched here because the
; carry was already set and AND does not affect the carry.
sbc #'A'-$0A
; Values less than $0A are invalid, so error out on those.
cmp #$0A ; if <$0A, set N flag to indicate error
; The result of SBC is signed, so it may be, e.g., -$01 = $FF.
; However, CMP does only unsigned comparisons so the carry flag
; would be set (indicating ≥) rather than clear (indicating <)
; for those "negative" results. But since we know our value in A
; is in range $00-$28, the most negative possible result produced
; by the SBC is -$28 = $D8, so we can check the N flag instead.
; Since the N flag is our error code, we need not even BMI.
.exit rts