Speed-coding for the 6502 – a simple example(colino.net)
colino.net
Speed-coding for the 6502 – a simple example
https://www.colino.net/wordpress/en/archives/2025/08/28/speed-coding-for-the-6502-a-simple-example/
15 comments
I wonder if building this table can be sped up by noticing a recurring pattern?
x 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ... 254 255
f(x) 0 0 1 2 3 3 4 5 6 6 7 8 9 9 10 11 12 12 ... 190 191
So something like sta table,y
iny
sta table,y
adc $0
iny
sta table,y
adc $0
iny
sta table,y
adc $0
iny
used as the loop body that should be repeated 64 times, should work. Will it take less than 6000 cycles total?I didn't check your code worked.. just copied it and ran the inner part of the loop once, but according to https://www.masswerk.at/6502/
It's about 2x faster. Your code uses 44 CPU cycles x 64
Edit: plus a branch instruction, maybe that adds 3 cycles x 64 I guess
It's about 2x faster. Your code uses 44 CPU cycles x 64
Edit: plus a branch instruction, maybe that adds 3 cycles x 64 I guess
What a delightful short read.
They could go one step further and calculate the table as needed and use it as a cache.
For an single image scaling it might get a little bit better.
They could go one step further and calculate the table as needed and use it as a cache.
For an single image scaling it might get a little bit better.
If you read the entire article, they do that at the end of the article.
Not quite. They build the entire table upfront, whether any individual entry is needed or not.
Making it an on-demand cache instead is a neat next step. Whether it helps or hurts depends on the actual input: If the input image uses every pixel value anyway, the additional overhead of checking whether the table entry is computed is just unnecessary extra with no value.
But if a typical image only uses a few pixel values, then the amortized cost of just calculating the few needed table entries may very well be significantly below the cost of the current algorithm.
If images are somewhere in between, or their characteristics not well known, then simply trying out both approaches with typical input data is a good approach!
Unless you’re perfectly happy with 0.2 seconds, for example because the runtime of some other parts take so long that dwarfs those 0.2s, then why bother.
Making it an on-demand cache instead is a neat next step. Whether it helps or hurts depends on the actual input: If the input image uses every pixel value anyway, the additional overhead of checking whether the table entry is computed is just unnecessary extra with no value.
But if a typical image only uses a few pixel values, then the amortized cost of just calculating the few needed table entries may very well be significantly below the cost of the current algorithm.
If images are somewhere in between, or their characteristics not well known, then simply trying out both approaches with typical input data is a good approach!
Unless you’re perfectly happy with 0.2 seconds, for example because the runtime of some other parts take so long that dwarfs those 0.2s, then why bother.
Even if you could get the "is this cell of the table calculated?" check down to one cycle (which you can't, 6502 branch instructions take 2-4 cycles, plus a few more operations to check the table calculation status), it's still gonna add one cycle to a hot loop that's done 43k times. Pre-calcing the entire table only burns 6k cycles, and can probably be done while displaying some transitional effect that requires very little CPU.
If we presume this scaling operation will be done more than one time (which it probably will be if it's getting this level of optimization) then it gets even worse.
If we presume this scaling operation will be done more than one time (which it probably will be if it's getting this level of optimization) then it gets even worse.
Good analysis. I guess I lost my cycle-counting foo, because on modern CPUs cycle-counting is a) infeasible on a superscalar, speculative, out-of-order CPU, and b) even if you did manage to do it, it hardly matters when a CPU cycle is less than a nanosecond, but any memory access that slips through the caches means many orders of magnitude higher latency.
Of course none of that applies here, but it colors the way you think about things…
Of course none of that applies here, but it colors the way you think about things…
meanwhile the 6502 is all "cache? what's cache", every 6502 optimization is basically a pessimization for modern CPUs.
Very neat. Breaking up multiplications and divisions into bit shifts, and lookup table to trade off memory for runtime, are indeed nothing new to engineers working on the low level, but this paints a very pretty picture of how this looks in practice.
[deleted]
I wrote a token ring networking simulator with error simulation (token dropping etc) on a 6502. 128 bytes of RAM. It was a squeeze.
I used a Hewlett Packard development system with a 12" hard disk.
Good times.
I used a Hewlett Packard development system with a 12" hard disk.
Good times.
[deleted]
1) you only need the first 1/4 of the Sine table since the remaining 3/4 are either the first 1/4 in reverse and/or with the sign flipped.
2) and of course Sine can also be used as a Cosine lookup if you add pi/2 radians to the cosine angle (wrapping around of course).
3) to avoid the size needed for a table of floats you can of course use integers (scaled by some factor) or fixed-point values.
4) and simple interpolation would get you seemingly more precision.
(Combining all the above was a bit gross so documentation and a good SPI helped.)