4 ms·
It could be related to the fact that if an instruction was not handled at all the CPU would lock up (search https://www.righto.com/2016/02/reverse-engineering-a
by wzdd 3y ago
It could be related to the fact that if an instruction was not handled at all the CPU would lock up (search https://www.righto.com/2016/02/reverse-engineering-arm1-instruction.html https://www.righto.com/2016/02/reverse-engineering-arm1-inst... for "kill"), so rather than add extra logic for illegal instructions the designers just decided to add undocumented ones.
The only problem with this theory is that there are in fact several opcodes which will make a 6502 lock up...
- anticensor 3y agoThey could have wired those instructions as a NOP, rather than aliasing another opcode...
- Someone 3y agoThey could, but transistors were expensive at the time. Why spend valuable space making sure all instructions are well defined? On modern CPUs designed for multi-processing and protected memory, you don’t want some instructions to accidentally cross privilege boundaries (can’t have an ‘illegal’ opcode accidentally be non-privileged and modify some privileged processor state), so you have to do some of that. Transistors also are cheap, so you can afford to.
- NobodyNada 3y agoThe 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.