What instructions can affect registers?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
What instructions can affect registers?
by on (#230233)
While it is common to use instructions like STA, STX and STY to modify registers, are there other instructions that can affect them? For instance, does DEC and INC have any effect on registers and do they behave as expected?
Re: What instructions can affect registers?
by on (#230235)
A lot of them. Mostly affect A (DEC A, ROL A, ROR A, ADC, SBC, AND, ASL A, TYA, TXA etc...), but also X and Y DEX, DEY, INY, INX, TAY, TAX, TSX etc...
Re: What instructions can affect registers?
by on (#230237)
Memory-mapped registers (to distinguish from CPU registers) could be modified by some Read-Modify-Write instruction like INC or LSR, but it's rare that there's already a useful value at that address to be loaded by the CPU and then written to the register.

The only wide-spread exception that I know of is with mapper registers (instead of 2A03/2C02 registers), where several games tickle the MMC1's reset functionality with a INC $FFFF.
Re: What instructions can affect registers?
by on (#230238)
I am sorry, I did not realize you are talking about memory mapped registers. I don't have exact list, but I do not recommend blindly using instructions other that ST*, because INC, for example does read/write. Some instructions may even make double read as well. For memory mapped registers, reading may produce unwanted effects where only write is actually needed.

Edit: grammar nonsense
Re: What instructions can affect registers?
by on (#230239)
Are you talking about MMIO registers (ex. $2002) or CPU registers (ex. A/X/Y)?

If the latter: sta/stx/sty don't modify CPU registers.

If the former: any opcode that does a write operation to an absolute address, directly or indirectly, can affect an MMIO register. A proper opcode list should contain a list of all of its addressing modes. Thusly, as an example, inc/dec can indeed be used to write to an MMIO register.

HOWEVER, it's very important you understand how these opcodes work under the hood (at the silicon/transistor level), on a per-cycle level. These are often called T-states (timing states). A good reference is here, though it contains some instructions for the 6510 CPU and I think some unofficial opcodes (blah, screw those). Scroll down to around line 891 for what I'm talking about.

inc/dec are what's called RMW instructions ("read-modify-write"), which means they behave in a way that is not immediately intuitive: they actually read from the destination address first, followed by writing the value read back to the same address (yes really!), followed by writing the new value desired to the address.

Why this is important: the read operation can cause oddities depending on if the MMIO register does something uniquely on a read, and the same goes for the fact that the MMIO register is written to twice. If there's a latch that increments or triggers based on read or write operation, you may find that the end result you desire is not quite what you get.

Here's an example: inc $2000. The CPU would issue a read from $2000 (it's a write-only register so I'm not sure what value you'd get back here, maybe open bus? Others can verify), followed by writing the value it got to $2000, followed by writing the incremented value to $2000.

An even scarier example would be inc $2007.

The rule of thumb is: if you aren't completely sure what the behaviour of the MMIO register is, don't use RMW instructions on them, stick with instructions which issue a single write and do not involve a read or issue two writes.
Re: What instructions can affect registers?
by on (#230240)
yaros wrote:
...affect A (DEC A,...


Wait. DEC has Accumulator addressing mode? All references I looked at only listed Zero Page, Absolute and their X variants
Re: What instructions can affect registers?
by on (#230241)
(No. DEC A is only present in later variations of the 6502, not the one inside the NES)
Re: What instructions can affect registers?
by on (#230245)
I believe reading from $2000 I think will read back from the PPU read buffer thing, so it depends on what the last values on that bus was before you use the instruction. (Basically: it's complicated and there's no reason to try and rely on this.)

Something you should avoid, though, is that using indexed / indirect / RMW instructions may do an extra read or write. On many MMIO registers it doesn't matter if you write the same value twice, for example, but if you try to STA (zp), Y to $2007 you'll end up skipping every second byte with an extra read in the instruction.

There's a cycle by cycle breakdown of instructions by addressing type here: http://nesdev.com/6502_cpu.txt

Summary:

Just use plain STA, STX, STY for most cases. STA abs, X is okay for APU (@ $4000), and might be okay for some mapper registers, if you're careful, but that question is already becoming complicated. Anything beyond this is advanced, and/or unreliable.

If you want to figure out the advanced stuff, refer to that document I linked above to see what an instruction does in terms of reads and writes. For RMW instructions like INC, ROR, etc. the value read is important, and that in itself can be complicated to determine for some things (e.g. PPU @ $2000), or other open bus behaviour. As always, mapper registers are a case by case basis, and you will need to look up the specific mapper you're using to see where they're mirrored and how they respond to individual reads and writes.
Re: What instructions can affect registers?
by on (#230248)
lidnariq wrote:
(No. DEC A is only present in later variations of the 6502, not the one inside the NES)

Sorry about that.
Re: What instructions can affect registers?
by on (#230249)
MOS Technology is the one who should be sorry about that. ;)
Re: What instructions can affect registers?
by on (#230250)
Not to go on a tangent, but aren't there some games that set / trigger mapper specific addresses with inc/dec or rol/ror?

And I mean normally ROM addresses where it wouldn't normally make sense.
Re: What instructions can affect registers?
by on (#230252)
dougeff wrote:
Not to go on a tangent, but aren't there some games that set / trigger mapper specific addresses with inc/dec or rol/ror?


Lidnariq covered the only example I know of:
lidnariq wrote:
The only wide-spread exception that I know of is with mapper registers (instead of 2A03/2C02 registers), where several games tickle the MMC1's reset functionality with a INC $FFFF.


This occurs in Bill & Ted's Excellent Adventure. There's a note about it on the wiki.

In this case it uses INC on a ROM location that holds an $FF to send that $FF to the MMC1's register to reset it. It's weird, but I guess it's smaller than LDA imm / STA abs, though not actually any faster than that. Note that the final write of the INC is actually a $00, but this instruction does an extra write of $FF before it gets incremented... and this should screw things up but apparently the MMC1 can't receive writes that fast and ignores the real (second) write, so it only gets the $FF. This is really annoying to emulate properly, and a lot of emulators will fail that game.

So... it's a dumb trick for saving 2 bytes of a 5 byte reset operation.

In cases where you need to write a constant to an MMIO that's mirrored across some range of ROM, you could INC on a ROM byte that's one less than what you need, and it will send two writes to that address (the value, then the incremented value). Again saving 2 bytes out of 5 for that operation, but with an extra write. On some mappers it'd probably be less stupid than relying on the MMC1 to ignore the second write. :P This critically depends on how the chosen mapper handles two writes in a row.


Don't know which games do this, though there are probably some. The MMC1 case is more noteworthy because it requires extra mapper emulation to compensate for the ignored write.

All the RMW instructions do a double write like this (INC, ROR, etc.), again check that document I linked above if you want reference for it. In this case look for the "Absolute addressing" section and see its Read-Modify-Write subsection.