📄 pokeysnd.c
字号:
/*
* pokeysnd.c - POKEY sound chip emulation, v2.4
*
* Copyright (C) 1996-1998 Ron Fries
* Copyright (C) 1998-2006 Atari800 development team (see DOC/CREDITS)
*
* This file is part of the Atari800 emulator project which emulates
* the Atari 400, 800, 800XL, 130XE, and 5200 8-bit computers.
*
* Atari800 is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Atari800 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Atari800; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//#include "config.h"
#ifdef ASAP /* external project, see http://asap.sf.net */
#include "asap_internal.h"
#else
#include "atari.h"
#ifndef __PLUS
#include "sndsave.h"
#else
#include "sound_win.h"
#endif
#endif
#include "mzpokeysnd.h"
#include "pokeysnd.h"
#ifdef WORDS_UNALIGNED_OK
# define READ_U32(x) (*(uint32 *) (x))
# define WRITE_U32(x, d) (*(uint32 *) (x) = (d))
#else
# ifdef WORDS_BIGENDIAN
# define READ_U32(x) (((*(unsigned char *)(x)) << 24) | ((*((unsigned char *)(x) + 1)) << 16) | \
((*((unsigned char *)(x) + 2)) << 8) | ((*((unsigned char *)(x) + 3))))
# define WRITE_U32(x, d) \
{ \
uint32 i = d; \
(*(unsigned char *) (x)) = (((i) >> 24) & 255); \
(*((unsigned char *) (x) + 1)) = (((i) >> 16) & 255); \
(*((unsigned char *) (x) + 2)) = (((i) >> 8) & 255); \
(*((unsigned char *) (x) + 3)) = ((i) & 255); \
}
# else
# define READ_U32(x) ((*(unsigned char *) (x)) | ((*((unsigned char *) (x) + 1)) << 8) | \
((*((unsigned char *) (x) + 2)) << 16) | ((*((unsigned char *) (x) + 3)) << 24))
# define WRITE_U32(x, d) \
{ \
uint32 i = d; \
(*(unsigned char *)(x)) = ((i) & 255); \
(*((unsigned char *)(x) + 1)) = (((i) >> 8) & 255); \
(*((unsigned char *)(x) + 2)) = (((i) >> 16) & 255); \
(*((unsigned char *)(x) + 3)) = (((i) >> 24) & 255); \
}
# endif
#endif
/* GLOBAL VARIABLE DEFINITIONS */
/* number of pokey chips currently emulated */
static uint8 Num_pokeys;
static uint8 AUDV[4 * MAXPOKEYS]; /* Channel volume - derived */
static uint8 Outbit[4 * MAXPOKEYS]; /* current state of the output (high or low) */
static uint8 Outvol[4 * MAXPOKEYS]; /* last output volume for each channel */
/* 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 bit4[POLY4_SIZE] =
#ifndef POKEY23_POLY
{1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0}; /* new table invented by Perry */
#else
{1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0}; /* original POKEY 2.3 table */
#endif
static uint8 bit5[POLY5_SIZE] =
#ifndef POKEY23_POLY
{1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0};
#else
{0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1};
#endif
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 */
static uint32 Samp_n_max, /* Sample max. For accuracy, it is *256 */
Samp_n_cnt[2]; /* Sample cnt. */
extern int atari_speaker;
#ifdef INTERPOLATE_SOUND
static uint16 last_val = 0; /* last output value */
#ifdef STEREO_SOUND
static uint16 last_val2 = 0; /* last output value */
#endif
#endif
/* Volume only emulations declarations */
#ifdef VOL_ONLY_SOUND
#define SAMPBUF_MAX 2000
int sampbuf_val[SAMPBUF_MAX]; /* volume values */
int sampbuf_cnt[SAMPBUF_MAX]; /* relative start time */
int sampbuf_ptr = 0; /* pointer to sampbuf */
int sampbuf_rptr = 0; /* pointer to read from sampbuf */
int sampbuf_last = 0; /* last absolute time */
int sampbuf_AUDV[4 * MAXPOKEYS]; /* prev. channel volume */
int sampbuf_lastval = 0; /* last volume */
int sampout; /* last out volume */
uint16 samp_freq;
int samp_consol_val = 0; /* actual value of console sound */
#ifdef STEREO_SOUND
int sampbuf_val2[SAMPBUF_MAX]; /* volume values */
int sampbuf_cnt2[SAMPBUF_MAX]; /* relative start time */
int sampbuf_ptr2 = 0; /* pointer to sampbuf */
int sampbuf_rptr2 = 0; /* pointer to read from sampbuf */
int sampbuf_last2 = 0; /* last absolute time */
int sampbuf_lastval2 = 0; /* last volume */
int sampout2; /* last out volume */
#endif
#endif /* VOL_ONLY_SOUND */
static uint32 snd_freq17 = FREQ_17_EXACT;
int32 snd_playback_freq = 44100;
uint8 snd_num_pokeys = 1;
static int snd_flags = 0;
static int mz_quality = 0; /* default quality for mzpokeysnd */
#ifdef __PLUS
int mz_clear_regs = 0;
#endif
int enable_new_pokey = TRUE;
#ifndef ASAP
int stereo_enabled = FALSE;
#endif
/* multiple sound engine interface */
static void Pokey_process_8(void *sndbuffer, unsigned sndn);
static void Pokey_process_16(void *sndbuffer, unsigned sndn);
static void null_pokey_process(void *sndbuffer, unsigned int sndn) {}
void (*Pokey_process_ptr)(void *sndbuffer, unsigned int sndn) = null_pokey_process;
static void Update_pokey_sound_rf(uint16, uint8, uint8, uint8);
static void null_pokey_sound(uint16 addr, uint8 val, uint8 chip, uint8 gain) {}
void (*Update_pokey_sound) (uint16 addr, uint8 val, uint8 chip, uint8 gain)
= null_pokey_sound;
#ifdef SERIO_SOUND
static void Update_serio_sound_rf(int out, UBYTE data);
static void null_serio_sound(int out, UBYTE data) {}
void (*Update_serio_sound)(int out, UBYTE data) = null_serio_sound;
int serio_sound_enabled = 1;
#endif
#ifdef CONSOLE_SOUND
static void Update_consol_sound_rf(int set);
static void null_consol_sound(int set) {}
void (*Update_consol_sound)(int set) = null_consol_sound;
int console_sound_enabled = 1;
#endif
#ifdef VOL_ONLY_SOUND
static void Update_vol_only_sound_rf(void);
static void null_vol_only_sound(void) {}
void (*Update_vol_only_sound)(void) = null_vol_only_sound;
#endif
/*****************************************************************************/
/* 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. */
/*****************************************************************************/
/*****************************************************************************/
/* Module: Pokey_sound_init() */
/* Purpose: to handle the power-up initialization functions */
/* these functions should only be executed on a cold-restart */
/* */
/* Author: Ron Fries */
/* Date: January 1, 1997 */
/* */
/* Inputs: freq17 - the value for the '1.79MHz' Pokey audio clock */
/* playback_freq - the playback frequency in samples per second */
/* num_pokeys - specifies the number of pokey chips to be emulated */
/* */
/* Outputs: Adjusts local globals - no return value */
/* */
/*****************************************************************************/
static int Pokey_sound_init_rf(uint32 freq17, uint16 playback_freq,
uint8 num_pokeys, unsigned int flags);
int Pokey_DoInit(void)
{
if (enable_new_pokey)
return Pokey_sound_init_mz(snd_freq17, (uint16) snd_playback_freq,
snd_num_pokeys, snd_flags, mz_quality
#ifdef __PLUS
, mz_clear_regs
#endif
);
else
return Pokey_sound_init_rf(snd_freq17, (uint16) snd_playback_freq,
snd_num_pokeys, snd_flags);
}
int Pokey_sound_init(uint32 freq17, uint16 playback_freq, uint8 num_pokeys,
unsigned int flags
#ifdef __PLUS
, int clear_regs
#endif
)
{
snd_freq17 = freq17;
snd_playback_freq = playback_freq;
snd_num_pokeys = num_pokeys;
snd_flags = flags;
#ifdef __PLUS
mz_clear_regs = clear_regs;
#endif
return Pokey_DoInit();
}
void Pokey_set_mzquality(int quality) /* specially for win32, perhaps not needed? */
{
mz_quality = quality;
}
void Pokey_process(void *sndbuffer, unsigned int sndn)
{
Pokey_process_ptr(sndbuffer, sndn);
#if !defined(__PLUS) && !defined(ASAP)
WriteToSoundFile(sndbuffer, sndn);
#endif
}
static int Pokey_sound_init_rf(uint32 freq17, uint16 playback_freq,
uint8 num_pokeys, unsigned int flags)
{
uint8 chan;
Update_pokey_sound = Update_pokey_sound_rf;
#ifdef SERIO_SOUND
Update_serio_sound = Update_serio_sound_rf;
#endif
#ifdef CONSOLE_SOUND
Update_consol_sound = Update_consol_sound_rf;
#endif
#ifdef VOL_ONLY_SOUND
Update_vol_only_sound = Update_vol_only_sound_rf;
#endif
Pokey_process_ptr = (flags & SND_BIT16) ? Pokey_process_16 : Pokey_process_8;
#ifdef VOL_ONLY_SOUND
samp_freq = playback_freq;
#endif
/* start all of the polynomial counters at zero */
P4 = 0;
P5 = 0;
P9 = 0;
P17 = 0;
/* calculate the sample 'divide by N' value based on the playback freq. */
Samp_n_max = ((uint32) freq17 << 8) / playback_freq;
Samp_n_cnt[0] = 0; /* initialize all bits of the sample */
Samp_n_cnt[1] = 0; /* 'divide by N' counter */
for (chan = 0; chan < (MAXPOKEYS * 4); chan++) {
Outvol[chan] = 0;
Outbit[chan] = 0;
Div_n_cnt[chan] = 0;
Div_n_max[chan] = 0x7fffffffL;
AUDV[chan] = 0;
#ifdef VOL_ONLY_SOUND
sampbuf_AUDV[chan] = 0;
#endif
}
/* set the number of pokey chips currently emulated */
Num_pokeys = num_pokeys;
return 0; /* OK */
}
/*****************************************************************************/
/* Module: Update_pokey_sound() */
/* Purpose: To process the latest control values stored in the AUDF, AUDC, */
/* and AUDCTL registers. It pre-calculates as much information as */
/* possible for better performance. This routine has not been */
/* optimized. */
/* */
/* Author: Ron Fries */
/* Date: January 1, 1997 */
/* */
/* Inputs: addr - the address of the parameter to be changed */
/* val - the new value to be placed in the specified address */
/* gain - specified as an 8-bit fixed point number - use 1 for no */
/* amplification (output is multiplied by gain) */
/* */
/* Outputs: Adjusts local globals - no return value */
/* */
/*****************************************************************************/
static void Update_pokey_sound_rf(uint16 addr, uint8 val, uint8 chip,
uint8 gain)
{
uint32 new_val = 0;
uint8 chan;
uint8 chan_mask;
uint8 chip_offs;
/* calculate the chip_offs for the channel arrays */
chip_offs = chip << 2;
/* determine which address was changed */
switch (addr & 0x0f) {
case _AUDF1:
/* AUDF[CHAN1 + chip_offs] = val; */
chan_mask = 1 << CHAN1;
if (AUDCTL[chip] & CH1_CH2) /* if ch 1&2 tied together */
chan_mask |= 1 << CHAN2; /* then also change on ch2 */
break;
case _AUDC1:
/* AUDC[CHAN1 + chip_offs] = val; */
AUDV[CHAN1 + chip_offs] = (val & VOLUME_MASK) * gain;
chan_mask = 1 << CHAN1;
break;
case _AUDF2:
/* AUDF[CHAN2 + chip_offs] = val; */
chan_mask = 1 << CHAN2;
break;
case _AUDC2:
/* AUDC[CHAN2 + chip_offs] = val; */
AUDV[CHAN2 + chip_offs] = (val & VOLUME_MASK) * gain;
chan_mask = 1 << CHAN2;
break;
case _AUDF3:
/* AUDF[CHAN3 + chip_offs] = val; */
chan_mask = 1 << CHAN3;
if (AUDCTL[chip] & CH3_CH4) /* if ch 3&4 tied together */
chan_mask |= 1 << CHAN4; /* then also change on ch4 */
break;
case _AUDC3:
/* AUDC[CHAN3 + chip_offs] = val; */
AUDV[CHAN3 + chip_offs] = (val & VOLUME_MASK) * gain;
chan_mask = 1 << CHAN3;
break;
case _AUDF4:
/* AUDF[CHAN4 + chip_offs] = val; */
chan_mask = 1 << CHAN4;
break;
case _AUDC4:
/* AUDC[CHAN4 + chip_offs] = val; */
AUDV[CHAN4 + chip_offs] = (val & VOLUME_MASK) * gain;
chan_mask = 1 << CHAN4;
break;
case _AUDCTL:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -