📄 pokey.c
字号:
/* calculate next bit */
x = ((x << a) + (x >> b) + c) & size;
}
}
static void Rand_init(UINT8 *p, int size, int a, int b, int c)
{
UINT32 i, x = 0;
for (i = 0; i < size; i++)
{
UINT8 rnd = x >> 3;
/* store the 8 bits of the new value */
*p++ = rnd;
/* calculate next bit */
x = ((x << a) + (x >> b) + c) & size;
}
}
/*****************************************************************************/
/* 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 */
/* */
/*****************************************************************************/
/* ASG 980126 - added a return parameter to indicate failure */
int Pokey_sound_init (int freq17, int playback_freq, int volume, int num_pokeys, int use_clip)
{
int chip, chan;
/* ASG 980126 - dynamically allocate this array */
poly17 = malloc (POLY17_SIZE+1);
if (!poly17) return 1;
/* HJB 980706 - random numbers are the upper 8 bits of poly 17 */
rand17 = malloc (POLY17_SIZE+1);
if (!rand17) return 1;
/* Initialize the poly counters */
Poly_init(poly4, POLY4_SIZE, POLY4_SHL, POLY4_SHR, POLY4_ADD);
Poly_init(poly5, POLY5_SIZE, POLY5_SHL, POLY5_SHR, POLY5_ADD);
Poly_init(poly9, POLY9_SIZE, POLY9_SHL, POLY9_SHR, POLY9_ADD);
Poly_init(poly17, POLY17_SIZE, POLY17_SHL, POLY17_SHR, POLY17_ADD);
Rand_init(rand17, POLY17_SIZE, POLY17_SHL, POLY17_SHR, POLY17_ADD);
/* start all of the polynomial counters at zero */
Poly_adjust = 0;
P4 = 0;
P5 = 0;
P9 = 0;
P17 = 0;
/* calculate the sample 'divide by N' value based on the playback freq. */
if (playback_freq)
Samp_n_max = ((UINT32)freq17 << 8) / playback_freq;
else
Samp_n_max = 1;
Samp_n_cnt[0] = 0; /* initialize all bits of the sample */
Samp_n_cnt[1] = 0; /* 'divide by N' counter */
for (chip = 0; chip < MAXPOKEYS; chip++) {
AUDCTL[chip] = 0;
Base_mult[chip] = DIV_64;
TIMER[chip][TIMER1] = 0;
TIMER[chip][TIMER2] = 0;
TIMER[chip][TIMER4] = 0;
KBCODE[chip] = 0x09; /* Atari 800 'no key' */
SEROUT[chip] = 0;
SERIN[chip] = 0;
IRQST[chip] = 0;
IRQEN[chip] = 0;
SKSTAT[chip] = 0;
SKCTL[chip] = SK_RESET; /* let the RNG run after reset */
}
for (chan = 0; chan < (MAXPOKEYS * 4); chan++) {
Outbit[chan] = 0;
Div_n_cnt[chan] = 0;
Div_n_max[chan] = 0x7fffffffL;
Div_n_tmr[chan] = 0;
AUDC[chan] = 0;
AUDF[chan] = 0;
AUDV[chan] = 0;
}
for (chip = 0; chip < num_pokeys; chip++) {
char name[40];
sprintf(name, "Pokey #%d", chip);
channel[chip] = stream_init(name, playback_freq, 8, chip, Pokey_process);
stream_set_volume(channel[chip],volume);
if (channel[chip] == -1)
return 1;
}
/* set the number of pokey chips currently emulated */
Num_pokeys = num_pokeys;
clip = use_clip; /* LBO 101297 */
/* ASG 980126 - return success */
return 0;
}
/*****************************************************************************/
/* Module: Pokey_Timer(int param) */
/* Purpose: Is called when a pokey timer expires. */
/* */
/* Author: Juergen Buchmueller */
/* Date: June, 27 1998 */
/* */
/* Inputs: param - chip * 8 + bitmask for timers (1,2,4) */
/* */
/* Outputs: modifies the IRQ status and calls a user supplied function */
/* */
/*****************************************************************************/
void Pokey_Timer(int param)
{
int chip;
/* split param into chip number and timer mask */
chip = param >> CHIP_SHIFT;
param &= TIMER_MASK;
/* check if some of the requested timer interrupts are enabled */
param &= IRQEN[chip];
if (param)
{
/* set the enabled timer irq status bits */
IRQST[chip] |= param;
/* call back an application supplied function to handle the interrupt */
if (intf->interrupt_cb[chip])
(*intf->interrupt_cb[chip])(param);
}
}
#if VERBOSE_SOUND
static char *audc2str(int val)
{
static char buff[80];
if (val & NOTPOLY5) {
if (val & PURE) {
strcpy(buff,"pure");
} else if (val & POLY4) {
strcpy(buff,"poly4");
} else {
strcpy(buff,"poly9/17");
}
} else {
if (val & PURE) {
strcpy(buff,"poly5");
} else if (val & POLY4) {
strcpy(buff,"poly4+poly5");
} else {
strcpy(buff,"poly9/17+poly5");
}
}
return buff;
}
static char *audctl2str(int val)
{
static char buff[80];
if (val & POLY9) {
strcpy(buff,"poly9");
} else {
strcpy(buff,"poly17");
}
if (val & CH1_179) {
strcpy(buff,"+ch1hi");
}
if (val & CH3_179) {
strcpy(buff,"+ch3hi");
}
if (val & CH1_CH2) {
strcpy(buff,"+ch1/2");
}
if (val & CH3_CH4) {
strcpy(buff,"+ch3/4");
}
if (val & CH1_FILTER) {
strcpy(buff,"+ch1filter");
}
if (val & CH2_FILTER) {
strcpy(buff,"+ch2filter");
}
if (val & CLOCK_15) {
strcpy(buff,"+clk15");
}
return buff;
}
#endif
/*****************************************************************************/
/* Module: Pokey_SerinReady(int param) */
/* Purpose: Is called when another data byte is ready at the SERIN register. */
/* */
/* Author: Juergen Buchmueller */
/* Date: June, 28 1998 */
/* */
/* Inputs: chip - chip number */
/* */
/* Outputs: modifies the IRQ status and calls a user supplied function */
/* */
/*****************************************************************************/
void Pokey_SerinReady(int chip)
{
if (IRQEN[chip] & IRQ_SERIN)
{
/* set the enabled timer irq status bits */
IRQST[chip] |= IRQ_SERIN;
/* call back an application supplied function to handle the interrupt */
if (intf->interrupt_cb[chip])
(*intf->interrupt_cb[chip])(IRQ_SERIN);
}
}
/*****************************************************************************/
/* 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 */
/* */
/*****************************************************************************/
void Update_pokey_sound (int addr, int val, int chip, int gain)
{
UINT32 new_val = 0;
UINT8 chan;
UINT8 chan_mask = 0;
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_C:
if (val == AUDF[CHAN1 + chip_offs])
return;
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_C:
if (val == AUDC[CHAN1 + chip_offs])
return;
AUDC[CHAN1 + chip_offs] = val;
AUDV[CHAN1 + chip_offs] = (val & VOLUME_MASK) * gain;
chan_mask = 1 << CHAN1;
break;
case AUDF2_C:
if (val == AUDF[CHAN2 + chip_offs])
return;
AUDF[CHAN2 + chip_offs] = val;
chan_mask = 1 << CHAN2;
break;
case AUDC2_C:
if (val == AUDC[CHAN2 + chip_offs])
return;
AUDC[CHAN2 + chip_offs] = val;
AUDV[CHAN2 + chip_offs] = (val & VOLUME_MASK) * gain;
chan_mask = 1 << CHAN2;
break;
case AUDF3_C:
if (val == AUDF[CHAN3 + chip_offs])
return;
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_C:
if (val == AUDC[CHAN3 + chip_offs])
return;
AUDC[CHAN3 + chip_offs] = val;
AUDV[CHAN3 + chip_offs] = (val & VOLUME_MASK) * gain;
chan_mask = 1 << CHAN3;
break;
case AUDF4_C:
if (val == AUDF[CHAN4 + chip_offs])
return;
AUDF[CHAN4 + chip_offs] = val;
chan_mask = 1 << CHAN4;
break;
case AUDC4_C:
if (val == AUDC[CHAN4 + chip_offs])
return;
AUDC[CHAN4 + chip_offs] = val;
AUDV[CHAN4 + chip_offs] = (val & VOLUME_MASK) * gain;
chan_mask = 1 << CHAN4;
break;
case AUDCTL_C:
if (val == AUDCTL[chip])
return;
AUDCTL[chip] = val;
chan_mask = 15; /* all channels */
/* determine the base multiplier for the 'div by n' calculations */
Base_mult[chip] = (AUDCTL[chip] & CLOCK_15) ? DIV_15 : DIV_64;
break;
case STIMER_C:
/* first remove any existing timers */
if (TIMER[chip][TIMER1])
timer_remove(TIMER[chip][TIMER1]);
if (TIMER[chip][TIMER2])
timer_remove(TIMER[chip][TIMER2]);
if (TIMER[chip][TIMER4])
timer_remove(TIMER[chip][TIMER4]);
TIMER[chip][TIMER1] = 0;
TIMER[chip][TIMER2] = 0;
TIMER[chip][TIMER4] = 0;
/* reset all counters to zero (side effect) */
Div_n_cnt[CHAN1 + chip_offs] = 0;
Div_n_cnt[CHAN2 + chip_offs] = 0;
Div_n_cnt[CHAN3 + chip_offs] = 0;
Div_n_cnt[CHAN4 + chip_offs] = 0;
/* joined chan#1 and chan#2 ? */
if (AUDCTL[chip] & CH1_CH2)
{
if (Div_n_tmr[CHAN2 + chip_offs] > MIN_TIMER)
{
/* set timer #1 _and_ #2 event after Div_n_tmr clocks of joined CHAN1+CHAN2 */
TIMER[chip][TIMER2] =
timer_pulse(1.0 * Div_n_tmr[CHAN2 + chip_offs] / intf->baseclock,
(chip << CHIP_SHIFT) | IRQ_TIMR2 | IRQ_TIMR1, Pokey_Timer);
}
}
else
{
if (Div_n_tmr[CHAN1 + chip_offs] > MIN_TIMER)
{
/* set timer #1 event after Div_n_tmr clocks of CHAN1 */
TIMER[chip][TIMER1] =
timer_pulse(1.0 * Div_n_tmr[CHAN1 + chip_offs] / intf->baseclock,
(chip << CHIP_SHIFT) | IRQ_TIMR1, Pokey_Timer);
}
if (Div_n_tmr[CHAN1 + chip_offs] > MIN_TIMER)
{
/* set timer #2 event after Div_n_tmr clocks of CHAN2 */
TIMER[chip][TIMER2] =
timer_pulse(1.0 * Div_n_tmr[CHAN2 + chip_offs] / intf->baseclock,
(chip << CHIP_SHIFT) | IRQ_TIMR2, Pokey_Timer);
}
}
/* NB: POKEY has no timer #3 :) */
if (AUDCTL[chip] & CH3_CH4)
{
/* not sure about this: if audc4 == 0000xxxx don't start timer 4 ? */
if (AUDC[CHAN4 + chip_offs] & 0xf0)
{
if (Div_n_tmr[CHAN4 + chip_offs] > MIN_TIMER)
{
/* set timer #4 event after Div_n_tmr clocks of CHAN4 */
TIMER[chip][TIMER4] =
timer_pulse(1.0 * Div_n_tmr[CHAN4 + chip_offs] / intf->baseclock,
(chip << CHIP_SHIFT) | IRQ_TIMR4, Pokey_Timer);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -