; ; Fixed point cosine/sine functions. ; ; int __fastcall__ cc65_sin (unsigned x); ; int __fastcall__ cc65_cos (unsigned x); ; ; Returns the cosine/sine for the given argument as angular degree. ; Valid argument range is 0..360 for both functions. They will return ; garbage if the argument is not in a valid range. Result is in 8.8 fixed ; point format, so $100 is 1.0 and $FF00 is -1.0. ; ; ; Ullrich von Bassewitz, 2009-10-29 ; .export _cc65_cos, _cc65_sin ; --------------------------------------------------------------------------- ; Sinus table covering values from 0..86° as 0.8 fixed point values. Values ; for 87..90° are actually 1.0 (= $100), will therefore not fit in the table ; and are covered specially in the code below. .rodata _cc65_sintab: .byte $00, $04, $09, $0D, $12, $16, $1B, $1F, $24, $28 .byte $2C, $31, $35, $3A, $3E, $42, $47, $4B, $4F, $53 .byte $58, $5C, $60, $64, $68, $6C, $70, $74, $78, $7C .byte $80, $84, $88, $8B, $8F, $93, $96, $9A, $9E, $A1 .byte $A5, $A8, $AB, $AF, $B2, $B5, $B8, $BB, $BE, $C1 .byte $C4, $C7, $CA, $CC, $CF, $D2, $D4, $D7, $D9, $DB .byte $DE, $E0, $E2, $E4, $E6, $E8, $EA, $EC, $ED, $EF .byte $F1, $F2, $F3, $F5, $F6, $F7, $F8, $F9, $FA, $FB .byte $FC, $FD, $FE, $FE, $FF, $FF, $FF ; --------------------------------------------------------------------------- ; Cosine function. Is actually implemented as cos(x) = sin(x+90) .code _cc65_cos: ; cos(x) = sin(x+90) clc adc #90 bcc @L1 inx ; If x is now larger than 360, we need to subtract 360. @L1: cpx #>360 bne @L2 cmp #<360 @L2: bcc _cc65_sin sbc #<360 bcs @L3 dex @L3: dex ; --------------------------------------------------------------------------- ; Sine function. Uses ; ; table lookup for 0..89° ; sin(x) = sin(180-x) for 90°..179° ; sin(x) = -sin(x-180) for 180..360° ; ; Plus special handling for the values missing in the table. _cc65_sin: ; If the high byte is non zero, argument is > 255 cpx #0 bne L3 cmp #180 bcs L4 ; 0..179° cmp #90 bcc L1 ; 90..179°. Value is identical to sin(180-val). Carry is set on entry. ; ; 180-val := -val + 180. ; With ; -val := (val ^ $FF) + 1 ; we get ; 180-val = (val ^ $FF) + 1 + 180 ; Since carry is set, we can drop the "+ 1". ; eor #$FF adc #180 ; 180-val ; 0..89°. Values for 87..90° are actually 1.0. Since this format doesn't fit ; into the table, we have to check for it manually. L1: cmp #87 bcc L2 ; The value is 1.0 ldx #>(1 << 8) lda #<(1 << 8) rts ; 0..86°. Read the value from the table. L2: tay ldx #0 lda _cc65_sintab,y rts ; 180..360°. sin(x) = -sin(x-180). Since the argument is in range 0..180 ; after the subtraction, we don't need to handle the high byte. L3: sec L4: sbc #180 cmp #90 bcc L5 ; 270..360°. Value is identical to -sin(180-val). Carry is set on entry. ; ; 180-val := -val + 180. ; With ; -val := (val ^ $FF) + 1 ; we get ; 180-val = (val ^ $FF) + 1 + 180 ; Since carry is set, we can drop the "+ 1". ; eor #$FF adc #180 ; 180-val ; 180..269°. Values for 267..269° are actually -1.0. Since this format doesn't ; fit into the table, we have to check for it manually. L5: ldx #$FF cmp #87 bcc L6 ; The value is -1.0 lda #<(-1 << 8) rts ; 180..266°. Read the value from the table. Carry is clear on entry. L6: tay txa ; A = $FF eor _cc65_sintab,y adc #1 bcc L7 inx L7: rts