/***************************************************************************************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}8384int pcm_context_save(uint8 *state)85{86uint8 tmp8;87int bufferptr = 0;8889tmp8 = (pcm.bank - pcm.ram) >> 12;9091save_param(pcm.chan, sizeof(pcm.chan));92save_param(pcm.out, sizeof(pcm.out));93save_param(&tmp8, 1);94save_param(&pcm.enabled, sizeof(pcm.enabled));95save_param(&pcm.status, sizeof(pcm.status));96save_param(&pcm.index, sizeof(pcm.index));97save_param(pcm.ram, sizeof(pcm.ram));9899return bufferptr;100}101102int pcm_context_load(uint8 *state)103{104uint8 tmp8;105int bufferptr = 0;106107load_param(pcm.chan, sizeof(pcm.chan));108load_param(pcm.out, sizeof(pcm.out));109110load_param(&tmp8, 1);111pcm.bank = &pcm.ram[(tmp8 & 0x0f) << 12];112113load_param(&pcm.enabled, sizeof(pcm.enabled));114load_param(&pcm.status, sizeof(pcm.status));115load_param(&pcm.index, sizeof(pcm.index));116load_param(pcm.ram, sizeof(pcm.ram));117118return bufferptr;119}120121void pcm_run(unsigned int length)122{123#ifdef LOG_PCM124error("[%d][%d]run %d PCM samples (from %d)\n", v_counter, s68k.cycles, length, pcm.cycles);125#endif126/* check if PCM chip is running */127if (pcm.enabled)128{129int i, j, l, r;130131/* generate PCM samples */132for (i=0; i<length; i++)133{134/* clear output */135l = r = 0;136137/* run eight PCM channels */138for (j=0; j<8; j++)139{140/* check if channel is enabled */141if (pcm.status & (1 << j))142{143/* read from current WAVE RAM address */144short data = pcm.ram[(pcm.chan[j].addr >> 11) & 0xffff];145146/* loop data ? */147if (data == 0xff)148{149/* reset WAVE RAM address */150pcm.chan[j].addr = pcm.chan[j].ls.w << 11;151152/* read again from WAVE RAM address */153data = pcm.ram[pcm.chan[j].ls.w];154}155else156{157/* increment WAVE RAM address */158pcm.chan[j].addr += pcm.chan[j].fd.w;159}160161/* infinite loop should not output any data */162if (data != 0xff)163{164/* check sign bit (output centered around 0) */165if (data & 0x80)166{167/* PCM data is positive */168data = data & 0x7f;169}170else171{172/* PCM data is negative */173data = -(data & 0x7f);174}175176/* multiply PCM data with ENV & stereo PAN data then add to L/R outputs (14.5 fixed point) */177l += ((data * pcm.chan[j].env * (pcm.chan[j].pan & 0x0F)) >> 5);178r += ((data * pcm.chan[j].env * (pcm.chan[j].pan >> 4)) >> 5);179}180}181}182183/* limiter */184if (l < -32768) l = -32768;185else if (l > 32767) l = 32767;186if (r < -32768) r = -32768;187else if (r > 32767) r = 32767;188189/* check if PCM left output changed */190if (pcm.out[0] != l)191{192blip_add_delta_fast(blip[0], i, l-pcm.out[0]);193pcm.out[0] = l;194}195196/* check if PCM right output changed */197if (pcm.out[1] != r)198{199blip_add_delta_fast(blip[1], i, r-pcm.out[1]);200pcm.out[1] = r;201}202}203}204else205{206/* check if PCM left output changed */207if (pcm.out[0])208{209blip_add_delta_fast(blip[0], 0, -pcm.out[0]);210pcm.out[0] = 0;211}212213/* check if PCM right output changed */214if (pcm.out[1])215{216blip_add_delta_fast(blip[1], 0, -pcm.out[1]);217pcm.out[1] = 0;218}219}220221/* end of blip buffer frame */222blip_end_frame(blip[0], length);223blip_end_frame(blip[1], length);224225/* update PCM master clock counter */226pcm.cycles += length * PCM_SCYCLES_RATIO;227}228229void pcm_update(unsigned int samples)230{231/* get number of internal clocks (samples) needed */232unsigned int clocks = blip_clocks_needed(blip[0], samples);233234/* run PCM chip */235if (clocks > 0)236{237pcm_run(clocks);238}239240/* reset PCM master clocks counter */241pcm.cycles = 0;242}243244void pcm_write(unsigned int address, unsigned char data)245{246/* synchronize PCM chip with SUB-CPU */247int clocks = s68k.cycles - pcm.cycles;248if (clocks > 0)249{250/* number of internal clocks (samples) to run */251clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;252pcm_run(clocks);253}254255#ifdef LOG_PCM256error("[%d][%d]PCM write %x -> 0x%02x (%X)\n", v_counter, s68k.cycles, address, data, s68k.pc);257#endif258259/* external RAM is mapped to $1000-$1FFF */260if (address >= 0x1000)261{262/* 4K bank access */263pcm.bank[address & 0xfff] = data;264return;265}266267/* internal area si mapped to $0000-$0FFF */268switch (address)269{270case 0x00: /* ENV register */271{272/* update channel ENV multiplier */273pcm.chan[pcm.index].env = data;274return;275}276277case 0x01: /* PAN register */278{279/* update channel stereo panning value */280pcm.chan[pcm.index].pan = data;281return;282}283284case 0x02: /* FD register (LSB) */285{286/* update channel WAVE RAM address increment LSB */287pcm.chan[pcm.index].fd.byte.l = data;288return;289}290291case 0x03: /* FD register (MSB) */292{293/* update channel WAVE RAM address increment MSB */294pcm.chan[pcm.index].fd.byte.h = data;295return;296}297298case 0x04: /* LS register (LSB) */299{300/* update channel WAVE RAM loop address LSB */301pcm.chan[pcm.index].ls.byte.l = data;302return;303}304305case 0x05: /* LS register (MSB) */306{307/* update channel WAVE RAM loop address MSB */308pcm.chan[pcm.index].ls.byte.h = data;309return;310}311312case 0x06: /* ST register */313{314/* update channel WAVE RAM start address (16.11 fixed point) */315pcm.chan[pcm.index].st = data << (8 + 11);316317/* reload WAVE RAM address if channel is OFF */318if (!(pcm.status & (1 << pcm.index)))319{320pcm.chan[pcm.index].addr = pcm.chan[pcm.index].st;321}322return;323}324325case 0x07: /* CTRL register */326{327if (data & 0x40)328{329/* channel selection (0-7) */330pcm.index = data & 0x07;331}332else333{334/* external RAM bank selection (16 x 4K) */335pcm.bank = &pcm.ram[(data & 0x0f) << 12];336}337338/* update PCM chip status (bit 7) */339pcm.enabled = data & 0x80;340return;341}342343case 0x08: /* ON/OFF register */344{345/* update PCM channels status */346pcm.status = ~data;347348/* reload WAVE RAM address pointers when channels are OFF */349if (data & 0x01) pcm.chan[0].addr = pcm.chan[0].st;350if (data & 0x02) pcm.chan[1].addr = pcm.chan[1].st;351if (data & 0x04) pcm.chan[2].addr = pcm.chan[2].st;352if (data & 0x08) pcm.chan[3].addr = pcm.chan[3].st;353if (data & 0x10) pcm.chan[4].addr = pcm.chan[4].st;354if (data & 0x20) pcm.chan[5].addr = pcm.chan[5].st;355if (data & 0x40) pcm.chan[6].addr = pcm.chan[6].st;356if (data & 0x80) pcm.chan[7].addr = pcm.chan[7].st;357return;358}359360default:361{362/* illegal access */363return;364}365}366}367368unsigned char pcm_read(unsigned int address)369{370/* synchronize PCM chip with SUB-CPU */371int clocks = s68k.cycles - pcm.cycles;372if (clocks > 0)373{374/* number of internal clocks (samples) to run */375clocks = (clocks + PCM_SCYCLES_RATIO - 1) / PCM_SCYCLES_RATIO;376pcm_run(clocks);377}378379#ifdef LOG_PCM380error("[%d][%d]PCM read (%X)\n", v_counter, s68k.cycles, address, s68k.pc);381#endif382383/* external RAM (TODO: verify if possible to read, some docs claim it's not !) */384if (address >= 0x1000)385{386/* 4K bank access */387return pcm.bank[address & 0xfff];388}389390/* read WAVE RAM address pointers */391if ((address >= 0x10) && (address < 0x20))392{393int index = (address >> 1) & 0x07;394395if (address & 1)396{397return (pcm.chan[index].addr >> (11 + 8)) & 0xff;398}399else400{401return (pcm.chan[index].addr >> 11) & 0xff;402}403}404405/* illegal access */406return 0xff;407}408409void pcm_ram_dma_w(unsigned int words)410{411uint16 data;412413/* CDC buffer source address */414uint16 src_index = cdc.dac.w & 0x3ffe;415416/* PCM-RAM destination address*/417uint16 dst_index = (scd.regs[0x0a>>1].w << 2) & 0xffe;418419/* update DMA destination address */420scd.regs[0x0a>>1].w += (words >> 1);421422/* update DMA source address */423cdc.dac.w += (words << 1);424425/* DMA transfer */426while (words--)427{428/* read 16-bit word from CDC buffer */429data = *(uint16 *)(cdc.ram + src_index);430431/* write 16-bit word to PCM RAM (endianness does not matter since PCM RAM is always accessed as byte)*/432*(uint16 *)(pcm.bank + dst_index) = data ;433434/* increment CDC buffer source address */435src_index = (src_index + 2) & 0x3ffe;436437/* increment PCM-RAM destination address */438dst_index = (dst_index + 2) & 0xffe;439}440}441442443444