| 1 | /* |
| 2 | * Emulation routines for the RF5C164 PCM chip |
| 3 | * (C) notaz, 2007, 2013 |
| 4 | * |
| 5 | * This work is licensed under the terms of MAME license. |
| 6 | * See COPYING file in the top-level directory. |
| 7 | */ |
| 8 | |
| 9 | #include <assert.h> |
| 10 | #include "../pico_int.h" |
| 11 | |
| 12 | #define PCM_STEP_SHIFT 11 |
| 13 | |
| 14 | void pcd_pcm_write(unsigned int a, unsigned int d) |
| 15 | { |
| 16 | unsigned int cycles = SekCyclesDoneS68k(); |
| 17 | if ((int)(cycles - Pico_mcd->pcm.update_cycles) >= 384) |
| 18 | pcd_pcm_sync(cycles); |
| 19 | |
| 20 | if (a < 7) |
| 21 | { |
| 22 | Pico_mcd->pcm.ch[Pico_mcd->pcm.cur_ch].regs[a] = d; |
| 23 | } |
| 24 | else if (a == 7) // control register |
| 25 | { |
| 26 | if (d & 0x40) |
| 27 | Pico_mcd->pcm.cur_ch = d & 7; |
| 28 | else |
| 29 | Pico_mcd->pcm.bank = d & 0xf; |
| 30 | Pico_mcd->pcm.control = d; |
| 31 | elprintf(EL_CD, "pcm control %02x", Pico_mcd->pcm.control); |
| 32 | } |
| 33 | else if (a == 8 && Pico_mcd->pcm.enabled != (u_char)~d) |
| 34 | { |
| 35 | // sound on/off |
| 36 | int was_enabled = Pico_mcd->pcm.enabled; |
| 37 | int i; |
| 38 | |
| 39 | for (i = 0; i < 8; i++) |
| 40 | if (!(was_enabled & (1 << i))) |
| 41 | Pico_mcd->pcm.ch[i].addr = |
| 42 | Pico_mcd->pcm.ch[i].regs[6] << (PCM_STEP_SHIFT + 8); |
| 43 | |
| 44 | Pico_mcd->pcm.enabled = ~d; |
| 45 | } |
| 46 | } |
| 47 | |
| 48 | unsigned int pcd_pcm_read(unsigned int a) |
| 49 | { |
| 50 | unsigned int d, cycles = SekCyclesDoneS68k(); |
| 51 | if ((int)(cycles - Pico_mcd->pcm.update_cycles) >= 384) |
| 52 | pcd_pcm_sync(cycles); |
| 53 | |
| 54 | d = Pico_mcd->pcm.ch[(a >> 1) & 7].addr >> PCM_STEP_SHIFT; |
| 55 | if (a & 1) |
| 56 | d >>= 8; |
| 57 | |
| 58 | return d & 0xff; |
| 59 | } |
| 60 | |
| 61 | void pcd_pcm_sync(unsigned int to) |
| 62 | { |
| 63 | unsigned int cycles = Pico_mcd->pcm.update_cycles; |
| 64 | int mul_l, mul_r, inc, smp; |
| 65 | struct pcm_chan *ch; |
| 66 | unsigned int addr; |
| 67 | int c, s, steps; |
| 68 | int *out; |
| 69 | |
| 70 | if ((int)(to - cycles) < 384) |
| 71 | return; |
| 72 | |
| 73 | steps = (to - cycles) / 384; |
| 74 | |
| 75 | // PCM disabled or all channels off |
| 76 | if (!(Pico_mcd->pcm.control & 0x80) || !Pico_mcd->pcm.enabled) |
| 77 | goto end; |
| 78 | |
| 79 | out = Pico_mcd->pcm_mixbuf + Pico_mcd->pcm_mixpos * 2; |
| 80 | Pico_mcd->pcm_mixbuf_dirty = 1; |
| 81 | if (Pico_mcd->pcm_mixpos + steps > PCM_MIXBUF_LEN) |
| 82 | // shouldn't happen |
| 83 | steps = PCM_MIXBUF_LEN - Pico_mcd->pcm_mixpos; |
| 84 | |
| 85 | for (c = 0; c < 8; c++) |
| 86 | { |
| 87 | if (!(Pico_mcd->pcm.enabled & (1 << c))) |
| 88 | continue; // channel disabled |
| 89 | |
| 90 | ch = &Pico_mcd->pcm.ch[c]; |
| 91 | addr = ch->addr; |
| 92 | inc = *(unsigned short *)&ch->regs[2]; |
| 93 | mul_l = ((int)ch->regs[0] * (ch->regs[1] & 0xf)) >> (5+1); |
| 94 | mul_r = ((int)ch->regs[0] * (ch->regs[1] >> 4)) >> (5+1); |
| 95 | |
| 96 | for (s = 0; s < steps; s++, addr = (addr + inc) & 0x7FFFFFF) |
| 97 | { |
| 98 | smp = Pico_mcd->pcm_ram[addr >> PCM_STEP_SHIFT]; |
| 99 | |
| 100 | // test for loop signal |
| 101 | if (smp == 0xff) |
| 102 | { |
| 103 | addr = *(unsigned short *)&ch->regs[4]; // loop_addr |
| 104 | smp = Pico_mcd->pcm_ram[addr]; |
| 105 | addr <<= PCM_STEP_SHIFT; |
| 106 | if (smp == 0xff) |
| 107 | break; |
| 108 | } |
| 109 | |
| 110 | if (smp & 0x80) |
| 111 | smp = -(smp & 0x7f); |
| 112 | |
| 113 | out[s*2 ] += smp * mul_l; // max 128 * 119 = 15232 |
| 114 | out[s*2+1] += smp * mul_r; |
| 115 | } |
| 116 | ch->addr = addr; |
| 117 | } |
| 118 | |
| 119 | end: |
| 120 | Pico_mcd->pcm.update_cycles = cycles + steps * 384; |
| 121 | Pico_mcd->pcm_mixpos += steps; |
| 122 | } |
| 123 | |
| 124 | void pcd_pcm_update(int *buf32, int length, int stereo) |
| 125 | { |
| 126 | int step, *pcm; |
| 127 | int p = 0; |
| 128 | |
| 129 | pcd_pcm_sync(SekCyclesDoneS68k()); |
| 130 | |
| 131 | if (!Pico_mcd->pcm_mixbuf_dirty || !(PicoOpt & POPT_EN_MCD_PCM)) |
| 132 | goto out; |
| 133 | |
| 134 | step = (Pico_mcd->pcm_mixpos << 16) / length; |
| 135 | pcm = Pico_mcd->pcm_mixbuf; |
| 136 | |
| 137 | if (stereo) { |
| 138 | while (length-- > 0) { |
| 139 | *buf32++ += pcm[0]; |
| 140 | *buf32++ += pcm[1]; |
| 141 | |
| 142 | p += step; |
| 143 | pcm += (p >> 16) * 2; |
| 144 | p &= 0xffff; |
| 145 | } |
| 146 | } |
| 147 | else { |
| 148 | while (length-- > 0) { |
| 149 | // mostly unused |
| 150 | *buf32++ += pcm[0]; |
| 151 | |
| 152 | p += step; |
| 153 | pcm += (p >> 16) * 2; |
| 154 | p &= 0xffff; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | memset(Pico_mcd->pcm_mixbuf, 0, |
| 159 | Pico_mcd->pcm_mixpos * 2 * sizeof(Pico_mcd->pcm_mixbuf[0])); |
| 160 | |
| 161 | out: |
| 162 | Pico_mcd->pcm_mixbuf_dirty = 0; |
| 163 | Pico_mcd->pcm_mixpos = 0; |
| 164 | } |
| 165 | |
| 166 | // vim:shiftwidth=2:ts=2:expandtab |