When to Use CLC+ADC Instead of INC?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
SMB2J-2Q
Posts: 154
Joined: Thu Jul 27, 2017 5:13 pm

When to Use CLC+ADC Instead of INC?

Post by SMB2J-2Q »

For certain subroutines, when should I rather use CLC+ADC or SEC+SBC instead of INC or DEC, respectively?

For example, this code in Super Mario All-Stars that checks the number of lives for both Super Mario Bros. and Super Mario Bros.: The Lost Levels:

Code: Select all

CODE_048596:
    LDA $075A       ;check current player's lives
    INC $075A       ;increase by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A5 ;if not, skip the rest of this routine
    LDA #$7F        ;otherwise set A to 127 
    STA $075A       ;store in A and leave
CODE_0485A5:
    RTL
Since some of the above code is 65816-based (the ending RTL), then we might be able to substitute an "INC A" (CLC+ADC #1 for 6502) for the second instruction and a "DEC A" (SEC+SBC #1 for 6502) for the fifth instruction instead:

Code: Select all

CODE_048596:
    LDA $075A       ;check current player's lives
    INC A           ;increase A by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A1 ;if not, skip the rest of this routine
    DEC A           ;otherwise subtract A to cap it at 127
    STA $075A       ;store in A and leave
CODE_0485A1:
    RTL
~Ben
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: When to Use CLC+ADC Instead of INC?

Post by Oziphantom »

typically you use inc when you don't want the value in A or to affect A. Which makes the code above very strange.As it loads the old value, inc's it then compares the old value to something..

Code: Select all

CODE_048596:
    LDA $075A       ;check current player's lives
    INC $075A       ;increase by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A5 ;if not, skip the rest of this routine
    LDA #$7F        ;otherwise set A to 127 
    STA $075A       ;store in A and leave
CODE_0485A5:
    RTL
   
I really wonder what they were doing when they wrote this..

Code: Select all

CODE_048596:
    LDA $075A       ;check current player's lives
    INC A           ;increase A by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A1 ;if not, skip the rest of this routine
    DEC A           ;otherwise subtract A to cap it at 127
    STA $075A       ;store in A and leave
CODE_0485A1:
    RTL
   
this is wrong as well unless all calling routines have a sta $075a after the JSL to this routine... However the simple fix would be

Code: Select all

CODE_048596:
    LDA $075A       ;check current player's lives
    INC A           ;increase A by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A1 ;if not, skip the rest of this routine
    DEC A           ;otherwise subtract A to cap it at 127
CODE_0485A1:
    STA $075A       ;store in A and leave
    RTL
   
the first one lets you get to 128, then we you go to 129 it will drop you back to 127, while the SNES version will lock you to 127 lives.
SMB2J-2Q
Posts: 154
Joined: Thu Jul 27, 2017 5:13 pm

Re: When to Use CLC+ADC Instead of INC?

Post by SMB2J-2Q »

Oziphantom wrote: Sun Feb 26, 2023 5:04 am typically you use inc when you don't want the value in A or to affect A. Which makes the code above very strange.As it loads the old value, inc's it then compares the old value to something..

Code: Select all

CODE_048596:
    LDA $075A       ;check current player's lives
    INC $075A       ;increase by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A5 ;if not, skip the rest of this routine
    LDA #$7F        ;otherwise set A to 127 
    STA $075A       ;store in A and leave
CODE_0485A5:
    RTL
   
I really wonder what they were doing when they wrote this..

Code: Select all

CODE_048596:
    LDA $075A       ;check current player's lives
    INC A           ;increase A by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A1 ;if not, skip the rest of this routine
    DEC A           ;otherwise subtract A to cap it at 127
    STA $075A       ;store in A and leave
CODE_0485A1:
    RTL
   
this is wrong as well unless all calling routines have a sta $075a after the JSL to this routine... However the simple fix would be

Code: Select all

CODE_048596:
    LDA $075A       ;check current player's lives
    INC A           ;increase A by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A1 ;if not, skip the rest of this routine
    DEC A           ;otherwise subtract A to cap it at 127
CODE_0485A1:
    STA $075A       ;store in A and leave
    RTL
   
the first one lets you get to 128, then we you go to 129 it will drop you back to 127, while the SNES version will lock you to 127 lives.
Yes, all of these would, as call-tos, begin with a JSL in other program banks.

A much simpler fix, though, was this one... the routine will exit after we actually get to 127 lives.

Code: Select all

CODE_048596:
LDA $075A       ;check current player's lives
CMP #$7F        ;have we reached 127 lives yet?
BCS CODE_0485A2 ;if so, skip the rest of this routine
INC $075A       ;otherwise increase by one
CODE_0485A2:
RTL             ;and leave
~Ben
Last edited by SMB2J-2Q on Sun Feb 26, 2023 8:59 am, edited 1 time in total.
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: When to Use CLC+ADC Instead of INC?

Post by Oziphantom »

6502 or 65816, if there is no where else that can set the lives to above 128

Code: Select all

CODE_048596:
inc $075A       ;add one to lives
bpl CODE_0485A2 ;<128 if so, skip the rest of this routine
dec $075A       ;go back to 127
CODE_0485A2:
RTL             ;and leave
a more generic method would be

Code: Select all

CODE_048596:
lda $075A
clc
adc #1 ; or however many you need to add
cmp #max
bcc CODE_048596_store
lda #max-1
CODE_048596_store:
sta $075A
rtl
if on a 65816 and the increment amount is always 1 inc a could be used in place of clc adc if the max is 128 the cmp bcc can be replaced with bpl
since the variable is 16 bit ABS inc doesn't offer any advantages space wise over clc adc# but does take more clocks on a 6502.
Drag
Posts: 1615
Joined: Mon Sep 27, 2004 2:57 pm
Contact:

Re: When to Use CLC+ADC Instead of INC?

Post by Drag »

There might not be logic behind why the programmer used one method vs another method; it could simply be because the programmer just needed the code to work and didn't really care about whether the code was the absolute most optimal code that could be written or not.

Programming is seen both as a craft and as low-skill labor, depending on what country you're in, which field you're in, and what year it is. At this point, the people who know 65-series assembly language are likely demosceners and/or hobbyists (greetings to our neighbor C64 and 2600 sceners!), so we have a sense of what it looks like when a program has a good structure and has optimal routines.

Back in 1980 though, when 65-series CPUs were alive in a more business sense, programming was seen less as a craft and more as a "we just need the program to do the tasks we sold to the client, and that's it" kind of labor. Ergo, commercially-written software is more likely to be written to just work, rather than written to look and function optimally.

Now consider the context behind the code you're looking at right now: Super Mario Bros is an early NES game, where the NES had a 6502 in a country where that CPU wasn't common (ergo, not as much 6502 experience went into the programming of that software, combined with "programming" potentially being considered "low-skill labor", i.e., "it just needs to work"), and Super Mario All-Stars is a port of that code from 6502 to 65C816 (the latter is backwards-compatible with the former, so the code has a layer of 65C816 "patches" on top of what was originally there). That might explain some of the weird choices we, in the present day with our present knowledge of 65-series assembly as hobbyists, are seeing. :P
Garth
Posts: 246
Joined: Wed Nov 30, 2016 4:45 pm
Location: Southern California
Contact:

Re: When to Use CLC+ADC Instead of INC?

Post by Garth »

BTW, instead of

Code: Select all

    INC $075A       ;increase by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A5 ;if not, skip the rest of this routine
you can omit the CMP #$80 and just branch on the N flag.
http://WilsonMinesCo.com/ lots of 6502 resources
SMB2J-2Q
Posts: 154
Joined: Thu Jul 27, 2017 5:13 pm

Re: When to Use CLC+ADC Instead of INC?

Post by SMB2J-2Q »

Garth wrote: Thu Mar 16, 2023 2:41 am BTW, instead of

Code: Select all

    INC $075A       ;increase by one if player collected: a) 1-UP Mushroom; b) 100 coins; c) destroyed eight or more enemies with a Koopa shell or Buzzy Beetle; or d) repeatedly stomped on a Koopa shell or Buzzy Beetle at least eight times
    CMP #$80        ;see if we have more than 127 lives
    BCC CODE_0485A5 ;if not, skip the rest of this routine
you can omit the CMP #$80 and just branch on the N flag.
Garth,

You do make a point...

Code: Select all

DecNumTimer:  dec FloateyNum_Timer,x       ;decrement value here
               cmp #$2b                     ;if not reached a certain point, branch  
               bne ChkTallEnemy
               cpy #$0b                     ;check offset for $0b
               bne LoadNumTiles             ;branch ahead if not found
               lda NumberofLives            ;check lives counter
               bmi LoadNumTiles             ;branch if negative flag set (at 127)
               inc NumberofLives            ;otherwise give player one extra life (1-up)
               lda #Sfx_ExtraLife
               sta Square2SoundQueue        ;and play the 1-up sound
and...

Code: Select all

GiveOneCoin:
      lda #$01               ;set digit modifier to add 1 coin
      sta DigitModifier+5    ;to the current player's coin tally
      ldx CurrentPlayer      ;get current player on the screen
      ldy CoinTallyOffsets,x ;get offset for player's coin tally
      jsr DigitsMathRoutine  ;update the coin tally
      inc CoinTally          ;increment onscreen player's coin amount
      lda CoinTally
      cmp #100               ;does player have 100 coins yet?
      bne CoinPoints         ;if not, skip all of this
      lda #$00
      sta CoinTally          ;otherwise, reinitialize coin amount
      lda NumberofLives      ;check number of lives
      bmi CoinPoints         ;branch if negative flag set (at 127)
      inc NumberofLives      ;otherwise give the player an extra life
      lda #Sfx_ExtraLife
      sta Square2SoundQueue  ;play 1-up sound
~Ben (SMB2J-2Q)
SMB2J-2Q
Posts: 154
Joined: Thu Jul 27, 2017 5:13 pm

Re: When to Use CLC+ADC Instead of INC?

Post by SMB2J-2Q »

Drag wrote: Sun Feb 26, 2023 12:57 pm There might not be logic behind why the programmer used one method vs another method; it could simply be because the programmer just needed the code to work and didn't really care about whether the code was the absolute most optimal code that could be written or not.

Programming is seen both as a craft and as low-skill labor, depending on what country you're in, which field you're in, and what year it is. At this point, the people who know 65-series assembly language are likely demosceners and/or hobbyists (greetings to our neighbor C64 and 2600 sceners!), so we have a sense of what it looks like when a program has a good structure and has optimal routines.

Back in 1980 though, when 65-series CPUs were alive in a more business sense, programming was seen less as a craft and more as a "we just need the program to do the tasks we sold to the client, and that's it" kind of labor. Ergo, commercially-written software is more likely to be written to just work, rather than written to look and function optimally.

Now consider the context behind the code you're looking at right now: Super Mario Bros is an early NES game, where the NES had a 6502 in a country where that CPU wasn't common (ergo, not as much 6502 experience went into the programming of that software, combined with "programming" potentially being considered "low-skill labor", i.e., "it just needs to work"), and Super Mario All-Stars is a port of that code from 6502 to 65C816 (the latter is backwards-compatible with the former, so the code has a layer of 65C816 "patches" on top of what was originally there). That might explain some of the weird choices we, in the present day with our present knowledge of 65-series assembly as hobbyists, are seeing. :P
Atari's fix for the original arcade Dig Dug regarding the kill screen at round 0 (or 256) that was present in the original NAMCO release (which also had the code below intact, but simply wasn't actually used) was interesting: if the round number counter hit zero, then A was reloaded with #156 ($9C hex), so that the round number would always be between 156 and 255.

Code: Select all

CheckRoundNum:
    ret nz     ;return if not zero
    ld a, #9c  ;else load a with #9c (156 decimal)
    ld (hl), a ;store into round number's memory
    ret        ;return


Hence, I wonder if Nintendo actually contemplated rolling over the lives counter back to 29 lives for SMB1 whenever the negative flag was set while scoring extra lives via turtle-tipping, which would've kept the lives counter ($075A) between 29 and 128?

Code: Select all

CheckLives:
    inc NumberofLives    ;increment life counter by one
    lda NumberofLives    ;check lives counter to see if we have more than 128 lives
    bpl LifesGood        ;if not past 128 lives, we're good
    lda #$1c             ;otherwise reset to 29 lives
    sta NumberofLives
LifesGood:
    rts
~Ben (SMB2J-2Q)
Last edited by SMB2J-2Q on Sat Jan 13, 2024 9:27 pm, edited 1 time in total.
SMB2J-2Q
Posts: 154
Joined: Thu Jul 27, 2017 5:13 pm

Re: When to Use CLC+ADC Instead of INC?

Post by SMB2J-2Q »

Also, in cases where there may be SEC + SBC #$01, wouldn't it be easier to use LSR instead, saving two bytes?

For example:

Code: Select all

lda AreaType
sec
sbc #$01
would become

Code: Select all

lda AreaType
lsr
In most cases, LSR can be used because if result of RAM address produces a value of $02 in A, LSR can be used to divide it by 2 to get 1, and if the RAM address produces a value of $01 in A, LSR can also be used to change 1 to 0 by division. But I wonder if using LSR vs. SEC+SBC can bring any unintended consequences?

~Ben (SMB2J-2Q)
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: When to Use CLC+ADC Instead of INC?

Post by Oziphantom »

dude learn how to code and what instructions do, stop throwing spaghetti against a wall and see what "just so happens to work in this one case I found".

Case in point LSR vs SEC SBC #1 this is saying is "÷2" the same as "-1" and if you only have a value that can be 1 then use it just so happens that 2÷2 = 1, but in the general case they are not even close to the same.
Fiskbit
Posts: 891
Joined: Sat Nov 18, 2017 9:15 pm

Re: When to Use CLC+ADC Instead of INC?

Post by Fiskbit »

Oziphantom: While I agree with the sentiment, 'learn to code' is not really productive feedback and will probably make your point less likely to land.

SMB2J-2Q: I think you would benefit a lot from making use of an instruction reference, like Obelisk, which tells you exactly what instructions do so you can see if they work for your use case. As for this specific question, yes, it is often the case that two mathematical operations are equivalent in some cases while being drastically different overall. For example, 2*2 and 2+2 both equal 4, but multiplication and addition are very meaningfully different. You can substitute them if you know your inputs are 2 and 2, but you can also just hardcode the result directly into the code and probably end up with faster, safer, and maybe even smaller code. (If you know AreaType is 2, why do you need to decrement it? You can just LDA #$01.)

A lot of your questions feel to me like a matter of premature optimization. Admittedly, from a hacker's perspective, saving a byte or two can be the difference between something being possible or not, but in general, it feels like you're worrying about very minor details while you might have a lot more to learn about getting things working in the first place, and those minor details can work against your goal by distracting you and making it harder to write clear, functioning code. There are always tiny optimizations you can make to your code to make it just a little more efficient, but it's so often just not worth the effort. Optimization efforts in real code take into account the savings of the optimization, the effort required, and the impact on maintainability. If you make your code full of obtuse things like this that save almost nothing, you're making it way harder on yourself and anyone else who ever needs to look at this code to understand it and change it without breaking it.
SMB2J-2Q
Posts: 154
Joined: Thu Jul 27, 2017 5:13 pm

Re: When to Use CLC+ADC Instead of INC?

Post by SMB2J-2Q »

Oziphantom wrote: Sat Jan 13, 2024 9:43 pm dude learn how to code and what instructions do, stop throwing spaghetti against a wall and see what "just so happens to work in this one case I found".

Case in point LSR vs SEC SBC #1 this is saying is "÷2" the same as "-1" and if you only have a value that can be 1 then use it just so happens that 2÷2 = 1, but in the general case they are not even close to the same.
I think I got it settled: use CMP #$01 under LDA AreaType, where the value stored in the CMP acts as a pointer to the correct area type.

Thank you!

~Ben (SMB2J-2Q)
Post Reply