6502 sinewave
Since I can't find my Leprechaun round tuit, I've told myself I have to work on SpaceWar! 7800 some more. One of the features I really want to add to SW78 is the starfield background (Expensive Planetarium). But the tile map is much larger than the screen, thus it needs to move in some way to show the whole thing. My original idea was to make the movement based on the spaceships wrapping around; they'd kinda drag the starfield with them. The disadvantage with this idea is there are two ships (though "fighting" to controll the starfield might be amusing) and the starfield wraps side to side but not top to bottom.
Then I had a thought - why not make the starfield drift model what it does in real life? Have the center of the screen follow a sine wave around the tile map. Even have the period of the sine wave be different than the width of the tile map so it wouldn't be a simple repeat. Nifty! Okay, but how to implement? The cheap & easy way out would be a lookup table. The main disadvantages is it would be limitted to 256 values (compared to the 1888 pixel width of the tile map) unless I went to a 16 bit pointer and used more ROM. Hopefully it could be done programatically in less space.
Of course there's this little problem - it has to run on a 6502. No FPU with built-in transecendental functions. No floating point registers. Heck, no hardware multiply or divide even. Just an 8 bit ALU with add and subtract with carry. Furthermore, I don't want to be burning up huge amounts of cycles in some fixed-point multiply function. I must be dreaming to even consider the possibility that such a thing could exits. (To be fair, I'll set my accuracy requirements low.)
To my surprise - it is that easy. The Second Order Oscillator from http://www.ied.com/~petr/Oscillators.html meets my requirements exactly.
The series y[n] = sin(n * a) can be calculated using y[n+1] = 2 * cos(a) * y[n] - y[n-1]
Wow! Perfect! 2 * cos(a) is a constant, and thus I can make it something (trivially) easy to calculate using simple adds and multi-byte fixed point arithmetic. What's more, if you scale y[n] and y[n-1] that scaling flows through to y[n+1] and all other values. So I don't even need to multiply by the height of the tile map; just set the initial values correctly.
I've done some testing using a spreadsheet and figured out that I need to use 8+16 bit fixed point with 2*cos(a) = 2 - 1/64K, so no shifts will be required, just subtract the integer portion (with sign extend) from the least significant byte. This gives a period of around 1609 pixels. I need to do some more thinking & testing to make sure the values don't overflow or decay. But it's definitely workable!
3 Comments
Recommended Comments