/*****************************************************************************/ /* */ /* Module: TIA Chip Sound Simulator */ /* Purpose: To emulate the sound generation hardware of the Atari TIA chip. */ /* Author: Ron Fries */ /* */ /* Revision History: */ /* 10-Sep-96 - V1.0 - Initial Release */ /* 14-Jan-97 - V1.1 - Cleaned up sound output by eliminating counter */ /* reset. */ /* */ /*****************************************************************************/ /* */ /* License Information and Copyright Notice */ /* ======================================== */ /* */ /* TiaSound is Copyright(c) 1996 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 "Atari2600EmulatorGlobals.h" #include "emuapi.h" /* define some data types to keep it platform independent */ #define int8 char #define int16 short #define int32 int #define uint8 unsigned int8 #define uint16 unsigned int16 #define uint32 unsigned int32 /* CONSTANT DEFINITIONS */ /* definitions for AUDCx (15, 16) */ #define SET_TO_1 0x00 /* 0000 */ #define POLY4 0x01 /* 0001 */ #define DIV31_POLY4 0x02 /* 0010 */ #define POLY5_POLY4 0x03 /* 0011 */ #define PURE1 0x04 /* 0100 */ #define PURE2 0x05 /* 0101 */ #define DIV31_PURE 0x06 /* 0110 */ #define POLY5_2 0x07 /* 0111 */ #define POLY9 0x08 /* 1000 */ #define POLY5 0x09 /* 1001 */ #define DIV31_POLY5 0x0a /* 1010 */ #define POLY5_POLY5 0x0b /* 1011 */ #define DIV3_PURE 0x0c /* 1100 */ #define DIV3_PURE2 0x0d /* 1101 */ #define DIV93_PURE 0x0e /* 1110 */ #define DIV3_POLY5 0x0f /* 1111 */ #define DIV3_MASK 0x0c #define AUDC0 0x15 #define AUDC1 0x16 #define AUDF0 0x17 #define AUDF1 0x18 #define AUDV0 0x19 #define AUDV1 0x1a /* the size (in entries) of the 4 polynomial tables */ #define POLY4_SIZE 0x000f #define POLY5_SIZE 0x001f #define POLY9_SIZE 0x01ff /* channel definitions */ #define CHAN1 0 #define CHAN2 1 //#define FALSE 0 //#define TRUE 1 /* LOCAL GLOBAL VARIABLE DEFINITIONS */ /* structures to hold the 6 tia sound control bytes */ uint8 AUDC[2]; /* AUDCx (15, 16) */ uint8 AUDF[2]; /* AUDFx (17, 18) */ uint8 AUDV[2]; /* AUDVx (19, 1A) */ static uint8 Outvol[2]; /* 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 tia 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] = { 1,1,0,1,1,1,0,0,0,0,1,0,1,0,0 }; static uint8 Bit5[POLY5_SIZE] = { 0,0,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,1,0,1,1,1,0,1,0,1,0,0,0,0,1 }; /* I've treated the 'Div by 31' counter as another polynomial because of */ /* the way it operates. It does not have a 50% duty cycle, but instead */ /* has a 13:18 ratio (of course, 13+18 = 31). This could also be */ /* implemented by using counters. */ static uint8 Div31[POLY5_SIZE] = { 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 }; /* Rather than have a table with 511 entries, I use a random number */ /* generator. */ static uint8 Bit9[POLY9_SIZE]; static uint8 P4[2]; /* Position pointer for the 4-bit POLY array */ static uint8 P5[2]; /* Position pointer for the 5-bit POLY array */ static uint16 P9[2]; /* Position pointer for the 9-bit POLY array */ static uint8 Div_n_cnt[2]; /* Divide by n counter. one for each channel */ static uint8 Div_n_max[2]; /* Divide by n maximum, one for each channel */ /* 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. */ static uint16 Samp_n_max; /* Sample max, multiplied by 256 */ static uint16 Samp_n_cnt; /* Sample cnt. */ extern unsigned char *sounddata; /*****************************************************************************/ /* Module: Tia_sound_init() */ /* Purpose: to handle the power-up initialization functions */ /* these functions should only be executed on a cold-restart */ /* */ /* Author: Ron Fries */ /* Date: September 10, 1996 */ /* */ /* Inputs: sample_freq - the value for the '30 Khz' Tia audio clock */ /* playback_freq - the playback frequency in samples per second */ /* */ /* Outputs: Adjusts local globals - no return value */ /* */ /*****************************************************************************/ void Tia_sound_init (uint16 sample_freq, uint16 playback_freq) { uint8 chan; int16 n; /* fill the 9bit polynomial with random bits */ for (n=0; n 1) { /* decrement and loop */ Div_n_cnt[chan]--; } /* otherwise if we've reached the bottom */ else if (Div_n_cnt[chan] == 1) { /* reset the counter */ Div_n_cnt[chan] = Div_n_max[chan]; /* the P5 counter has multiple uses, so we inc it here */ P5[chan]++; if (P5[chan] == POLY5_SIZE) P5[chan] = 0; /* check clock modifier for clock tick */ /* if we're using pure tones OR we're using DIV31 and the DIV31 bit is set OR we're using POLY5 and the POLY5 bit is set */ if (((AUDC[chan] & 0x02) == 0) || (((AUDC[chan] & 0x01) == 0) && Div31[P5[chan]]) || (((AUDC[chan] & 0x01) == 1) && Bit5[P5[chan]])) { if (AUDC[chan] & 0x04) /* pure modified clock selected */ { if (Outvol[chan]) /* if the output was set */ Outvol[chan] = 0; /* turn it off */ else Outvol[chan] = AUDV[chan]; /* else turn it on */ } else if (AUDC[chan] & 0x08) /* check for p5/p9 */ { if (AUDC[chan] == POLY9) /* check for poly9 */ { /* inc the poly9 counter */ P9[chan]++; if (P9[chan] == POLY9_SIZE) P9[chan] = 0; if (Bit9[P9[chan]]) /* if poly9 bit is set */ Outvol[chan] = AUDV[chan]; else Outvol[chan] = 0; } else /* must be poly5 */ { if (Bit5[P5[chan]]) Outvol[chan] = AUDV[chan]; else Outvol[chan] = 0; } } else /* poly4 is the only remaining option */ { /* inc the poly4 counter */ P4[chan]++; if (P4[chan] == POLY4_SIZE) P4[chan] = 0; if (Bit4[P4[chan]]) Outvol[chan] = AUDV[chan]; else Outvol[chan] = 0; } } } } /* decrement the sample counter - value is 256 since the lower byte contains the fractional part */ Samp_n_cnt -= 256; /* if the count down has reached zero */ if (Samp_n_cnt < 256) { /* adjust the sample counter */ Samp_n_cnt += Samp_n_max; /* calculate the latest output value and place in buffer */ *(buffer++) = Outvol[0] + Outvol[1]; /* and indicate one less byte to process */ n--; } } } /*****************************************************************************/ /* Module: Tia_process() */ /* Purpose: To fill the output buffer with the sound output based on the */ /* tia chip parameters. This routine has been optimized. */ /* */ /* Author: Ron Fries */ /* Date: September 10, 1996 */ /* */ /* Inputs: *buffer - pointer to the buffer where the audio output will */ /* be placed */ /* n - size of the playback buffer */ /* */ /* Outputs: the buffer will be filled with n bytes of audio - no return val */ /* */ /*****************************************************************************/ void Tia_process_2 ( unsigned short *buffer, uint16 n) { uint8 audc0,audv0,audc1,audv1; uint8 div_n_cnt0,div_n_cnt1; uint8 p5_0, p5_1,outvol_0,outvol_1; audc0 = AUDC[0]; audv0 = AUDV[0]; audc1 = AUDC[1]; audv1 = AUDV[1]; /* make temporary local copy */ p5_0 = P5[0]; p5_1 = P5[1]; outvol_0 = Outvol[0]; outvol_1 = Outvol[1]; div_n_cnt0 = Div_n_cnt[0]; div_n_cnt1 = Div_n_cnt[1]; /* loop until the buffer is filled */ while (n) { /* Process channel 0 */ if (div_n_cnt0 > 1) { div_n_cnt0--; } else if (div_n_cnt0 == 1) { div_n_cnt0 = Div_n_max[0]; /* the P5 counter has multiple uses, so we inc it here */ p5_0++; if (p5_0 == POLY5_SIZE) p5_0 = 0; /* check clock modifier for clock tick */ if (((audc0 & 0x02) == 0) || (((audc0 & 0x01) == 0) && Div31[p5_0]) || (((audc0 & 0x01) == 1) && Bit5[p5_0])) { if (audc0 & 0x04) /* pure modified clock selected */ { if (outvol_0) /* if the output was set */ outvol_0 = 0; /* turn it off */ else outvol_0 = audv0; /* else turn it on */ } else if (audc0 & 0x08) /* check for p5/p9 */ { if (audc0 == POLY9) /* check for poly9 */ { /* inc the poly9 counter */ P9[0]++; if (P9[0] == POLY9_SIZE) P9[0] = 0; if (Bit9[P9[0]]) outvol_0 = audv0; else outvol_0 = 0; } else /* must be poly5 */ { if (Bit5[p5_0]) outvol_0 = audv0; else outvol_0 = 0; } } else /* poly4 is the only remaining option */ { /* inc the poly4 counter */ P4[0]++; if (P4[0] == POLY4_SIZE) P4[0] = 0; if (Bit4[P4[0]]) outvol_0 = audv0; else outvol_0 = 0; } } } /* Process channel 1 */ if (div_n_cnt1 > 1) { div_n_cnt1--; } else if (div_n_cnt1 == 1) { div_n_cnt1 = Div_n_max[1]; /* the P5 counter has multiple uses, so we inc it here */ p5_1++; if (p5_1 == POLY5_SIZE) p5_1 = 0; /* check clock modifier for clock tick */ if (((audc1 & 0x02) == 0) || (((audc1 & 0x01) == 0) && Div31[p5_1]) || (((audc1 & 0x01) == 1) && Bit5[p5_1])) { if (audc1 & 0x04) /* pure modified clock selected */ { if (outvol_1) /* if the output was set */ outvol_1 = 0; /* turn it off */ else outvol_1 = audv1; /* else turn it on */ } else if (audc1 & 0x08) /* check for p5/p9 */ { if (audc1 == POLY9) /* check for poly9 */ { /* inc the poly9 counter */ P9[1]++; if (P9[1] == POLY9_SIZE) P9[1] = 0; if (Bit9[P9[1]]) outvol_1 = audv1; else outvol_1 = 0; } else /* must be poly5 */ { if (Bit5[p5_1]) outvol_1 = audv1; else outvol_1 = 0; } } else /* poly4 is the only remaining option */ { /* inc the poly4 counter */ P4[1]++; if (P4[1] == POLY4_SIZE) P4[1] = 0; if (Bit4[P4[1]]) outvol_1 = audv1; else outvol_1 = 0; } } } /* decrement the sample counter - value is 256 since the lower byte contains the fractional part */ Samp_n_cnt -= 256; /* if the count down has reached zero */ if (Samp_n_cnt < 256) { /* adjust the sample counter */ Samp_n_cnt += Samp_n_max; /* calculate the latest output value and place in buffer */ *(buffer++) = (outvol_0 + outvol_1)*256; /* and indicate one less byte to process */ n--; } } /* save for next round */ P5[0] = p5_0; P5[1] = p5_1; Outvol[0] = outvol_0; Outvol[1] = outvol_1; Div_n_cnt[0] = div_n_cnt0; Div_n_cnt[1] = div_n_cnt1; }