Jump to content
  • entries
    334
  • comments
    900
  • views
    258,304

6502 sinewave


EricBall

578 views

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

The biggest thing to watch for when using that style of sine-wave generator is to ensure that numerical roundoff errors don't cause things to go wonky. I'd suggest writing a simple program on the PC to find out what your calculations are going to do before you code them on the Atari.

Link to comment

The biggest thing to watch for when using that style of sine-wave generator is to ensure that numerical roundoff errors don't cause things to go wonky. I'd suggest writing a simple program on the PC to find out what your calculations are going to do before you code them on the Atari.

Yep... I did exactly that. Some spreadsheet testing showed that simply using the MSB for the 1/64K caused the peaks to slowly decay. I thought that fiddling with the Carry bit as a kind of rounding might work, so I made up a small C program to test it out. (More control than the spreadseet & I could let it run longer.) It works perfectly, staying within bounds and not decaying.

 

EP_Y_T	DS	3; working space for EP_Y calculation
EP_Y	DS	3; current value MSB FIRST
EP_Y_P	DS	3; previous value

EP_Y_CALC	; y[n+1] = (2-1/65536)y[n] - y[n-1]
LDX	#2
SEC
1$	LDA	EP_Y,X
SBC	EP_Y_P,X
STA	EP_Y_T,X
DEX
BPL	1$
BIT	EP_Y
BPL
SEC		; EP.Y negative, sign extend
LDA	EP_Y_T+2
SBC	EP_Y+2
STA	EP_Y_T+2
LDA	EP_Y_T+1
SBC	#-1
STA	EP_Y_T+1
LDA	EP_Y_T
SBC	#-1
STA	EP_Y_T
BIT	EP_Y_T+1
BPL	5$	; round down
SEC		; round up
BMI	6$
2$	BIT	EP_Y_T+1
BPL	3$
SEC		; round down
BMI	4$
3$	CLC		; round up
4$	LDA	EP_Y_T+2; EP.Y positive
SBC	EP_Y+2
STA	EP_Y_T+2
LDA	EP_Y_T+1
SBC	#0
STA	EP_Y_T+1
LDA	EP_Y_T
SBC	#0
STA	EP_Y_T
5$	CLC
6$	LDX	#2
7$	LDA	EP_Y,X
STA	EP_Y_P,X
ADC	EP_Y_T,X
STA	EP_Y,X
DEX
BPL	7$

 

The code isn't complex, just a little long to handle both the sign extend and to handle the rounding for both positive and negative values.

Link to comment
Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...