# Going in Circles

The math for moving an object around in a circle is quite straightforward, as we can use a couple of functions SIN and COS, which calculate the value of a position on a sine wave for a given angle, returning values between 0 and 1, here is the output of sin for angles 0-45 degrees!

`sin(0°) = 0 sin(16°) = 0.275637 sin(32°) = 0.529919 sin(1°) = 0.017452 sin(17°) = 0.292372 sin(33°) = 0.544639 sin(2°) = 0.034899 sin(18°) = 0.309017 sin(34°) = 0.559193 sin(3°) = 0.052336 sin(19°) = 0.325568 sin(35°) = 0.573576 sin(4°) = 0.069756 sin(20°) = 0.34202 sin(36°) = 0.587785 sin(5°) = 0.087156 sin(21°) = 0.358368 sin(37°) = 0.601815 sin(6°) = 0.104528 sin(22°) = 0.374607 sin(38°) = 0.615661 sin(7°) = 0.121869 sin(23°) = 0.390731 sin(39°) = 0.62932 sin(8°) = 0.139173 sin(24°) = 0.406737 sin(40°) = 0.642788 sin(9°) = 0.156434 sin(25°) = 0.422618 sin(41°) = 0.656059 sin(10°) = 0.173648 sin(26°) = 0.438371 sin(42°) = 0.669131 sin(11°) = 0.190809 sin(27°) = 0.45399 sin(43°) = 0.681998 sin(12°) = 0.207912 sin(28°) = 0.469472 sin(44°) = 0.694658 sin(13°) = 0.224951 sin(29°) = 0.48481 sin(45°) = 0.707107`

Cos will output a similar set of numbers as sin but at 90 degrees offset, therefore using sine and cos we could write something like this to get the position on a circle.

x = sin(angle)
y = cos(angle)

However it will be a very small circle as all the values are floating point values between 0 and 1, so on a game screen we would not see any movement due to the pixel resolution. Luckily there is a simple fix, we just need to multiply the values by the radius of our circle.

So now if we draw a sprite at the x and y position, and then add say 10 to the angle then draw another sprite and so on, we will see a circle forming!

for angle= 0 to 360 step 10
drawSprite(x,y)
loop

This would be great if we were writing in a higher language and did not care about speed, however floating point in z80 on the Spectrum next is slow and would not be suitable for a game engine, therefore we have to come up with a faster solution.

## Tables, Tables and Tables

Pre Calculating the sine and cosines and storing them in a table before we run our code would speed things up greatly, however representing a floating point number in z80 is over complex as doing floating point math in z80 is hard and there is a much better solution.

## Enter Fixed Point

A fixed-point number representation is a data type for a number that has a fixed number of digits before the point and a fixed number of digits after the point, which sounds simple but how do we do that in z80?

Remember the numbers were between 0 and 1 which means they only need a few bits before the point and we can use the rest of the bits as after the point for the fractions, so we could represent our sine and cosine values as a 2.6 fixed point number having 2 bits before the point and 6 fractional bits after the point.

Here are Sin and Cos tables in a 2.6 fixed point and some sin and cos functions in z80.

```sinTable:	.db	\$00,\$02,\$03,\$05,\$06,\$08,\$09,\$0b,\$0c,\$0e
.db	\$10,\$11,\$13,\$14,\$16,\$17,\$18,\$1a,\$1b,\$1d
.db	\$1e,\$20,\$21,\$22,\$24,\$25,\$26,\$27,\$29,\$2a
.db	\$2b,\$2c,\$2d,\$2e,\$2f,\$30,\$31,\$32,\$33,\$34
.db	\$35,\$36,\$37,\$38,\$38,\$39,\$3a,\$3b,\$3b,\$3c
.db	\$3c,\$3d,\$3d,\$3e,\$3e,\$3e,\$3f,\$3f,\$3f,\$40
.db	\$40,\$40,\$40,\$40,\$40

cosTable:	.db	\$40,\$40,\$40,\$40,\$40,\$40,\$3f,\$3f,\$3f,\$3e
.db	\$3e,\$3e,\$3d,\$3d,\$3c,\$3c,\$3b,\$3b,\$3a,\$39
.db	\$38,\$38,\$37,\$36,\$35,\$34,\$33,\$32,\$31,\$30
.db	\$2f,\$2e,\$2d,\$2c,\$2b,\$2a,\$29,\$27,\$26,\$25
.db	\$24,\$22,\$21,\$20,\$1e,\$1d,\$1b,\$1a,\$18,\$17
.db	\$16,\$14,\$13,\$11,\$10,\$0e,\$0c,\$0b,\$09,\$08
.db	\$06,\$05,\$03,\$02,\$02

//----------------------------------------------------------------------
// sin	function
//
// in	a 	degrees
//
// out	e 	contains the sine 2.6 format
//
// dirty	hl,de
//----------------------------------------------------------------------

sin:		ld	h,0				// clear the high part of hl
ld	l,a				// move the angle into the low part of hl
ld	de,sinTable			// get the base of the sin table
add	hl,de				// add the angle to the base to get the index into the sin Table
ld	e,(hl)				// get the SIN
ret					// e contains the sine in 2.6 format

//----------------------------------------------------------------------
// cosine function
// in	a	degrees
//
// out	e 	contains the cosine 2.6 format
//
// dirty	hl,de
//----------------------------------------------------------------------

cos:		ld	h,0				// clear the high part of hl
ld	l,a				// move the angle into the low part of hl
ld	de,cosTable			// get the base of the cos table
add	hl,de				// add the angle to the base to get the index into the cos Table
ld	e,(hl)				// get the COS
ret					// e contains the sine in 2.6 format
```

Notice the tables are very small (64 bytes each) and therefore not every angle in the 360 degree circle can be represented, which means I have limited my angles to 256 angles so the angle can fit into 8 bits also note that I have divided my circle into 4 quadrants hence 64 bytes for each of the tables.

360/256 gives you an angle step of 1.4 degrees give or take

To get the x and y coordinates in z80 is just a matter of working out which quadrant you are in, read the tables, and bit shifting them down 6 bits after the radius calculations are done.

Here is the code to do the magic, it could probably be a little faster, but hey it works!

```// ----------------------------------------------------------------------
// position on the edge of a circle
// input		radius -  store the radius in this variable before calling
// input		a,angle
// input		bc y center of the circle
// input		de x center of the circle

// output		bc x position on the circle
// output		de y position on the circle
//
// dirty everything
//----------------------------------------------------------------------

posOnCircle:	cp	64				// ae we less than 64
jp	m,A_0_64			// yes but for some reason its not right so we will need another check
cp	128				// are we between 64 and 128
jp	m,A_64_128			// yup
cp	192				// are we between 128 and 192
jp	m,A_128_192			// yarp so do that code
ret
a_192_256:	push	bc				// save the x center
sub	192				// take off 192 making it 0-64
ld	b,a				// now reverse the angle
ld	a,64				// making it
sub	b				// 64-0
push	de				// save the y center
call	getSinCos			// get the cos and sin for this angle
pop	hl				// get the y center
sub	hl,bc				// take y center from the cos result
ld	bc,hl				// and stick it in y return pos
pop	hl				// now get the x center
ld	de,hl				// stick it in the x return pos
ret					// and return
A_128_192:	sub	128
push	bc
push	de
call	getSinCos			// get the cos and sin for this angle
pop	hl				// get the y center
sub	hl,bc				// take y center from the cos result
ld	bc,hl				// and stick it in y return pos
pop	hl				// now get the x center
sub	hl,de				// take off the sin result
ld	de,hl				// stick it in the x return pos
ret
A_64_128:		push	bc
sub	64
ld	b,a
ld	a,64
sub	b
push	de
call	getSinCos			// get the cos and sin for this angle
pop	hl				// get the y center
ld	bc,hl				// and stick it in y return pos
pop	hl				// now get the x center
sub	hl,de				// take off the sin result
ld	de,hl				// stick it in the x return pos
ret
A_0_64:		bit	7,a				// test to see if its negative by bit testing bit 7
jp	nz,a_192_256
push	bc
push	de
call	getSinCos				// get the cos and sin for this angle
pop	hl				// get the y center
ld	bc,hl				// and stick it in y return pos
pop	hl				// now get the x center
ld	de,hl				// stick it in the x return pos
ret
//----------------------------------------------------------------------
// calculate the x y pos
//----------------------------------------------------------------------
getSinCos:	call	sin				// get the sin(angle) in e
mul	d,e				// multiply sin by the radius
ld	b,6				// 2.6 fixed point
bsrf	de,b				// do down shift the result
push	de				// save x pos
call	cos				// get the cos(angle) in e
mul	d,e				// multiply sin by the radius
ld	b,6				// 2.6 fixed point
bsrf	de,b				// do down shift the result
pop	bc				// get the x pos
ret	```

For other types of games you could easily modify the above code, to have a different radius for the x and the y which would give you ellipses instead of circles.

I have ripped this out of my game code for my shoot em up i am writing, as you can see I am increasing the radius of the sprites in the circles!

Enjoy!

## 3 thoughts on “Going in Circles”

1. Jake B says:

Thanks Patricia, this is another excellent gift to the community. Look forward to getting my head around it (just got VS CODE environment working and am now building a linked list system – thank to your previous posts!).

2. Andrew Seed says:

As the cos and sin differ by 90 degrees you need a sine table of 0 to 360+90 – where 90 to 36+90 is the entries from the sin table.
As you are using speccy Next instructions you sin and cos can be written

sin: ld hl,sine_table
ld (hl),a
ret

and the same for cos

cos: ld hl,cos_table
ld (hl),a
ret
If you stick to 256 byte alignment for the tables

sin: ld h,sin_table>>8
ld l,a
ld (hl),a
ret

3. patricia curtis says:

I found a bug in the comments detailing the calling convention, please see the comment at the top of posOnCircle its been updated.