This isn't really an answer to the question, but the comments repeatedly talk about Sophie Wilson's claim to be able to execute 100k+ ARM instructions per second. This is an impossibility on any normal 6502 and so she must have misremembered or the commenters must be misinterpreting what she said. However, it's hard to show why it is wrong in a comment.
This is a refutation of the claim and a demonstration of why it can't be true.
Take supercat's example:
add r0,r1,r2
This instruction adds the content of r2 to r1 and places the result in r0. Registers are 32 bits wide, so in the 6502, the minimum program is
CLC ; 2 cycles
LDA r1_0 ; 3 cycles for zero page
ADC r2_0 ; 3 cycles for zero page
STA r0_0 ; 3 cycles for zero page
LDA r1_1 ; 3 cycles for zero page
ADC r2_1 ; 3 cycles for zero page
STA r0_1 ; 3 cycles for zero page
LDA r1_2 ; 3 cycles for zero page
ADC r2_2 ; 3 cycles for zero page
STA r0_2 ; 3 cycles for zero page
LDA r1_3 ; 3 cycles for zero page
ADC r2_3 ; 3 cycles for zero page
STA r0_3 ; 3 cycles for zero page
38 clock cycles in total.
But note that all of the addresses are hardcoded. Using this scheme you would need 16 x 16 x 16 blocks of code just to account for all possible ways to add two registers together. You have to be a bit more clever.
Note that the last operand need not be a register, so it would make sense to fetch it from an address in a zero page location. The simplest solution for the other source and destination registers would be to do the same thing so you would end up with some code like this (note that the Y register is used to index the byte number):
CLC ; 2 cycles
LDY #0 ; 2 cycles
LDA (sourceR),Y ; 5 cycles for zero page
ADC (operand),Y ; 5 cycles for zero page
STA (destR),Y ; 5 cycles for zero page
INY ; 2 cycles
LDA (sourceR),Y ; 5 cycles for zero page
ADC (operand),Y ; 5 cycles for zero page
STA (destR),Y ; 5 cycles for zero page
INY ; 2 cycles
LDA (sourceR),Y ; 5 cycles for zero page
ADC (operand),Y ; 5 cycles for zero page
STA (destR),Y ; 5 cycles for zero page
INY ; 2 cycles
LDA (sourceR),Y ; 5 cycles for zero page
ADC (operand),Y ; 5 cycles for zero page
STA (destR),Y ; 5 cycles for zero page
70 clock cycles.
On top of that, you need to add instruction decoding. This will involve, at a minimum, reading all four bytes of the instruction and because it is specified by the PC, you must do four zero page indirect loads and increment the PC either between each load or add four to it at the end (that's a 32 bit add).
It would look something like this:
LDY #0 ; 2 cycles
LDA (r15),Y ; 5 cycles (or 6)
; Do something ? cycles
INY ; 2 cycles
LDA (r15),Y ; 5 cycles (or 6)
; Do something ? cycles
INY ; 2 cycles
LDA (r15),Y ; 5 cycles (or 6)
; Do something ? cycles
INY ; 2 cycles
LDA (r15),Y ; 5 cycles (or 6)
; Do something ? cycles
CLC ; 2 cycles
LDA r15 ; 3 cycles
ADC #4 ; 2 cycles
STA r15 ; 3 cycles
BCC endIncr15 ; 2 or 3 cycles
INC r15+1 ; 5 cycles
BNE endIncr15 ; 2 or 3 cycles
INC r15+2 ; 5 cycles
BNE endIncr15 ; 2 or 3 cycles
INC r15+3 ; 5 cycles
A minimum of 38 cycles, assuming that adding 4 to the PC does not cause a carry.
We are up to a total of at least 108 clock cycles without even considering the decoding logic. Thinking about that, just to get the registers we need to do a mask (2 cycles) and two shifts (4 cycles) for each one to multiply the register number by four to get its address (NB the destination register is held in the top four bits of the third byte of the instruction, so actually, we need to divide it by four). That's another 18 cycles.
On a 4MHz 6502, we are already down to a maximum theoretical ARM speed of around 30,000 instructions per second for the simplest possible add instruction.
Update:
superheat has come up with an optimisation for the add which is a compromise between my original all hard coded example and the generalised zero page indirect example.
You can have 16 copies of the following
CLC ; 2 cycles
LDA regByte0,X ; 4 cycles
ADC regByte0,Y ; 4 cycles
STA regDestByte0 ; 3 cycles (assuming in zero page)
LDA regByte1,X ; 4 cycles
ADC regByte1,Y ; 4 cycles
STA regDestByte1 ; 3 cycles (assuming in zero page)
LDA regByte2,X ; 4 cycles
ADC regByte2,Y ; 4 cycles
STA regDestByte2 ; 3 cycles (assuming in zero page)
LDA regByte3,X ; 4 cycles
ADC regByte3,Y ; 4 cycles
STA regDestByte3 ; 3 cycles (assuming in zero page)
which comes in at 46 cycles instead of 70. X
and Y
are used as the the register numbers (or 2x or 4x of the register number depending on how the bytes of the registers are organised in memory).
However, you wouldn't use this in practice because one of the source operands can be far more general than the content of a register. It's far more likely that you would calculate the effective address of the second operand, put it in a zero page location and then indirectly address off that.
And of course, none of the solutions addresses the fact that we have hard coded an ADC
between the load and the store. In reality, you'd do a JMP
through a vector table (5 extra cycles) and then a JMP
back to the relevant store (3 cycles) and you'd probably fetch both operands into two zero page locations, do the calculation into another zero page location and then store all four bytes back to the specified register, especially if you weren't deliberately optimising for speed.
So, although supercat's optimisation would improve on my upper estimate of 30k operations per second, it wouldn't be enough to get to 100k and it does not take into account (neither did may example) the fact that generalising to more than just adding adds a whole lot more overhead that makes my 30k look optimistic.