Page 1 of 1

Counting "trick"/memory copy

Posted: Mon May 22, 2017 5:27 am
by pixel
In the course of trying to hammer Pulse into unexpanded machines alongside darkatx' title screen and audio player for borays tune "No syrup" (don't miss the original recording) a false assumption got in the way: that the INC and DEC instructions would modify the carry flag. That "Oh no!" scream from Bridlington must have been heard in Denmark. But out came this counting trick which I haven't seen anywhere else yet. Anybody seeing issues with this one?

Code: Select all

.export moveram

.importzp s, d, c

.code

.proc moveram
    ldy #0
    ldx c
    sty c
    inc c+1			; *honk!*
    cmp #0
    bne copy_backwards

copy_forwards:
l:  lda (s),y
    sta (d),y
    inc s
    beq k
n:  inc d
    beq m
q:  dex
    bne l
    dec c+1
    bne l
    rts

k:  inc s+1
    jmp n

m:  inc d+1
    jmp q

copy_backwards:
l2: lda (s),y
    sta (d),y
    dec s
    lda s
    cmp #$ff
    beq m2
n2: dec d
    lda d
    cmp #$ff
    beq j2
q2: dex
    bne l2
    dec c+1
    bne l2
    rts

m2: dec s+1
    jmp n2

j2: dec d+1
    jmp q2
.endproc                                                                                                                                                                                                                          

Re: Counting "trick"/memory copy

Posted: Mon May 22, 2017 6:01 am
by Mike
Usually, copy routines on the 65xx with LDA/STA (),Y modify Y with INY/DEY instead of doing a costly INC/DEC on the low-byte ZP address base. That way, the fix-up of the high-byte only needs to be done on over-/underflow of Y, simultaneously for both src and dst pointers, and doesn't require independent checks of both low-bytes on each loop iteration. Like:

Code: Select all

.loop
 LDA (src),Y
 STA (dst),Y
 INY
 BNE loop
 INC src+1
 INC dst+1
 DEX
 BNE loop
... and the loop is entered with a non-0 value of Y, if the transfer contains a non-complete page (adjusting src and dst beforehand).

Likewise for the other direction:

Code: Select all

.start
 CPY #0        /* fix-up for a single */
 BEQ skip      /* byte in the last page */
.loop
 LDA (src),Y
 STA (dst),Y
 DEY
 BNE loop
.skip
 LDA (src),Y   /* these */
 STA (dst),Y   /* three */
 DEY           /* go extra */
 DEC src+1
 DEC dst+1
 DEX
 BNE loop
... where the routine is entered with a non-255 value of Y for a non-complete page. Y=0 needs the special fix-up once at the start, otherwise errorneously a full page is copied. The high-bytes of src and dst need to be adjusted to point to the last page.

Re: Counting "trick"/memory copy

Posted: Mon May 22, 2017 6:30 am
by pixel
Mike wrote:Usually, copy routines on the 65xx with LDA/STA (),Y modify Y with INY/DEY instead of doing a costly INC/DEC on the low-byte ZP address base. That way, the fix-up of the high-byte only needs to be done on over-/underflow of Y, simultaneously for both src and dst pointer, and doesn't require independent checks of both low-bytes on each loop iteration.
:shock: Oh, crap. How could I be THAT stupid!?!? At least it's now obvious why I'd better ask for review... an update of 'g'. Thanks, Mike!

Code: Select all

.export moveram                                                                                                                                                                                                                   

.importzp s, d, c

.code

.proc moveram
    ldy #0
    ldx c
    sty c
    inc c+1			; <- the counting trick
    cmp #0
    bne copy_backwards

copy_forwards:
l:  lda (s),y
    sta (d),y
    iny
    beq k
q:  dex
    bne l
    dec c+1
    bne l
    rts

k:  inc s+1
    inc d+1
    jmp q

copy_backwards:
l2: lda (s),y
    sta (d),y
    dey
    cpy #$ff
    beq m2
q2: dex
    bne l2
    dec c+1
    bne l2
    rts

m2: dec s+1
    dec d+1
    jmp q2
.endproc

Re: Counting "trick"/memory copy

Posted: Wed Feb 07, 2018 1:32 am
by Mike
I found a nice collection of memory move routines on 6502.org: Practical Memory Move Routines.
Bruce Clark wrote:Here are some reasonably fast general-purpose routines for moving blocks of memory. You simply specify the address to move from, the address to move to, and the size of the block. [...] These routines are intended to be both flexible and practical, without being excessively lengthy or excessively slow. To that end, they can be placed in ROM or in RAM.

There are three routines moving memory upward (i.e. to a higher address), each of which is tailored to a slightly different set of input parameters.

Re: Counting "trick"/memory copy

Posted: Mon Feb 19, 2018 4:04 pm
by pixel
My gut feeling was about right: there's something wrong with this trick. The high byte must only be incremented if the low byte is not 0.