8

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.

9
  • 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, 2018 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?
    – user
    Jul 9, 2018 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, 2018 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, 2018 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, 2018 at 13:42

1 Answer 1

9

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.

2
  • 1
    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, 2018 at 0:46
  • Glad I could help eventually, even if only obliquely!
    – Tommy
    Jul 11, 2018 at 2:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .