summaryrefslogtreecommitdiff
path: root/apps/codecs/libatrac/fixp_math.h
blob: 014c5aa5590f37a1255408aca5fda35bb70a9a31 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 * $Id$
 *
 * Copyright (C) 2009 Mohamed Tarek
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/
#include <stdlib.h>
#include <inttypes.h>

/* Macros for converting between various fixed-point representations and floating point. */
#define ONE_16 (1L << 16)
#define fixtof64(x)       (float)((float)(x) / (float)(1 << 16))        //does not work on int64_t!
#define ftofix32(x)       ((int32_t)((x) * (float)(1 << 16) + ((x) < 0 ? -0.5 : 0.5)))
#define ftofix31(x)       ((int32_t)((x) * (float)(1 << 31) + ((x) < 0 ? -0.5 : 0.5)))
#define fix31tof64(x)     (float)((float)(x) / (float)(1 << 31))

/* Fixed point math routines for use in atrac3.c */

#if defined(CPU_ARM)
    /* Calculates: result = (X*Y)>>16 */
    #define fixmul16(X,Y) \
     ({ \
        int32_t lo; \
        int32_t hi; \
        asm volatile ( \
           "smull %[lo], %[hi], %[x], %[y] \n\t" /* multiply */ \
           "mov   %[lo], %[lo], lsr #16    \n\t" /* lo >>= 16 */ \
           "orr   %[lo], %[lo], %[hi], lsl #16"  /* lo |= (hi << 16) */ \
           : [lo]"=&r"(lo), [hi]"=&r"(hi) \
           : [x]"r"(X), [y]"r"(Y)); \
        lo; \
     })
     
    /* Calculates: result = (X*Y)>>31 */
    /* Use scratch register r12 */
    #define fixmul31(X,Y) \
     ({ \
        int32_t lo; \
        int32_t hi; \
        asm volatile ( \
           "smull %[lo], %[hi], %[x], %[y] \n\t" /* multiply */ \
           "mov   %[lo], %[lo], lsr #31    \n\t" /* lo >>= 31 */ \
           "orr   %[lo], %[lo], %[hi], lsl #1"   /* lo |= (hi << 1) */ \
           : [lo]"=&r"(lo), [hi]"=&r"(hi) \
           : [x]"r"(X), [y]"r"(Y)); \
        lo; \
     })
#elif defined(CPU_COLDFIRE)
    /* Calculates: result = (X*Y)>>16 */
    #define fixmul16(X,Y) \
    ({ \
        int32_t t, x = (X); \
        asm volatile ( \
            "mac.l    %[x],%[y],%%acc0\n\t" /* multiply */ \
            "mulu.l   %[y],%[x]       \n\t" /* get lower half, avoid emac stall */ \
            "movclr.l %%acc0,%[t]     \n\t" /* get higher half */ \
            "lsr.l    #1,%[t]         \n\t" /* hi >>= 1 to compensate emac shift */ \
            "move.w   %[t],%[x]       \n\t" /* combine halfwords */\
            "swap     %[x]            \n\t" \
            : [t]"=&d"(t), [x] "+d" (x) \
            : [y] "d" ((Y))); \
        x; \
    })

    #define fixmul31(X,Y) \
    ({ \
       int32_t t; \
       asm volatile ( \
          "mac.l %[x], %[y], %%acc0\n\t"   /* multiply */ \
          "movclr.l %%acc0, %[t]\n\t"      /* get higher half as result */ \
          : [t] "=d" (t) \
          : [x] "r" ((X)), [y] "r" ((Y))); \
       t; \
    })
#else
    static inline int32_t fixmul16(int32_t x, int32_t y)
    {
        int64_t temp;
        temp = x;
        temp *= y;
    
        temp >>= 16;
    
        return (int32_t)temp;
    }
    
    static inline int32_t fixmul31(int32_t x, int32_t y)
    {
        int64_t temp;
        temp = x;
        temp *= y;
    
        temp >>= 31;        //16+31-16 = 31 bits
    
        return (int32_t)temp;
    }
#endif
*/ #define SPEEDY LCD_HEIGHT * 2 /* Recorder: 128 iRiver: 256 */ #define RES 100 #define MOVE_STEP LCD_HEIGHT / 32 /* move pad this many steps up/down each move */ /* variable button definitions */ #if CONFIG_KEYPAD == RECORDER_PAD #define PONG_QUIT BUTTON_OFF #define PONG_LEFT_UP BUTTON_F1 #define PONG_LEFT_DOWN BUTTON_LEFT #define PONG_RIGHT_UP BUTTON_F3 #define PONG_RIGHT_DOWN BUTTON_RIGHT #elif CONFIG_KEYPAD == ONDIO_PAD #define PONG_QUIT BUTTON_OFF #define PONG_LEFT_UP BUTTON_LEFT #define PONG_LEFT_DOWN BUTTON_MENU #define PONG_RIGHT_UP BUTTON_UP #define PONG_RIGHT_DOWN BUTTON_DOWN #elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || \ (CONFIG_KEYPAD == IRIVER_H300_PAD) #define PONG_QUIT BUTTON_OFF #define PONG_LEFT_UP BUTTON_UP #define PONG_LEFT_DOWN BUTTON_DOWN #define PONG_RIGHT_UP BUTTON_ON #define PONG_RIGHT_DOWN BUTTON_MODE #elif (CONFIG_KEYPAD == IPOD_4G_PAD) || \ (CONFIG_KEYPAD == IPOD_3G_PAD) #define PONG_QUIT BUTTON_SELECT #define PONG_LEFT_UP BUTTON_MENU #define PONG_LEFT_DOWN BUTTON_LEFT #define PONG_RIGHT_UP BUTTON_RIGHT #define PONG_RIGHT_DOWN BUTTON_PLAY #elif (CONFIG_KEYPAD == IAUDIO_X5_PAD) #define PONG_QUIT BUTTON_POWER #define PONG_LEFT_UP BUTTON_UP #define PONG_LEFT_DOWN BUTTON_DOWN #define PONG_RIGHT_UP BUTTON_REC #define PONG_RIGHT_DOWN BUTTON_PLAY #elif (CONFIG_KEYPAD == GIGABEAT_PAD) #define PONG_QUIT BUTTON_A #define PONG_LEFT_UP BUTTON_UP #define PONG_LEFT_DOWN BUTTON_DOWN #define PONG_RIGHT_UP BUTTON_POWER #define PONG_RIGHT_DOWN BUTTON_MENU #endif static struct plugin_api* rb; struct pong { int ballx; /* current X*RES position of the ball */ int bally; /* current Y*RES position of the ball */ int w_pad[2]; /* wanted current Y positions of pads */ int e_pad[2]; /* existing current Y positions of pads */ int ballspeedx; /* */ int ballspeedy; /* */ int score[2]; }; void singlepad(int x, int y, int set) { if(set) { rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT); } else { rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(x, y, PAD_WIDTH, PAD_HEIGHT); rb->lcd_set_drawmode(DRMODE_SOLID); } } void pad(struct pong *p, int pad) { static int xpos[2]={0, LCD_WIDTH-PAD_WIDTH}; /* clear existing pad */ singlepad(xpos[pad], p->e_pad[pad], 0); /* draw wanted pad */ singlepad(xpos[pad], p->w_pad[pad], 1); /* existing is now the wanted */ p->e_pad[pad] = p->w_pad[pad]; } bool wallcollide(struct pong *p, int pad) { /* we have already checked for pad-collision, just check if this hits the wall */ if(pad) { /* right-side */ if(p->ballx > LCD_WIDTH*RES) return true; } else { if(p->ballx < 0) return true; } return false; } /* returns true if the ball has hit a pad, and then the info variable will have extra angle info */ bool padcollide(struct pong *p, int pad, int *info) { int x = p->ballx/RES; int y = p->bally/RES; if((y < (p->e_pad[pad]+PAD_HEIGHT)) && (y + BALL_HEIGTH > p->e_pad[pad])) { /* Y seems likely right */ /* store the delta between ball-middle MINUS pad-middle, so it returns: 0 when the ball hits exactly the middle of the pad positive numbers when the ball is below the middle of the pad negative numbers when the ball is above the middle of the pad max number is +- PAD_HEIGHT/2 */ *info = (y+BALL_HEIGTH/2) - (p->e_pad[pad] + PAD_HEIGHT/2); if(pad) { /* right-side */ if((x + BALL_WIDTH) > (LCD_WIDTH - PAD_WIDTH)) return true; /* phump */ } else { if(x <= 0) return true; } } return false; /* nah */ } void bounce(struct pong *p, int pad, int info) { (void)pad; /* not used right now */ p->ballspeedx = -p->ballspeedx; /* info is the hit-angle into the pad */ if(p->ballspeedy > 0) { /* downwards */ if(info > 0) { /* below the middle of the pad */ p->ballspeedy += info * RES/3; } else if(info < 0) { /* above the middle */ p->ballspeedy = info * RES/2; } } else { /* upwards */ if(info > 0) { /* below the middle of the pad */ p->ballspeedy = info * RES/2; } else if(info < 0) { /* above the middle */ p->ballspeedy -= info * RES/3; } } p->ballspeedy += rb->rand()%21-10; #if 0 fprintf(stderr, "INFO: %d YSPEED: %d\n", info, p->ballspeedy); #endif } void score(struct pong *p, int pad) { rb->splash(HZ/4, true, "%s scores!", pad?"right":"left"); rb->lcd_clear_display(); p->score[pad]++; /* then move the X-speed of the ball and give it a random Y position */ p->ballspeedx = -p->ballspeedx; p->bally = rb->rand()%(LCD_HEIGHT-BALL_HEIGTH); /* restore Y-speed to default */ p->ballspeedy = (p->ballspeedy > 0) ? SPEEDY : -SPEEDY; /* set the existing pad positions to something weird to force pad updates */ p->e_pad[0] = -1; p->e_pad[1] = -1; } void ball(struct pong *p) { int x = p->ballx/RES; int y = p->bally/RES; int newx; int newy; int info; /* movement */ p->ballx += p->ballspeedx; p->bally += p->ballspeedy; newx = p->ballx/RES; newy = p->bally/RES; /* detect if ball hits a wall */ if(newy + BALL_HEIGTH > LCD_HEIGHT) { /* hit floor, bounce */ p->ballspeedy = -p->ballspeedy; newy = LCD_HEIGHT - BALL_HEIGTH; p->bally = newy * RES; } else if(newy < 0) { /* hit ceiling, bounce */ p->ballspeedy = -p->ballspeedy; p->bally = 0; newy = 0; } /* detect if ball hit pads */ if(padcollide(p, 0, &info)) bounce(p, 0, info); else if(padcollide(p, 1, &info)) bounce(p, 1, info); else if(wallcollide(p, 0)) score(p, 1); else if(wallcollide(p, 1)) score(p, 0); /* clear old position */ rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID); rb->lcd_fillrect(x, y, BALL_WIDTH, BALL_HEIGTH); rb->lcd_set_drawmode(DRMODE_SOLID); /* draw the new ball position */ rb->lcd_fillrect(newx, newy, BALL_WIDTH, BALL_HEIGTH); } void padmove(int *pos, int dir) { *pos += dir; if(*pos > (LCD_HEIGHT-PAD_HEIGHT)) *pos = (LCD_HEIGHT-PAD_HEIGHT); else if(*pos < 0) *pos = 0; } int keys(struct pong *p) { int key; int time = 4; /* number of ticks this function will loop reading keys */ int start = *rb->current_tick; int end = start + time; while(end > *rb->current_tick) { key = rb->button_get_w_tmo(end - *rb->current_tick); if(key & PONG_QUIT) return 0; /* exit game NOW */ if(key & PONG_LEFT_DOWN) /* player left goes down */ padmove(&p->w_pad[0], MOVE_STEP); if(key & PONG_LEFT_UP) /* player left goes up */ padmove(&p->w_pad[0], -MOVE_STEP); if(key & PONG_RIGHT_DOWN) /* player right goes down */ padmove(&p->w_pad[1], MOVE_STEP); if(key & PONG_RIGHT_UP) /* player right goes up */ padmove(&p->w_pad[1], -MOVE_STEP); if(rb->default_event_handler(key) == SYS_USB_CONNECTED) return -1; /* exit game because of USB */ } return 1; /* return 0 to exit game */ } void showscore(struct pong *p) { static char buffer[20]; int w; rb->snprintf(buffer, sizeof(buffer), "%d - %d", p->score[0], p->score[1]); w = rb->lcd_getstringsize((unsigned char *)buffer, NULL, NULL); rb->lcd_putsxy( (LCD_WIDTH / 2) - (w / 2), 0, (unsigned char *)buffer); } /* this is the plugin entry point */ enum plugin_status plugin_start(struct plugin_api* api, void* parameter) { struct pong pong; int game = 1; /* init the struct with some silly values to start with */ pong.ballx = 20*RES; pong.bally = 20*RES; pong.e_pad[0] = 0; pong.w_pad[0] = 7; pong.e_pad[1] = 0; pong.w_pad[1] = 40; pong.ballspeedx = SPEEDX; pong.ballspeedy = SPEEDY; pong.score[0] = pong.score[1] = 0; /* lets start at 0 - 0 ;-) */ /* if you don't use the parameter, you can do like this to avoid the compiler warning about it */ (void)parameter; rb = api; /* use the "standard" rb pointer */ /* Clear screen */ rb->lcd_clear_display(); /* go go go */ while(game > 0) { showscore(&pong); pad(&pong, 0); /* draw left pad */ pad(&pong, 1); /* draw right pad */ ball(&pong); /* move and draw ball */ rb->lcd_update(); game = keys(&pong); /* deal with keys */ } return (game == 0) ? PLUGIN_OK : PLUGIN_USB_CONNECTED; } #endif /* HAVE_LCD_BITMAP */