]> git.sur5r.net Git - cc65/blob - libsrc/common/cc65_sincos.s
Merged the sine/cosine routines into one file, because they're often used
[cc65] / libsrc / common / cc65_sincos.s
1 ;
2 ; Fixed point cosine/sine functions.
3 ;
4 ; Returns the cosine/sine for the given argument as angular degree.
5 ; Valid argument range is 0..360 for both functions. They will return
6 ; garbage if the argument is not in a valid range. Result is in 8.8 fixed
7 ; point format, so $100 is 1.0 and $FF00 is -1.0.
8 ;
9 ;
10 ; Ullrich von Bassewitz, 2009-10-29
11 ;
12
13         .export         _cc65_cos, _cc65_sin
14
15
16 ; ---------------------------------------------------------------------------
17 ; Sinus table covering values from 0..86° as 0.8 fixed point values. Values
18 ; for 87..90° are actually 1.0 (= $100), will therefore not fit in the table
19 ; and are covered specially in the code below.
20
21 .rodata
22
23 _cc65_sintab:
24         .byte   $00, $04, $09, $0D, $12, $16, $1B, $1F, $24, $28
25         .byte   $2C, $31, $35, $3A, $3E, $42, $47, $4B, $4F, $53
26         .byte   $58, $5C, $60, $64, $68, $6C, $70, $74, $78, $7C
27         .byte   $80, $84, $88, $8B, $8F, $93, $96, $9A, $9E, $A1
28         .byte   $A5, $A8, $AB, $AF, $B2, $B5, $B8, $BB, $BE, $C1
29         .byte   $C4, $C7, $CA, $CC, $CF, $D2, $D4, $D7, $D9, $DB
30         .byte   $DE, $E0, $E2, $E4, $E6, $E8, $EA, $EC, $ED, $EF
31         .byte   $F1, $F2, $F3, $F5, $F6, $F7, $F8, $F9, $FA, $FB
32         .byte   $FC, $FD, $FE, $FE, $FF, $FF, $FF
33
34
35
36 ; ---------------------------------------------------------------------------
37 ; Cosine function. Is actually implemented as cos(x) = sin(x+90)
38
39 .code
40
41 _cc65_cos:
42
43 ; cos(x) = sin(x+90)
44
45         clc
46         adc     #90
47         bcc     @L1
48         inx
49
50 ; If x is now larger than 360, we need to subtract 360.
51
52 @L1:    cpx     #>360
53         bne     @L2
54         cmp     #<360
55 @L2:    bcc     _cc65_sin
56
57         sbc     #<360
58         bcs     @L3
59         dex
60 @L3:    dex
61
62 ; ---------------------------------------------------------------------------
63 ; Sine function. Uses
64 ;
65 ;       table lookup            for 0..89°
66 ;       sin(x) = sin(180-x)     for 90°..179°
67 ;       sin(x) = -sin(x-180)    for 180..360°
68 ;
69 ; Plus special handling for the values missing in the table.
70
71 _cc65_sin:
72
73 ; If the high byte is non zero, argument is > 255
74
75         cpx     #0
76         bne     L3
77         cmp     #180
78         bcs     L4
79
80 ; 0..179°
81
82         cmp     #90
83         bcc     L1
84
85 ; 90..179°. Value is identical to sin(180-val). Carry is set on entry.
86 ;
87 ;       180-val := -val + 180.
88 ; With
89 ;       -val := (val ^ $FF) + 1
90 ; we get
91 ;       180-val = (val ^ $FF) + 1 + 180
92 ; Since carry is set, we can drop the "+ 1".
93 ;
94
95         eor     #$FF
96         adc     #180            ; 180-val
97
98 ; 0..89°. Values for 87..90° are actually 1.0. Since this format doesn't fit
99 ; into the table, we have to check for it manually.
100
101 L1:     cmp     #87
102         bcc     L2
103
104 ; The value is 1.0
105
106         ldx     #>(1 << 8)
107         lda     #<(1 << 8)
108         rts
109
110 ; 0..86°. Read the value from the table.
111
112 L2:     tay
113         ldx     #0
114         lda     _cc65_sintab,y
115         rts
116
117 ; 180..360°. sin(x) = -sin(x-180). Since the argument is in range 0..180
118 ; after the subtraction, we don't need to handle the high byte.
119
120 L3:     sec
121 L4:     sbc     #180
122
123         cmp     #90
124         bcc     L5
125
126 ; 270..360°. Value is identical to -sin(180-val). Carry is set on entry.
127 ;
128 ;       180-val := -val + 180.
129 ; With
130 ;       -val := (val ^ $FF) + 1
131 ; we get
132 ;       180-val = (val ^ $FF) + 1 + 180
133 ; Since carry is set, we can drop the "+ 1".
134 ;
135
136         eor     #$FF
137         adc     #180            ; 180-val
138
139 ; 180..269°. Values for 267..269° are actually -1.0. Since this format doesn't
140 ; fit into the table, we have to check for it manually.
141
142 L5:     ldx     #$FF
143         cmp     #87
144         bcc     L6
145
146 ; The value is -1.0
147
148         lda     #<(-1 << 8)
149         rts
150
151 ; 180..266°. Read the value from the table. Carry is clear on entry.
152
153 L6:     tay
154         txa                     ; A = $FF
155         eor     _cc65_sintab,y
156         adc     #1
157         bcc     L7
158         inx
159 L7:     rts
160
161