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

📄 pokey.c

📁 这个是延伸mame的在wince平台下的游戏模拟器的代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/*****************************************************************************/
/*                                                                           */
/* Module:	POKEY Chip Emulator, V3.1										 */
/* Purpose: To emulate the sound generation hardware of the Atari POKEY chip.*/
/* Author:  Ron Fries                                                        */
/*                                                                           */
/* Revision History:                                                         */
/*                                                                           */
/* 09/22/96 - Ron Fries - Initial Release                                    */
/* 01/14/97 - Ron Fries - Corrected a minor problem to improve sound quality */
/*                        Also changed names from POKEY11.x to POKEY.x       */
/* 01/17/97 - Ron Fries - Added support for multiple POKEY chips.            */
/* 03/31/97 - Ron Fries - Made some minor mods for MAME (changed to signed   */
/*                        8-bit sample, increased gain range, removed        */
/*                        _disable() and _enable().)                         */
/* 04/06/97 - Brad Oliver - Some cross-platform modifications. Added         */
/*                          big/little endian #defines, removed <dos.h>,     */
/*                          conditional defines for TRUE/FALSE               */
/* 08/08/97 - Brad Oliver - Added code to read the random number register.   */
/*            & Eric Smith  This code relies on writes to SKCNTL as well.    */
/* 06/27/98 - pullmoll	  - implemented missing POKEY registers.			 */
/*						  Since this is a major change I incremeneted to	 */
/*						  V3.0; detailed list of changes below. 			 */
/* 11/19/98 - pullmoll	  - support for MAMEs streams interface.			 */
/*                                                                           */
/* V3.1 Detailed Changes													 */
/* ---------------------                                                     */
/*																			 */
/* Changed Pokey_process to calculate one chip at a time. It is now called   */
/* by streams.c after it's given as parameter callback to stream_init().     */
/* Removed COMP16 support since the new polynome, random code would not      */
/* work with 16 bit machines (128K RNG buffer). 							 */
/* Other minor cleanups in code: made all arguments int (should be more 	 */
/* efficient on 32bit machines). Remove already commented DOS dependencies	 */
/* _enable() and _disable() (interrupts).									 */
/*                                                                           */
/* V3.0 Detailed Changes                                                     */
/* ---------------------                                                     */
/*                                                                           */
/* Now emulates all aspects of a real POKEY chip, including the KBCODE, 	 */
/* SERIN, SEROUT registers and TIMERS 1,2 and 4. The code of pokyintf.c and  */
/* pokey.c is now combined to avoid a large number of otherwise unneeded	 */
/* externs and calls between the two pieces of code.						 */
/*                                                                           */
/* The intf structure now holds additional function pointers for reading	 */
/* serial input, writing serial output and for a interrupt call back.		 */
/* These function pointers are optional; current implementations using the	 */
/* POKEY can set them to zero. Because of the timers the code now depends	 */
/* on the presence of MAME's timer.c (or equivalent functions).              */
/*																			 */
/* POKEY supported 3 timers, called TIMER1, TIMER2 and TIMER4. They were	 */
/* fired by the corresponding AUDF1, AUDF2 and AUDF4 divide by N counters.	 */
/* Once a counter reaches zero, one or more of the bits 0, 1 and 2 in the	 */
/* IRQST register that are enabled in IRQEN are set and an IRQ is issued.	 */
/* To avoid time consuming checks inside the sound generation code I used	 */
/* the MAME/MESS specific timer code instead. Therefore the changes should	 */
/* not lead to a speed decrease if the timers are not used at all.			 */
/*																			 */
/* The code uses timer_pulse(duration, mask, callback) to set a function	 */
/* that is repeatedly called after Div_n_max[chan]/intf->baseclock time is	 */
/* gone. This function checks for enabled timer IRQs in IRQEN and sets the	 */
/* corresponding bits in the IRQST register; it then calls an application	 */
/* supllied callback to handle the IRQ which could call cpu_cause_interrupt  */
/* or take the appropriate action.											 */
/*																			 */
/* The timers are disabled by a call to timer_enable(handle, 0), if a write  */
/* to IRQEN disables the corresponding bits; if the bits are set, timers are */
/* started again with timer_enable(handle, !0). This avoids breaking the CPU */
/* emulation to make unneeded calls to the timer handler, which would do	 */
/* nothing at all (there is no timer status other than IRQST).				 */
/*                                                                           */
/* The sound generation code also emulate the filters now.					 */
/* The effect is pretty much identical to what I hear on my Atari 800XL :)	 */
/*                                                                           */
/* V2.0 Detailed Changes                                                     */
/* ---------------------                                                     */
/*                                                                           */
/* Now maintains both a POLY9 and POLY17 counter.  Though this slows the     */
/* emulator in general, it was required to support mutiple POKEYs since      */
/* each chip can individually select POLY9 or POLY17 operation.  Also,       */
/* eliminated the Poly17_size variable.                                      */
/*                                                                           */
/* Changed address of POKEY chip.  In the original, the chip was fixed at    */
/* location D200 for compatibility with the Atari 800 line of 8-bit          */
/* computers. The update function now only examines the lower four bits, so  */
/* the location for all emulated chips is effectively xxx0 - xxx8.           */
/*                                                                           */
/* The Update_pokey_sound function has two additional parameters which       */
/* selects the desired chip and selects the desired gain.                    */
/*                                                                           */
/* Added clipping to reduce distortion, configurable at compile-time.        */
/*                                                                           */
/* The Pokey_sound_init function has an additional parameter which selects   */
/* the number of pokey chips to emulate.                                     */
/*                                                                           */
/* The output will be amplified by gain/16.  If the output exceeds the       */
/* maximum value after the gain, it will be limited to reduce distortion.    */
/* The best value for the gain depends on the number of POKEYs emulated      */
/* and the maximum volume used.  The maximum possible output for each        */
/* channel is 15, making the maximum possible output for a single chip to    */
/* be 60.  Assuming all four channels on the chip are used at full volume,   */
/* a gain of 64 can be used without distortion.  If 4 POKEY chips are        */
/* emulated and all 16 channels are used at full volume, the gain must be    */
/* no more than 16 to prevent distortion.  Of course, if only a few of the   */
/* 16 channels are used or not all channels are used at full volume, a       */
/* larger gain can be used.                                                  */
/*                                                                           */
/* The Pokey_process routine automatically processes all channels.			 */
/* No additional calls or functions are required.							 */
/*                                                                           */
/* The unoptimized Pokey_process2() function has been removed.               */
/*                                                                           */
/*****************************************************************************/
/*                                                                           */
/*                 License Information and Copyright Notice                  */
/*                 ========================================                  */
/*                                                                           */
/* PokeySound is Copyright(c) 1996-1997 by Ron Fries                         */
/*                                                                           */
/* This library is free software; you can redistribute it and/or modify it   */
/* under the terms of version 2 of the GNU Library General Public License    */
/* as published by the Free Software Foundation.                             */
/*                                                                           */
/* This library is distributed in the hope that it will be useful, but       */
/* WITHOUT ANY WARRANTY; without even the implied warranty of                */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library */
/* General Public License for more details.                                  */
/* To obtain a copy of the GNU Library General Public License, write to the  */
/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   */
/*                                                                           */
/* Any permitted reproduction of these routines, in whole or in part, must   */
/* bear this legend.                                                         */
/*                                                                           */
/*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include "driver.h"

#ifndef REGISTER
#define REGISTER register
#endif

#define VERBOSE         0
#define VERBOSE_SOUND	0
#define VERBOSE_TIMER	0
#define VERBOSE_POLY	0
#define VERBOSE_RANDOM	0

/* CONSTANT DEFINITIONS */

/* definitions for AUDCx (D201, D203, D205, D207) */
#define NOTPOLY5    0x80     /* selects POLY5 or direct CLOCK */
#define POLY4       0x40     /* selects POLY4 or POLY17 */
#define PURE        0x20     /* selects POLY4/17 or PURE tone */
#define VOL_ONLY    0x10     /* selects VOLUME OUTPUT ONLY */
#define VOLUME_MASK 0x0f     /* volume mask */

/* definitions for AUDCTL (D208) */
#define POLY9       0x80     /* selects POLY9 or POLY17 */
#define CH1_179     0x40     /* selects 1.78979 MHz for Ch 1 */
#define CH3_179     0x20     /* selects 1.78979 MHz for Ch 3 */
#define CH1_CH2     0x10     /* clocks channel 1 w/channel 2 */
#define CH3_CH4     0x08     /* clocks channel 3 w/channel 4 */
#define CH1_FILTER  0x04     /* selects channel 1 high pass filter */
#define CH2_FILTER  0x02     /* selects channel 2 high pass filter */
#define CLOCK_15    0x01     /* selects 15.6999kHz or 63.9210kHz */

/* for accuracy, the 64kHz and 15kHz clocks are exact divisions of
   the 1.79MHz clock */
#define DIV_64      28       /* divisor for 1.79MHz clock to 64 kHz */
#define DIV_15      114      /* divisor for 1.79MHz clock to 15 kHz */

/* SIZE     the size (in entries) of the polynomial tables */
/* SHL/SHR	the rotate factors for the poly counters */
/* ADD		constant value to be added to the poly counters */
#define POLY4_BITS  4
#define POLY5_BITS  5
#define POLY9_BITS  9
#define POLY17_BITS 17
#define POLY4_SIZE	((1<<POLY4_BITS)-1)
#define POLY5_SIZE	((1<<POLY5_BITS)-1)
#define POLY9_SIZE	((1<<POLY9_BITS)-1)
#define POLY17_SIZE ((1<<POLY17_BITS)-1)

#define POLY4_SHL   3
#define POLY4_SHR	1
#define POLY4_ADD	0x04

#define POLY5_SHL	3
#define POLY5_SHR	2
#define POLY5_ADD	0x08

#define POLY9_SHL	2
#define POLY9_SHR	7
#define POLY9_ADD	0x80

#define POLY17_SHL	7
#define POLY17_SHR	10
#define POLY17_ADD	0x18000

/* channel/chip definitions */
#define CHAN1       0
#define CHAN2       1
#define CHAN3       2
#define CHAN4       3
#define CHIP1       0
#define CHIP2       4
#define CHIP3       8
#define CHIP4      12
#define SAMPLE    127

/* interrupt enable/status definitions (D20E) */
#define IRQ_BREAK   0x80
#define IRQ_KEYBD   0x40
#define IRQ_SERIN   0x20
#define IRQ_SEROR   0x10
#define IRQ_SEROC	0x08
#define IRQ_TIMR4	0x04
#define IRQ_TIMR2	0x02
#define IRQ_TIMR1	0x01

/* SK status definitions (R/D20F) */
#define SK_FRAME    0x80
#define SK_OVERRUN	0x40
#define SK_KBERR	0x20
#define SK_SERIN	0x10
#define SK_SHIFT	0x08
#define SK_KEYBD	0x04
#define SK_SEROUT	0x02

/* SK control definitions (W/D20F) */
#define SK_BREAK    0x80
#define SK_BPS      0x70
#define SK_FM       0x08
#define SK_PADDLE   0x04
#define SK_RESET    0x03

/* timer definitions */
#define TIMER1      0
#define TIMER2		1
#define TIMER4		2

#define TIMER_MASK  (IRQ_TIMR4|IRQ_TIMR2|IRQ_TIMR1)
#define CHIP_SHIFT  3

#define MIN_TIMER	4


/* STATIC VARIABLE DEFINITIONS */

static int channel[MAXPOKEYS];

static struct POKEYinterface *intf;

/* number of pokey chips currently emulated */
static int Num_pokeys;

/* structures to hold the 9 pokey control bytes */
static UINT8 AUDF[4 * MAXPOKEYS];	/* AUDFx (D200, D202, D204, D206) */
static UINT8 AUDC[4 * MAXPOKEYS];	/* AUDCx (D201, D203, D205, D207) */
static UINT8 AUDCTL[MAXPOKEYS]; 	/* AUDCTL (D208) */

static void *TIMER[MAXPOKEYS][3];		   /* timers returned by timer.c */
static UINT8 KBCODE[MAXPOKEYS]; 		   /* KBCODE (R/D209) */
static UINT8 SERIN[MAXPOKEYS];			   /* SERIN (R/D20D) */
static UINT8 SEROUT[MAXPOKEYS]; 		   /* SEROUT (W/D20D) */
static UINT8 IRQST[MAXPOKEYS];			   /* IRQST (R/D20E) */
static UINT8 IRQEN[MAXPOKEYS];			   /* IRQEN (W/D20E) */
static UINT8 SKSTAT[MAXPOKEYS]; 		   /* SKSTAT (R/D20F) */
static UINT8 SKCTL[MAXPOKEYS];			   /* SKCTL (W/D20F) */

#ifdef CLIP
static UINT16 AUDV[4 * MAXPOKEYS];	/* Channel volume - derived */
#else
static UINT8 AUDV[4 * MAXPOKEYS];	/* Channel volume - derived */
#endif

static UINT8 Outbit[4 * MAXPOKEYS]; /* last output volume for each channel */


static UINT8 RANDOM[MAXPOKEYS]; 	/* The random number for each pokey */

/* Initialze the bit patterns for the polynomials. */

/* The 4bit and 5bit patterns are the identical ones used in the pokey chip. */
/* Though the patterns could be packed with 8 bits per byte, using only a */
/* single bit per byte keeps the math simple, which is important for */
/* efficient processing. */

static UINT8 poly4[POLY4_SIZE];
static UINT8 poly5[POLY5_SIZE];
static UINT8 poly9[POLY9_SIZE];
/* ASG 980126 - changed this to a dynamically allocated array */
static UINT8 *poly17;
static UINT8 *rand17;

static UINT32 Poly_adjust; /* the amount that the polynomial will need */
                           /* to be adjusted to process the next bit */

static UINT32 P4=0,   /* Global position pointer for the 4-bit	POLY array */
              P5=0,   /* Global position pointer for the 5-bit  POLY array */
              P9=0,   /* Global position pointer for the 9-bit  POLY array */
              P17=0;  /* Global position pointer for the 17-bit POLY array */

static UINT32 Div_n_cnt[4 * MAXPOKEYS],   /* Divide by n counter. one for each channel */
			  Div_n_max[4 * MAXPOKEYS],   /* Divide by n maximum, one for each channel */
			  Div_n_tmr[4 * MAXPOKEYS];   /* Divide by n real value (for timers) */

static UINT32 Samp_n_max,	  /* Sample max.  For accuracy, it is *256 */
              Samp_n_cnt[2];  /* Sample cnt. */

static UINT32 Base_mult[MAXPOKEYS]; /* selects either 64kHz or 15kHz clock mult */

static UINT8  clip; /* LBO 101297 */

/*****************************************************************************/
/* In my routines, I treat the sample output as another divide by N counter  */
/* For better accuracy, the Samp_n_cnt has a fixed binary decimal point      */
/* which has 8 binary digits to the right of the decimal point.  I use a two */
/* byte array to give me a minimum of 40 bits, and then use pointer math to  */
/* reference either the 24.8 whole/fraction combination or the 32-bit whole  */
/* only number.  This is mainly used to keep the math simple for             */
/* optimization. See below:                                                  */
/*                                                                           */
/* Representation on little-endian machines:                                 */
/* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx */
/* fraction   whole    whole    whole      whole   unused   unused   unused  */
/*                                                                           */
/* Samp_n_cnt[0] gives me a 32-bit int 24 whole bits with 8 fractional bits, */
/* while (UINT32 *)((UINT8 *)(&Samp_n_cnt[0])+1) gives me the 32-bit whole	 */
/* number only.                                                              */
/*                                                                           */
/* Representation on big-endian machines:                                    */
/* xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | xxxxxxxx xxxxxxxx xxxxxxxx.xxxxxxxx */
/*  unused   unused   unused    whole      whole    whole    whole  fraction */
/*                                                                           */
/* Samp_n_cnt[1] gives me a 32-bit int 24 whole bits with 8 fractional bits, */
/* while (UINT32 *)((UINT8 *)(&Samp_n_cnt[0])+3) gives me the 32-bit whole	 */
/* number only.                                                              */
/*																			 */
/* JAMC note:																 */
/* this strategy only works if ARCH allows use of unaligned integer pointers */
/* So , i've changed it to:                                                  */
/* Samp_n_cnt[0] stores whole part											 */
/* Samp_n_cnt[1] stores fractional part 									 */
/*****************************************************************************/


/*****************************************************************************/
/* Module:	Poly_init() 													 */
/* Purpose: Hopefully exact emulation of the poly counters					 */
/*			Based on a description from Perry McFarlane posted on			 */
/*			comp.sys.atari.8bit 1996/12/06: 								 */
/*	I have been working on writing a simple program to play pokey chip		 */
/*	sounds on the pc.  While detailed technical information is available	 */
/*	in the 400/800 hardware manual including a schematic diagram of the 	 */
/*	operation of the pokey chip it lacks a description of the precise		 */
/*	operation of the polynomial counters which generate the pseudorandom	 */
/*	bit patterns used to make the distortion.  I have experimented and i	 */
/*	believe i have the exact formula which is used.  let x=0 then			 */
/*	x=((x<<a)+(x>>b)+c)%m gives the next value of x, and x%2 is the bit 	 */
/*	used. if n is the # of bits in the poly counter, then a+b=n and m=2^n.	 */
/*	the sequence of bits generated has a period of 2^n-1.  acutally what	 */
/*	this is is just a circular shift, plus an addition.  I empirically		 */
/*	determined the values of a,b, and c which generate bit patterns 		 */
/*	matching those used by the pokey.										 */
/*	4 bits a=3 b=1 c=4														 */
/*	5 bits a=3 b=2 c=8														 */
/*	9 bits a=2 b=7 c=128													 */
/*	17bits a=7 b=10 c=98304 												 */
/*	i suspect a similar formula is used for the atari vcs chips , perhaps	 */
/*	for other sound generation hardware 									 */
/*                                                                           */
/* Author:	Juergen Buchmueller 											 */
/* Date:	June 29, 1998													 */
/*                                                                           */
/* Inputs:	p - pointer to the polynome buffer (one byte per bit)			 */
/*			size - length of polynome (2^bits-1)							 */
/*			a - left shift factor											 */
/*			b - right shift factor											 */
/*			c - value for addition											 */
/*                                                                           */
/* Outputs: fill the buffer at p with poly bit 0							 */
/*                                                                           */
/*****************************************************************************/
static void Poly_init(UINT8 *p, int size, int a, int b, int c)
{
UINT32 i, x = 0;
    for (i = 0; i < size; i++)
	{
	UINT8 bit = x & 1;
        /* store new value */
		*p++ = bit;

⌨️ 快捷键说明

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