9

The 6502 has three indirect addressing modes: Indirect, Indexed Indirect (Indirect,X) and Indirect Indexed (Indirect,Y). Indirect is only used on the JMP instruction, no other. Why was this mode limited to only JMP, rather than being more generally available? It seems very useful, for example, to store a pointer to some data structure which is changed as the program runs via LDA.

Similarly, what are the design choices which lead to having both indirect,X and indirect,Y? I understand how they differ, but I don't really understand what specific purpose the designers intended each mode be used for, especially indexed indirect (which needs to store its entire table in zero page, and is often touted as "rarely-used" in "6502 intro"-type articles). It seems much more useful to be able to use indirect indexed, i.e. ($hh),Y, with both the X and Y registers instead of only Y.

1
  • I try to consider the Z.PAGE,X mode (also with LDX Z.PAGE,Y) to solve those cases not easily solved by the more oft-used addressing modes. The zero page should not be used sparingly, IMO. I carve out chunks of it as local, reusable, work space.
    – Brian H
    Dec 1, 2021 at 15:05

2 Answers 2

10

According to this programming manual from Synertek, pg.94 onward:

In solving a certain class of problems, it is sometimes necessary to have an address which is a truly computed value, not just a base address with some type of offset, but a value which is calculated or sometimes obtained as a group of addresses. In order to implement this type of indexing or addressing, the use of indirect addressing has been introduced.

then, regarding indirection through X (pg.96):

The major use of indexed indirect is in picking up data from a table or list of addresses to perform an operation. Examples where indexed indirect is applicable is in polling I/O devices or performing string or multiple string operations. Indexed indirect addressing uses the index register X.

One of the advantages of this type of indexing is that a 16-bit address can be fetched with only two bytes of memory, the byte that contains the OP CODE and the byte that contains the indirect pointer.

Regarding indexing through Y (pg.98):

The indirect indexed instruction combines a feature of indirect addressing and a capability of indexing. The usefulness of this instuction is primarily for those operations in which one of several values could be used as part of a subroutine. By having an indirect pointer to the base operation and by using the index register Y in the normal counter type form, one can have the advantages of an address that points anywhere in memory, combined with the advantages of the counter offset capability of the index register.

Thus, some insights and intentions of the 6502 designers could be grasped from this manual.

PS: all pages are in reader numbering, not the original paper book ones.

PS2: I was unable to find the original MOS Technology 6502 programming manual and I'm not sure there was any, also I don't know how this Synertek manual relates to the original one.

3
  • 2
    The original series consists of the MCS 6500 Family Hardware Manual (6500-10A), the MCS 6500 Family Programming Manual (6500-50A), the 6500 MPU Data Sheet and the MCS6500 Instruction Set Summary. --- above quotes are to be found on p 83, 85 and 87 of the 1976 programming manual.
    – Raffzahn
    Dec 1, 2021 at 13:55
  • 1
    Indexing through both X and Y might have been useful if supported, but I've yet to see a good use for indexing through X alone. If one had e.g. a table of strings whose addresses were stored in zero page, indexing through X would only allow one to access the first byte of each unless one did a read-modify-write sequence on the pointer in question.
    – supercat
    Dec 1, 2021 at 16:20
  • The value of (ind,x) addressing mode could have been enormously enhanced if bit 0 of the opcode was ignored for purposes of address computation but stored in a special latch near the A0 output pin, and if A0 would be forced high during the main memory fetch performed by an (ind,x) addressing mode if the special latch was set. This would mean that if the address of, and address in, a pointer stored in zero-page were both even, one could use it to efficiently access the upper and lower bytes of a 16-bit value pointed to thereby. Such a design could be very useful for something like FORTH.
    – supercat
    Dec 1, 2021 at 23:54
4

When looking at the 6500 ISA it's mandatory to keep in mind that it's not a middle of the road schoolbook CPU like its predecessor, the 6800, or the later 68k. It uses resources similar to, but less than, the 6800, in more specialized order to gain more versatility. Part of this is done by dropping some functionality (*1), but more often by trading orthogonality for added addressing capabilities.

The 6800 had exactly one memory addressing mode beside ZP and ABS, loading a 16 bit pointer into its only index register X and using that plus an 8 bit offset for addressing. It was maybe the most relevant idea about the 6500 ISA to drop that 16 bit register for two 8 bit ones and turn the single indexed operation into SIX different ways of addressing.

Lets be honest, wasn't that versatile way of addressing what did set the 6502 apart from all other early 8 bit ISAs?

Of course doing so comes with a cost. 6 variants for memory operations (plus immediate) eat not only up a lot of microcode space, which can only in part be reclaimed by 'compression' but also fills the opcode space. The 6500 has 21 memory operations. Having all 6 addressing modes assigned orthogonality to them would already fill up half the opcode space.

That's already a reason why not all memory operations support all addressing modes. Only modes that are thought of as useful for each are added. BIT being the most visible example, with only ZP and ABS - after all, in an embedded system ports are usually at fixed locations.

The same optimization goes for the use of index registers. They are not symmetric, but have dedicated usage. Yes, that's exactly like Intel did with the 8086 (or Zilog with the Z80): registers are not equal but have special functions where they excel. It's a common way to add more versatile functions without blowing too much resources.

The other very important point when musing about the way the 6500 ISA is built is to keep in mind that it was targeted at embedded operation, not general computation and even less personal computing, which wasn't really a thing at the time. So it's about tight RAM and ROM and fixed function, not managing huge swathes of highly dynamic data.

Indirect is only used on the JMP instruction, no other. Why was this mode limited to only JMP,

Maybe because it came (almost) for free. The ability to do an indirect jump is needed to perform reset/interrupts, as they as well fetch target vectors. Having an indirect jump is a very convenient way to build context dependent operation (e.g. state machines) (*2).

rather than being more generally available? It seems very useful,

Making it more generally available would need more opcodes, increasing the CPU size for something that can equally well be reached by using (ZP),Y addressing with Y cleared. That saves a whole set of opcodes.

Also, Rockwell introduced them as (ZP) with their 6500 line, later available with the 65C02.

Similarly, what are the design choices [...] for, especially indexed indirect [...] often touted as "rarely-used [...]

True, but it becomes useful when a table of pointers set according to machine state is to be worked on (*2). Again, think state machines, or managing a list of buffers, one for each port and direction.

Not to mention, that it not only adds another addressing mode, but enables, as a side effect, the use of (ZP,X) with X cleared as (ZP) - like before with (ZP),Y. That way either register can be 'used' for non indexed indirect while still having two different modes.

It seems much more useful to be able to use indirect indexed, i.e. ($hh),Y, with both the X and Y registers instead of only Y.

That function is already available as (ZP),X, so no need to waste opcodes doing the same (less common) operation on Y as well. Keep in mind, 6502 registers are specialized.


*1 - Like 16-bit operations and having two fully functional accumulators.

*2 - Again, think embedded operation.

6
  • 2
    I'm curious how often (zp,x) mode ends up being employed with an X value other than zero. I would think that replacing it with (zp) mode would if anything allow the chip to be a tiny bit smaller. I'm also curious whether the designers of the 6502 instruction set expected JMP (abs) and JMP to be processed with almost identical instruction-decode logic that would load temp during cycles 2 and (if present) 4, and load PCH while copying temp to PCL during cycles 3 and (if present) 5. Then the only difference between them would have been whether cycle 3 is regarded as the last cycle.
    – supercat
    Dec 1, 2021 at 20:26
  • @supercat Not really, as the same operation (add X and an 8 bit constant from instruction) must as well be available for ZP,X addressing - which is important for short code.
    – Raffzahn
    Dec 2, 2021 at 0:14
  • Supporting both (zp),y addressing and (zp,x) addressing requires having logic that can load the LSB and MSB of the target address on the third and fourth cycles, respectively, when using the former, but the fourth and fifth cycles, respectively, when using the latter. Does the 6502 manage to avoid having to use extra logic in the state decoder to accommodate that?
    – supercat
    Dec 2, 2021 at 0:21
  • 1
    Thank you, that explains a lot, especially reminding me that each mnemonic isn't one instruction, its several. Its hard for me to remember these aren't overloaded Java functions, but actual physical circuits, and more functions = more circuits. I've one further question: you mention "They [the index registers] are not symmetric, but have dedicated usage." Is it explicit what the differences are? For example, is Y to be used to iterate through a page of memory, and X is always intended to be a counter in a loop? Or are the differences more subtle than that?
    – nexus_2006
    Dec 2, 2021 at 20:59
  • @nexus_2006: The X register alone is usable for transfers to/from the stack pointer, and generally for use with zero-page indexed addressing mode (save for circuitry that replaces X-based indexing with Y-based indexing on LDX/STX). The X register is also the only one that works with the relatively useless indexed indirect (ind,x) addressing mode, and the Y register is the only one that works with the useful indirect indexed (ind),Y addressing mode. Interestingly, almost all other CPUs I've seen that combine indexing and indirection put the indexing first, and that design works well...
    – supercat
    Sep 29 at 16:14

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.