# Example Programs

This Appendix collects all the programs referred to in the course of this manual.

## `hello1.oph`

    .word $0801
    .org  $0801
    .outfile "hello.prg"

            .word next, 10          ; Next line and current line number
            .byte $9e," 2064",0     ; SYS 2064
    next:   .word 0                 ; End of program

    .advance 2064

            ldx #0
    loop:   lda hello, x
            beq done
            jsr $ffd2
            inx
            bne loop
    done:   rts

    hello:  .byte "HELLO, WORLD!", 0

## `hello2.oph`

    .word $0801
    .org  $0801
    .outfile "hello.prg"

    .scope
            .word _next, 10         ; Next line and current line number
            .byte $9e," 2064",0     ; SYS 2064
    _next:  .word 0                 ; End of program
    .scend

    .advance 2064

    .alias chrout $ffd2

            ldx #0
    *       lda hello, x
            beq +
            jsr chrout
            inx
            bne -
    *       rts

    hello:  .byte "HELLO, WORLD!", 0

## `c64-1.oph`

    .word $0801
    .org  $0801

    .scope
            .word _next, 10         ; Next line and current line number
            .byte $9e," 2064",0     ; SYS 2064
    _next:  .word 0                 ; End of program
    .scend

    .advance 2064

    .require "../platform/c64kernal.oph"

## `c64kernal.oph`

    ; KERNAL routine aliases (C64)

    .alias  acptr           $ffa5
    .alias  chkin           $ffc6
    .alias  chkout          $ffc9
    .alias  chrin           $ffcf
    .alias  chrout          $ffd2
    .alias  ciout           $ffa8
    .alias  cint            $ff81
    .alias  clall           $ffe7
    .alias  close           $ffc3
    .alias  clrchn          $ffcc
    .alias  getin           $ffe4
    .alias  iobase          $fff3
    .alias  ioinit          $ff84
    .alias  listen          $ffb1
    .alias  load            $ffd5
    .alias  membot          $ff9c
    .alias  memtop          $ff99
    .alias  open            $ffc0
    .alias  plot            $fff0
    .alias  ramtas          $ff87
    .alias  rdtim           $ffde
    .alias  readst          $ffb7
    .alias  restor          $ff8a
    .alias  save            $ffd8
    .alias  scnkey          $ff9f
    .alias  screen          $ffed
    .alias  second          $ff93
    .alias  setlfs          $ffba
    .alias  setmsg          $ff90
    .alias  setnam          $ffbd
    .alias  settim          $ffdb
    .alias  settmo          $ffa2
    .alias  stop            $ffe1
    .alias  talk            $ffb4
    .alias  tksa            $ff96
    .alias  udtim           $ffea
    .alias  unlsn           $ffae
    .alias  untlk           $ffab
    .alias  vector          $ff8d

    ; Character codes for the colors.
    .alias  color'0         144
    .alias  color'1         5
    .alias  color'2         28
    .alias  color'3         159
    .alias  color'4         156
    .alias  color'5         30
    .alias  color'6         31
    .alias  color'7         158
    .alias  color'8         129
    .alias  color'9         149
    .alias  color'10        150
    .alias  color'11        151
    .alias  color'12        152
    .alias  color'13        153
    .alias  color'14        154
    .alias  color'15        155

    ; ...and reverse video
    .alias  reverse'on      18
    .alias  reverse'off     146

    ; ...and character set
    .alias  upper'case      142
    .alias  lower'case      14

## `hello3.oph`

    .include "c64-1.oph"
    .outfile "hello.prg"

    .macro print
            ldx #0
    _loop:  lda _1, x
            beq _done
            jsr chrout
            inx
            bne _loop
    _done:
    .macend

    .macro greet
            `print hello1
            `print _1
            `print hello2
    .macend

            lda #147
            jsr chrout
            `greet target1
            `greet target2
            `greet target3
            `greet target4
            `greet target5
            `greet target6
            `greet target7
            `greet target8
            `greet target9
            `greet target10
            rts

    hello1: .byte "HELLO, ",0
    hello2: .byte "!", 13, 0

    target1:  .byte "PROGRAMMER", 0
    target2:  .byte "ROOM", 0
    target3:  .byte "BUILDING", 0
    target4:  .byte "NEIGHBORHOOD", 0
    target5:  .byte "CITY", 0
    target6:  .byte "NATION", 0
    target7:  .byte "WORLD", 0
    target8:  .byte "SOLAR SYSTEM", 0
    target9:  .byte "GALAXY", 0
    target10: .byte "UNIVERSE", 0

## `hello4a.oph`

    .include "c64-1.oph"
    .outfile "hello.prg"

    .macro print
            ldx #0
    _loop:  lda _1, x
            beq _done
            jsr chrout
            inx
            bne _loop
    _done:
    .macend

    .macro greet
            lda #30
            jsr delay
            `print hello1
            `print _1
            `print hello2
    .macend

            lda #147
            jsr chrout
            `greet target1
            `greet target2
            `greet target3
            `greet target4
            `greet target5
            `greet target6
            `greet target7
            `greet target8
            `greet target9
            `greet target10
            rts

    hello1: .byte "HELLO, ",0
    hello2: .byte "!", 13, 0

    target1:  .byte "PROGRAMMER", 0
    target2:  .byte "ROOM", 0
    target3:  .byte "BUILDING", 0
    target4:  .byte "NEIGHBORHOOD", 0
    target5:  .byte "CITY", 0
    target6:  .byte "NATION", 0
    target7:  .byte "WORLD", 0
    target8:  .byte "SOLAR SYSTEM", 0
    target9:  .byte "GALAXY", 0
    target10: .byte "UNIVERSE", 0

    ; DELAY routine.  Executes 2,560*(A) NOP statements.
    delay:  tax
            ldy #00
    *       nop
            nop
            nop
            nop
            nop
            nop
            nop
            nop
            nop
            nop
            iny
            bne -
            dex
            bne -
            rts

## `hello4b.oph`

    .include "c64-1.oph"
    .outfile "hello.prg"

    .macro print
            ldx #0
    _loop:  lda _1, x
            beq _done
            jsr chrout
            inx
            bne _loop
    _done:
    .macend

    .macro greet
            lda #30
            jsr delay
            `print hello1
            `print _1
            `print hello2
    .macend

            lda #147
            jsr chrout
            lda #lower'case
            jsr chrout
            `greet target1
            `greet target2
            `greet target3
            `greet target4
            `greet target5
            `greet target6
            `greet target7
            `greet target8
            `greet target9
            `greet target10
            rts

    hello1: .byte "Hello, ",0
    hello2: .byte "!", 13, 0

    target1:  .byte "programmer", 0
    target2:  .byte "room", 0
    target3:  .byte "building", 0
    target4:  .byte "neighborhood", 0
    target5:  .byte "city", 0
    target6:  .byte "nation", 0
    target7:  .byte "world", 0
    target8:  .byte "Solar System", 0
    target9:  .byte "Galaxy", 0
    target10: .byte "Universe", 0

    ; DELAY routine.  Executes 2,560*(A) NOP statements.
    delay:  tax
            ldy #00
    *       nop
            nop
            nop
            nop
            nop
            nop
            nop
            nop
            nop
            nop
            iny
            bne -
            dex
            bne -
            rts

## `hello4c.oph`

    .include "c64-1.oph"
    .outfile "hello.prg"

    .macro print
            ldx #0
    _loop:  lda _1, x
            beq _done
            jsr chrout
            inx
            bne _loop
    _done:
    .macend

    .macro greet
            lda #30
            jsr delay
            `print hello1
            `print _1
            `print hello2
    .macend

            lda #147
            jsr chrout
            lda #lower'case
            jsr chrout
            `greet target1
            `greet target2
            `greet target3
            `greet target4
            `greet target5
            `greet target6
            `greet target7
            `greet target8
            `greet target9
            `greet target10
            rts

    .charmap 'A, "abcdefghijklmnopqrstuvwxyz"
    .charmap 'a, "ABCDEFGHIJKLMNOPQRSTUVWXYZ"

    hello1: .byte "Hello, ",0
    hello2: .byte "!", 13, 0

    target1:  .byte "programmer", 0
    target2:  .byte "room", 0
    target3:  .byte "building", 0
    target4:  .byte "neighborhood", 0
    target5:  .byte "city", 0
    target6:  .byte "nation", 0
    target7:  .byte "world", 0
    target8:  .byte "Solar System", 0
    target9:  .byte "Galaxy", 0
    target10: .byte "Universe", 0

    ; DELAY routine.  Executes 2,560*(A) NOP statements.
    delay:  tax
            ldy #00
    *       nop
            nop
            nop
            nop
            nop
            nop
            nop
            nop
            nop
            nop
            iny
            bne -
            dex
            bne -
            rts

## `hello5.oph`

    .include "c64-1.oph"
    .outfile "hello.prg"

    .data
    .org $C000
    .text

    .macro print
            ldx #0
    _loop:  lda _1, x
            beq _done
            jsr chrout
            inx
            bne _loop
    _done:
    .macend

    .macro greet
            lda #30
            jsr delay
            `print hello1
            `print _1
            `print hello2
    .macend

            lda #147
            jsr chrout
            `greet target1
            `greet target2
            `greet target3
            `greet target4
            `greet target5
            `greet target6
            `greet target7
            `greet target8
            `greet target9
            `greet target10
            rts

    hello1: .byte "HELLO, ",0
    hello2: .byte "!", 13, 0

    target1:  .byte "PROGRAMMER", 0
    target2:  .byte "ROOM", 0
    target3:  .byte "BUILDING", 0
    target4:  .byte "NEIGHBORHOOD", 0
    target5:  .byte "CITY", 0
    target6:  .byte "NATION", 0
    target7:  .byte "WORLD", 0
    target8:  .byte "SOLAR SYSTEM", 0
    target9:  .byte "GALAXY", 0
    target10: .byte "UNIVERSE", 0

    ; DELAY routine.  Takes values from the Accumulator and pauses
    ; for that many jiffies (1/60th of a second).
    .scope
    .data
    .space _tmp 1
    .space _target 1

    .text

    delay:  sta _tmp        ; save argument (rdtim destroys it)
            jsr rdtim
            clc
            adc _tmp        ; add current time to get target
            sta _target
    *       jsr rdtim
            cmp _target
            bmi -           ; Buzz until target reached
            rts
    .scend

    .checkpc $A000
    .data
    .checkpc $D000

## `hello6.oph`

    .include "c64-1.oph"
    .outfile "hello.prg"

    .data
    .org $C000
    .space cache 2
    .text

    .macro print
            lda #<_1
            ldx #>_1
            jsr printstr
    .macend

    .macro greet
            lda #30
            jsr delay
            `print hello1
            `print _1
            `print hello2
    .macend

            ; Save the zero page locations that PRINTSTR uses.
            lda $10
            sta cache
            lda $11
            sta cache+1

            lda #147
            jsr chrout
            `greet target1
            `greet target2
            `greet target3
            `greet target4
            `greet target5
            `greet target6
            `greet target7
            `greet target8
            `greet target9
            `greet target10

            ; Restore the zero page values printstr uses.
            lda cache
            sta $10
            lda cache+1
            sta $11

            rts

    hello1: .byte "HELLO, ",0
    hello2: .byte "!", 13, 0

    target1:  .byte "PROGRAMMER", 0
    target2:  .byte "ROOM", 0
    target3:  .byte "BUILDING", 0
    target4:  .byte "NEIGHBORHOOD", 0
    target5:  .byte "CITY", 0
    target6:  .byte "NATION", 0
    target7:  .byte "WORLD", 0
    target8:  .byte "SOLAR SYSTEM", 0
    target9:  .byte "GALAXY", 0
    target10: .byte "UNIVERSE", 0

    ; DELAY routine.  Takes values from the Accumulator and pauses
    ; for that many jiffies (1/60th of a second).
    .scope
    .data
    .space _tmp 1
    .space _target 1

    .text

    delay:  sta _tmp        ; save argument (rdtim destroys it)
            jsr rdtim
            clc
            adc _tmp        ; add current time to get target
            sta _target
    *       jsr rdtim
            cmp _target
            bmi -           ; Buzz until target reached
            rts
    .scend

    ; PRINTSTR routine.  Accumulator stores the low byte of the address,
    ; X register stores the high byte.  Destroys the values of $10 and
    ; $11.

    .scope
    printstr:
            sta $10
            stx $11
            ldy #$00
    _lp:    lda ($10),y
            beq _done
            jsr chrout
            iny
            bne _lp
    _done:  rts
    .scend

    .checkpc $A000
    .data
    .checkpc $D000

## `hello7.oph`

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

    .data
    .org $C000
    .text

    .macro print
            lda #<_1
            ldx #>_1
            jsr printstr
    .macend

    .macro greet
            lda #30
            jsr delay
            `print hello1
            `print _1
            `print hello2
    .macend

            lda #147
            jsr chrout
            `greet target1
            `greet target2
            `greet target3
            `greet target4
            `greet target5
            `greet target6
            `greet target7
            `greet target8
            `greet target9
            `greet target10

            rts

    hello1: .byte "HELLO, ",0
    hello2: .byte "!", 13, 0

    target1:  .byte "PROGRAMMER", 0
    target2:  .byte "ROOM", 0
    target3:  .byte "BUILDING", 0
    target4:  .byte "NEIGHBORHOOD", 0
    target5:  .byte "CITY", 0
    target6:  .byte "NATION", 0
    target7:  .byte "WORLD", 0
    target8:  .byte "SOLAR SYSTEM", 0
    target9:  .byte "GALAXY", 0
    target10: .byte "UNIVERSE", 0

    ; DELAY routine.  Takes values from the Accumulator and pauses
    ; for that many jiffies (1/60th of a second).
    .scope
    .data
    .space _tmp 1
    .space _target 1

    .text

    delay:  sta _tmp        ; save argument (rdtim destroys it)
            jsr rdtim
            clc
            adc _tmp        ; add current time to get target
            sta _target
    *       jsr rdtim
            cmp _target
            bmi -           ; Buzz until target reached
            rts
    .scend

    ; PRINTSTR routine.  Accumulator stores the low byte of the address,
    ; X register stores the high byte.  Destroys the values of $10 and
    ; $11.

    .scope
    .data zp
    .space _ptr 2
    .text
    printstr:
            sta _ptr
            stx _ptr+1
            ldy #$00
    _lp:    lda (_ptr),y
            beq _done
            jsr chrout
            iny
            bne _lp
    _done:  rts
    .scend

    .checkpc $A000

    .data
    .checkpc $D000

    .data zp
    .checkpc $90

## `structuredemo.oph`

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

            jsr print'unsorted
            jsr insertion'sort
            jsr print'list
            rts

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; Linked list data: head, next, lb, hb.
    ; lb/hb: Low/high bytes of the data array.  These are immutable and
    ;        kept with the program text.
    ; head:  Array index of the first element in the list, or #$FF if the
    ;        list is empty
    ; next:  Array of successor indices.  If you've just read element X,
    ;        the value of memory location next+X is the index of the
    ;        next element.  If next is #$FF, you've reached the end of
    ;        the list.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    .data
    .org    $C000
    .space  head    1
    .space  next    16

    .text
    lb:   .byte <$838,<$618,<$205,<$984,<$724,<$301,<$249,<$946
          .byte <$925,<$043,<$114,<$697,<$985,<$633,<$312,<$086
    hb:   .byte >$838,>$618,>$205,>$984,>$724,>$301,>$249,>$946
          .byte >$925,>$043,>$114,>$697,>$985,>$633,>$312,>$086

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; insertion'sort:  Sorts the list defined by head, next, hb, lb.
    ; Arguments:  None.
    ; Modifies:   All registers destroyed, head and next array sorted.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    insertion'sort:
            lda #$FF        ; Clear list by storing the terminator in 'head'
            sta head
            ldx #$0         ; Loop through the lb/hb array, adding each
    insertion'sort'loop:    ; element one at a time
            txa
            pha
            jsr insert_elt
            pla
            tax
            inx
            cpx #$10
            bne insertion'sort'loop
            rts

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; insert_elt: Insert an element into the linked list.  Maintains the
    ;             list in sorted, ascending order.  Used by
    ;             insertion'sort.
    ; Arguments:  X register holds the index of the element to add.
    ; Modifies:   All registers destroyed; head and next arrays updated
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    .data
    .space lbtoinsert 1
    .space hbtoinsert 1
    .space indextoinsert 1

    .text

    insert_elt:
            ldy head                        ; If the list is empty, make
            cpy #$FF                        ; head point at it, and return.
            bne insert_elt'list'not'empty
            stx head
            tya
            sta next,x
            rts
    insert_elt'list'not'empty:
            lda lb,x                        ; Cache the data we're inserting
            sta lbtoinsert
            lda hb,x
            sta hbtoinsert
            stx indextoinsert
            ldy head                        ; Compare the first value with
            sec                             ; the data.  If the data must
            lda lb,y                        ; be inserted at the front...
            sbc lbtoinsert
            lda hb,y
            sbc hbtoinsert
            bmi insert_elt'not'smallest
            tya                             ; Set its next pointer to the
            sta next,x                      ; old head, update the head
            stx head                        ; pointer, and return.
            rts
    insert_elt'not'smallest:
            ldx head
    insert_elt'loop:                        ; At this point, we know that
            lda next,x                      ; argument > data[X].
            tay
            cpy #$FF                        ; if next[X] = #$FF, insert arg at end.
            beq insert_elt'insert'after'current
            lda lb,y                        ; Otherwise, compare arg to
            sec                             ; data[next[X]].  If we insert
            sbc lbtoinsert                  ; before that...
            lda hb,y
            sbc hbtoinsert
            bmi insert_elt'goto'next
    insert_elt'insert'after'current:        ; Fix up all the next links
            tya
            ldy indextoinsert
            sta next,y
            tya
            sta next,x
            rts                             ; and return.
    insert_elt'goto'next:                   ; Otherwise, let X = next[X]
            tya                             ; and go looping again.
            tax
            jmp insert_elt'loop

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; print'unsorted: Steps through the data array and prints each value.
    ; Standalone procedure.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    print'unsorted:
            lda #<unsorted'hdr
            ldx #>unsorted'hdr
            jsr put'string
            ldy #$00
    print'unsorted'loop:
            lda hb, Y
            jsr print'hex
            lda lb, y
            jsr print'hex
            lda #$20
            jsr chrout
            iny
            cpy #$10
            bne print'unsorted'loop
            lda #$0D
            jsr chrout
            rts

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; print'list: Starts at head, and prints out every value in the
    ;             linked list.
    ; Standalone procedure.
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    print'list:
            lda #<sorted'hdr
            ldx #>sorted'hdr
            jsr put'string
            ldy head
    print'list'loop:
            cpy #$FF
            beq print'list'done
            lda hb, y
            jsr print'hex
            lda lb, y
            jsr print'hex
            lda #$20
            jsr chrout
            lda next, Y
            tay
            jmp print'list'loop
    print'list'done:
            lda #$0d
            jsr chrout
            rts

    ;; String data for the above routines.

    unsorted'hdr:
            .byte 147               ; Clear screen first!
            .byte "UNSORTED DATA:",13,0

    sorted'hdr:
            .byte "SORTED DATA:",13,0

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; print'hex: outputs a two-character hex representation of a one-
    ;            byte value.
    ; Arguments: Byte to print in accumulator
    ; Modifies: .A and .X
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    print'hex:
            pha
            clc
            lsr
            lsr
            lsr
            lsr
            tax
            lda hexstr,x
            jsr chrout
            pla
            and #$0F
            tax
            lda hexstr,X
            jsr chrout
            rts

    ; Character data array for print'hex.
    hexstr: .byte "0123456789ABCDEF"

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ; put'string: outputs a C-style null terminated string with length
    ;             less than 256 to the screen.  If 256 bytes are written
    ;             without finding a terminator, the routine ends quietly.
    ; Arguments: Low byte of string address in .A, high byte in .X
    ; Modifies: .A and .Y
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

    .data zp
    .space put'string'addr 2

    .text
    put'string:
            sta put'string'addr
            stx put'string'addr+1
            ldy #$00
    put'string'loop:
            lda (put'string'addr),y
            beq put'string'done
            jsr chrout
            iny
            bne put'string'loop
    put'string'done:
            rts

## `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