5

I have a kernel in my 6502 game that writes two dots to a sprite, so for example:

........
........
.XX..XX.
.XX..XX.
........
........

Either 0, 1, or 2 dots can be on at a time. This is done one scanline at a time, so we're only looking at how to write the middle two lines.

So far I've implemented this by doing the following:

  1. The accumulator is set to 0b01100000
  2. X is set to 0b00000110
  3. For the left dot, I use STA $1B (3 cycles) to only turn that dot on.
  4. For the right dot, I use STX $1B (3 cycles) to turn that dot on.
  5. For both dots, I can use the undocumented instruction SAX ($87) to write A | X to the value, so SAX $1B (3 cycles) to turn both on.

But if I need to turn both dots off, the quickest way I know of to write 0b00000000 to a register is to store $00 in Y and then write it. Of course, this means I can't use Y for other purposes in my code.

I don't need a trick that takes 3 cycles (4 or 5 are fine) but are there any possibilities, without consuming a register, to write 0x00 to a memory location in zero page on the 6502?

EDIT: The above description of the problem is incorrect (see comments). I actually set A=01100000, X=00000110, Y=01100110, and use SAX to write $00. The question as posed (writing 0x00 to a register without storing 0x00 in a register) is still answered in the replies.

  • It's late so I'm probably being an idiot but why is SAX not writing zero? The values you say are in A and X have no set bits in common. – Tommy Jul 9 '18 at 2:13
  • I'm not familiar with the 6502, but a common trick on x86 was to XOR a value with itself (x XOR x = 0, for any x; on x86, a register-to-register XOR encoded in two bytes instead of three for a 16-bit register store, so you'd save a byte by doing so). Might that work on the 6502? – a CVn Jul 9 '18 at 8:57
  • @MichaelKjörling alas there are no register-to-register XORs (/EORs) on the 6502. Unrelated: the 65C02 added STZ, store zero, which would have been perfect here. – Tommy Jul 9 '18 at 13:30
  • 3
    Also, I really think SAX writes the and, not the or. E.g. oxyron.de/html/opcodes02.html or, to quote nesdev.com/6502_cpu.txt : "Many undocumented commands do not use AND between registers, the CPU just throws the bytes to a bus simultaneously and lets the open-collector drivers perform the AND. I.e. the command called 'SAX', which is in the STORE section (opcodes $A0...$BF), stores the result of (A & X) by this way." (though that should be e.g. not i.e.) – Tommy Jul 9 '18 at 13:32
  • 2
    Not directly addressing the question of a faster way to write 0, but is it maybe an option to reformulate and instead put the desired dot bit pattern %00, %01, %10, %11 in an index register, and do a lookup into a 4 value table translating it to %00000000, %00000110, % 01100000, %01100110 ? – Retrograde Jul 9 '18 at 13:42
5

My second bite of the cherry: another of the undocumented opcodes is SYA/SHY/SAY which will:

AND Y register with the high byte of the target address of the argument + 1. Store the result in memory.

If you were willing and able to switch your usage of X and A then you could have 01100000 in X, therefore the argument you supply to SAY would be 001b - b01100000 = ffbb:

9C bb ff          ; SAY $ffbb,x

So that would store Y & (ff+1) to ffbb + b01100000, i.e. it would store Y & 0 = 0 to 1b, in five cycles. Your Y can hold anything, and isn't affected.

  • Thank you! Using SAY and XAS make this possible! ldx #%01100110; ldy #00 then .byte $9e, GRP0, #%000001110 for right dot, .byte $9e, GRP0, #%111100000 for left dot, stx GRP0 for both dots, and sty GRP0 for neither. Accumulator is unaffected :) – trim Jul 11 '18 at 0:46
  • Glad I could help eventually, even if only obliquely! – Tommy Jul 11 '18 at 2:08

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.