make gp2x mp3 playback functional (need to unpack and compile helix decoder separatel...
authorkub <kub@kub.escape.de>
Mon, 18 Mar 2019 22:14:07 +0000 (23:14 +0100)
committerkub <derkub@gmail.com>
Tue, 30 Jul 2019 14:34:40 +0000 (16:34 +0200)
13 files changed:
Makefile
platform/common/helix/Makefile [new file with mode: 0644]
platform/common/helix/lib.c [new file with mode: 0644]
platform/common/mp3.c
platform/common/mp3.h
platform/common/mp3_helix.c
platform/common/mp3_sync.c [new file with mode: 0644]
platform/gp2x/940ctl.c
platform/gp2x/code940/940.c
platform/gp2x/code940/Makefile
platform/gp2x/code940/mp3test.c
platform/gp2x/code940/uClibc/memset.s
platform/gp2x/code940/uClibc/wrappers.c

index 0df41f5..dff08b2 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -159,8 +159,10 @@ endif
 
 endif # USE_FRONTEND
 
-OBJS += platform/common/mp3.o
+OBJS += platform/common/mp3.o platform/common/mp3_sync.o
 ifeq "$(PLATFORM_MP3)" "1"
+platform/common/mp3_helix.o: CFLAGS += -Iplatform/libpicofe
+OBJS += platform/common/mp3_helix.o
 else ifeq "$(HAVE_LIBAVCODEC)" "1"
 OBJS += platform/common/mp3_libavcodec.o
 else
diff --git a/platform/common/helix/Makefile b/platform/common/helix/Makefile
new file mode 100644 (file)
index 0000000..0021ea8
--- /dev/null
@@ -0,0 +1,42 @@
+CROSS ?= arm-linux-gnueabi-
+
+CC = $(CROSS)gcc
+AS = $(CROSS)as
+AR = $(CROSS)ar
+TOOLCHAIN = $(notdir $(CROSS))
+
+CFLAGS += -Ipub -O2 -Wall -fstrict-aliasing -ffast-math
+ifneq ($(findstring arm-,$(TOOLCHAIN)),)
+CFLAGS += -mcpu=arm940t -mtune=arm940t -mfloat-abi=soft -mfpu=fpa -mabi=apcs-gnu -mno-thumb-interwork
+ASFLAGS = -mcpu=arm940t -mfloat-abi=soft -mfpu=fpa -mabi=apcs-gnu
+OBJS += real/arm/asmpoly_gcc.o
+else
+CFLAGS += -m32
+ASFLAGS += -m32
+OBJS += real/polyphase.o
+endif
+
+LIB = $(TOOLCHAIN)helix_mp3.a
+SHLIB = $(TOOLCHAIN)helix_mp3.so
+
+all: $(LIB) $(SHLIB)
+
+
+OBJS += mp3dec.o mp3tabs.o
+#OBJS += ipp/bitstream.o ipp/buffers.o ipp/dequant.o ipp/huffman.o ipp/imdct.o ipp/subband.o
+OBJS += real/bitstream.o real/buffers.o real/dct32.o real/dequant.o real/dqchan.o real/huffman.o
+OBJS += real/hufftabs.o real/imdct.o real/scalfact.o real/stproc.o real/subband.o real/trigtabs.o
+
+OBJS += lib.o
+
+real/arm/asmpoly_gcc.o: real/arm/asmpoly_gcc.s
+       $(CC) -o $@ $(ASFLAGS) -c $<
+
+$(LIB) : $(OBJS)
+       $(AR) r $@ $^
+$(SHLIB) : $(OBJS) /home/build/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib/gcc/arm-open2x-linux/4.1.1/libgcc.a
+       $(CC) -o $@ -nostdlib -shared $(CFLAGS) $^
+
+clean:
+       $(RM) -f $(OBJS)
+
diff --git a/platform/common/helix/lib.c b/platform/common/helix/lib.c
new file mode 100644 (file)
index 0000000..d7c511b
--- /dev/null
@@ -0,0 +1,122 @@
+#include <stdlib.h>
+#include <stdint.h>
+
+// libgcc has this with gcc 4.x
+void raise(int sig)
+{
+}
+
+// very limited heap functions for helix decoder
+
+static char heap[65000] __attribute__((aligned(16)));
+static long heap_offs;
+
+void __malloc_init(void)
+{
+       heap_offs = 0;
+}
+
+void *malloc(size_t size)
+{
+        void *chunk = heap + heap_offs;
+        size = (size+15) & ~15;
+        if (heap_offs + size > sizeof(heap))
+                return NULL;
+        else {
+                heap_offs += size;
+                return chunk;
+        }
+}
+
+void free(void *chunk)
+{
+       if (chunk == heap)
+               heap_offs = 0;
+}
+
+#if 0
+void *memcpy (void *dest, const void *src, size_t n)
+{
+       char       *_dest = dest;
+        const char *_src = src;
+       while (n--) *_dest++ = *_src++;
+       return dest;
+}
+
+void *memmove (void *dest, const void *src, size_t n)
+{
+       char       *_dest = dest+n;
+        const char *_src = src+n;
+       if (dest <= src || dest >= _src)
+               return memcpy(dest, src, n);
+       while (n--) *--_dest = *--_src;
+       return dest;
+}
+#else
+/* memcpy/memmove in C with some simple optimizations.
+ * ATTN does dirty aliasing tricks with undefined behaviour by standard.
+ * (this works fine with gcc, though...)
+ */
+void *memcpy(void *dest, const void *src, size_t n)
+{
+       struct _16 { uint32_t a[4]; };
+       union { const void *v; char *c; uint64_t *l; struct _16 *s; }
+               ss = { src }, ds = { dest };
+       const int lm = sizeof(uint32_t)-1;
+
+       if ((((unsigned)ss.c ^ (unsigned)ds.c) & lm) == 0) {
+               /* fast copy if pointers have the same aligment */
+               while (((unsigned)ss.c & lm) && n > 0)  /* align to word */
+                       *ds.c++ = *ss.c++, n--;
+               while (n >= sizeof(struct _16)) /* copy 16 bytes blocks */
+                       *ds.s++ = *ss.s++, n -= sizeof(struct _16);
+               if (n >= sizeof(uint64_t))      /* copy leftover 8 byte block */
+                       *ds.l++ = *ss.l++, n -= sizeof(uint64_t);
+       } else {
+               /* byte copy if pointers are unaligned */
+               while (n >= 8) {                /* copy 8 byte blocks */
+                       *ds.c++ = *ss.c++, n--; *ds.c++ = *ss.c++, n--;
+                       *ds.c++ = *ss.c++, n--; *ds.c++ = *ss.c++, n--;
+                       *ds.c++ = *ss.c++, n--; *ds.c++ = *ss.c++, n--;
+                       *ds.c++ = *ss.c++, n--; *ds.c++ = *ss.c++, n--;
+               }
+       }
+       /* copy max. 8 leftover bytes */
+       while (n > 0)
+               *ds.c++ = *ss.c++, n--;
+       return dest;
+}
+
+void *memmove (void *dest, const void *src, size_t n)
+{
+       struct _16 { uint32_t a[4]; };
+       union { const void *v; char *c; uint64_t *l; struct _16 *s; }
+               ss = { src+n }, ds = { dest+n };
+       const int lm = sizeof(uint32_t)-1;
+
+       if (dest <= src || dest >= src+n)
+               return memcpy(dest, src, n);
+
+       if ((((unsigned)ss.c ^ (unsigned)ds.c) & lm) == 0) {
+               /* fast copy if pointers have the same aligment */
+               while (((unsigned)ss.c & lm) && n > 0)
+                       *--ds.c = *--ss.c, n--;
+               while (n >= sizeof(struct _16))
+                       *--ds.s = *--ss.s, n -= sizeof(struct _16);
+               if (n >= sizeof(uint64_t))
+                       *--ds.l = *--ss.l, n -= sizeof(uint64_t);
+       } else {
+               /* byte copy if pointers are unaligned */
+               while (n >= 8) {
+                       *--ds.c = *--ss.c, n--; *--ds.c = *--ss.c, n--;
+                       *--ds.c = *--ss.c, n--; *--ds.c = *--ss.c, n--;
+                       *--ds.c = *--ss.c, n--; *--ds.c = *--ss.c, n--;
+                       *--ds.c = *--ss.c, n--; *--ds.c = *--ss.c, n--;
+               }
+       }
+       /* copy max. 8 leftover bytes */
+       while (n > 0)
+               *--ds.c = *--ss.c, n--;
+       return dest;
+}
+#endif
index c84962c..346e019 100644 (file)
@@ -21,33 +21,6 @@ unsigned short mpeg1_l3_bitrates[16] = {
        0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320
 };
 
-int mp3_find_sync_word(const unsigned char *buf, int size)
-{
-       const unsigned char *p, *pe;
-
-       /* find byte-aligned syncword - need 12 (MPEG 1,2) or 11 (MPEG 2.5) matching bits */
-       for (p = buf, pe = buf + size - 3; p <= pe; p++)
-       {
-               int pn;
-               if (p[0] != 0xff)
-                       continue;
-               pn = p[1];
-               if ((pn & 0xf8) != 0xf8 || // currently must be MPEG1
-                   (pn & 6) == 0) {       // invalid layer
-                       p++; continue;
-               }
-               pn = p[2];
-               if ((pn & 0xf0) < 0x20 || (pn & 0xf0) == 0xf0 || // bitrates
-                   (pn & 0x0c) != 0) { // not 44kHz
-                       continue;
-               }
-
-               return p - buf;
-       }
-
-       return -1;
-}
-
 static int try_get_bitrate(unsigned char *buf, int buf_size)
 {
        int offs1, offs = 0;
index eb66db8..4a2b230 100644 (file)
@@ -12,8 +12,8 @@ int mp3dec_decode(FILE *f, int *file_pos, int file_len);
 extern unsigned short mpeg1_l3_bitrates[16];
 
 #ifdef __GP2X__
-void mp3_update_local(int *buffer, int length, int stereo);
-void mp3_start_play_local(void *f, int pos);
+int _mp3dec_start(FILE *f, int fpos_start);
+int _mp3dec_decode(FILE *f, int *file_pos, int file_len);
 #endif
 
 #endif // __COMMON_MP3_H__
index b278529..75be8df 100644 (file)
@@ -9,6 +9,7 @@
 
 #include <stdio.h>
 #include <string.h>
+#include <dlfcn.h>
 
 #include <pico/pico_int.h>
 #include <pico/sound/mix.h>
@@ -20,10 +21,15 @@ static HMP3Decoder mp3dec;
 static unsigned char mp3_input_buffer[2 * 1024];
 
 #ifdef __GP2X__
-#define mp3_update mp3_update_local
-#define mp3_start_play mp3_start_play_local
+#define mp3dec_decode _mp3dec_decode
+#define mp3dec_start _mp3dec_start
 #endif
 
+static void *libhelix;
+HMP3Decoder (*p_MP3InitDecoder)(void);
+void (*p_MP3FreeDecoder)(HMP3Decoder);
+int (*p_MP3Decode)(HMP3Decoder, unsigned char **, int *, short *, int);
+
 int mp3dec_decode(FILE *f, int *file_pos, int file_len)
 {
        unsigned char *readPtr;
@@ -51,7 +57,7 @@ int mp3dec_decode(FILE *f, int *file_pos, int file_len)
                bytesLeft -= offset;
 
                had_err = err;
-               err = MP3Decode(mp3dec, &readPtr, &bytesLeft, cdda_out_buffer, 0);
+               err = p_MP3Decode(mp3dec, &readPtr, &bytesLeft, cdda_out_buffer, 0);
                if (err) {
                        if (err == ERR_MP3_MAINDATA_UNDERFLOW && !had_err) {
                                // just need another frame
@@ -86,10 +92,31 @@ int mp3dec_decode(FILE *f, int *file_pos, int file_len)
 
 int mp3dec_start(FILE *f, int fpos_start)
 {
+       if (libhelix == NULL) {
+               libhelix = dlopen("./libhelix.so", RTLD_NOW);
+               if (libhelix == NULL) {
+                       lprintf("mp3dec: load libhelix.so: %s\n", dlerror());
+                       return -1;
+               }
+
+               p_MP3InitDecoder = dlsym(libhelix, "MP3InitDecoder");
+               p_MP3FreeDecoder = dlsym(libhelix, "MP3FreeDecoder");
+               p_MP3Decode = dlsym(libhelix, "MP3Decode");
+
+               if (p_MP3InitDecoder == NULL || p_MP3FreeDecoder == NULL
+                   || p_MP3Decode == NULL)
+               {
+                       lprintf("mp3dec: missing symbol(s) in libhelix.so\n");
+                       dlclose(libhelix);
+                       libhelix = NULL;
+                       return -1;
+               }
+       }
+
        // must re-init decoder for new track
        if (mp3dec)
-               MP3FreeDecoder(mp3dec);
-       mp3dec = MP3InitDecoder();
+               p_MP3FreeDecoder(mp3dec);
+       mp3dec = p_MP3InitDecoder();
 
        return (mp3dec == 0) ? -1 : 0;
 }
diff --git a/platform/common/mp3_sync.c b/platform/common/mp3_sync.c
new file mode 100644 (file)
index 0000000..509c259
--- /dev/null
@@ -0,0 +1,27 @@
+
+int mp3_find_sync_word(const unsigned char *buf, int size)
+{
+       const unsigned char *p, *pe;
+
+       /* find byte-aligned syncword - need 12 (MPEG 1,2) or 11 (MPEG 2.5) matching bits */
+       for (p = buf, pe = buf + size - 3; p <= pe; p++)
+       {
+               int pn;
+               if (p[0] != 0xff)
+                       continue;
+               pn = p[1];
+               if ((pn & 0xf8) != 0xf8 || // currently must be MPEG1
+                   (pn & 6) == 0) {       // invalid layer
+                       p++; continue;
+               }
+               pn = p[2];
+               if ((pn & 0xf0) < 0x20 || (pn & 0xf0) == 0xf0 || // bitrates
+                   (pn & 0x0c) != 0) { // not 44kHz
+                       continue;
+               }
+
+               return p - buf;
+       }
+
+       return -1;
+}
index c270bfe..31408d0 100644 (file)
@@ -425,8 +425,7 @@ int YM2612UpdateOne_940(int *buffer, int length, int stereo, int is_buf_empty)
 int mp3dec_decode(FILE *f, int *file_pos, int file_len)\r
 {\r
        if (!(PicoIn.opt & POPT_EXT_FM)) {\r
-               //mp3_update_local(buffer, length, stereo);\r
-               return 0;\r
+               return _mp3dec_decode(f, file_pos, file_len);\r
        }\r
 \r
        // check if playback was started, track not ended\r
@@ -457,8 +456,7 @@ int mp3dec_decode(FILE *f, int *file_pos, int file_len)
 int mp3dec_start(FILE *f, int fpos_start)\r
 {\r
        if (!(PicoIn.opt & POPT_EXT_FM)) {\r
-               //mp3_start_play_local(f, pos);\r
-               return -1;\r
+               return _mp3dec_start(f, fpos_start);\r
        }\r
 \r
        if (loaded_mp3 != f)\r
index 760816e..f79db1e 100644 (file)
@@ -2,7 +2,7 @@
 // (c) Copyright 2006-2007, Grazvydas "notaz" Ignotas\r
 \r
 #include "940shared.h"\r
-#include "../../common/mp3.h"\r
+#include "../../common/helix/pub/mp3dec.h"\r
 \r
 static _940_data_t *shared_data = (_940_data_t *)   0x00100000;\r
 static _940_ctl_t  *shared_ctl  = (_940_ctl_t *)    0x00200000;\r
@@ -19,7 +19,7 @@ void drain_wb(void);
 // is changed by other core just before we update it\r
 void set_if_not_changed(int *val, int oldval, int newval);\r
 \r
-void _memcpy(void *dst, const void *src, int count);\r
+extern void *memcpy(void *dest, const void *src, unsigned long n);\r
 \r
 //     asm volatile ("mov r0, #0" ::: "r0");\r
 //     asm volatile ("mcr p15, 0, r0, c7, c6,  0" ::: "r0"); /* flush dcache */\r
@@ -153,6 +153,8 @@ void Main940(void)
        int job = 0;\r
        ym2612_940 = &shared_data->ym2612;\r
 \r
+//     extern unsigned __bss_start__, __bss_end__;\r
+//     memset(&__bss_start__, 0, &__bss_end__ - &__bss_start__);\r
 \r
        for (;;)\r
        {\r
@@ -167,6 +169,7 @@ void Main940(void)
                                shared_ctl->writebuff0[0] = shared_ctl->writebuff1[0] = 0xffff;\r
                                YM2612Init_(shared_ctl->baseclock, shared_ctl->rate);\r
                                /* Helix mp3 decoder */\r
+                               __malloc_init();\r
                                shared_data->mp3dec = MP3InitDecoder();\r
                                break;\r
 \r
@@ -185,7 +188,7 @@ void Main940(void)
 \r
                        case JOB940_PICOSTATESAVE2:\r
                                YM2612PicoStateSave2(0, 0);\r
-                               _memcpy(shared_ctl->writebuff0, ym2612_940->REGS, 0x200);\r
+                               memcpy(shared_ctl->writebuff0, ym2612_940->REGS, 0x200);\r
                                break;\r
 \r
                        case JOB940_PICOSTATELOAD2_PREP:\r
@@ -193,7 +196,7 @@ void Main940(void)
                                break;\r
 \r
                        case JOB940_PICOSTATELOAD2:\r
-                               _memcpy(ym2612_940->REGS, shared_ctl->writebuff0, 0x200);\r
+                               memcpy(ym2612_940->REGS, shared_ctl->writebuff0, 0x200);\r
                                YM2612PicoStateLoad2(0, 0);\r
                                break;\r
 \r
@@ -207,6 +210,7 @@ void Main940(void)
 \r
                        case JOB940_MP3RESET:\r
                                if (shared_data->mp3dec) MP3FreeDecoder(shared_data->mp3dec);\r
+                               __malloc_init();\r
                                shared_data->mp3dec = MP3InitDecoder();\r
                                break;\r
                }\r
@@ -215,4 +219,3 @@ void Main940(void)
                dcache_clean();\r
        }\r
 }\r
-\r
index e327d13..8561551 100644 (file)
@@ -1,17 +1,23 @@
 # you may or may not need to change this\r
-#devkit_path = x:/stuff/dev/devkitgp2x/\r
-devkit_path ?= $(HOME)/opt/devkitGP2X/\r
-lgcc_path = $(devkit_path)lib/gcc/arm-linux/4.0.3/\r
-CROSS = arm-linux-\r
+#devkit_path ?= $(HOME)/opt/devkitGP2X/\r
+#lgcc_path = $(devkit_path)lib/gcc/arm-linux/4.0.3/\r
 #CROSS = $(devkit_path)bin/arm-linux-\r
+#devkit_path ?= $(HOME)/opt/open2x\r
+#lgcc_path = $(devkit_path)/gcc-4.1.1-glibc-2.3.6/lib/gcc/arm-open2x-linux/4.1.1/\r
+#CROSS ?= $(devkit_path)/gcc-4.1.1-glibc-2.3.6/bin/arm-open2x-linux-\r
+#devkit_path ?= $(HOME)/opt/arm-unknown-linux-gnu\r
+#lgcc_path = $(HOME)/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib/gcc/arm-open2x-linux/4.1.1/\r
+#CROSS ?= $(devkit_path)/bin/arm-unknown-linux-gnu-\r
+lgcc_path = $(HOME)/opt/open2x/gcc-4.1.1-glibc-2.3.6/lib/gcc/arm-open2x-linux/4.1.1/\r
+CROSS ?= arm-linux-gnueabi-\r
 \r
 # settings\r
 #up = 1\r
 \r
-CFLAGS += -O2 -Wall -fomit-frame-pointer -fstrict-aliasing -ffast-math\r
-CFLAGS += -I../.. -I. -D__GP2X__ -DARM\r
-CFLAGS += -mcpu=arm940t -mtune=arm940t\r
-LDFLAGS = -static -s -e code940 -Ttext 0x0 -L$(lgcc_path) -lgcc\r
+CFLAGS += -O2 -Wall -mno-thumb-interwork -fstrict-aliasing -ffast-math\r
+CFLAGS += -I../../common/helix/pub -I../../.. -I. -D__GP2X__ -DARM\r
+CFLAGS += -mcpu=arm940t -mtune=arm940t -mabi=apcs-gnu -mfloat-abi=soft -mfpu=fpa\r
+LDFLAGS = -static -e code940 -Ttext 0x0 -L$(lgcc_path) -lgcc\r
 \r
 GCC = $(CROSS)gcc\r
 STRIP = $(CROSS)strip\r
@@ -36,7 +42,9 @@ all: $(BIN)
 # stuff for 940 core\r
 \r
 # init, emu_control, emu\r
-OBJS940 += 940init.o 940.o 940ym2612.o memcpy.o misc_arm.o mp3.o\r
+OBJS940 += 940init.o 940.o 940ym2612.o misc_arm.o mp3_sync.o\r
+# the asm memcpy code crashes job LOAD2 on 940. Possibly a globbered reg?\r
+# OBJS940 += memcpy.o\r
 # the asm code seems to be faster when run on 920, but not on 940 for some reason\r
 # OBJS940 += ../../Pico/sound/ym2612_asm.o\r
 \r
@@ -44,12 +52,13 @@ OBJS940 += 940init.o 940.o 940ym2612.o memcpy.o misc_arm.o mp3.o
 OBJS940 += uClibc/memset.o uClibc/s_floor.o uClibc/e_pow.o uClibc/e_sqrt.o uClibc/s_fabs.o\r
 OBJS940 += uClibc/s_scalbn.o uClibc/s_copysign.o uClibc/k_sin.o uClibc/k_cos.o uClibc/s_sin.o\r
 OBJS940 += uClibc/e_rem_pio2.o uClibc/k_rem_pio2.o uClibc/e_log.o uClibc/wrappers.o\r
+LIBHELIX ?= ../../common/helix/$(notdir $(CROSS))helix_mp3.a\r
 \r
 $(BIN) : code940.elf\r
        @echo ">>>" $@\r
        $(OBJCOPY) -O binary $< $@\r
 \r
-code940.elf : $(OBJS940) ../../common/helix/$(CROSS)helix-mp3.a\r
+code940.elf : $(OBJS940) $(LIBHELIX)\r
        @echo ">>>" $@\r
        $(LD) $^ $(LDFLAGS) -o $@ -Map code940.map\r
 \r
@@ -64,8 +73,12 @@ misc_arm.o : ../../../pico/misc_arm.s
        @echo ">>>" $@\r
        $(GCC) $(CFLAGS) -DEXTERNAL_YM2612 -c $< -o $@\r
 \r
-../../common/helix/helix_mp3.a:\r
-       @make -C ../../common/helix/\r
+mp3_sync.o: ../../common/mp3_sync.c\r
+       @echo ">>>" $@\r
+       $(GCC) $(CFLAGS) -Os -DCODE940 -c $< -o $@\r
+\r
+$(LIBHELIX):\r
+       @$(MAKE) -C ../../common/helix/ CROSS=$(CROSS)\r
 \r
 \r
 up: $(BIN)\r
@@ -82,7 +95,7 @@ tidy:
 ##\r
 OBJSMP3T = mp3test.o ../gp2x.o ../asmutils.o ../usbjoy.o\r
 \r
-mp3test.gpe : $(OBJSMP3T) ../helix/helix_mp3.a\r
+mp3test.gpe : $(OBJSMP3T) $(LIBHELIX)\r
        $(GCC) -static -o $@ $^\r
        $(STRIP) $@\r
        @cp -v $@ /mnt/gp2x/mnt/sd\r
index 9072d85..cd2a665 100644 (file)
@@ -13,7 +13,7 @@
 //#include "emu.h"\r
 //#include "menu.h"\r
 #include "../asmutils.h"\r
-#include "../helix/pub/mp3dec.h"\r
+#include "../../helix/pub/mp3dec.h"\r
 \r
 /* we will need some gp2x internals here */\r
 extern volatile unsigned short *gp2x_memregs; /* from minimal library rlyeh */\r
index 0923014..80cdcb5 100644 (file)
@@ -22,7 +22,7 @@
                .text\r
                .global memset\r
                .type memset,%function\r
-               .align 4\r
+               .align 2\r
 \r
 memset:\r
        mov     a4, a1\r
index cc4e269..ce95a48 100644 (file)
@@ -4,9 +4,17 @@ double pow(double x, double y)
 {\r
        return __ieee754_pow(x, y);\r
 }\r
+double __pow_finite(double x, double y)\r
+{\r
+       return __ieee754_pow(x, y);\r
+}\r
 \r
 \r
 double log(double x)\r
 {\r
        return __ieee754_log(x);\r
 }\r
+double __log_finite(double x)\r
+{\r
+       return __ieee754_log(x);\r
+}\r