This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Simple switch trick

Simple switch trick
by on (#28688)
I wonder something about the simple switch trick on the 6502. As simple switches can be slightly annoying to code in assembly, a trick is to use the CMP instruction to skip what would be the "else" statement in C :
For example, let's do a piece of code which eturns 01 if the thing to test is positive, and $02 else. The "clean" way to do it is :
Code:
 lda something_to_test
 bmi _else
 lda #$01
 jmp _endif
_else
 lda #$02
_endif
 rts

However, doing it in a trickier way in order to save 2 bytes and one label (labels are annoying to define), the trick way is to do that.
Code:
 lda something_to_test
 bmi _else
 lda #$01
 .db $cd (cmp $xxxx opcode)
_else
 lda #$02
 rts


Now what I have been wondering if that the cmp instruction does a dummy read to somewhere. There is nothign bad about it, exept if the read accidentally $2002 or $2007, or one of their numerous mirrors (for example reading $38a7 would also read $2007). In that case the results could be catastrophic, as the screen would be gabraged.

Since the next instruction (to skip) is always a two byte instruction, the one performing this trick has to be very carefull about the argument of the two byte instruction in question. If the argument is included between $20 and $3f, he has to become carefull. Something as innocent as sta $20 for example would in fact read $2085 (mirror $2005) which normally have no effect, but I'd still avoid reading it. Luckily, no two byte instruction has $7 or $e as their last nyble of their opcode, making reading accidentally $2007 technically impossible (maybe I missed one tough). But something like ldx $20 would read $20a2 (so $2002) and this could possibly affect the PPU during rendering.
Am I correct to worry about such things or am I imaginating things.

by on (#28689)
Reading 2002 would clear the vblank flag, that's about it. I bet reading joystick input would have a much greater effect, but usually joystick reading is done all at once.
That's pretty cool, I hadn't thought of that trick. It works much better on the Z80, since you can just use a conditional jump with an opposite condition.
Re: Simple switch trick
by on (#28691)
Bregalad wrote:
(labels are annoying to define)


Which is exactly why I can't live without nameless (or at least local) labels anymore. Having to give unique names to every single label regardless of how minor it is just isn't practical.

Anyway your trick is pretty clever. This kind of thing can lead to really hard to read code, though, as well as problems like undesirable reads (like you mention)


Either/or checks like this can usually be done pretty simply by returning the C flag state rather than setting A. From there you can use C in some calculations to get the desired value of A -- or perhaps use a LUT if calculations would be impractical.

How I would probably approach that same situation:

Code:
  LDA something_to_test
  CLC
  BPL :+
    SEC
: LDA #$00
  ADC #$01
  RTS


Of course since you're checking a single bit position this could be optimized further without needing any branches:

Code:
  LDA something_to_test
  ASL A         ; move N to C
  LDA #$02
  SBC #$00
  RTS


But these methods probably aren't practical in most real world situations. In which case I would probably lean towards the use of a LUT:

Code:
mylut:
  .db $01, $02

myroutine:
  LDX #$00
  LDA something_to_test
  BPL :+
    INX
: LDA mylut,X
  RTS


Granted... this isn't quite as compact as your trick.

by on (#28696)
Unnames labels rock, exept for switches as shown as above because they have to be nested and that becomes a headache. Finding label's name can be a headache as well. However, unnamed labels really REALLY rock for loops, where they are never nested (they are hierarchised instead).

Yes, in the case of the $01/$02 switch there is plenty of way to do this, but you may want to read two different variables instead of just switching between two constants (I actually do this in my current project's game engine). In that case you don't do it just with the carry. And that may not be a routine that returns a value, as shown in the example, but a piece of code directly using the constant (or variables) for actual computing.

Reading the controller could have some effect, but I don't think it would be so bad, anyways.

by on (#28967)
I was just viewing SMB's disassembly, and it actually does this trick!

Code:
Bridge_High:
      lda #$06  ;start on the seventh row from top of screen
      .db $2c   ;BIT instruction opcode

Bridge_Middle:
      lda #$07  ;start on the eighth row
      .db $2c   ;BIT instruction opcode

Bridge_Low:
      lda #$09             ;start on the tenth row


waddaya know!

by on (#28969)
It's a pretty common technique on other processors too. You basically want a one-byte branch instruction, so you use a multi-byte instruction which has a benign effect. There are all those two-byte and three-byte unofficial NOPs on the NES that I know Lolo 3 uses one of, though they probably do some kind of memory access. If the 6502 didn't pointlessly set the flags for lda #, you could do this:
Code:
    cmp ...
    lda #value1
    b... skip
    lda #value2
skip:

This works if your branch is based on the C or V flags, since lda # doesn't affect them. You could also use X or Y as a workaround:
Code:
    ldx #value1
    cmp ...
    b... skip
    ldx #value2
skip:
    txa ; if even necessary

It would be interesting to check all the unofficial NOPs in case there's one that doesn't do any extra memory accesses.