fibonacci.oph

.include "../platform/c64_0.oph"
.require "../platform/c64kernal.oph"
.outfile "fibonacci.prg"

        lda     #<opening     ; Print opening text
        sta     fun'args
        lda     #>opening
        sta     fun'args+1
        jsr     print'string

        lda     #$00
        sta     fun'vars      ; Count num from 0 to 19
*       lda     fun'vars      ; Main loop: print num, with leading space if <10
        cmp     #$09
        bcs     +
        lda     #$20
        jsr     chrout
        lda     fun'vars
*       sta     fun'args      ; Copy num to args, print it, plus ": "
        inc     fun'args
        lda     #$00
        sta     fun'args+1
        jsr     print'dec
        lda     #$3A
        jsr     chrout
        lda     #$20
        jsr     chrout
        lda     fun'vars      ; Copy num to args, call fib, print result
        sta     fun'args
        jsr     fib
        jsr     print'dec
        lda     #$0D          ; Newline
        jsr     chrout
        inc     fun'vars      ; Increment num; if it's 20, we're done.
        lda     fun'vars
        cmp     #20
        bne     --            ; Otherwise, loop.
        rts

opening:
.byte   147, "           FIBONACCI SEQUENCE",13,13,0

.scope
; Uint16 fib (Uint8 x): compute Xth fibonnaci number.
; fib(0) = fib(1) = 1.
; Stack usage: 3.

fib:    lda     #$03
        jsr     save'stack

        lda     fun'vars    ; If x < 2, goto _base.
        cmp     #$02
        bcc     _base

        dec     fun'args    ; Otherwise, call fib(x-1)...
        jsr     fib
        lda     fun'args    ; Copy the result to local variable...
        sta     fun'vars+1
        lda     fun'args+1
        sta     fun'vars+2
        lda     fun'vars    ; Call fib(x-2)...
        sec
        sbc     #$02
        sta     fun'args
        jsr     fib
        clc                 ; And add the old result to it, leaving it
        lda     fun'args    ; in the 'result' location.
        adc     fun'vars+1
        sta     fun'args
        lda     fun'args+1
        adc     fun'vars+2
        sta     fun'args+1
        jmp     _done       ; and then we're done.

_base:  ldy     #$01        ; In the base case, just copy 1 to the
        sty     fun'args    ; result.
        dey
        sty     fun'args+1

_done:  lda     #$03
        jsr     restore'stack
        rts
.scend

.scope
; Stack routines: init'stack, save'stack, restore'stack
.data zp
.space _sp      $02
.space _counter $01
.space fun'args $10
.space fun'vars $40

.text
init'stack:
        lda     #$00
        sta     _sp
        lda     #$A0
        sta     _sp+1
        rts

save'stack:
        sta     _counter
        sec
        lda     _sp
        sbc     _counter
        sta     _sp
        lda     _sp+1
        sbc     #$00
        sta     _sp+1
        ldy     #$00
*       lda     fun'vars, y
        sta     (_sp), y
        lda     fun'args, y
        sta     fun'vars, y
        iny
        dec     _counter
        bne -
        rts

restore'stack:
        pha
        sta     _counter
        ldy     #$00
*       lda     (_sp), y
        sta     fun'vars, y
        iny
        dec     _counter
        bne -
        pla
        clc
        adc     _sp
        sta     _sp
        lda     _sp+1
        adc     #$00
        sta     _sp+1
        rts
.scend


; Utility functions.  print'dec prints an unsigned 16-bit integer.
; It's ugly and long, mainly because we don't bother with niceties
; like "division".  print'string prints a zero-terminated string.

.scope
.data
.org    fun'args
        .space  _val            2
        .space  _step           2
        .space  _res            1
        .space  _allowzero      1
.text
print'dec:
        lda     #$00
        sta     _allowzero
        lda     #<10000
        sta     _step
        lda     #>10000
        sta     _step+1
        jsr     repsub'16
        lda     #<1000
        sta     _step
        lda     #>1000
        sta     _step+1
        jsr     repsub'16
        lda     #0
        sta     _step+1
        lda     #100
        sta     _step
        jsr     repsub'16
        lda     #10
        sta     _step
        jsr     repsub'16
        lda     _val
        jsr     _print
        rts

repsub'16:
        lda     #$00
        sta     _res
*       lda     _val
        sec
        sbc     _step
        lda     _val+1
        sbc     _step+1
        bcc     _done
        lda     _val
        sec
        sbc     _step
        sta     _val
        lda     _val+1
        sbc     _step+1
        sta     _val+1
        inc     _res
        jmp     -
_done:  lda     _res
        ora     _allowzero
        beq     _ret
        sta     _allowzero
        lda     _res
_print: clc
        adc     #'0
        jsr     chrout
_ret:   rts
.scend

print'string:
        ldy     #$00
*       lda     (fun'args), y
        beq     +
        jsr     chrout
        iny
        jmp     -
*       rts