📄 pokey.c
字号:
/*****************************************************************************/
/* */
/* 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 + -