25

On the 6502 CPU, this instruction:

LDA $0380,Y

takes either 4 or 5 cycles, depending on whether the indexing crosses a page boundary. But this instruction:

STA $0380,Y

takes 5 cycles regardless of page boundaries. Why is one fixed and the other variable?

27

The 6502 had 16-bit addressing but only an 8-bit adder. For an indexed load or store, the index register had to be added to the base address in two steps. As an optimization, the 6502 will load from memory as soon as the first part of the add is complete. If the add didn't cause a carry out, the loaded value is kept. If it did cause a carry, the value is fetched again from the correct address, requiring an extra cycle.

For a write operation, speculatively storing data at the wrong address would be problematic, so the write is held until the address calculation is complete.

As noted in the "bugs & quirks" section of the wikipedia 6502 page, this behavior could cause unexpected effects if used on a memory-mapped I/O area.

13

As noted, the 6502 has only single 8-bit adder, plus a special increment circuit for the top half of the program counter. Having a full 16-bit adder would avoid the page-boundary penalty for branch instructions, but wouldn't help with indexed loads. By the time the CPU fetches the upper byte of a base address, the 8-bit ALU will have already done everything it needs to do with the lower half of the address, and would be available to compute the upper half. The reason the 6502 can't immediately issue the next access for a page-crossed address isn't that the ALU is too short, but rather that it's too slow.

Each time the 6502 receives a byte of data it must immediately turn around and supply the next address. In cases where the upper or lower byte of the next address matches the fetched data verbatim, the data can be latched and ready for the next cycle, but in all other cases the 6502 must output the next address before it has had a chance to examine the newly-fetched byte.

Compare the execution of ADC 12,X and ADC $3456,X. In either case, the 6502 will start by fetching the opcode (possibly while finishing up the previous instruction). Even while it's fetching the opcode, it knows what it will do next cycle (fetch the following byte).

While the CPU is fetching the byte following the opcode, it can look at the first byte and determine that it's going to need to add the byte that it's fetching to the value in x.

In the third cycle of either instruction, the CPU will add the value fetched in the second cycle to the value in X. When using the ADC $3456,X instruction, the CPU will have something else useful it can do at the same time: fetch the upper byte of the address. When using the ADC $12,X instruction, however, there won't be anything useful for the CPU to do while performing the address computation, so it will just perform a dummy cycle with the last thing on the bus ($12).

In either case, the fourth cycle will use the newly-calculated address along with either $00 [for the ZP,X form] or the newly-fetched upper byte [for the ABS,X form]. During that cycle, the ABS,X instruction will also compute the value of (upper byte + 1) in case that turns out to be needed, and will figure out what should happen on the cycle after that.

If it turns out that there was no carry from the lower half, then instruction execution will be complete so the 6502 can fetch the next instruction while it actually adds the newly-fetched value to the accumulator. Otherwise the 6502 will have to add another cycle to perform a fetch from the correct address.

For the 6502 to avoid a cycle penalty when an indexed load crosses a page boundary, it would have to have something useful it could do during that cycle. Adding a little sequencing logic might make it possible for the 6502 to unconditionally fetch the first byte of the next opcode during the address computation stage, but the 6502 normally uses the first instruction-fetch cycle to perform "cleanup" from the previous instruction. Pushing the cleanup to the second cycle of an instruction might be possible, but would require adding some additional internal registers and logic. I don't think a 16-bit ALU would be needed, though:

ADC $1234,X
First byte was fetched during previous instruction
Fetch second byte while completing previous instruction
Fetch third byte while computing LSB of address
Fetch first byte of next instruction while computing MSB of address
Fetch byte from computed address
Fetch second byte of next instruction while performing ADC.

Reordering operations that way would eliminate the page-boundary cycle penalty, but I don't know that the performance gain would be sufficient to justify the cost [as well as the quirky behavior that would result when switching banks while code is executing].

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.