Search code examples
assembly65816

Trying to understand some 65c816 assembly code—what is the purpose of this ordering of instructions?


I'm new to assembly and trying to piece together what this piece of code is doing. Here's the Super NES 65c816 reference I'm using.

01: $A4917E AD E5 05    LDA $05E5  [$A4:05E5]
02: $A49181 29 3F 00    AND #$003F
03: $A49184 AA          TAX
04: $A49185 AD E5 05    LDA $05E5  [$A4:05E5]
05: $A49188 89 02 00    BIT #$0002
06: $A4918B D0 05       BNE $05    [$9192]
07: $A4918D 8A          TXA
08: $A4918E 49 FF FF    EOR #$FFFF
09: $A49191 AA          TAX
10: $A49192 8A          TXA
11: $A49193 18          CLC
12: $A49194 6D 7A 0F    ADC $0F7A  [$A4:0F7A]
13: $A49197 85 12       STA $12    [$00:0012]
14: $A49199 AD E5 05    LDA $05E5  [$A4:05E5]
15: $A4919C 29 00 1F    AND #$1F00
16: $A4919F EB          XBA
17: $A491A0 85 14       STA $14    [$00:0014]

What I know already is that $05E5 is the storage location of the game's random number generator (RNG). It's a 16-bit value. What I'm trying to figure out is what kind of math the game's trying to do with this random number.

Here's a more trivial example, for starters:

$86AE1C AD E5 05    LDA $05E5  [$86:05E5]
$86AE1F 29 01 00    AND #$0001
$86AE22 F0 05       BEQ $05    [$AE29]

Here it's clearly just using the random number for a coin flip—the highest entropy bit being 0 or 1—to decide to branch or not.

Getting back to the original example, let me say what (I think) I understand, in pieces:

01: $A4917E AD E5 05    LDA $05E5  [$A4:05E5]
02: $A49181 29 3F 00    AND #$003F
03: $A49184 AA          TAX

This is loading the random number into the accumulator, say 11010101001100010, and AND'ing it to 0000000000111111, yielding 0000000000100010 in the accumulator, which it then transfers to register X.

04: $A49185 AD E5 05    LDA $05E5  [$A4:05E5]

Next it loads the same random number into the accumulator—I guess because we lost it during the AND operation? (The random number is generated elsewhere, executing once approximately every 1/60th of a second. But it's guaranteed to be the same here.)

05: $A49188 89 02 00    BIT #$0002
06: $A4918B D0 05       BNE $05    [$9192]

Here's where it gets fuzzy for me. I read multiple resources online (though not specific to the 65816) saying BIT is like an AND but without mutating either operand. But it seems like there are some nuances having to do with flags.

  1. Continuing with my example, am I correct to believe that BIT #$0002 on the accumulator value of 11010101001100010 would be 1, because the second lowest bit is 1?

  2. Am I correct to believe that as a result of the bit test, the z flag's value will have been set to 0, precisely the opposite of the previous result?

  3. Am I correct to believe that it will then branch (to instructions at address $05), since Z=0?

Okay, if I've gotten those fundamentals down, then these are my real questions:

  • Why wouldn't the developers (who in those days optimized everything) have swapped lines 01-03 with 04-06? That is, if there's a chance you'll end up jumping to some totally different piece of code, why not test and do the jumping first, and only if not branching, do the AND operation? (Wait, is it possible register X might be used wherever the code jumps to?)

  • Could someone help me understand the next few instructions? In particular, what is the point of executing TAX then TXA? Isn't that just swapping then unswapping values?

Sorry this is many questions in one, but hopefully it's okay because I'm referring to the same set of instructions. Thanks in advance for any assistance.

P.S. Here's the page about BIT in a textbook I have about the 65816, in case it helps.

enter image description here

I could not understand the paragraph starting, "BIT is usually used immediately preceding a conditional branch instruction:", though it seems relevant...


Solution

  • Answering one of the questions about these lines

    06: $A4918B D0 05       BNE $05    [$9192]
    07: $A4918D 8A          TXA
    08: $A4918E 49 FF FF    EOR #$FFFF
    09: $A49191 AA          TAX
    10: $A49192 8A          TXA
    

    Why is TAX followed by TXA?

    It is because of the branch at line 6, which if taken will execute from line 10.

    The TAX shown is matching the previous TXA (not the following one) as in

    07: $A4918D 8A          TXA
    08: $A4918E 49 FF FF    EOR #$FFFF
    09: $A49191 AA          TAX
    

    Some whitespace or labels in the code would make this "phrasing" clearer, although the branch does give the destination address.


    Questions 1-3 about the Z flag: you are correct in saying that the Z flag is set if the result being tested is 0.

    1. Am I correct to believe that it will then branch (to instructions at address $05), since Z=0?

    Yes, but the $05 is not an address, it is a signed displacement (from the instruction that would have followed, because the PC program counter (aka IP instruction pointer) would already have been advanced by the time the decision is taken).