192 lines
8.2 KiB
C++
192 lines
8.2 KiB
C++
//////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Namcot N106 //
|
||
// Norix //
|
||
// written 2001/09/18 //
|
||
// last modify ----/--/-- //
|
||
//////////////////////////////////////////////////////////////////////////
|
||
#include "APU_N106.h"
|
||
#include "state.h"
|
||
|
||
#include "DebugOut.h"
|
||
|
||
#define CHANNEL_VOL_SHIFT 6
|
||
|
||
APU_N106::APU_N106()
|
||
{
|
||
// 最初だけTONEの初期化を行う
|
||
::ZeroMemory( tone, sizeof(tone) );
|
||
|
||
// 仮設定
|
||
cpu_clock = APU_CLOCK;
|
||
cycle_rate = (DWORD)(cpu_clock*12.0f*(1<<20)/(45.0f*22050.0f));
|
||
}
|
||
|
||
APU_N106::~APU_N106()
|
||
{
|
||
}
|
||
|
||
void APU_N106::Reset( FLOAT fClock, INT nRate )
|
||
{
|
||
for( INT i = 0; i < 8; i++ ) {
|
||
::ZeroMemory( &op[i], sizeof(op[i]) );
|
||
op[i].tonelen = 0x10<<18;
|
||
}
|
||
|
||
address = 0;
|
||
addrinc = 1;
|
||
channel_use = 8;
|
||
|
||
Setup( fClock, nRate );
|
||
|
||
// TONEの初期化はしない...
|
||
}
|
||
|
||
void APU_N106::Setup( FLOAT fClock, INT nRate )
|
||
{
|
||
cpu_clock = fClock;
|
||
cycle_rate = (DWORD)(cpu_clock*12.0f*(1<<20)/(45.0f*nRate));
|
||
}
|
||
|
||
void APU_N106::Write( WORD addr, BYTE data )
|
||
{
|
||
if( addr == 0x4800 ) {
|
||
// tone[address*2+0] = (INT)(data&0x0F);
|
||
// tone[address*2+1] = (INT)(data >>4);
|
||
tone[address*2+0] = data&0x0F;
|
||
tone[address*2+1] = data>>4;
|
||
|
||
if( address >= 0x40 ) {
|
||
INT no = (address-0x40)>>3;
|
||
DWORD tonelen;
|
||
CHANNEL& ch = op[no];
|
||
|
||
switch( address & 7 ) {
|
||
case 0x00:
|
||
ch.freq = (ch.freq&~0x000000FF)|(DWORD)data;
|
||
break;
|
||
case 0x02:
|
||
ch.freq = (ch.freq&~0x0000FF00)|((DWORD)data<<8);
|
||
break;
|
||
case 0x04:
|
||
ch.freq = (ch.freq&~0x00030000)|(((DWORD)data&0x03)<<16);
|
||
tonelen = (0x20-(data&0x1c))<<18;
|
||
ch.databuf = (data&0x1c)>>2;
|
||
if( ch.tonelen != tonelen ) {
|
||
ch.tonelen = tonelen;
|
||
ch.phase = 0;
|
||
}
|
||
break;
|
||
case 0x06:
|
||
ch.toneadr = data;
|
||
break;
|
||
case 0x07:
|
||
ch.vol = data&0x0f;
|
||
ch.volupdate = 0xFF;
|
||
if( no == 7 )
|
||
channel_use = ((data>>4)&0x07)+1;
|
||
break;
|
||
}
|
||
}
|
||
|
||
if( addrinc ) {
|
||
address = (address+1)&0x7f;
|
||
}
|
||
} else if( addr == 0xF800 ) {
|
||
address = data&0x7F;
|
||
addrinc = data&0x80;
|
||
}
|
||
}
|
||
|
||
BYTE APU_N106::Read( WORD addr )
|
||
{
|
||
// $4800 dummy read!!
|
||
if( addr == 0x0000 ) {
|
||
if( addrinc ) {
|
||
address = (address+1)&0x7F;
|
||
}
|
||
}
|
||
|
||
return (BYTE)(addr>>8);
|
||
}
|
||
|
||
INT APU_N106::Process( INT channel )
|
||
{
|
||
if( channel >= (8-channel_use) && channel < 8 ) {
|
||
return ChannelRender( op[channel] );
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
INT APU_N106::GetFreq( INT channel )
|
||
{
|
||
if( channel < 8 ) {
|
||
channel &= 7;
|
||
if( channel < (8-channel_use) )
|
||
return 0;
|
||
|
||
CHANNEL* ch = &op[channel&0x07];
|
||
if( !ch->freq || !ch->vol )
|
||
return 0;
|
||
INT temp = channel_use*(8-ch->databuf)*4*45;
|
||
if( !temp )
|
||
return 0;
|
||
return (INT)(256.0*(double)cpu_clock*12.0*ch->freq/((double)0x40000*temp));
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
INT APU_N106::ChannelRender( CHANNEL& ch )
|
||
{
|
||
DWORD phasespd = channel_use<<20;
|
||
|
||
ch.phaseacc -= cycle_rate;
|
||
if( ch.phaseacc >= 0 ) {
|
||
if( ch.volupdate ) {
|
||
ch.output = ((INT)tone[((ch.phase>>18)+ch.toneadr)&0xFF]*ch.vol)<<CHANNEL_VOL_SHIFT;
|
||
ch.volupdate = 0;
|
||
}
|
||
return ch.output;
|
||
}
|
||
|
||
while( ch.phaseacc < 0 ) {
|
||
ch.phaseacc += phasespd;
|
||
ch.phase += ch.freq;
|
||
}
|
||
while( ch.tonelen && (ch.phase >= ch.tonelen)) {
|
||
ch.phase -= ch.tonelen;
|
||
}
|
||
|
||
ch.output = ((INT)tone[((ch.phase>>18)+ch.toneadr)&0xFF]*ch.vol)<<CHANNEL_VOL_SHIFT;
|
||
|
||
return ch.output;
|
||
}
|
||
|
||
INT APU_N106::GetStateSize()
|
||
{
|
||
return 3*sizeof(BYTE) + 8*sizeof(CHANNEL) + sizeof(tone);
|
||
}
|
||
|
||
void APU_N106::SaveState( LPBYTE p )
|
||
{
|
||
SETBYTE( p, addrinc );
|
||
SETBYTE( p, address );
|
||
SETBYTE( p, channel_use );
|
||
|
||
SETBLOCK( p, op, sizeof(op) );
|
||
SETBLOCK( p, tone, sizeof(tone) );
|
||
}
|
||
|
||
void APU_N106::LoadState( LPBYTE p )
|
||
{
|
||
GETBYTE( p, addrinc );
|
||
GETBYTE( p, address );
|
||
GETBYTE( p, channel_use );
|
||
|
||
GETBLOCK( p, op, sizeof(op) );
|
||
GETBLOCK( p, tone, sizeof(tone) );
|
||
}
|
||
|