Quick Post: Commodore 64 Simple Addition Efficiency

Quick Post: Commodore 64 Simple Addition Efficiency

There are many hacks you can do in Assembly language on the Commodore.

Here's a very easy one with impressive results according to my analog stopwatch.

We want to count to $ffffff or 2563 (224) which is 16,777,216

We wont print to the screen to save a lot of time, and just put an "a" in the top right corner when we're done.

One way (and yes, I want to know all the other ways you know of to do this) would be:

    *=c000
    
    counter 
    lda #0

loop clc
    lda result
    adc #1
    sta result
    lda result+1
    adc #0
    sta result+1
    lda result+2
    adc #0
    sta result+2
    
    cmp #$00
    bne loop
    
    lda #$01
    sta $0400 ;print an a top left when done
    
    rts
    
    result .byte 0,0,0

This takes almost 12 minutes to complete on an NTSC Commodore 64. Not bad for a 1Mhz machine.

But we can skip some of this addition in our loop in 2 places using the same technique. Since the second and third additions are just adding the carry bit, we can skip it if carry is clear. So now we have:

    *=c000

counter 
    lda #0

loop clc
    lda result
    adc #1
    sta result
    bcc ahead     ; branch ahead if carry clear
    lda result+1
    adc #0
    sta result+1
    bcc ahead	  ; branch ahead if carry clear
    lda result+2
    adc #0
    sta result+2
    
    cmp #$00
ahead 
    bne loop

    lda #$01
    sta $0400 ;print an a top left when done
    
    rts
    
    result .byte 0,0,0

This takes 5.5 minutes. More than twice as fast.

Finally, we need to handle the remainder counts. There are probably about 50 ways to do this and this might not be the most efficient, but it works.

*=c000

counter 
    lda #0

loop clc
    lda result
    adc #1
    sta result
    bcc ahead     ; branch ahead if carry clear
    lda result+1
    adc #0
    sta result+1
    bcc ahead	  ; branch ahead if carry clear
    lda result+2
    adc #0
    sta result+2
    
    cmp #$00
ahead 
    bne loop
    
    lda result+1
remain1
    cmp #$ff
    beq ahead
    clc
    adc #1
    sta result+1
    jmp remain1
ahead1
    lda result
remain
    cmp #$ff
    beq ahead
    clc
    adc #1
    sta result
    jmp remain
ahead
   
done  

    lda #$01
    sta $0400 ;print an a top left when done
    
    rts
    
    result .byte 0,0,0

While I don't think any of us is doing a ton of serious "work" on a Commodore any more, it's definitely interesting to think about efficiencies like this. Solving this particular one helped me "see" a problem with a client in my day job and we managed to regain about 30% of server resources by looking at an existing problem for solutions like this.