[comp.sys.cbm] Recursive routines in assembler

if18@vlsi.polymtl.ca (12/06/90)

In article 1140, s887212@minyos.xx.rmit.oz.au (Stephen Riehm) wrote:

>I am trying to implement a highly recursive routine.. which requires some
>local variables. (ie: this routine can easily do 600 iterations.. each with
>4 variables). Naturally this wont fit on the c64's stack.. so I have tried
>making my own (included below) which half works (the push half).


Actually, I don't think your routine PUSH works correctly. That's mainly
because you used a TXS instruction instead of TSX ! 
You are filling up your user stack but your not getting the data from
the computer stack... And you're modifying the STACK POINTER !!

Here is your own code with some corrections and/or possible optimizations 
between []s:


push onto user stack

   sei
   ldy #$00
   jsr $dec.(fd)  /* lda $fd ; beq skip ; dec $fe ; skip dec $fd ; rts */
   lda $var1                    ^ --> [Use BNE SKIP instead of BEQ SKIP]
   sta ($fd),y
   jsr $dec.(fd)
   lda $var2
   sta ($fd),y
   jsr $dec.(fd)
   lda $var3
   sta ($fd),y
   jsr $dec.(fd)          [ All ok up to here... except for $dec.(fd) ]

                          [ Change the rest...]
   txs                           TSX           (not txs !)
   inx                                         (inx not needed)
   inx                                         ( "   "     "  )
   inx                                         ( "   "     "  )
   lda $0100,x                   lda $0103,x
   sta ($fd),y                   sta ($fd),y
   inx                                         (inx not needed)
   jsr $dec.(fd)                 jsr $dec.(fd)
   lda $0100,x                   lda $0104,x
   sta ($fd),y                   sta ($fd),y
   lda $fe,x                     lda $0101,x
   sta $0100,x                   sta $0103,x
   dex                                         (dex not needed)
   lda $fe,x                     lda $0102,x
   sta $0100,x                   sta $0104,x
   dex                           inx          (update stack ptr)
   txs                           inx
   cli                           txs
   rts                           cli
                                 rts


First of all, txs moves the contents from the X register to the Stack pointer
register. In your case, you want to do the opposite i.e. from the Stack ptr 
to the X register; you should use TSX for that.

lda $fe,x (and the likes) are tricky for new programmers because they DO NOT
cross the boundary between page 0 and page 1 (which is the stack, by the way). It comes back to the first address in the same page. For example, if the X register value was 3 and you used lda $fe,x  ,you would have the contents of address $0001 instead of $0101 !  This only occurs when you use indexing with a 
ZERO PAGE instruction. If you want to cross the boundary (i.e. get $0101 in
the example), you should use lda $00fe,x which is the ABSOLUTE instruction.
Some assemblers compile lda $00fe,x as if it were lda $fe,x ; make sure your
assembler doesn't do that.

By the way, page 1 (from $0100 to $01ff) is, as I said, the stack region
for the C-64. It starts from $01ff downward so in your case, you should
increment the Stack ptr since you want to remove an address from the Stack
(that was already ok).

Another problem that you had was:

>   jsr $dec.(fd)       /* lda $fd ; beq skip ; dec $fe ; skip dec $fd ; rts */

You should have used BNE SKIP instead of BEQ SKIP since you want to dec $fe
only when the contents of the accumulator (lda $fd) is 0.


Here's the second part of your code and the necessary modifications:


pop from user stack

   sei
   ldy #$00
   tsx                [Ok up to here]
 
                      [Change the rest...]
   inx                                       (inx not needed)
   lda $0100,x         lda $0101,x 
   sta $fe,x           sta $00ff,x           (use ABSOLUTE mode !)
   inx                                       (inx not needed)
   lda $0100,x         lda $0102,x
   sta $fe,x           sta $0100,x
   lda ($fd),y         lda ($fd),y
   sta $0100,x         sta $0102,x
   dex                                       (dex not needed)
   iny                 iny
   lda ($fd),y         lda ($fd),y
   sta $0100,x         sta $0101,x
   dex                                      (this dex is not needed anymore)
   dex                 dex
   dex                 dex
   txs                 txs                  (update stack ptr)

   jsr $inc.(fd)                            (not needed)
   jsr $inc.(fd)                            (not needed)
   ldy #$00            iny
   lda ($fd),y         lda ($fd),y
   sta $var3           sta $var3
   jsr $inc.(fd)       iny
   lda ($fd),y         lda ($fd),y
   sta $var2           sta $var2
   jsr $inc.(fd)       iny
   lda ($fd),y         lda ($fd),y
   sta $var1           sta $var1
   jsr $inc.(fd)       tya
   cli                 sec
   rts                 adc $fd        (Add Yreg+1 to $fd)
                       sta $fd
                       lda $fe        (this could be optimized even more...)
                       adc #$00          
                       sta $fe
                       cli
                       rts


Here again, you should have used the ABSOLUTE instruction instead of the
ZERO PAGE instruction.

You also had a problem with $inc.(fd):

>   jsr $inc.(fd)     /* inc $fd ; beq skip ; inc $fe ; skip rts */ 

You should have used BNE SKIP instead of BEQ SKIP since you want to
increment $fe only when $fd returns to 0. However, you don't need the
subroutine $inc.(fd) anymore because the user stack ptr (at $fd-$fe) is
updated at the end of pop. You actually save bytes if you remove the
subroutine $inc.(fd) (i.e. lda $fd; BNE SKIP; inc $fe; skip rts) from your
code and use the above (with adc $fd).

These routines could be optimized even more (as I mentionned); however,
they should work fine the way they are.

I didn't have time to test these routines but they seem to be working
correctly with the modifications!  Good luck!

--


   Miguel Pedro
   Send mail to if18@info.polymtl.ca (NOT @vlsi.polymtl.ca !!!)