cog/Frameworks/GME/vgmplay/chips/scd_pcm.c

511 lines
11 KiB
C

/***********************************************************/
/* */
/* PCM.C : PCM RF5C164 emulator */
/* */
/* This source is a part of Gens project */
/* Written by Stéphane Dallongeville (gens@consolemul.com) */
/* Copyright (c) 2002 by Stéphane Dallongeville */
/* */
/***********************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mamedef.h"
#include "scd_pcm.h"
int PCM_Init(void *chip, int Rate);
void PCM_Set_Rate(void *chip, int Rate);
void PCM_Reset(void *chip);
void PCM_Write_Reg(void *chip, unsigned int Reg, unsigned int Data);
int PCM_Update(void *chip, int **buf, int Length);
#define PCM_STEP_SHIFT 11
/*static unsigned char VolTabIsInit = 0x00;
static int PCM_Volume_Tab[256 * 256];*/
//unsigned char Ram_PCM[64 * 1024];
//int PCM_Enable;
/**
* PCM_Init(): Initialize the PCM chip.
* @param Rate Sample rate.
* @return 0 if successful.
*/
int PCM_Init(void *_info, int Rate)
{
struct pcm_chip_ *chip = (struct pcm_chip_ *)_info;
int i/*, j, out*/;
/*if (! VolTabIsInit)
{
for (i = 0; i < 0x100; i++)
{
for (j = 0; j < 0x100; j++)
{
if (i & 0x80)
{
out = -(i & 0x7F);
out *= j;
PCM_Volume_Tab[(j << 8) + i] = out;
}
else
{
out = i * j;
PCM_Volume_Tab[(j << 8) + i] = out;
}
}
}
VolTabIsInit = 0x01;
}*/
chip->Smpl0Patch = 0;
for (i = 0; i < 8; i ++)
chip->Channel[i].Muted = 0x00;
chip->RAMSize = 64 * 1024;
chip->RAM = (unsigned char*)malloc(chip->RAMSize);
PCM_Reset(chip);
PCM_Set_Rate(chip, Rate);
return 0;
}
/**
* PCM_Reset(): Reset the PCM chip.
*/
void PCM_Reset(void *_info)
{
struct pcm_chip_ *chip = (struct pcm_chip_ *)_info;
int i;
struct pcm_chan_* chan;
// Clear the PCM memory.
memset(chip->RAM, 0x00, chip->RAMSize);
chip->Enable = 0;
chip->Cur_Chan = 0;
chip->Bank = 0;
/* clear channel registers */
for (i = 0; i < 8; i++)
{
chan = &chip->Channel[i];
chan->Enable = 0;
chan->ENV = 0;
chan->PAN = 0;
chan->St_Addr = 0;
chan->Addr = 0;
chan->Loop_Addr = 0;
chan->Step = 0;
chan->Step_B = 0;
chan->Data = 0;
}
}
/**
* PCM_Set_Rate(): Change the PCM sample rate.
* @param Rate New sample rate.
*/
void PCM_Set_Rate(void *_info, int Rate)
{
struct pcm_chip_ *chip = (struct pcm_chip_ *)_info;
int i;
if (Rate == 0)
return;
//chip->Rate = (float) (32 * 1024) / (float) Rate;
chip->Rate = (float) (31.8 * 1024) / (float) Rate;
for (i = 0; i < 8; i++)
{
chip->Channel[i].Step =
(int) ((float) chip->Channel[i].Step_B * chip->Rate);
}
}
/**
* PCM_Write_Reg(): Write to a PCM register.
* @param Reg Register ID.
* @param Data Data to write.
*/
void PCM_Write_Reg(void *_info, unsigned int Reg, unsigned int Data)
{
struct pcm_chip_ *chip = (struct pcm_chip_ *)_info;
int i;
struct pcm_chan_* chan = &chip->Channel[chip->Cur_Chan];
Data &= 0xFF;
switch (Reg)
{
case 0x00:
/* evelope register */
chan->ENV = Data;
chan->MUL_L = (Data * (chan->PAN & 0x0F)) >> 5;
chan->MUL_R = (Data * (chan->PAN >> 4)) >> 5;
break;
case 0x01:
/* pan register */
chan->PAN = Data;
chan->MUL_L = ((Data & 0x0F) * chan->ENV) >> 5;
chan->MUL_R = ((Data >> 4) * chan->ENV) >> 5;
break;
case 0x02:
/* frequency step (LB) registers */
chan->Step_B &= 0xFF00;
chan->Step_B += Data;
chan->Step = (int)((float)chan->Step_B * chip->Rate);
//LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
// "Step low = %.2X Step calculated = %.8X",
// Data, chan->Step);
break;
case 0x03:
/* frequency step (HB) registers */
chan->Step_B &= 0x00FF;
chan->Step_B += Data << 8;
chan->Step = (int)((float)chan->Step_B * chip->Rate);
//LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
// "Step high = %.2X Step calculated = %.8X",
// Data, chan->Step);
break;
case 0x04:
chan->Loop_Addr &= 0xFF00;
chan->Loop_Addr += Data;
//LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
// "Loop low = %.2X Loop = %.8X",
// Data, chan->Loop_Addr);
break;
case 0x05:
/* loop address registers */
chan->Loop_Addr &= 0x00FF;
chan->Loop_Addr += Data << 8;
//LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
// "Loop high = %.2X Loop = %.8X",
// Data, chan->Loop_Addr);
break;
case 0x06:
/* start address registers */
chan->St_Addr = Data << (PCM_STEP_SHIFT + 8);
//chan->Addr = chan->St_Addr;
//LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
// "Start addr = %.2X New Addr = %.8X",
// Data, chan->Addr);
break;
case 0x07:
/* control register */
/* mod is H */
if (Data & 0x40)
{
/* select channel */
chip->Cur_Chan = Data & 0x07;
}
/* mod is L */
else
{
/* pcm ram bank select */
chip->Bank = (Data & 0x0F) << 12;
}
/* sounding bit */
if (Data & 0x80)
chip->Enable = 0xFF; // Used as mask
else
chip->Enable = 0;
//LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
// "General Enable = %.2X", Data);
break;
case 0x08:
/* sound on/off register */
Data ^= 0xFF;
//LOG_MSG(pcm, LOG_MSG_LEVEL_DEBUG1,
// "Channel Enable = %.2X", Data);
for (i = 0; i < 8; i++)
{
chan = &chip->Channel[i];
if (!chan->Enable)
chan->Addr = chan->St_Addr;
}
for (i = 0; i < 8; i++)
{
chip->Channel[i].Enable = Data & (1 << i);
}
}
}
/**
* PCM_Update(): Update the PCM buffer.
* @param buf PCM buffer.
* @param Length Buffer length.
*/
int PCM_Update(void *_info, int **buf, int Length)
{
struct pcm_chip_ *chip = (struct pcm_chip_ *)_info;
int i, j;
int *bufL, *bufR; //, *volL, *volR;
unsigned int Addr, k;
struct pcm_chan_ *CH;
bufL = buf[0];
bufR = buf[1];
// clear buffers
memset(bufL, 0, Length * sizeof(int));
memset(bufR, 0, Length * sizeof(int));
// if PCM disable, no sound
if (!chip->Enable)
return 1;
#if 0
// faster for short update
for (j = 0; j < Length; j++)
{
for (i = 0; i < 8; i++)
{
CH = &(chip->Channel[i]);
// only loop when sounding and on
if (CH->Enable && ! CH->Muted)
{
Addr = CH->Addr >> PCM_STEP_SHIFT;
if (Addr & 0x10000)
{
for(k = CH->Old_Addr; k < 0x10000; k++)
{
if (chip->RAM[k] == 0xFF)
{
CH->Old_Addr = Addr = CH->Loop_Addr;
CH->Addr = Addr << PCM_STEP_SHIFT;
break;
}
}
if (Addr & 0x10000)
{
//CH->Addr -= CH->Step;
CH->Enable = 0;
break;
}
}
else
{
for(k = CH->Old_Addr; k <= Addr; k++)
{
if (chip->RAM[k] == 0xFF)
{
CH->Old_Addr = Addr = CH->Loop_Addr;
CH->Addr = Addr << PCM_STEP_SHIFT;
break;
}
}
}
// test for loop signal
if (chip->RAM[Addr] == 0xFF)
{
Addr = CH->Loop_Addr;
CH->Addr = Addr << PCM_STEP_SHIFT;
}
if (chip->RAM[Addr] & 0x80)
{
CH->Data = chip->RAM[Addr] & 0x7F;
bufL[j] -= CH->Data * CH->MUL_L;
bufR[j] -= CH->Data * CH->MUL_R;
}
else
{
CH->Data = chip->RAM[Addr];
bufL[j] += CH->Data * CH->MUL_L;
bufR[j] += CH->Data * CH->MUL_R;
}
// update address register
//CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF;
CH->Addr += CH->Step;
CH->Old_Addr = Addr + 1;
}
}
}
#endif
#if 1
// for long update
for (i = 0; i < 8; i++)
{
CH = &(chip->Channel[i]);
// only loop when sounding and on
if (CH->Enable && ! CH->Muted)
{
Addr = CH->Addr >> PCM_STEP_SHIFT;
//volL = &(PCM_Volume_Tab[CH->MUL_L << 8]);
//volR = &(PCM_Volume_Tab[CH->MUL_R << 8]);
for (j = 0; j < Length; j++)
{
// test for loop signal
if (chip->RAM[Addr] == 0xFF)
{
CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT;
if (chip->RAM[Addr] == 0xFF)
break;
else
j--;
}
else
{
if (chip->RAM[Addr] & 0x80)
{
CH->Data = chip->RAM[Addr] & 0x7F;
bufL[j] -= CH->Data * CH->MUL_L;
bufR[j] -= CH->Data * CH->MUL_R;
}
else
{
CH->Data = chip->RAM[Addr];
// this improves the sound of Cosmic Fantasy Stories,
// although it's definately false behaviour
if (! CH->Data && chip->Smpl0Patch)
CH->Data = -0x7F;
bufL[j] += CH->Data * CH->MUL_L;
bufR[j] += CH->Data * CH->MUL_R;
}
// update address register
k = Addr + 1;
CH->Addr = (CH->Addr + CH->Step) & 0x7FFFFFF;
Addr = CH->Addr >> PCM_STEP_SHIFT;
for (; k < Addr; k++)
{
if (chip->RAM[k] == 0xFF)
{
CH->Addr = (Addr = CH->Loop_Addr) << PCM_STEP_SHIFT;
break;
}
}
}
}
if (chip->RAM[Addr] == 0xFF)
{
CH->Addr = CH->Loop_Addr << PCM_STEP_SHIFT;
}
}
}
#endif
return 0;
}
void rf5c164_update(void *chip, stream_sample_t **outputs, int samples)
{
PCM_Update(chip, outputs, samples);
}
int device_start_rf5c164(void **_info, int clock, int CHIP_SAMPLING_MODE, int CHIP_SAMPLE_RATE)
{
/* allocate memory for the chip */
//rf5c164_state *chip = get_safe_token(device);
struct pcm_chip_ *chip;
int rate;
chip = (struct pcm_chip_ *) calloc(1, sizeof(struct pcm_chip_));
*_info = (void *) chip;
rate = (clock & 0x7FFFFFFF) / 384;
if (((CHIP_SAMPLING_MODE & 0x01) && rate < CHIP_SAMPLE_RATE) ||
CHIP_SAMPLING_MODE == 0x02)
rate = CHIP_SAMPLE_RATE;
PCM_Init(chip, rate);
chip->Smpl0Patch = (clock & 0x80000000) >> 31;
/* allocate the stream */
//chip->stream = stream_create(device, 0, 2, device->clock / 384, chip, rf5c68_update);
return rate;
}
void device_stop_rf5c164(void *_info)
{
struct pcm_chip_ *chip = (struct pcm_chip_ *)_info;
free(chip->RAM); chip->RAM = NULL;
free(chip);
return;
}
void device_reset_rf5c164(void *chip)
{
//struct pcm_chip_ *chip = &PCM_Chip[ChipID];
PCM_Reset(chip);
}
void rf5c164_w(void *chip, offs_t offset, UINT8 data)
{
//struct pcm_chip_ *chip = &PCM_Chip[ChipID];
PCM_Write_Reg(chip, offset, data);
}
void rf5c164_mem_w(void *_info, offs_t offset, UINT8 data)
{
struct pcm_chip_ *chip = (struct pcm_chip_ *)_info;
chip->RAM[chip->Bank | offset] = data;
}
void rf5c164_write_ram(void *_info, offs_t DataStart, offs_t DataLength, const UINT8* RAMData)
{
struct pcm_chip_ *chip = (struct pcm_chip_ *)_info;
DataStart |= chip->Bank;
if (DataStart >= chip->RAMSize)
return;
if (DataStart + DataLength > chip->RAMSize)
DataLength = chip->RAMSize - DataStart;
memcpy(chip->RAM + DataStart, RAMData, DataLength);
return;
}
void rf5c164_set_mute_mask(void *_info, UINT32 MuteMask)
{
struct pcm_chip_ *chip = (struct pcm_chip_ *)_info;
unsigned char CurChn;
for (CurChn = 0; CurChn < 8; CurChn ++)
chip->Channel[CurChn].Muted = (MuteMask >> CurChn) & 0x01;
return;
}