⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ay8910.c

📁 著名ARC模拟器源码,包括多个平台
💻 C
📖 第 1 页 / 共 2 页
字号:
/***************************************************************************  ay8910.c  Emulation of the AY-3-8910 / YM2149 sound chip.  Based on various code snippets by Ville Hallik, Michael Cuddy,  Tatsuyuki Satoh, Fabrice Frances, Nicola Salmoria.***************************************************************************/#include "driver.h"#include <stdio.h>#include "ay8910.h"#include "streams.h"#include "sasound.h"#define MAX_OUTPUT 0x7fff#define STEP 0x8000struct AY8910{	int Channel;	int SampleRate;	mem_read_handler PortAread;	mem_read_handler PortBread;	mem_write_handler PortAwrite;	mem_write_handler PortBwrite;	int register_latch;	unsigned char Regs[16];	unsigned int UpdateStep;	int PeriodA,PeriodB,PeriodC,PeriodN,PeriodE;	int CountA,CountB,CountC,CountN,CountE;	unsigned int VolA,VolB,VolC,VolE;	unsigned char EnvelopeA,EnvelopeB,EnvelopeC;	unsigned char OutputA,OutputB,OutputC,OutputN;	signed char CountEnv;	unsigned char Hold,Alternate,Attack,Holding;	int RNG;	unsigned int VolTable[32];};/* register id's */#define AY_AFINE	(0)#define AY_ACOARSE	(1)#define AY_BFINE	(2)#define AY_BCOARSE	(3)#define AY_CFINE	(4)#define AY_CCOARSE	(5)#define AY_NOISEPER	(6)#define AY_ENABLE	(7)#define AY_AVOL		(8)#define AY_BVOL		(9)#define AY_CVOL		(10)#define AY_EFINE	(11)#define AY_ECOARSE	(12)#define AY_ESHAPE	(13)#define AY_PORTA	(14)#define AY_PORTB	(15)static struct AY8910 AYPSG[MAX_8910];		/* array of PSG's */void _AYWriteReg(int n, int r, int v){	struct AY8910 *PSG = &AYPSG[n];	int old;	PSG->Regs[r] = v;	/* A note about the period of tones, noise and envelope: for speed reasons,*/	/* we count down from the period to 0, but careful studies of the chip     */	/* output prove that it instead counts up from 0 until the counter becomes */	/* greater or equal to the period. This is an important difference when the*/	/* program is rapidly changing the period to modulate the sound.           */	/* To compensate for the difference, when the period is changed we adjust  */	/* our internal counter.                                                   */	/* Also, note that period = 0 is the same as period = 1. This is mentioned */	/* in the YM2203 data sheets. However, this does NOT apply to the Envelope */	/* period. In that case, period = 0 is half as period = 1. */	switch( r )	{	case AY_AFINE:	case AY_ACOARSE:		PSG->Regs[AY_ACOARSE] &= 0x0f;		old = PSG->PeriodA;		PSG->PeriodA = (PSG->Regs[AY_AFINE] + 256 * PSG->Regs[AY_ACOARSE]) * PSG->UpdateStep;		if (PSG->PeriodA == 0) PSG->PeriodA = PSG->UpdateStep;		PSG->CountA += PSG->PeriodA - old;		if (PSG->CountA <= 0) PSG->CountA = 1;		break;	case AY_BFINE:	case AY_BCOARSE:		PSG->Regs[AY_BCOARSE] &= 0x0f;		old = PSG->PeriodB;		PSG->PeriodB = (PSG->Regs[AY_BFINE] + 256 * PSG->Regs[AY_BCOARSE]) * PSG->UpdateStep;		if (PSG->PeriodB == 0) PSG->PeriodB = PSG->UpdateStep;		PSG->CountB += PSG->PeriodB - old;		if (PSG->CountB <= 0) PSG->CountB = 1;		break;	case AY_CFINE:	case AY_CCOARSE:		PSG->Regs[AY_CCOARSE] &= 0x0f;		old = PSG->PeriodC;		PSG->PeriodC = (PSG->Regs[AY_CFINE] + 256 * PSG->Regs[AY_CCOARSE]) * PSG->UpdateStep;		if (PSG->PeriodC == 0) PSG->PeriodC = PSG->UpdateStep;		PSG->CountC += PSG->PeriodC - old;		if (PSG->CountC <= 0) PSG->CountC = 1;		break;	case AY_NOISEPER:		PSG->Regs[AY_NOISEPER] &= 0x1f;		old = PSG->PeriodN;		PSG->PeriodN = PSG->Regs[AY_NOISEPER] * PSG->UpdateStep;		if (PSG->PeriodN == 0) PSG->PeriodN = PSG->UpdateStep;		PSG->CountN += PSG->PeriodN - old;		if (PSG->CountN <= 0) PSG->CountN = 1;		break;	case AY_AVOL:		PSG->Regs[AY_AVOL] &= 0x1f;		PSG->EnvelopeA = PSG->Regs[AY_AVOL] & 0x10;		PSG->VolA = PSG->EnvelopeA ? PSG->VolE : PSG->VolTable[PSG->Regs[AY_AVOL] ? PSG->Regs[AY_AVOL]*2+1 : 0];		break;	case AY_BVOL:		PSG->Regs[AY_BVOL] &= 0x1f;		PSG->EnvelopeB = PSG->Regs[AY_BVOL] & 0x10;		PSG->VolB = PSG->EnvelopeB ? PSG->VolE : PSG->VolTable[PSG->Regs[AY_BVOL] ? PSG->Regs[AY_BVOL]*2+1 : 0];		break;	case AY_CVOL:		PSG->Regs[AY_CVOL] &= 0x1f;		PSG->EnvelopeC = PSG->Regs[AY_CVOL] & 0x10;		PSG->VolC = PSG->EnvelopeC ? PSG->VolE : PSG->VolTable[PSG->Regs[AY_CVOL] ? PSG->Regs[AY_CVOL]*2+1 : 0];		break;	case AY_EFINE:	case AY_ECOARSE:		old = PSG->PeriodE;		PSG->PeriodE = ((PSG->Regs[AY_EFINE] + 256 * PSG->Regs[AY_ECOARSE])) * PSG->UpdateStep;		if (PSG->PeriodE == 0) PSG->PeriodE = PSG->UpdateStep / 2;		PSG->CountE += PSG->PeriodE - old;		if (PSG->CountE <= 0) PSG->CountE = 1;		break;	case AY_ESHAPE:		/* envelope shapes:		C AtAlH		0 0 x x  \___		0 1 x x  /___		1 0 0 0  \\\\		1 0 0 1  \___		1 0 1 0  \/\/		          ___		1 0 1 1  \		1 1 0 0  ////		          ___		1 1 0 1  /		1 1 1 0  /\/\		1 1 1 1  /___		The envelope counter on the AY-3-8910 has 16 steps. On the YM2149 it		has twice the steps, happening twice as fast. Since the end result is		just a smoother curve, we always use the YM2149 behaviour.		*/		PSG->Regs[AY_ESHAPE] &= 0x0f;		PSG->Attack = (PSG->Regs[AY_ESHAPE] & 0x04) ? 0x1f : 0x00;		if ((PSG->Regs[AY_ESHAPE] & 0x08) == 0)		{			/* if Continue = 0, map the shape to the equivalent one which has Continue = 1 */			PSG->Hold = 1;			PSG->Alternate = PSG->Attack;		}		else		{			PSG->Hold = PSG->Regs[AY_ESHAPE] & 0x01;			PSG->Alternate = PSG->Regs[AY_ESHAPE] & 0x02;		}		PSG->CountE = PSG->PeriodE;		PSG->CountEnv = 0x1f;		PSG->Holding = 0;		PSG->VolE = PSG->VolTable[PSG->CountEnv ^ PSG->Attack];		if (PSG->EnvelopeA) PSG->VolA = PSG->VolE;		if (PSG->EnvelopeB) PSG->VolB = PSG->VolE;		if (PSG->EnvelopeC) PSG->VolC = PSG->VolE;		break;	case AY_PORTA:		if ((PSG->Regs[AY_ENABLE] & 0x40) == 0)		  //logerror("warning: write to 8910 #%d Port A set as input\n",n);if (PSG->PortAwrite) (*PSG->PortAwrite)(0,v);		// else logerror("PC %04x: warning - write %02x to 8910 #%d Port A\n",cpu_get_pc(),v,n);		break;	case AY_PORTB:		if ((PSG->Regs[AY_ENABLE] & 0x80) == 0)		  //logerror("warning: write to 8910 #%d Port B set as input\n",n);if (PSG->PortBwrite) (*PSG->PortBwrite)(0,v);		//else logerror("PC %04x: warning - write %02x to 8910 #%d Port B\n",cpu_get_pc(),v,n);		break;	}}/* write a register on AY8910 chip number 'n' */void AYWriteReg(int chip, int r, int v){	struct AY8910 *PSG = &AYPSG[chip];	if (r > 15) return;	if (r < 14)	{		if (r == AY_ESHAPE || PSG->Regs[r] != v)		{			/* update the output buffer before changing the register */			stream_update(PSG->Channel,0);		}	}	_AYWriteReg(chip,r,v);}unsigned char AYReadReg(int n, int r){	struct AY8910 *PSG = &AYPSG[n];	if (r > 15) return 0;	switch (r)	{	case AY_PORTA:		if ((PSG->Regs[AY_ENABLE] & 0x40) != 0)		  //logerror("warning: read from 8910 #%d Port A set as output\n",n);if (PSG->PortAread) PSG->Regs[AY_PORTA] = (*PSG->PortAread)(0);		//else logerror("PC %04x: warning - read 8910 #%d Port A\n",cpu_get_pc(),n);		break;	case AY_PORTB:		if ((PSG->Regs[AY_ENABLE] & 0x80) != 0)		  //logerror("warning: read from 8910 #%d Port B set as output\n",n);if (PSG->PortBread) PSG->Regs[AY_PORTB] = (*PSG->PortBread)(0);		//else logerror("PC %04x: warning - read 8910 #%d Port B\n",cpu_get_pc(),n);		break;	}	return PSG->Regs[r];}void AY8910Write(int chip,int a,int data){	struct AY8910 *PSG = &AYPSG[chip];	if (a & 1)	{	/* Data port */		AYWriteReg(chip,PSG->register_latch,data);	}	else	{	/* Register port */		PSG->register_latch = data & 0x0f;	}}int AY8910Read(int chip){	struct AY8910 *PSG = &AYPSG[chip];	return AYReadReg(chip,PSG->register_latch);}/* AY8910 interface */READ_HANDLER( AY8910_read_port_0_r ) { return AY8910Read(0); }READ_HANDLER( AY8910_read_port_1_r ) { return AY8910Read(1); }READ_HANDLER( AY8910_read_port_2_r ) { return AY8910Read(2); }READ_HANDLER( AY8910_read_port_3_r ) { return AY8910Read(3); }READ_HANDLER( AY8910_read_port_4_r ) { return AY8910Read(4); }WRITE_HANDLER( AY8910_control_port_0_w ) { AY8910Write(0,0,data); }WRITE_HANDLER( AY8910_control_port_1_w ) { AY8910Write(1,0,data); }WRITE_HANDLER( AY8910_control_port_2_w ) { AY8910Write(2,0,data); }WRITE_HANDLER( AY8910_control_port_3_w ) { AY8910Write(3,0,data); }WRITE_HANDLER( AY8910_control_port_4_w ) { AY8910Write(4,0,data); }WRITE_HANDLER( AY8910_write_port_0_w ) { AY8910Write(0,1,data); }WRITE_HANDLER( AY8910_write_port_1_w ) { AY8910Write(1,1,data); }WRITE_HANDLER( AY8910_write_port_2_w ) { AY8910Write(2,1,data); }WRITE_HANDLER( AY8910_write_port_3_w ) { AY8910Write(3,1,data); }WRITE_HANDLER( AY8910_write_port_4_w ) { AY8910Write(4,1,data); }static void AY8910Update(int chip,INT16 **buffer,int length){	struct AY8910 *PSG = &AYPSG[chip];	INT16 *buf1,*buf2,*buf3;	int outn;	buf1 = buffer[0];	buf2 = buffer[1];	buf3 = buffer[2];	/* The 8910 has three outputs, each output is the mix of one of the three */	/* tone generators and of the (single) noise generator. The two are mixed */	/* BEFORE going into the DAC. The formula to mix each channel is: */	/* (ToneOn | ToneDisable) & (NoiseOn | NoiseDisable). */	/* Note that this means that if both tone and noise are disabled, the output */	/* is 1, not 0, and can be modulated changing the volume. */	/* If the channels are disabled, set their output to 1, and increase the */	/* counter, if necessary, so they will not be inverted during this update. */	/* Setting the output to 1 is necessary because a disabled channel is locked */	/* into the ON state (see above); and it has no effect if the volume is 0. */	/* If the volume is 0, increase the counter, but don't touch the output. */	if (PSG->Regs[AY_ENABLE] & 0x01)	{		if (PSG->CountA <= length*STEP) PSG->CountA += length*STEP;		PSG->OutputA = 1;	}	else if (PSG->Regs[AY_AVOL] == 0)	{		/* note that I do count += length, NOT count = length + 1. You might think */		/* it's the same since the volume is 0, but doing the latter could cause */		/* interferencies when the program is rapidly modulating the volume. */		if (PSG->CountA <= length*STEP) PSG->CountA += length*STEP;	}	if (PSG->Regs[AY_ENABLE] & 0x02)	{		if (PSG->CountB <= length*STEP) PSG->CountB += length*STEP;		PSG->OutputB = 1;	}	else if (PSG->Regs[AY_BVOL] == 0)	{		if (PSG->CountB <= length*STEP) PSG->CountB += length*STEP;	}	if (PSG->Regs[AY_ENABLE] & 0x04)	{		if (PSG->CountC <= length*STEP) PSG->CountC += length*STEP;		PSG->OutputC = 1;	}	else if (PSG->Regs[AY_CVOL] == 0)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -