From abe0ea43b561e2def84cf921b1d6d8c82819ea69 Mon Sep 17 00:00:00 2001 From: notaz Date: Thu, 8 Feb 2007 20:12:45 +0000 Subject: [PATCH] some audio problems fixed, missing files added git-svn-id: file:///home/notaz/opt/svn/PicoDrive@31 be3aeb3a-fb24-0410-a615-afba39da0efa --- Pico/cd/pcm.c | 130 ++++++++++++++++ Pico/cd/pcm.h | 7 + Pico/sound/sound.c | 4 +- platform/gp2x/940ctl_ym2612.c | 241 ++++++++++++++---------------- platform/gp2x/code940/940.c | 140 +++++++---------- platform/gp2x/code940/940init.s | 175 +++++++++++++++------- platform/gp2x/code940/940shared.h | 22 +-- platform/gp2x/mp3.c | 134 +++++++++++++++++ platform/gp2x/mp3.h | 4 + 9 files changed, 576 insertions(+), 281 deletions(-) create mode 100644 Pico/cd/pcm.c create mode 100644 Pico/cd/pcm.h create mode 100644 platform/gp2x/mp3.c create mode 100644 platform/gp2x/mp3.h diff --git a/Pico/cd/pcm.c b/Pico/cd/pcm.c new file mode 100644 index 0000000..1de04d6 --- /dev/null +++ b/Pico/cd/pcm.c @@ -0,0 +1,130 @@ +#include "../PicoInt.h" +#include "pcm.h" + +static unsigned int g_rate = 0; // 18.14 fixed point + +void pcm_write(unsigned int a, unsigned int d) +{ +//printf("pcm_write(%i, %02x)\n", a, d); + + if (a < 7) + { + Pico_mcd->pcm.ch[Pico_mcd->pcm.cur_ch].regs[a] = d; + } + else if (a == 7) // control register + { + if (d & 0x40) Pico_mcd->pcm.cur_ch = d & 7; + else Pico_mcd->pcm.bank = d & 0xf; + Pico_mcd->pcm.control = d; + // dprintf("pcm control=%02x", Pico_mcd->pcm.control); + } + else if (a == 8) // sound on/off + { + if (!(Pico_mcd->pcm.enabled & 0x01)) Pico_mcd->pcm.ch[0].addr = + Pico_mcd->pcm.ch[0].regs[6] << (PCM_STEP_SHIFT + 8); + if (!(Pico_mcd->pcm.enabled & 0x02)) Pico_mcd->pcm.ch[1].addr = + Pico_mcd->pcm.ch[1].regs[6] << (PCM_STEP_SHIFT + 8); + if (!(Pico_mcd->pcm.enabled & 0x04)) Pico_mcd->pcm.ch[2].addr = + Pico_mcd->pcm.ch[2].regs[6] << (PCM_STEP_SHIFT + 8); + if (!(Pico_mcd->pcm.enabled & 0x08)) Pico_mcd->pcm.ch[3].addr = + Pico_mcd->pcm.ch[3].regs[6] << (PCM_STEP_SHIFT + 8); + if (!(Pico_mcd->pcm.enabled & 0x10)) Pico_mcd->pcm.ch[4].addr = + Pico_mcd->pcm.ch[4].regs[6] << (PCM_STEP_SHIFT + 8); + if (!(Pico_mcd->pcm.enabled & 0x20)) Pico_mcd->pcm.ch[5].addr = + Pico_mcd->pcm.ch[5].regs[6] << (PCM_STEP_SHIFT + 8); + if (!(Pico_mcd->pcm.enabled & 0x40)) Pico_mcd->pcm.ch[6].addr = + Pico_mcd->pcm.ch[6].regs[6] << (PCM_STEP_SHIFT + 8); + if (!(Pico_mcd->pcm.enabled & 0x80)) Pico_mcd->pcm.ch[7].addr = + Pico_mcd->pcm.ch[7].regs[6] << (PCM_STEP_SHIFT + 8); +// printf("addr %x %x %x %x %x %x %x %x\n", Pico_mcd->pcm.ch[0].addr, Pico_mcd->pcm.ch[1].addr +// , Pico_mcd->pcm.ch[2].addr, Pico_mcd->pcm.ch[3].addr, Pico_mcd->pcm.ch[4].addr, Pico_mcd->pcm.ch[5].addr +// , Pico_mcd->pcm.ch[6].addr, Pico_mcd->pcm.ch[7].addr); + + Pico_mcd->pcm.enabled = ~d; +//printf("enabled=%02x\n", Pico_mcd->pcm.enabled); + } +} + + +void pcm_set_rate(int rate) +{ + double step = 31.8 * 1024.0 / (double) rate; // max <4 @ 8000Hz + step *= 256*256/4; + g_rate = (unsigned int) step; + printf("g_rate: %08x\n", g_rate); +} + + +// TODO: make use of the fact that max_length == 3 + +void pcm_update(int *buffer, int length, int stereo) +{ + struct pcm_chan *ch; + unsigned int step, addr; + int mul_l, mul_r, smp; + int i, j, k; + int *out; + + + // PCM disabled or all channels off (to be checked by caller) + //if (!(Pico_mcd->pcm.control & 0x80) || !Pico_mcd->pcm.enabled) return; + +//printf("-- upd %i\n", length); + + for (i = 0; i < 8; i++) + { + if (!(Pico_mcd->pcm.enabled & (1 << i))) continue; // channel disabled + + out = buffer; + ch = &Pico_mcd->pcm.ch[i]; + + addr = ch->addr; // >> PCM_STEP_SHIFT; + mul_l = ((int)ch->regs[0] * (ch->regs[1] & 0xf)) >> (5+1); // (env * pan) >> 5 + mul_r = ((int)ch->regs[0] * (ch->regs[1] >> 4)) >> (5+1); + step = ((unsigned int)(*(unsigned short *)&ch->regs[2]) * g_rate) >> 14; // freq step +// printf("step=%i, cstep=%i, mul_l=%i, mul_r=%i, ch=%i, addr=%x, en=%02x\n", +// *(unsigned short *)&ch->regs[2], step, mul_l, mul_r, i, addr, Pico_mcd->pcm.enabled); + + if (!stereo && mul_l < mul_r) mul_l = mul_r; + + for (j = 0; j < length; j++) + { +// printf("addr=%08x\n", addr); + smp = Pico_mcd->pcm_ram[addr >> PCM_STEP_SHIFT]; + + // test for loop signal + if (smp == 0xff) + { + addr = *(unsigned short *)&ch->regs[4]; // loop_addr + smp = Pico_mcd->pcm_ram[addr]; + addr <<= PCM_STEP_SHIFT; + if (smp == 0xff) break; + } + + if (smp & 0x80) smp = -(smp & 0x7f); + + *out++ += smp * mul_l; // max 128 * 119 = 15232 + if(stereo) + *out++ += smp * mul_r; + + // update address register + k = (addr >> PCM_STEP_SHIFT) + 1; + addr = (addr + step) & 0x7FFFFFF; + + for(; k < (addr >> PCM_STEP_SHIFT); k++) + { + if (Pico_mcd->pcm_ram[k] == 0xff) + { + addr = (unsigned int)(*(unsigned short *)&ch->regs[4]) << PCM_STEP_SHIFT; // loop_addr + break; + } + } + } + + if (Pico_mcd->pcm_ram[addr >> PCM_STEP_SHIFT] == 0xff) + addr = (unsigned int)(*(unsigned short *)&ch->regs[4]) << PCM_STEP_SHIFT; // loop_addr + + ch->addr = addr; + } +} + diff --git a/Pico/cd/pcm.h b/Pico/cd/pcm.h new file mode 100644 index 0000000..8756bb0 --- /dev/null +++ b/Pico/cd/pcm.h @@ -0,0 +1,7 @@ + +#define PCM_STEP_SHIFT 11 + +void pcm_write(unsigned int a, unsigned int d); +void pcm_set_rate(int rate); +void pcm_update(int *buffer, int length, int stereo); + diff --git a/Pico/sound/sound.c b/Pico/sound/sound.c index 18cc46e..a9f2f68 100644 --- a/Pico/sound/sound.c +++ b/Pico/sound/sound.c @@ -255,8 +255,8 @@ int sound_render(int offset, int length) pcm_update(buf32, length, stereo); // CD: CDDA audio -// if ((PicoMCD & 1) && (PicoOpt & 0x800)) -// mp3_update(PsndBuffer+offset, length, stereo); + if ((PicoMCD & 1) && (PicoOpt & 0x800)) + mp3_update(buf32, length, stereo); // convert + limit to normal 16bit output if (stereo) diff --git a/platform/gp2x/940ctl_ym2612.c b/platform/gp2x/940ctl_ym2612.c index 29fd8bf..1059b5d 100644 --- a/platform/gp2x/940ctl_ym2612.c +++ b/platform/gp2x/940ctl_ym2612.c @@ -12,9 +12,8 @@ #include "emu.h" #include "menu.h" #include "asmutils.h" +#include "mp3.h" #include "../../Pico/PicoInt.h" - -// tmp #include "../../Pico/sound/mix.h" /* we will need some gp2x internals here */ @@ -23,8 +22,8 @@ extern volatile unsigned long *gp2x_memregl; static unsigned char *shared_mem = 0; static _940_data_t *shared_data = 0; -static _940_ctl_t *shared_ctl = 0; -static unsigned char *mp3_mem = 0; +_940_ctl_t *shared_ctl = 0; +unsigned char *mp3_mem = 0; #define MP3_SIZE_MAX (0x1000000 - 4*640*480) @@ -226,26 +225,17 @@ int YM2612PicoTick_940(int n) return 0; } -static int g_busy = 10; -static void wait_busy_940(void) +#define CHECK_BUSY(job) \ + (gp2x_memregs[0x3b46>>1] & (1<<(job-1))) + +static void wait_busy_940(int job) { int i; -#if 0 - printf("940 busy, entering wait loop.. (cnt: %i, wc: %i, ve: ", shared_ctl->loopc, shared_ctl->waitc); - for (i = 0; i < 8; i++) - printf("%i ", shared_ctl->vstarts[i]); - printf(")\n"); - for (i = 0; shared_ctl->busy; i++) - { - spend_cycles(1024); /* needs tuning */ - } - printf("wait iterations: %i\n", i); -#else - for (i = 0; /*shared_ctl->busy*/gp2x_memregs[0x3B3E>>1] && i < 0x10000; i++) + job--; + for (i = 0; (gp2x_memregs[0x3b46>>1] & (1<vstarts[i]); printf(")\n"); - printf("irq pending flags: DUALCPU %04x (see 15?), SRCPND %08lx (see 26), INTPND %08lx\n", + printf("irq pending flags: DUALCPU %04x, SRCPND %08lx (see 26), INTPND %08lx\n", gp2x_memregs[0x3b46>>1], gp2x_memregl[0x4500>>2], gp2x_memregl[0x4510>>2]); - printf("last irq PC: %08x, lastjob: 0x%03x, busy: %x, lastbusy: %x, g_busy: %x\n", shared_ctl->last_irq_pc, - shared_ctl->lastjob, gp2x_memregs[0x3B3E>>1]/*shared_ctl->busy*/, shared_ctl->lastbusy, g_busy); + printf("last lr: %08x, lastjob: %i\n", shared_ctl->last_lr, shared_ctl->lastjob); printf("trying to interrupt..\n"); gp2x_memregs[0x3B3E>>1] = 0xffff; - for (i = 0; /*shared_ctl->busy*/gp2x_memregs[0x3B3E>>1] && i < 0x10000; i++) + for (i = 0; gp2x_memregs[0x3b46>>1] && i < 0x10000; i++) spend_cycles(8*1024); printf("i = 0x%x\n", i); - printf("irq pending flags: DUALCPU %04x (see 15?), SRCPND %08lx (see 26), INTPND %08lx\n", + printf("irq pending flags: DUALCPU %04x, SRCPND %08lx (see 26), INTPND %08lx\n", gp2x_memregs[0x3b46>>1], gp2x_memregl[0x4500>>2], gp2x_memregl[0x4510>>2]); - printf("last irq PC: %08x, lastjob: 0x%03x, busy: %x\n", shared_ctl->last_irq_pc, shared_ctl->lastjob, gp2x_memregs[0x3B3E>>1]/*shared_ctl->busy*/); + printf("last lr: %08x, lastjob: %i\n", shared_ctl->last_lr, shared_ctl->lastjob); strcpy(menuErrorMsg, "940 crashed."); engineState = PGS_Menu; crashed_940 = 1; -#endif } -static void add_job_940(int job0, int job1) +static void add_job_940(int job) { -/* if (gp2x_memregs[0x3b46>>1] || shared_ctl->busy) - { - printf("!!add_job_940: irq pending flags: DUALCPU %04x (see 15?), SRCPND %08lx (see 26), INTPND %08lx, busy: %x\n", - gp2x_memregs[0x3b46>>1], gp2x_memregl[0x4500>>2], gp2x_memregl[0x4510>>2], shared_ctl->busy); + if (job <= 0 || job > 16) { + printf("add_job_940: bad job: %i\n", job); + return; } -*/ - shared_ctl->jobs[0] = job0; - shared_ctl->jobs[1] = job1; - //shared_ctl->busy = ++g_busy; // 1; - gp2x_memregs[0x3B3E>>1] = ++g_busy; // set busy flag -// gp2x_memregs[0x3B3E>>1] = 0xffff; // cause interrupt + + // generate interrupt for this job + job--; + gp2x_memregs[(0x3B20+job*2)>>1] = 1; + +// printf("added %i, pending %04x\n", job+1, gp2x_memregs[0x3b46>>1]); } @@ -293,8 +280,6 @@ void YM2612PicoStateLoad_940(void) { int i, old_A1 = addr_A1; - if (/*shared_ctl->busy*/gp2x_memregs[0x3B3E>>1]) wait_busy_940(); - // feed all the registers and update internal state for(i = 0; i < 0x100; i++) { YM2612Write_940(0, i); @@ -307,7 +292,7 @@ void YM2612PicoStateLoad_940(void) addr_A1 = old_A1; - add_job_940(JOB940_PICOSTATELOAD, 0); + add_job_940(JOB940_PICOSTATELOAD); } @@ -338,6 +323,11 @@ void YM2612Init_940(int baseclock, int rate) gp2x_memregs[0x3B40>>1] = 0; // disable DUALCPU interrupts for 920 gp2x_memregs[0x3B42>>1] = 1; // enable DUALCPU interrupts for 940 + gp2x_memregl[0x4504>>2] = 0; // make sure no FIQs will be generated + gp2x_memregl[0x4508>>2] = ~(1<<26); // unmask DUALCPU ints in the undocumented 940's interrupt controller + + + if (shared_mem == NULL) { shared_mem = (unsigned char *) mmap(0, 0x210000, PROT_READ|PROT_WRITE, MAP_SHARED, memdev, 0x2000000); @@ -406,24 +396,20 @@ void YM2612Init_940(int baseclock, int rate) loaded_mp3 = 0; - /* now cause 940 to init it's ym2612 stuff */ - shared_ctl->baseclock = baseclock; - shared_ctl->rate = rate; - shared_ctl->jobs[0] = JOB940_INITALL; - shared_ctl->jobs[1] = 0; - //shared_ctl->busy = 1; - gp2x_memregs[0x3B3E>>1] = 1; // set busy flag - gp2x_memregs[0x3B46>>1] = 0xffff; // clear pending DUALCPU interrupts for 940 - gp2x_memregl[0x4500>>2] = 0; // clear pending IRQs in SRCPND - gp2x_memregl[0x4510>>2] = 0; // clear pending IRQs in INTPND - gp2x_memregl[0x4508>>2] = ~(1<<26); // unmask DUALCPU ints in the undocumented 940's interrupt controller + gp2x_memregl[0x4500>>2] = 0xffffffff; // clear pending IRQs in SRCPND + gp2x_memregl[0x4510>>2] = 0xffffffff; // clear pending IRQs in INTPND /* start the 940 */ Reset940(0, 2); Pause940(0); // YM2612ResetChip_940(); // will be done on JOB940_YM2612INIT + + /* now cause 940 to init it's ym2612 stuff */ + shared_ctl->baseclock = baseclock; + shared_ctl->rate = rate; + add_job_940(JOB940_INITALL); } @@ -435,72 +421,85 @@ void YM2612ResetChip_940(void) return; } - if (/*shared_ctl->busy*/gp2x_memregs[0x3B3E>>1]) wait_busy_940(); - internal_reset(); - add_job_940(JOB940_YM2612RESETCHIP, 0); + add_job_940(JOB940_YM2612RESETCHIP); } -//extern int pcm_buffer[2*44100/50]; -/* -static void mix_samples(int *dest_buf, short *mp3_buf, int len, int stereo) +int YM2612UpdateOne_940(int *buffer, int length, int stereo, int is_buf_empty) { -// int *pcm = pcm_buffer + offset * 2; + int *ym_buf = shared_data->ym_buffer; - if (stereo) - { - for (; len > 0; len--) - { - int lm, rm; - lm = *mp3_buf++; rm = *mp3_buf++; - *dest_buf++ += lm - lm/2; *dest_buf++ += rm - rm/2; - } + //printf("YM2612UpdateOne_940()\n"); + + if (CHECK_BUSY(JOB940_YM2612UPDATEONE)) wait_busy_940(JOB940_YM2612UPDATEONE); + + // mix in ym buffer + if (is_buf_empty) memcpy32(buffer, ym_buf, length< 0; len--) +// { +// *dest_buf++ += *ym_buf++; +// } + + if (shared_ctl->writebuffsel == 1) { + shared_ctl->writebuff0[writebuff_ptr] = 0xffff; } else { - for (; len > 0; len--) - { - int l = *mp3_buf++; - *dest_buf++ = l - l/2; - } + shared_ctl->writebuff1[writebuff_ptr] = 0xffff; } + writebuff_ptr = 0; + + /* predict sample counter for next frame */ + if (PsndLen_exc_add) { + if (PsndLen_exc_cnt + PsndLen_exc_add >= 0x10000) length = PsndLen + 1; + else length = PsndLen; + } + + /* give 940 ym job */ + shared_ctl->writebuffsel ^= 1; + shared_ctl->length = length; + shared_ctl->stereo = stereo; + + add_job_940(JOB940_YM2612UPDATEONE); + + return 1; } -*/ -// here we assume that length is different between games, but constant in one game static int mp3_samples_ready = 0, mp3_buffer_offs = 0; -static int mp3_play_bufsel = 0; +static int mp3_play_bufsel = 0, mp3_job_started = 0; -int YM2612UpdateOne_940(int *buffer, int length, int stereo, int is_buf_empty) +void mp3_update(int *buffer, int length, int stereo) { - int length_mp3 = Pico.m.pal ? 44100/50 : 44100/60; // mp3s are locked to 44100Hz stereo - int *ym_buf = shared_data->mix_buffer; -// int *dest_buf = buffer; - int cdda_on, mp3_job = 0; -// int len; + int length_mp3; + int cdda_on; - // emulating CD, enabled in opts, not data track, CDC is reading, playback was started, track not ended - cdda_on = (PicoMCD & 1) && (PicoOpt & 0x800) && !(Pico_mcd->s68k_regs[0x36] & 1) && - (Pico_mcd->scd.Status_CDC & 1) && loaded_mp3; + // not data track, CDC is reading, playback was started, track not ended + cdda_on = !(Pico_mcd->s68k_regs[0x36] & 1) && (Pico_mcd->scd.Status_CDC & 1) && + loaded_mp3 && shared_ctl->mp3_offs < shared_ctl->mp3_len; - //printf("YM2612UpdateOne_940()\n"); - if (/*shared_ctl->busy*/gp2x_memregs[0x3B3E>>1]) wait_busy_940(); + if (!cdda_on) return; - // track ended? - cdda_on = cdda_on && shared_ctl->mp3_offs < shared_ctl->mp3_len; + if (!(PicoOpt&0x200)) { + mp3_update_local(buffer, length, stereo); + return; + } - // mix in ym buffer - if (is_buf_empty) memcpy32(buffer, ym_buf, length< 0; len--) -// { -// *dest_buf++ += *ym_buf++; -// } + /* do we have to wait? */ + if (mp3_job_started && mp3_samples_ready < length_mp3) { + if (CHECK_BUSY(JOB940_MP3DECODE)) wait_busy_940(JOB940_MP3DECODE); + mp3_job_started = 0; + mp3_samples_ready += 1152; + } /* mix mp3 data, only stereo */ - if (cdda_on && mp3_samples_ready >= length_mp3) + if (mp3_samples_ready >= length_mp3) { int shr = 0; void (*mix_samples)(int *dest_buf, short *mp3_buf, int count) = mix_16h_to_32; @@ -508,15 +507,15 @@ int YM2612UpdateOne_940(int *buffer, int length, int stereo, int is_buf_empty) else if (PsndRate == 11025) { mix_samples = mix_16h_to_32_s2; shr = 2; } if (1152 - mp3_buffer_offs >= length_mp3) { - mix_samples(buffer, shared_data->mp3_buffer[mp3_play_bufsel] + mp3_buffer_offs*2, (length_mp3>>shr)<<1); + mix_samples(buffer, shared_data->mp3_buffer[mp3_play_bufsel] + mp3_buffer_offs*2, length<<1); mp3_buffer_offs += length_mp3; } else { - // collect from both buffers.. + // collect samples from both buffers.. int left = 1152 - mp3_buffer_offs; if (mp3_play_bufsel == 0) { - mix_samples(buffer, shared_data->mp3_buffer[0] + mp3_buffer_offs*2, (length_mp3>>shr)<<1); + mix_samples(buffer, shared_data->mp3_buffer[0] + mp3_buffer_offs*2, length<<1); mp3_buffer_offs = length_mp3 - left; mp3_play_bufsel = 1; } else { @@ -530,38 +529,13 @@ int YM2612UpdateOne_940(int *buffer, int length, int stereo, int is_buf_empty) mp3_samples_ready -= length_mp3; } - if (shared_ctl->writebuffsel == 1) { - shared_ctl->writebuff0[writebuff_ptr] = 0xffff; - } else { - shared_ctl->writebuff1[writebuff_ptr] = 0xffff; - } - writebuff_ptr = 0; - - /* predict sample counter for next frame */ - if (PsndLen_exc_add) { - if (PsndLen_exc_cnt + PsndLen_exc_add >= 0x10000) length = PsndLen + 1; - else length = PsndLen; - } - - /* give 940 ym job */ - shared_ctl->writebuffsel ^= 1; - shared_ctl->length = length; - shared_ctl->stereo = stereo; - - // make sure we will have enough mp3 samples next frame - if (cdda_on && mp3_samples_ready < length_mp3) + // ask to decode more if we already can + if (!mp3_job_started) { + mp3_job_started = 1; shared_ctl->mp3_buffsel ^= 1; - mp3_job = JOB940_MP3DECODE; - mp3_samples_ready += 1152; + add_job_940(JOB940_MP3DECODE); } - - add_job_940(JOB940_YM2612UPDATEONE, mp3_job); - //spend_cycles(512); - //printf("SRCPND: %08lx, INTMODE: %08lx, INTMASK: %08lx, INTPEND: %08lx\n", - // gp2x_memregl[0x4500>>2], gp2x_memregl[0x4504>>2], gp2x_memregl[0x4508>>2], gp2x_memregl[0x4510>>2]); - - return 1; } @@ -584,6 +558,12 @@ void mp3_start_play(FILE *f, int pos) // pos is 0-1023 // else printf("done. mp3 too large, not all data loaded.\n"); shared_ctl->mp3_len = ftell(f); loaded_mp3 = f; + + if (PicoOpt&0x200) { + // as we are going to change 940's cacheable area, we must invalidate it's cache.. + if (CHECK_BUSY(JOB940_MP3DECODE)) wait_busy_940(JOB940_MP3DECODE); + add_job_940(JOB940_INVALIDATE_DCACHE); + } } // seek.. @@ -596,9 +576,12 @@ void mp3_start_play(FILE *f, int pos) // pos is 0-1023 shared_ctl->mp3_offs = byte_offs; - // reset buffer pointers.. + // reset buffer pointers and stuff.. mp3_samples_ready = mp3_buffer_offs = mp3_play_bufsel = 0; + mp3_job_started = 0; shared_ctl->mp3_buffsel = 1; // will change to 0 on first decode + + if (!(PicoOpt&0x200)) mp3_start_local(); } @@ -608,7 +591,7 @@ int mp3_get_offset(void) int cdda_on; cdda_on = (PicoMCD & 1) && (PicoOpt&0x800) && !(Pico_mcd->s68k_regs[0x36] & 1) && - (Pico_mcd->scd.Status_CDC & 1) && loaded_mp3 && shared_ctl->mp3_offs < shared_ctl->mp3_len; + (Pico_mcd->scd.Status_CDC & 1) && loaded_mp3; if (cdda_on) { offs1024 = shared_ctl->mp3_offs << 7; diff --git a/platform/gp2x/code940/940.c b/platform/gp2x/code940/940.c index 18d282c..b242603 100644 --- a/platform/gp2x/code940/940.c +++ b/platform/gp2x/code940/940.c @@ -6,10 +6,11 @@ static unsigned char *mp3_data = (unsigned char *) 0x01000000; YM2612 *ym2612_940; // from init.s -void wait_irq(void); +int wait_get_job(int oldjob); void spend_cycles(int c); -void cache_clean(void); -void cache_clean_flush(void); +void dcache_clean(void); +void dcache_clean_flush(void); +void drain_wb(void); // this should help to resolve race confition where shared var // is changed by other core just before we update it void set_if_not_changed(int *val, int oldval, int newval); @@ -41,7 +42,6 @@ static void mp3_decode(void) shared_data->mp3_buffer[shared_ctl->mp3_buffsel], 0); if (err) { if (err == ERR_MP3_INDATA_UNDERFLOW) { - shared_ctl->mp3_offs = shared_ctl->mp3_len; // EOF set_if_not_changed(&shared_ctl->mp3_offs, mp3_offs, shared_ctl->mp3_len); return; } else if (err <= -6 && err >= -12) { @@ -56,97 +56,73 @@ static void mp3_decode(void) } -void Main940(int startvector, int pc_at_irq) +void Main940(void) { - int mix_buffer = shared_data->mix_buffer; + int *ym_buffer = shared_data->ym_buffer; + int job = 0; ym2612_940 = &shared_data->ym2612; - // debug - shared_ctl->vstarts[startvector]++; - shared_ctl->last_irq_pc = pc_at_irq; - // asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); - -// for (;;) + for (;;) { - int job_num = 0; -/* - while (!shared_ctl->busy) - { - //shared_ctl->waitc++; - spend_cycles(8*1024); - } -*/ -/* - if (!shared_ctl->busy) - { - wait_irq(); - } - shared_ctl->lastbusy = shared_ctl->busy; -*/ + job = wait_get_job(job); + + shared_ctl->lastjob = job; - for (job_num = 0; job_num < MAX_940JOBS; job_num++) + switch (job) { - shared_ctl->lastjob = (job_num << 8) | shared_ctl->jobs[job_num]; - - switch (shared_ctl->jobs[job_num]) - { - case JOB940_INITALL: - /* ym2612 */ - shared_ctl->writebuff0[0] = shared_ctl->writebuff1[0] = 0xffff; - YM2612Init_(shared_ctl->baseclock, shared_ctl->rate); - /* Helix mp3 decoder */ - shared_data->mp3dec = MP3InitDecoder(); - break; - - case JOB940_YM2612RESETCHIP: - YM2612ResetChip_(); - break; - - case JOB940_PICOSTATELOAD: - YM2612PicoStateLoad_(); - break; - - case JOB940_YM2612UPDATEONE: { - int i, dw, *wbuff; - if (shared_ctl->writebuffsel == 1) { - wbuff = (int *) shared_ctl->writebuff1; - } else { - wbuff = (int *) shared_ctl->writebuff0; - } - - /* playback all writes */ - for (i = 2048/2; i > 0; i--) { - UINT16 d; - dw = *wbuff++; - d = dw; - if (d == 0xffff) break; - YM2612Write_(d >> 8, d); - d = (dw>>16); - if (d == 0xffff) break; - YM2612Write_(d >> 8, d); - } - - YM2612UpdateOne_(mix_buffer, shared_ctl->length, shared_ctl->stereo, 1); - break; + case JOB940_INITALL: + /* ym2612 */ + shared_ctl->writebuff0[0] = shared_ctl->writebuff1[0] = 0xffff; + YM2612Init_(shared_ctl->baseclock, shared_ctl->rate); + /* Helix mp3 decoder */ + shared_data->mp3dec = MP3InitDecoder(); + break; + + case JOB940_INVALIDATE_DCACHE: + drain_wb(); + dcache_clean_flush(); + break; + + case JOB940_YM2612RESETCHIP: + YM2612ResetChip_(); + break; + + case JOB940_PICOSTATELOAD: + YM2612PicoStateLoad_(); + break; + + case JOB940_YM2612UPDATEONE: { + int i, dw, *wbuff; + if (shared_ctl->writebuffsel == 1) { + wbuff = (int *) shared_ctl->writebuff1; + } else { + wbuff = (int *) shared_ctl->writebuff0; } - case JOB940_MP3DECODE: - mp3_decode(); - break; + /* playback all writes */ + for (i = 2048/2; i > 0; i--) { + UINT16 d; + dw = *wbuff++; + d = dw; + if (d == 0xffff) break; + YM2612Write_(d >> 8, d); + d = (dw>>16); + if (d == 0xffff) break; + YM2612Write_(d >> 8, d); + } + + YM2612UpdateOne_(ym_buffer, shared_ctl->length, shared_ctl->stereo, 1); + break; } - } - cache_clean(); -// asm volatile ("mov r0, #0" ::: "r0"); -// asm volatile ("mcr p15, 0, r0, c7, c10, 4" ::: "r0"); /* drain write buffer, should be done on nonbuffered write */ -// cache_clean_flush(); + case JOB940_MP3DECODE: + mp3_decode(); + break; + } shared_ctl->loopc++; - -// // shared_ctl->busy = 0; // shared mem is not reliable? - - wait_irq(); + dcache_clean(); } } diff --git a/platform/gp2x/code940/940init.s b/platform/gp2x/code940/940init.s index 750e0ae..2a17a9c 100644 --- a/platform/gp2x/code940/940init.s +++ b/platform/gp2x/code940/940init.s @@ -1,8 +1,13 @@ -.global code940 +@ vim:filetype=armasm: + .equ mmsp2_regs, (0xc0000000-0x02000000) @ assume we live @ 0x2000000 bank +.equ shared_ctl, 0x00200000 @ this is where shared_ctl struncture is located + -code940: @ interrupt table: +@ exception table: +.global code940 +code940: b .b_reset @ reset b .b_undef @ undefined instructions b .b_swi @ software interrupt @@ -32,19 +37,23 @@ code940: @ interrupt table: mov r12, #5 b .Begin .b_irq: - mov r12, #6 mov sp, #0x100000 @ reset stack sub sp, sp, #4 - mov r1, #mmsp2_regs - orr r2, r1, #0x3B00 - orr r2, r2, #0x0046 - mvn r3, #0 - strh r3, [r2] @ clear any pending interrupts from the DUALCPU unit - orr r2, r1, #0x4500 - str r3, [r2] @ clear all pending interrupts in irq controller's SRCPND register - orr r2, r2, #0x0010 - str r3, [r2] @ clear all pending interrupts in irq controller's INTPND register - b .Enter + mov r0, #shared_ctl @ remember where we were when interrupt happened + add r0, r0, #0x20 + str lr, [r0] + mov r0, #shared_ctl @ increment exception counter (for debug) + add r0, r0, #(6*4) + ldr r1, [r0] + add r1, r1, #1 + str r1, [r0] + + bl Main940 + + @ we should never get here + b .b_reserved + + .b_fiq: mov r12, #7 b .Begin @@ -81,8 +90,13 @@ code940: @ interrupt table: mcr p15, 0, r0, c6, c4, 0 mcr p15, 0, r0, c6, c4, 1 - @ set regions 1 and 4 to be cacheable (so the first 2M and mp3 area will be cacheable) - mov r0, #(1<<1)|(1<<4) + @ region 5: 4K 0x00000000-0x00001000 (boot code protection region) + mov r0, #(0x0b<<1)|1 + mcr p15, 0, r0, c6, c5, 0 + mcr p15, 0, r0, c6, c5, 1 + + @ set regions 1, 4 and 5 to be cacheable (so the first 2M and mp3 area will be cacheable) + mov r0, #(1<<1)|(1<<4)|(1<<5) mcr p15, 0, r0, c2, c0, 0 mcr p15, 0, r0, c2, c0, 1 @@ -90,10 +104,13 @@ code940: @ interrupt table: mov r0, #(1<<1) mcr p15, 0, r0, c3, c0, 0 - @ set protection, allow access only to regions 1 and 2 - mov r0, #(3<<8)|(3<<6)|(3<<4)|(3<<2)|(0) @ data: [full, full, full, full, no access] for regions [4 3 2 1 0] + @ set access protection + @ data: [no, full, full, full, full, no access] for regions [5 4 3 2 1 0] + mov r0, #(0<<10)|(3<<8)|(3<<6)|(3<<4)|(3<<2)|(0) mcr p15, 0, r0, c5, c0, 0 - mov r0, #(0<<8)|(0<<6)|(0<<4)|(3<<2)|(0) @ instructions: [no access, no, no, full, no] + @ instructions: [full, no access, no, no, full, no] + mov r0, #(0<< 6)|(0<<4)|(3<<2)|(0) + orr r0, r0, #(3<<10)|(0<<8) mcr p15, 0, r0, c5, c0, 1 mrc p15, 0, r0, c1, c0, 0 @ fetch current control reg @@ -109,19 +126,74 @@ code940: @ interrupt table: mov r0, #0 mcr p15, 0, r0, c7, c6, 0 -.Enter: - mov r0, r12 - mov r1, lr - bl Main940 + @ remember which exception vector we came from (increment counter for debug) + mov r0, #shared_ctl + add r0, r0, r12, lsl #2 + ldr r1, [r0] + add r1, r1, #1 + str r1, [r0] + + @ remember last lr (for debug) + mov r0, #shared_ctl + add r0, r0, #0x20 + str lr, [r0] + + @ ready to take first job-interrupt +wait_for_irq: + mrs r0, cpsr + bic r0, r0, #0x80 + msr cpsr_c, r0 @ enable interrupts + + mov r0, #0 + mcr p15, 0, r0, c7, c0, 4 @ wait for IRQ +@ mcr p15, 0, r0, c15, c8, 2 + nop + nop + b .b_reserved + - @ we should never get here -@.b_deadloop: -@ b .b_deadloop - b .b_reserved +@ next job getter +.global wait_get_job @ int oldjob +wait_get_job: + mov r3, #mmsp2_regs + orr r2, r3, #0x3B00 + orr r2, r2, #0x0046 @ DUALPEND940 register + ldrh r12,[r2] -@ so asm utils are also defined here: + tst r0, r0 + beq wgj_no_old + sub r0, r0, #1 + mov r1, #1 + mov r1, r1, lsl r0 + strh r1, [r2] @ clear finished job's pending bit + bic r12,r12,r1 + +wgj_no_old: + tst r12,r12 + beq wgj_no_jobs + mov r0, #0 +wgj_loop: + add r0, r0, #1 + movs r12,r12,lsr #1 + bxcs lr + b wgj_loop + +wgj_no_jobs: + mvn r0, #0 + orr r2, r3, #0x4500 + str r0, [r2] @ clear all pending interrupts in irq controller's SRCPND register + orr r2, r2, #0x0010 + str r0, [r2] @ clear all pending interrupts in irq controller's INTPND register + b wait_for_irq + +.pool + + + + +@ some asm utils are also defined here: .global spend_cycles @ c spend_cycles: @@ -135,16 +207,16 @@ spend_cycles: @ clean-flush function from ARM940T technical reference manual -.global cache_clean_flush +.global dcache_clean_flush -cache_clean_flush: +dcache_clean_flush: mov r1, #0 @ init line counter ccf_outer_loop: mov r0, #0 @ segment counter ccf_inner_loop: orr r2, r1, r0 @ make segment and line address mcr p15, 0, r2, c7, c14, 2 @ clean and flush that line - add r0, r0, #0x10 @ incremet secment counter + add r0, r0, #0x10 @ incremet segment counter cmp r0, #0x40 @ complete all 4 segments? bne ccf_inner_loop add r1, r1, #0x04000000 @ increment line counter @@ -153,17 +225,18 @@ ccf_inner_loop: bx lr + @ clean-only version -.global cache_clean +.global dcache_clean -cache_clean: +dcache_clean: mov r1, #0 @ init line counter cf_outer_loop: mov r0, #0 @ segment counter cf_inner_loop: orr r2, r1, r0 @ make segment and line address mcr p15, 0, r2, c7, c10, 2 @ clean that line - add r0, r0, #0x10 @ incremet secment counter + add r0, r0, #0x10 @ incremet segment counter cmp r0, #0x40 @ complete all 4 segments? bne cf_inner_loop add r1, r1, #0x04000000 @ increment line counter @@ -172,31 +245,13 @@ cf_inner_loop: bx lr -.global wait_irq - -wait_irq: - mov r0, #mmsp2_regs - orr r0, r0, #0x3B00 - orr r1, r0, #0x0042 - mov r3, #0 - strh r3, [r1] @ disable interrupts - orr r2, r0, #0x003E - strh r3, [r2] @ remove busy flag - mov r3, #1 - strh r3, [r1] @ enable interrupts - - mrs r0, cpsr - bic r0, r0, #0x80 - msr cpsr_c, r0 @ enable interrupts +@ drain write buffer +.global drain_wb +drain_wb: mov r0, #0 - mcr p15, 0, r0, c7, c0, 4 @ wait for IRQ -@ mcr p15, 0, r0, c15, c8, 2 - nop - nop - b .b_reserved - -.pool + mcr p15, 0, r0, c7, c10, 4 + bx lr .global set_if_not_changed @ int *val, int oldval, int newval @@ -208,4 +263,10 @@ set_if_not_changed: strne r3, [r0] @ restore value which was changed there by other core bx lr -@ vim:filetype=armasm: + + +@ pad the protected region. +.rept 1024 +.long 0 +.endr + diff --git a/platform/gp2x/code940/940shared.h b/platform/gp2x/code940/940shared.h index c208e67..8953196 100644 --- a/platform/gp2x/code940/940shared.h +++ b/platform/gp2x/code940/940shared.h @@ -1,30 +1,33 @@ #include "../../../Pico/sound/ym2612.h" #include "../helix/pub/mp3dec.h" +// max 16 jobs enum _940_job_t { JOB940_INITALL = 1, + JOB940_INVALIDATE_DCACHE, JOB940_YM2612RESETCHIP, JOB940_YM2612UPDATEONE, - JOB940_PICOSTATELOAD, JOB940_MP3DECODE, - JOB940_NUMJOBS + JOB940_PICOSTATELOAD, }; -#define MAX_940JOBS 2 +//#define MAX_940JOBS 2 typedef struct { - YM2612 ym2612; /* current state of the emulated YM2612 */ + YM2612 ym2612; /* current state of the emulated YM2612 */ HMP3Decoder mp3dec; /* mp3 decoder's handle */ - int mix_buffer[44100/50*2]; /* this is where the YM2612 samples will be mixed to */ + int ym_buffer[44100/50*2]; /* this is where the YM2612 samples will be mixed to */ short mp3_buffer[2][1152*2]; /* buffers for mp3 decoder's output */ } _940_data_t; typedef struct { - int jobs[MAX_940JOBS]; /* jobs for second core */ - int busy_; /* unused */ + int vstarts[8]; /* debug: 00: number of starts from each of 8 vectors */ + int last_lr; /* debug: 20: last exception's lr */ +// int jobs[MAX_940JOBS]; /* jobs for second core */ +// int busy_; /* unused */ int length; /* number of samples to mix (882 max) */ int stereo; /* mix samples as stereo, doubles sample count automatically */ int baseclock; /* ym2612 settings */ @@ -35,11 +38,8 @@ typedef struct int mp3_len; /* data len of loaded mp3 */ int mp3_offs; /* current playback offset (just after last decoded frame) */ int mp3_buffsel; /* which output buffer to decode to */ - int vstarts[8]; /* debug: number of starts from each of 8 vectors */ - int loopc; /* debug: main loop counter */ + int loopc; /* debug: main loop counter */ int mp3_errors; /* debug: mp3 decoder's error counter */ int mp3_lasterr; /* debug: mp3 decoder's last error */ - int last_irq_pc; /* debug: PC value when IRQ happened */ int lastjob; /* debug: last job id */ - int lastbusy; /* debug: */ } _940_ctl_t; diff --git a/platform/gp2x/mp3.c b/platform/gp2x/mp3.c new file mode 100644 index 0000000..e37b774 --- /dev/null +++ b/platform/gp2x/mp3.c @@ -0,0 +1,134 @@ +#include +#include + +#include "../../Pico/sound/mix.h" +#include "code940/940shared.h" +#include "helix/pub/mp3dec.h" + +static short mp3_out_buffer[2*1152]; +static HMP3Decoder mp3dec = 0; +static int mp3_buffer_offs = 0; + +extern _940_ctl_t *shared_ctl; +extern unsigned char *mp3_mem; +extern int PsndRate; + + +static int try_get_header(unsigned char *buff, MP3FrameInfo *fi) +{ + int ret, offs1, offs = 0; + + while (1) + { + offs1 = MP3FindSyncWord(buff + offs, 2048 - offs); + if (offs1 < 0) return -2; + offs += offs1; + if (2048 - offs < 4) return -3; + + // printf("trying header %08x\n", *(int *)(buff + offs)); + + ret = MP3GetNextFrameInfo(mp3dec, fi, buff + offs); + if (ret == 0 && fi->bitrate != 0) break; + offs++; + } + + return ret; +} + +int mp3_get_bitrate(FILE *f, int len) +{ + unsigned char buff[2048]; + MP3FrameInfo fi; + int ret; + + memset(buff, 0, 2048); + + if (!mp3dec) mp3dec = MP3InitDecoder(); + + fseek(f, 0, SEEK_SET); + ret = fread(buff, 1, 2048, f); + fseek(f, 0, SEEK_SET); + if (ret <= 0) return -1; + + ret = try_get_header(buff, &fi); + if (ret != 0 || fi.bitrate == 0) { + // try to read somewhere around the middle + fseek(f, len>>1, SEEK_SET); + fread(buff, 1, 2048, f); + fseek(f, 0, SEEK_SET); + ret = try_get_header(buff, &fi); + } + if (ret != 0) return ret; + + // printf("bitrate: %i\n", fi.bitrate / 1000); + + return fi.bitrate / 1000; +} + + +static void mp3_decode(void) +{ + // tried copying this to cached mem, no improvement noticed + int mp3_offs = shared_ctl->mp3_offs; + unsigned char *readPtr = mp3_mem + mp3_offs; + int bytesLeft = shared_ctl->mp3_len - mp3_offs; + int offset; // frame offset from readPtr + int err; + + if (bytesLeft <= 0) return; // EOF, nothing to do + + offset = MP3FindSyncWord(readPtr, bytesLeft); + if (offset < 0) { + shared_ctl->mp3_offs = shared_ctl->mp3_len; + return; // EOF + } + readPtr += offset; + bytesLeft -= offset; + + err = MP3Decode(mp3dec, &readPtr, &bytesLeft, mp3_out_buffer, 0); + if (err) { + if (err == ERR_MP3_INDATA_UNDERFLOW) { + shared_ctl->mp3_offs = shared_ctl->mp3_len; // EOF + return; + } else if (err <= -6 && err >= -12) { + // ERR_MP3_INVALID_FRAMEHEADER, ERR_MP3_INVALID_* + // just try to skip the offending frame.. + readPtr++; + } + shared_ctl->mp3_errors++; + shared_ctl->mp3_lasterr = err; + } + shared_ctl->mp3_offs = readPtr - mp3_mem; +} + + +void mp3_update_local(int *buffer, int length, int stereo) +{ + int length_mp3, shr = 0; + void (*mix_samples)(int *dest_buf, short *mp3_buf, int count) = mix_16h_to_32; + + length_mp3 = length; + if (PsndRate == 22050) { mix_samples = mix_16h_to_32_s1; length_mp3 <<= 1; shr = 1; } + else if (PsndRate == 11025) { mix_samples = mix_16h_to_32_s2; length_mp3 <<= 2; shr = 2; } + + if (1152 - mp3_buffer_offs >= length_mp3) { + mix_samples(buffer, mp3_out_buffer + mp3_buffer_offs*2, length<<1); + + mp3_buffer_offs += length_mp3; + } else { + int left = 1152 - mp3_buffer_offs; + + mix_samples(buffer, mp3_out_buffer + mp3_buffer_offs*2, (left>>shr)<<1); + mp3_decode(); + mp3_buffer_offs = length_mp3 - left; + mix_samples(buffer + ((left>>shr)<<1), mp3_out_buffer, (mp3_buffer_offs>>shr)<<1); + } +} + + +void mp3_start_local(void) +{ + mp3_buffer_offs = 0; + mp3_decode(); +} + diff --git a/platform/gp2x/mp3.h b/platform/gp2x/mp3.h new file mode 100644 index 0000000..022d89c --- /dev/null +++ b/platform/gp2x/mp3.h @@ -0,0 +1,4 @@ + +void mp3_update_local(int *buffer, int length, int stereo); +void mp3_start_local(void); + -- 2.39.2