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