From 58e4256460bd553416498a8d9d8d7cdec50b4df6 Mon Sep 17 00:00:00 2001 From: Luke Iannini Date: Wed, 21 Mar 2007 09:08:16 +0000 Subject: This commit was generated by cvs2svn to compensate for changes in r7511, which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/mmonoplayer/; revision=7512 --- atari2600~.c | 485 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 485 insertions(+) create mode 100644 atari2600~.c diff --git a/atari2600~.c b/atari2600~.c new file mode 100644 index 0000000..a0aff2d --- /dev/null +++ b/atari2600~.c @@ -0,0 +1,485 @@ +/*****************************************************************************/ +/* */ +/* TIA Chip Sound Simulator for PD */ +/* Purpose: To emulate the sound generation hardware of the Atari TIA chip. */ +/* Author(s): Kyle Buza, Ron Fries */ +/* */ +/*****************************************************************************/ + +#include +#include "m_pd.h" + +static t_class *atari_2600_class; + +typedef signed char Int8; +typedef signed int Int16; + +#ifdef WIN32 +#define int8 char +#define int16 short +#define int32 int +#else +#define int8 char +#define int16 int +#define int32 long +#endif + +#define uint8 unsigned int8 +#define uint16 unsigned int16 +#define uint32 unsigned int32 + +/* 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 PURE 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 + +/* 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 }; + +typedef struct _atari_2600 +{ + t_object x_obj; + t_float x15; + t_float x16; + t_float x17; + t_float x18; + t_float x19; + t_float x1a; + long Bit9[POLY9_SIZE]; + long P4[2]; + long P5[2]; + long P9[2]; + long Div_n_cnt[2]; + long Div_n_max[2]; + long AUDC[2]; + long AUDF[2]; + long AUDV[2]; + long Outvol[2]; + long volume; +} t_atari_2600; + +void *atari_2600_new(void); +t_int *atari_2600_perform(t_int *w); +void atari_2600_dsp(t_atari_2600 *x, t_signal **sp, short *count); + +void Tia_sound_init(t_atari_2600 *x, uint16 sample_freq, uint16 playback_freq); +void Update_tia_sound(t_atari_2600 *x, uint16 addr, uint8 val); +void Tia_process (t_atari_2600 *x, t_float *buffer, uint16 n); + +void atari_2600_tilde_setup(void) +{ + atari_2600_class = class_new(gensym("atari_2600~"), + (t_newmethod)atari_2600_new, 0, sizeof(t_atari_2600), CLASS_DEFAULT, A_DEFFLOAT, 0); + class_addmethod(atari_2600_class, (t_method)atari_2600_dsp, gensym("dsp"), 0); +} + +void *atari_2600_new(void) +{ + t_atari_2600 *x = (t_atari_2600 *)pd_new(atari_2600_class); + x->x15 = 0; + x->x16 = 0; + x->x17 = 0; + x->x18 = 0; + x->x19 = 0; + x->x1a = 0; + floatinlet_new(&x->x_obj, &x->x15); + floatinlet_new(&x->x_obj, &x->x17); + floatinlet_new(&x->x_obj, &x->x19); + floatinlet_new(&x->x_obj, &x->x16); + floatinlet_new(&x->x_obj, &x->x18); + floatinlet_new(&x->x_obj, &x->x1a); + outlet_new(&x->x_obj, &s_signal); + + Tia_sound_init(x, 32000, 32000); + return (x); +} + +void atari_2600_dsp(t_atari_2600 *x, t_signal **sp, short *count) +{ + dsp_add(atari_2600_perform, 3, sp[0]->s_vec, sp[0]->s_n, x); +} + +t_int *atari_2600_perform(t_int *w) +{ + t_float *outL = (t_float *)(w[1]); + t_atari_2600 *x = (t_atari_2600 *)(w[3]); + + Update_tia_sound(x, 0x15, x->x15); + Update_tia_sound(x, 0x16, x->x16); + Update_tia_sound(x, 0x17, x->x17); + Update_tia_sound(x, 0x18, x->x18); + Update_tia_sound(x, 0x19, x->x19); + Update_tia_sound(x, 0x1a, x->x1a); + + Tia_process(x, outL, (int)(w[2])); + + return (w + 4); +} + + +/*****************************************************************************/ +/* */ +/* 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. */ +/* */ +/*****************************************************************************/ + +void Tia_sound_init (t_atari_2600 *x, uint16 sample_freq, uint16 playback_freq) +{ + uint8 chan; + int16 n; + + /* fill the 9bit polynomial with random bits */ + for (n=0; nBit9[n] = rand() & 0x01; /* fill poly9 with random bits */ + } + + /* initialize the local globals */ + for (chan = CHAN1; chan <= CHAN2; chan++) + { + x->Outvol[chan] = 0; + x->Div_n_cnt[chan] = 0; + x->Div_n_max[chan] = 0; + x->AUDC[chan] = 0; + x->AUDF[chan] = 0; + x->AUDV[chan] = 0; + x->P4[chan] = 0; + x->P5[chan] = 0; + x->P9[chan] = 0; + } + + x->volume = 100; +} + +void Update_tia_sound(t_atari_2600 *x, uint16 addr, uint8 val) +{ + uint16 new_val = 0; + uint8 chan; + + /* determine which address was changed */ + switch (addr) + { + case AUDC0: + x->AUDC[0] = val & 0x0f; + chan = 0; + break; + + case AUDC1: + x->AUDC[1] = val & 0x0f; + chan = 1; + break; + + case AUDF0: + x->AUDF[0] = val & 0x1f; + chan = 0; + break; + + case AUDF1: + x->AUDF[1] = val & 0x1f; + chan = 1; + break; + + case AUDV0: + x->AUDV[0] = (val & 0x0f) << 3; + chan = 0; + break; + + case AUDV1: + x->AUDV[1] = (val & 0x0f) << 3; + chan = 1; + break; + + default: + chan = 255; + break; + } + + /* if the output value changed */ + if (chan != 255) + { + /* an AUDC value of 0 is a special case */ + if (x->AUDC[chan] == SET_TO_1) + { + /* indicate the clock is zero so no processing will occur */ + new_val = 0; + + /* and set the output to the selected volume */ + x->Outvol[chan] = x->AUDV[chan]; + } + else + { + /* otherwise calculate the 'divide by N' value */ + new_val = x->AUDF[chan] + 1; + + /* if bits 2 & 3 are set, then multiply the 'div by n' count by 3 */ + if ((x->AUDC[chan] & DIV3_MASK) == DIV3_MASK) + { + new_val *= 3; + } + } + + /* only reset those channels that have changed */ + if (new_val != x->Div_n_max[chan]) + { + /* reset the divide by n counters */ + x->Div_n_max[chan] = new_val; + + /* if the channel is now volume only or was volume only */ + if ((x->Div_n_cnt[chan] == 0) || (new_val == 0)) + { + /* reset the counter (otherwise let it complete the previous) */ + x->Div_n_cnt[chan] = new_val; + } + } + } + +} + +void Tia_process(t_atari_2600 *x, t_float *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 = x->AUDC[0]; + audv0 = x->AUDV[0]; + audc1 = x->AUDC[1]; + audv1 = x->AUDV[1]; + + /* make temporary local copy */ + p5_0 = x->P5[0]; + p5_1 = x->P5[1]; + outvol_0 = x->Outvol[0]; + outvol_1 = x->Outvol[1]; + div_n_cnt0 = x->Div_n_cnt[0]; + div_n_cnt1 = x->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 = x->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 */ + x->P9[0]++; + if (x->P9[0] == POLY9_SIZE) + x->P9[0] = 0; + + if (x->Bit9[x->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 */ + x->P4[0]++; + if (x->P4[0] == POLY4_SIZE) + x->P4[0] = 0; + + if (Bit4[x->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 = x->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 */ + x->P9[1]++; + if (x->P9[1] == POLY9_SIZE) + x->P9[1] = 0; + + if (x->Bit9[x->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 */ + x->P4[1]++; + if (x->P4[1] == POLY4_SIZE) + x->P4[1] = 0; + + if (Bit4[x->P4[1]]) + outvol_1 = audv1; + else + outvol_1 = 0; + } + } + } + { + uint32 val = ((((uint32)outvol_0 + (uint32)outvol_1) * x->volume)/100); + float va = (((float)val) - 100.0)/100.0; + *(buffer++) = va; + n--; + } + } + + /* save for next round */ + x->P5[0] = p5_0; + x->P5[1] = p5_1; + x->Outvol[0] = outvol_0; + x->Outvol[1] = outvol_1; + x->Div_n_cnt[0] = div_n_cnt0; + x->Div_n_cnt[1] = div_n_cnt1; +} -- cgit v1.2.1