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

410 lines
10 KiB
C

/*********************************************************/
/* SEGA 16ch 8bit PCM */
/*********************************************************/
#include "mamedef.h"
#include <memory.h>
#include <stdlib.h>
#include <stdio.h>
//#include "sndintrf.h"
//#include "streams.h"
#include "segapcm.h"
typedef struct _segapcm_state segapcm_state;
struct _segapcm_state
{
UINT8 *ram;
UINT8 low[16];
UINT32 ROMSize;
UINT8 *rom;
#ifdef _DEBUG
UINT8 *romusage;
#endif
int bankshift;
int bankmask;
int rgnmask;
sega_pcm_interface intf;
UINT8 Muted[16];
//sound_stream * stream;
};
#ifndef _DEBUG
//UINT8 SegaPCM_NewCore = 0x00;
#else
//UINT8 SegaPCM_NewCore = 0x01;
static void sega_pcm_fwrite_romusage(void *_info);
#endif
/*INLINE segapcm_state *get_safe_token(const device_config *device)
{
assert(device != NULL);
assert(device->token != NULL);
assert(device->type == SOUND);
assert(sound_get_type(device) == SOUND_SEGAPCM);
return (segapcm_state *)device->token;
}*/
//static STREAM_UPDATE( SEGAPCM_update )
void SEGAPCM_update(void *info, stream_sample_t **outputs, int samples)
{
//segapcm_state *spcm = (segapcm_state *)param;
segapcm_state *spcm = (segapcm_state *)info;
int rgnmask = spcm->rgnmask;
int ch;
/* clear the buffers */
memset(outputs[0], 0, samples*sizeof(stream_sample_t));
memset(outputs[1], 0, samples*sizeof(stream_sample_t));
// reg function
// ------------------------------------------------
// 0x00 ?
// 0x01 ?
// 0x02 volume left
// 0x03 volume right
// 0x04 loop address (08-15)
// 0x05 loop address (16-23)
// 0x06 end address
// 0x07 address delta
// 0x80 ?
// 0x81 ?
// 0x82 ?
// 0x83 ?
// 0x84 current address (08-15), 00-07 is internal?
// 0x85 current address (16-23)
// 0x86 bit 0: channel disable?
// bit 1: loop disable
// other bits: bank
// 0x87 ?
/* loop over channels */
for (ch = 0; ch < 16; ch++)
{
#if 0
//if (! SegaPCM_NewCore)
//{
/* only process active channels */
if (!(spcm->ram[0x86+8*ch] & 1) && ! spcm->Muted[ch])
{
UINT8 *base = spcm->ram+8*ch;
UINT8 flags = base[0x86];
const UINT8 *rom = spcm->rom + ((flags & spcm->bankmask) << spcm->bankshift);
#ifdef _DEBUG
UINT8 *romusage = spcm->romusage + ((flags & spcm->bankmask) << spcm->bankshift);
#endif
UINT32 addr = (base[5] << 16) | (base[4] << 8) | spcm->low[ch];
UINT16 loop = (base[0x85] << 8) | base[0x84];
UINT8 end = base[6] + 1;
UINT8 delta = base[7];
UINT8 voll = base[2] & 0x7F;
UINT8 volr = base[3] & 0x7F;
int i;
/* loop over samples on this channel */
for (i = 0; i < samples; i++)
{
INT8 v = 0;
/* handle looping if we've hit the end */
if ((addr >> 16) == end)
{
if (!(flags & 2))
addr = loop << 8;
else
{
flags |= 1;
break;
}
}
/* fetch the sample */
v = rom[(addr >> 8) & rgnmask] - 0x80;
#ifdef _DEBUG
if ((romusage[(addr >> 8) & rgnmask] & 0x03) == 0x02 && (voll || volr))
printf("Access to empty ROM section! (0x%06lX)\n",
((flags & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask);
romusage[(addr >> 8) & rgnmask] |= 0x01;
#endif
/* apply panning and advance */
outputs[0][i] += v * voll;
outputs[1][i] += v * volr;
addr += delta;
}
/* store back the updated address and info */
base[0x86] = flags;
base[4] = addr >> 8;
base[5] = addr >> 16;
spcm->low[ch] = flags & 1 ? 0 : addr;
}
//}
//else
//{
#else
UINT8 *regs = spcm->ram+8*ch;
/* only process active channels */
if (!(regs[0x86] & 1) && ! spcm->Muted[ch])
{
const UINT8 *rom = spcm->rom + ((regs[0x86] & spcm->bankmask) << spcm->bankshift);
#ifdef _DEBUG
UINT8 *romusage = spcm->romusage + ((regs[0x86] & spcm->bankmask) << spcm->bankshift);
#endif
UINT32 addr = (regs[0x85] << 16) | (regs[0x84] << 8) | spcm->low[ch];
UINT32 loop = (regs[0x05] << 16) | (regs[0x04] << 8);
UINT8 end = regs[6] + 1;
int i;
/* loop over samples on this channel */
for (i = 0; i < samples; i++)
{
INT8 v = 0;
/* handle looping if we've hit the end */
if ((addr >> 16) == end)
{
if (regs[0x86] & 2)
{
regs[0x86] |= 1;
break;
}
else addr = loop;
}
/* fetch the sample */
v = rom[(addr >> 8) & rgnmask] - 0x80;
#ifdef _DEBUG
if ((romusage[(addr >> 8) & rgnmask] & 0x03) == 0x02 && (regs[2] || regs[3]))
printf("Access to empty ROM section! (0x%06lX)\n",
((regs[0x86] & spcm->bankmask) << spcm->bankshift) + (addr >> 8) & rgnmask);
romusage[(addr >> 8) & rgnmask] |= 0x01;
#endif
/* apply panning and advance */
// fixed Bitmask for volume multiplication, thanks to ctr -Valley Bell
outputs[0][i] += v * (regs[2] & 0x7F);
outputs[1][i] += v * (regs[3] & 0x7F);
addr = (addr + regs[7]) & 0xffffff;
}
/* store back the updated address */
regs[0x84] = addr >> 8;
regs[0x85] = addr >> 16;
spcm->low[ch] = regs[0x86] & 1 ? 0 : addr;
}
//}
#endif
}
}
//static DEVICE_START( segapcm )
int device_start_segapcm(void **_info, int clock, int intf_bank)
{
const UINT32 STD_ROM_SIZE = 0x80000;
//const sega_pcm_interface *intf = (const sega_pcm_interface *)device->static_config;
sega_pcm_interface *intf;
int mask, rom_mask, len;
//segapcm_state *spcm = get_safe_token(device);
segapcm_state *spcm;
spcm = (segapcm_state *) calloc(1, sizeof(segapcm_state));
*_info = (void *) spcm;
intf = &spcm->intf;
intf->bank = intf_bank;
//spcm->rom = (const UINT8 *)device->region;
//spcm->ram = auto_alloc_array(device->machine, UINT8, 0x800);
spcm->ROMSize = STD_ROM_SIZE;
spcm->rom = malloc(STD_ROM_SIZE);
#ifdef _DEBUG
spcm->romusage = malloc(STD_ROM_SIZE);
#endif
spcm->ram = (UINT8*)malloc(0x800);
#ifndef _DEBUG
//memset(spcm->rom, 0xFF, STD_ROM_SIZE);
// filling 0xFF would actually be more true to the hardware,
// (unused ROMs have all FFs)
// but 0x80 is the effective 'zero' byte
memset(spcm->rom, 0x80, STD_ROM_SIZE);
#else
// filling with FF makes it easier to find bugs in a .wav-log
memset(spcm->rom, 0xFF, STD_ROM_SIZE);
memset(spcm->romusage, 0x02, STD_ROM_SIZE);
#endif
//memset(spcm->ram, 0xff, 0x800); // RAM Clear is done at device_reset
spcm->bankshift = (UINT8)(intf->bank);
mask = intf->bank >> 16;
if(!mask)
mask = BANK_MASK7>>16;
len = STD_ROM_SIZE;
spcm->rgnmask = len - 1;
for(rom_mask = 1; rom_mask < len; rom_mask *= 2);
rom_mask--;
spcm->bankmask = mask & (rom_mask >> spcm->bankshift);
//spcm->stream = stream_create(device, 0, 2, device->clock / 128, spcm, SEGAPCM_update);
//state_save_register_device_item_array(device, 0, spcm->low);
//state_save_register_device_item_pointer(device, 0, spcm->ram, 0x800);
for (mask = 0; mask < 16; mask ++)
spcm->Muted[mask] = 0x00;
return clock / 128;
}
//static DEVICE_STOP( segapcm )
void device_stop_segapcm(void *_info)
{
//segapcm_state *spcm = get_safe_token(device);
segapcm_state *spcm = (segapcm_state *)_info;
free(spcm->rom); spcm->rom = NULL;
#ifdef _DEBUG
//sega_pcm_fwrite_romusage(ChipID);
free(spcm->romusage);
#endif
free(spcm->ram);
free(spcm);
return;
}
//static DEVICE_RESET( segapcm )
void device_reset_segapcm(void *_info)
{
//segapcm_state *spcm = get_safe_token(device);
segapcm_state *spcm = (segapcm_state *)_info;
memset(spcm->ram, 0xFF, 0x800);
return;
}
//WRITE8_DEVICE_HANDLER( sega_pcm_w )
void sega_pcm_w(void *_info, offs_t offset, UINT8 data)
{
//segapcm_state *spcm = get_safe_token(device);
segapcm_state *spcm = (segapcm_state *)_info;
//stream_update(spcm->stream);
spcm->ram[offset & 0x07ff] = data;
}
//READ8_DEVICE_HANDLER( sega_pcm_r )
UINT8 sega_pcm_r(void *_info, offs_t offset)
{
//segapcm_state *spcm = get_safe_token(device);
segapcm_state *spcm = (segapcm_state *)_info;
//stream_update(spcm->stream);
return spcm->ram[offset & 0x07ff];
}
void sega_pcm_write_rom(void *_info, offs_t ROMSize, offs_t DataStart, offs_t DataLength,
const UINT8* ROMData)
{
segapcm_state *spcm = (segapcm_state *)_info;
if (spcm->ROMSize != ROMSize)
{
unsigned long int mask, rom_mask;
spcm->rom = (UINT8*)realloc(spcm->rom, ROMSize);
#ifdef _DEBUG
spcm->romusage = (UINT8*)realloc(spcm->romusage, ROMSize);
#endif
spcm->ROMSize = ROMSize;
memset(spcm->rom, 0x80, ROMSize);
#ifdef _DEBUG
memset(spcm->romusage, 0x02, ROMSize);
#endif
// recalculate bankmask
mask = spcm->intf.bank >> 16;
if (! mask)
mask = BANK_MASK7 >> 16;
//spcm->rgnmask = ROMSize - 1;
for (rom_mask = 1; rom_mask < ROMSize; rom_mask *= 2);
rom_mask --;
spcm->rgnmask = rom_mask; // fix for ROMs with e.g 0x60000 bytes (stupid M1)
spcm->bankmask = mask & (rom_mask >> spcm->bankshift);
}
if (DataStart > ROMSize)
return;
if (DataStart + DataLength > ROMSize)
DataLength = ROMSize - DataStart;
memcpy(spcm->rom + DataStart, ROMData, DataLength);
#ifdef _DEBUG
memset(spcm->romusage + DataStart, 0x00, DataLength);
#endif
return;
}
#ifdef _DEBUG
static void sega_pcm_fwrite_romusage(void *_info)
{
segapcm_state *spcm = (segapcm_state *)_info;
FILE* hFile;
hFile = fopen("SPCM_ROMUsage.bin", "wb");
if (hFile == NULL)
return;
fwrite(spcm->romusage, 0x01, spcm->ROMSize, hFile);
fclose(hFile);
return;
}
#endif
void segapcm_set_mute_mask(void *_info, UINT32 MuteMask)
{
segapcm_state *spcm = (segapcm_state *)_info;
unsigned char CurChn;
for (CurChn = 0; CurChn < 16; CurChn ++)
spcm->Muted[CurChn] = (MuteMask >> CurChn) & 0x01;
return;
}
/**************************************************************************
* Generic get_info
**************************************************************************/
/*DEVICE_GET_INFO( segapcm )
{
switch (state)
{
// --- the following bits of info are returned as 64-bit signed integers ---
case DEVINFO_INT_TOKEN_BYTES: info->i = sizeof(segapcm_state); break;
// --- the following bits of info are returned as pointers to data or functions ---
case DEVINFO_FCT_START: info->start = DEVICE_START_NAME( segapcm ); break;
case DEVINFO_FCT_STOP: // Nothing break;
case DEVINFO_FCT_RESET: // Nothing break;
// --- the following bits of info are returned as NULL-terminated strings ---
case DEVINFO_STR_NAME: strcpy(info->s, "Sega PCM"); break;
case DEVINFO_STR_FAMILY: strcpy(info->s, "Sega custom"); break;
case DEVINFO_STR_VERSION: strcpy(info->s, "1.0"); break;
case DEVINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break;
case DEVINFO_STR_CREDITS: strcpy(info->s, "Copyright Nicola Salmoria and the MAME Team"); break;
}
}*/