4 ms·
The 6502 decodes instructions by matching the combination of opcode byte + cycle counter against bitmasks stored in a “ROM”. The ROM outputs signals to the cont
by NobodyNada 3y ago
The 6502 decodes instructions by matching the combination of opcode byte + cycle counter against bitmasks stored in a “ROM”. The ROM outputs signals to the control logic telling it to do things like accessing a register, performing an ALU operation, accessing memory, advancing to the next instruction, etc.
This design meant the designers could efficiently reuse functionality across multiple instructions by assigning them similar opcodes, and creating a decode ROM entry that matches all of those opcodes. This becomes clearer if you look at an opcode matrix: https://www.nesdev.org/wiki/CPU_unofficial_opcodes https://www.nesdev.org/wiki/CPU_unofficial_opcodes
For example, from a brief glance at the chart I noticed patterns like:
- All of the instructions matching the bit pattern `xxx111xx` read an operand from memory using the ‘absolute, y’ addressing mode
- All instructions matching the bit pattern `xxxyyy01` perform an ALU operation, where xxx specifies an operation and yyy specifies an addressing mode (with the exception of xxx=100, which performs a store rather than an ALU op)
- Branch instructions all match the pattern `xxx10000`, where xxx specifies the predicate
- … and many more similar patterns.
The decode ROM looks at the opcode and says “if these bits are 1s and those bits are 0s, do this on that cycle”. So if you feed it an opcode that is not valid, then it activates arbitrary bits of logic based on whatever patterns in the decode ROM your opcode happened to trigger. The result is that the processor behaves strangely, combining bits and pieces of logic meant for other, sometimes unrelated instructions. Some opcodes cause the CPU to lock up entirely, because they don’t match any patterns in the decode ROM that trigger the “go to the next instruction” logic.
Understanding how instruction decoding works will hopefully help reframe questions about undocumented opcodes:
- “Why did they add undocumented opcodes?” They didn’t add them, they exist by happenstance — feed the instruction decoder garbage input, you get garbage output.
- “Why didn’t they just make them all NOPs/crashes/aliases of existing instructions?” Because that would have required an enormous amount of extra logic on the decode ROM — now your decoder needs to support 256 opcodes rather than ~160, and it can’t do nearly as many partial-matching shortcuts. On a processor that only has ~3,000 transistors, you’re not going to add hundreds more to decode opcodes that programs will never (intentionally) use.
- “Why didn’t they just document them?” Because 1) they didn’t add them on purpose in the first place, so they may not have thoroughly understood their behavior & it may have changed throughout development/hardware revisions 2) most of them don’t do anything useful, 3) many of them behave in very strange and unpredictable ways, and 4) they wanted to leave the opcode space open so that future backwards-compatible processors could use them to do actually useful things.