Descending Loop Termination: BPL or BNE?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
MenhirMike
Posts: 18
Joined: Mon Jul 24, 2023 6:07 pm

Descending Loop Termination: BPL or BNE?

Post by MenhirMike »

A lot of tutorials about looping in descending order use BPL, like so:

Code: Select all

        LDY #$13
@loop:
        LDA apu_regs_init,Y
        STA $4000,Y
        DEY
        BPL @loop
Now, personally I would normally choose BNE instead because in my mental model, I think of "While Y is not 0" rather than "While Y is positive".

So I'm just curious if this is just a question of personal preference, or if there are any other reasons to prefer one or the other?

One thing I can think of is that numbers between $80-$FF would also terminate the loop since those are not positive, so if I have a loop that decrements Y by more than 1 each iteration, I can underflow e.g., from $01 to $FE (DEY 3 times during the loop iteration) - but that seems like a poor choice by default since stopping the loop when it's less than $00 sounds like a bug unless it's deliberate.

Cost in Bytes and Cycles is identical. So yeah, just personal preference?
Fiskbit
Site Admin
Posts: 973
Joined: Sat Nov 18, 2017 9:15 pm

Re: Descending Loop Termination: BPL or BNE?

Post by Fiskbit »

0 is positive, so BPL will stop when Y becomes negative (less than 0), while BNE will stop when Y becomes 0. In the example code, using BNE would skip the final iteration, thus not writing to $4000. Compensating for this makes the code more complex; you need to start Y one higher and subtract 1 from the target addresses, and this may cause loads to unnecessarily incur a page-crossing penalty.

I suspect that when you have an iteration count, BNE is common, but when you're iterating on an index, BPL is more common.
MenhirMike
Posts: 18
Joined: Mon Jul 24, 2023 6:07 pm

Re: Descending Loop Termination: BPL or BNE?

Post by MenhirMike »

Thank you, I completely didn't consider that 0 is positive, that makes perfect sense then!
Oziphantom
Posts: 1603
Joined: Tue Feb 07, 2017 2:03 am

Re: Descending Loop Termination: BPL or BNE?

Post by Oziphantom »

you have to use both cases as BPL only works if the iteration is for a 128 items or less, i.e you load the index with max 127.
i.e

Code: Select all

    ldy #kNumItems-1 ; 1 <= kNumItems <= 128
    lda #0
-   sta $0200,y
    dey
    bpl -
 
For 128-255 items you do bne and -1 on the address

Code: Select all

    ldy #kNumItems ; 1 <= kNumItems <= 255
    lda #0
-   sta $0200-1,y
    dey
    bne -
for 256 items you do bne with no subtraction

Code: Select all

    ldy #0 ; set 256 items so in this case $200-$2ff inclusive 
    tay
-   sta $0200,y
    dey
    bne -
for less thought you can do the address-1 and bne as then you don't have to worry if your constant >= 128 or not and just "apply the pattern". For safety though you can use assembler errors/assert to check that the value doesn't go over 127 and warn you you need to change the looping method.

The main issue with the always BNE issue is that if you have a debugger with labels ( as we are so lucky in this modern era to have ) you won't see sta myLabel-1,t but rather sta $01ff,y which is confusing, if you do make this method though you can also do myLabelMinus1 = myLabel -1 which when they show up in the debugger. Or go bleeding edge modern age and use one that gives you source code level debugging.

Another rare trap with the address-1 mode is the $100 case i.e $0100-1 is $FF which is in the zp and zp,x does not leave the zp. I.e

Code: Select all

ldx #1
lda $ff,x
; A now holds the contents of $00 not $100
for this case you will need to instruct your assembler to use ABS addressing mode for the load, please consult the documentation of your preferred assembler on how to do this.
MenhirMike
Posts: 18
Joined: Mon Jul 24, 2023 6:07 pm

Re: Descending Loop Termination: BPL or BNE?

Post by MenhirMike »

Thanks for the callout on 128 items! I think in that case I might also consider unrolling the loop into two loops. Sacrificing a little bit of ROM space (and another 2 cycles for a second ldy #7F or whatever the amount over 128 is) to not deal with the headache.

PS: In ca65, one can use a: as a prefix to force absolute mode, like lda a:$ff,x. Still, I think I'd rather avoid the Address-1 trick unless I'm really in a pickle in terms of ROM space. Or in case I have to use indirect addressing and can't just easily take an address from memory and add 128 to it.
User avatar
Bregalad
Posts: 8062
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: Descending Loop Termination: BPL or BNE?

Post by Bregalad »

Oziphantom wrote: Sun Jul 07, 2024 4:40 am

Code: Select all

    ldy #kNumItems ; 1 <= kNumItems <= 255
    lda #0
-   sta $0200-1,y
    dey
    bne -
Although I'll work, this is ugly.
The proper way to do a downward counting including zero would be to pre-decrement Y instead of post-decrementing it.

Code: Select all

   ldy #kNumItems
   lda #$00
-  dey
   sta $0200,Y
   bne -
In cases where the instruction between "dey" and "bne" would scrap the Z flag, you'd need an extra CMP #$00 instruction which is sure wasteful. Or use one the index registers for actual index, and the other one for counting downwards.
Useless, lumbering half-wits don't scare us.
Post Reply