/***************************************************************************************1* Genesis Plus2* PCM sound chip (315-5476A) (RF5C164 compatible)3*4* Copyright (C) 2012 Eke-Eke (Genesis Plus GX)5*6* Redistribution and use of this code or any derivative works are permitted7* provided that the following conditions are met:8*9* - Redistributions may not be sold, nor may they be used in a commercial10* product or activity.11*12* - Redistributions that are modified from the original source must include the13* complete source code, including the source code for all components used by a14* binary built from the modified sources. However, as a special exception, the15* source code distributed need not include anything that is normally distributed16* (in either source or binary form) with the major components (compiler, kernel,17* and so on) of the operating system on which the executable runs, unless that18* component itself accompanies the executable.19*20* - Redistributions must reproduce the above copyright notice, this list of21* conditions and the following disclaimer in the documentation and/or other22* materials provided with the distribution.23*24* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"25* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE26* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE27* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE28* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR29* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF30* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN32* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)33* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE34* POSSIBILITY OF SUCH DAMAGE.35*36****************************************************************************************/37#include "shared.h"3839#define PCM_SCYCLES_RATIO (384 * 4)4041#define pcm scd.pcm_hw4243static blip_t* blip[2];4445void pcm_init(blip_t* left, blip_t* right)46{47/* number of SCD master clocks run per second */48double mclk = snd.frame_rate ? (SCYCLES_PER_LINE * (vdp_pal ? 313 : 262) * snd.frame_rate) : SCD_CLOCK;4950/* PCM chips is running at original rate and is synchronized with SUB-CPU */51/* Chip output is resampled to desired rate using Blip Buffer. */52blip[0] = left;53blip[1] = right;54blip_set_rates(left, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);55blip_set_rates(right, mclk / PCM_SCYCLES_RATIO, snd.sample_rate);56}5758void pcm_reset(void)59{60/* reset chip & clear external RAM */61memset(&pcm, 0, sizeof(pcm_t));6263/* reset default bank */64pcm.bank = pcm.ram;6566/* reset channels stereo panning */67pcm.chan[0].pan = 0xff;68pcm.chan[1].pan = 0xff;69pcm.chan[2].pan = 0xff;70pcm.chan[3].pan = 0xff;71pcm.chan[4].pan = 0xff;72pcm.chan[5].pan = 0xff;73pcm.chan[6].pan = 0xff;74pcm.chan[7].pan = 0xff;7576/* reset master clocks counter */77pcm.cycles = 0;7879/* clear blip buffers */80blip_clear(blip[0]);81blip_clear(blip[1]);82}838485void pcm_run(unsigned int length)86{87#ifdef LOG_PCM88error("[%d][%d]run %d PCM samples (from %d)\n", v_counter, s68k.cycles, length, pcm.cycles);89#endif90/* check if PCM chip is running */91if (pcm.enabled)92{93int i, j, l, r;9495/* generate PCM samples */96for (i=0; i<length; i++)97{98/* clear output */99l = r = 0;100101/* run eight PCM channels */102for (j=0; j<8; j++)103{104/* check if channel is enabled */105if (pcm.status & (1 << j))106{107/* read from current WAVE RAM address */108short data = pcm.ram[(pcm.chan[j].addr >> 11) & 0xffff];109110/* loop data ? */111if (data == 0xff)112{113/* reset WAVE RAM address */114pcm.chan[j].addr = pcm.chan[j].ls.w << 11;115116/* read again from WAVE RAM address */117data = pcm.ram[pcm.chan[j].ls.w];118}119else120{121/* increment WAVE RAM address */122pcm.chan[j].addr += pcm.chan[j].fd.w;123}124125/* infinite loop should not output any data */126if (data != 0xff)127{128/* check sign bit (output centered around 0) */129if (data & 0x80)130{131/* PCM data is positive */132data = data & 0x7f;133}134else135{136/* PCM data is negative */137data = -(data & 0x7f);138}139140/* multiply PCM data with ENV & stereo PAN data then add to L/R outputs (14.5 fixed point) */141l += ((data * pcm.chan[j].env * (pcm.chan[j].pan & 0x0F)) >> 5);142r += ((data * pcm.chan[j].env * (pcm.chan[j].pan >> 4)) >> 5);143}144}145}146147/* limiter */148if (l < -32768) l = -32768;149else if (l > 32767) l = 32767;150if (r < -32768) r = -32768;151else if (r > 32767) r = 32767;152153/* check if PCM left output changed */154if (pcm.out[0] != l)155{156blip_add_delta_fast(blip[0], i, l-pcm.out[0]);157pcm.out[0] = l;158}159160/* check if PCM right output changed */161if (pcm.out[1] != r)162{163blip_add_delta_fast(blip[1], i, r-pcm.out[1]);164pcm.out[1] = r;165}166}167}168else169{170/* check if PCM left output changed */171if (pcm.out[0])172{173blip_add_delta_fast(blip[0], 0, -pcm.out[0]);174pcm.out[0] = 0;175}176177/* check if PCM right output changed */178if (pcm.out[1])179{180blip_add_delta_fast(blip[1], 0, -pcm.out[1]);181pcm.out[1] = 0;182}183}184185/* end of blip buffer frame */186blip_end_frame(blip[0], length);187blip_end_frame(blip[1], length);188189/* update PCM master clock counter */190pcm.cycles += length * PCM_SCYCLES_RATIO;191}192193void pcm_update(unsigned int samples)194{195/* get number of internal clocks (samples) needed */196unsigned int clocks = blip_clocks_needed(blip[0], samples);197198/* run PCM chip */199if (clocks > 0)200{201pcm_run(clocks);202}203204/* reset PCM master clocks counter */205pcm.cycles = 0;206}207208void pcm_write(unsigned int address, unsigned char data)209{210/* synchronize PCM chip with SUB-CPU */211int clocks = s68k.cycles - pcm.cycles;212if (clocks > 0)213{214/* number of internal clocks (samples) to run */215clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;216pcm_run(clocks);217}218219#ifdef LOG_PCM220error("[%d][%d]PCM write %x -> 0x%02x (%X)\n", v_counter, s68k.cycles, address, data, s68k.pc);221#endif222223/* external RAM is mapped to $1000-$1FFF */224if (address >= 0x1000)225{226/* 4K bank access */227pcm.bank[address & 0xfff] = data;228return;229}230231/* internal area si mapped to $0000-$0FFF */232switch (address)233{234case 0x00: /* ENV register */235{236/* update channel ENV multiplier */237pcm.chan[pcm.index].env = data;238return;239}240241case 0x01: /* PAN register */242{243/* update channel stereo panning value */244pcm.chan[pcm.index].pan = data;245return;246}247248case 0x02: /* FD register (LSB) */249{250/* update channel WAVE RAM address increment LSB */251pcm.chan[pcm.index].fd.byte.l = data;252return;253}254255case 0x03: /* FD register (MSB) */256{257/* update channel WAVE RAM address increment MSB */258pcm.chan[pcm.index].fd.byte.h = data;259return;260}261262case 0x04: /* LS register (LSB) */263{264/* update channel WAVE RAM loop address LSB */265pcm.chan[pcm.index].ls.byte.l = data;266return;267}268269case 0x05: /* LS register (MSB) */270{271/* update channel WAVE RAM loop address MSB */272pcm.chan[pcm.index].ls.byte.h = data;273return;274}275276case 0x06: /* ST register */277{278/* update channel WAVE RAM start address (16.11 fixed point) */279pcm.chan[pcm.index].st = data << (8 + 11);280281/* reload WAVE RAM address if channel is OFF */282if (!(pcm.status & (1 << pcm.index)))283{284pcm.chan[pcm.index].addr = pcm.chan[pcm.index].st;285}286return;287}288289case 0x07: /* CTRL register */290{291if (data & 0x40)292{293/* channel selection (0-7) */294pcm.index = data & 0x07;295}296else297{298/* external RAM bank selection (16 x 4K) */299pcm.bank = &pcm.ram[(data & 0x0f) << 12];300}301302/* update PCM chip status (bit 7) */303pcm.enabled = data & 0x80;304return;305}306307case 0x08: /* ON/OFF register */308{309/* update PCM channels status */310pcm.status = ~data;311312/* reload WAVE RAM address pointers when channels are OFF */313if (data & 0x01) pcm.chan[0].addr = pcm.chan[0].st;314if (data & 0x02) pcm.chan[1].addr = pcm.chan[1].st;315if (data & 0x04) pcm.chan[2].addr = pcm.chan[2].st;316if (data & 0x08) pcm.chan[3].addr = pcm.chan[3].st;317if (data & 0x10) pcm.chan[4].addr = pcm.chan[4].st;318if (data & 0x20) pcm.chan[5].addr = pcm.chan[5].st;319if (data & 0x40) pcm.chan[6].addr = pcm.chan[6].st;320if (data & 0x80) pcm.chan[7].addr = pcm.chan[7].st;321return;322}323324default:325{326/* illegal access */327return;328}329}330}331332unsigned char pcm_read(unsigned int address)333{334/* synchronize PCM chip with SUB-CPU */335int clocks = s68k.cycles - pcm.cycles;336if (clocks > 0)337{338/* number of internal clocks (samples) to run */339clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;340pcm_run(clocks);341}342343#ifdef LOG_PCM344error("[%d][%d]PCM read (%X)\n", v_counter, s68k.cycles, address, s68k.pc);345#endif346347/* external RAM (TODO: verify if possible to read, some docs claim it's not !) */348if (address >= 0x1000)349{350/* 4K bank access */351return pcm.bank[address & 0xfff];352}353354/* read WAVE RAM address pointers */355if ((address >= 0x10) && (address < 0x20))356{357int index = (address >> 1) & 0x07;358359if (address & 1)360{361return (pcm.chan[index].addr >> (11 + 8)) & 0xff;362}363else364{365return (pcm.chan[index].addr >> 11) & 0xff;366}367}368369/* illegal access */370return 0xff;371}372373void pcm_ram_dma_w(unsigned int words)374{375uint16 data;376377/* CDC buffer source address */378uint16 src_index = cdc.dac.w & 0x3ffe;379380/* PCM-RAM destination address*/381uint16 dst_index = (scd.regs[0x0a>>1].w << 2) & 0xffe;382383/* update DMA destination address */384scd.regs[0x0a>>1].w += (words >> 1);385386/* update DMA source address */387cdc.dac.w += (words << 1);388389/* DMA transfer */390while (words--)391{392/* read 16-bit word from CDC buffer */393data = *(uint16 *)(cdc.ram + src_index);394395/* write 16-bit word to PCM RAM (endianness does not matter since PCM RAM is always accessed as byte)*/396*(uint16 *)(pcm.bank + dst_index) = data ;397398/* increment CDC buffer source address */399src_index = (src_index + 2) & 0x3ffe;400401/* increment PCM-RAM destination address */402dst_index = (dst_index + 2) & 0xffe;403}404}405406407408