vrcvisnd.c
  1  /*
  2  ** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
  3  **
  4  **
  5  ** This program is free software; you can redistribute it and/or
  6  ** modify it under the terms of version 2 of the GNU Library General 
  7  ** Public License as published by the Free Software Foundation.
  8  **
  9  ** This program is distributed in the hope that it will be useful, 
 10  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 12  ** Library General Public License for more details.  To obtain a 
 13  ** copy of the GNU Library General Public License, write to the Free 
 14  ** Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 15  **
 16  ** Any permitted reproduction of these routines, in whole or in part,
 17  ** must bear this legend.
 18  **
 19  **
 20  ** vrcvisnd.c
 21  **
 22  ** VRCVI sound hardware emulation
 23  ** $Id: vrcvisnd.c,v 1.2 2001/04/27 14:37:11 neil Exp $
 24  */
 25  
 26  #include "noftypes.h"
 27  #include "vrcvisnd.h"
 28  #include "nes_apu.h"
 29  
 30  typedef struct vrcvirectangle_s
 31  {
 32     bool enabled;
 33  
 34     uint8 reg[3];
 35     
 36     float accum;
 37     uint8 adder;
 38  
 39     int32 freq;
 40     int32 volume;
 41     uint8 duty_flip;
 42  } vrcvirectangle_t;
 43  
 44  typedef struct vrcvisawtooth_s
 45  {
 46     bool enabled;
 47     
 48     uint8 reg[3];
 49     
 50     float accum;
 51     uint8 adder;
 52     uint8 output_acc;
 53  
 54     int32 freq;
 55     uint8 volume;
 56  } vrcvisawtooth_t;
 57  
 58  typedef struct vrcvisnd_s
 59  {
 60     vrcvirectangle_t rectangle[2];
 61     vrcvisawtooth_t saw;
 62     float incsize;
 63  } vrcvisnd_t;
 64  
 65  
 66  static vrcvisnd_t vrcvi;
 67  
 68  /* VRCVI rectangle wave generation */
 69  static int32 vrcvi_rectangle(vrcvirectangle_t *chan)
 70  {
 71     /* reg0: 0-3=volume, 4-6=duty cycle
 72     ** reg1: 8 bits of freq
 73     ** reg2: 0-3=high freq, 7=enable
 74     */
 75  
 76     chan->accum -= vrcvi.incsize; /* # of clocks per wave cycle */
 77     while (chan->accum < 0)
 78     {
 79        chan->accum += chan->freq;
 80        chan->adder = (chan->adder + 1) & 0x0F;
 81     }
 82  
 83     /* return if not enabled */
 84     if (false == chan->enabled)
 85        return 0;
 86  
 87     if (chan->adder < chan->duty_flip)
 88        return -(chan->volume);
 89     else
 90        return chan->volume;
 91  }
 92  
 93  /* VRCVI sawtooth wave generation */
 94  static int32 vrcvi_sawtooth(vrcvisawtooth_t *chan)
 95  {
 96     /* reg0: 0-5=phase accumulator bits
 97     ** reg1: 8 bits of freq
 98     ** reg2: 0-3=high freq, 7=enable
 99     */
100  
101     chan->accum -= vrcvi.incsize; /* # of clocks per wav cycle */
102     while (chan->accum < 0)
103     {
104        chan->accum += chan->freq;
105        chan->output_acc += chan->volume;
106        
107        chan->adder++;
108        if (7 == chan->adder)
109        {
110           chan->adder = 0;
111           chan->output_acc = 0;
112        }
113     }
114  
115     /* return if not enabled */
116     if (false == chan->enabled)
117        return 0;
118  
119     return (chan->output_acc >> 3) << 9;
120  }
121  
122  /* mix vrcvi sound channels together */
123  static int32 vrcvi_process(void)
124  {
125     int32 output;
126  
127     output = vrcvi_rectangle(&vrcvi.rectangle[0]);
128     output += vrcvi_rectangle(&vrcvi.rectangle[1]);
129     output += vrcvi_sawtooth(&vrcvi.saw);
130  
131     return output;
132  }
133  
134  /* write to registers */
135  static void vrcvi_write(uint32 address, uint8 value)
136  {
137     int chan = (address >> 12) - 9;
138  
139     switch (address & 0xB003)
140     {
141     case 0x9000:
142     case 0xA000:
143        vrcvi.rectangle[chan].reg[0] = value;
144        vrcvi.rectangle[chan].volume = (value & 0x0F) << 8;
145        vrcvi.rectangle[chan].duty_flip = (value >> 4) + 1;
146        break;
147  
148     case 0x9001:
149     case 0xA001:
150        vrcvi.rectangle[chan].reg[1] = value;
151        vrcvi.rectangle[chan].freq = ((vrcvi.rectangle[chan].reg[2] & 0x0F) << 8) + value + 1;
152        break;
153  
154     case 0x9002:
155     case 0xA002:
156        vrcvi.rectangle[chan].reg[2] = value;
157        vrcvi.rectangle[chan].freq = ((value & 0x0F) << 8) + vrcvi.rectangle[chan].reg[1] + 1;
158        vrcvi.rectangle[chan].enabled = (value & 0x80) ? true : false;
159        break;
160  
161     case 0xB000:
162        vrcvi.saw.reg[0] = value;
163        vrcvi.saw.volume = value & 0x3F;
164        break;
165  
166     case 0xB001:
167        vrcvi.saw.reg[1] = value;
168        vrcvi.saw.freq = (((vrcvi.saw.reg[2] & 0x0F) << 8) + value + 1) << 1;
169        break;
170  
171     case 0xB002:
172        vrcvi.saw.reg[2] = value;
173        vrcvi.saw.freq = (((value & 0x0F) << 8) + vrcvi.saw.reg[1] + 1) << 1;
174        vrcvi.saw.enabled = (value & 0x80) ? true : false;
175        break;
176  
177     default:
178        break;
179     }
180  }
181  
182  /* reset state of vrcvi sound channels */
183  static void vrcvi_reset(void)
184  {
185     int i;
186     apu_t apu;
187  
188     /* get the phase period from the apu */
189     apu_getcontext(&apu);
190     vrcvi.incsize = apu.cycle_rate;
191  
192     /* preload regs */
193     for (i = 0; i < 3; i++)
194     {
195        vrcvi_write(0x9000 + i, 0);
196        vrcvi_write(0xA000 + i, 0);
197        vrcvi_write(0xB000 + i, 0);
198     }
199  }
200  
201  static apu_memwrite vrcvi_memwrite[] =
202  {
203     { 0x9000, 0x9002, vrcvi_write }, /* vrc6 */
204     { 0xA000, 0xA002, vrcvi_write },
205     { 0xB000, 0xB002, vrcvi_write },
206     {     -1,     -1, NULL }
207  };
208  
209  apuext_t vrcvi_ext =
210  {
211     NULL, /* no init */
212     NULL, /* no shutdown */
213     vrcvi_reset,
214     vrcvi_process,
215     NULL, /* no reads */
216     vrcvi_memwrite
217  };
218  
219  /*
220  ** $Log: vrcvisnd.c,v $
221  ** Revision 1.2  2001/04/27 14:37:11  neil
222  ** wheeee
223  **
224  ** Revision 1.1  2001/04/27 12:54:40  neil
225  ** blah
226  **
227  ** Revision 1.1.1.1  2001/04/27 07:03:54  neil
228  ** initial
229  **
230  ** Revision 1.2  2000/11/05 22:21:00  matt
231  ** help me!
232  **
233  ** Revision 1.1  2000/10/24 12:20:00  matt
234  ** changed directory structure
235  **
236  ** Revision 1.17  2000/10/10 13:58:18  matt
237  ** stroustrup squeezing his way in the door
238  **
239  ** Revision 1.16  2000/10/03 11:56:20  matt
240  ** better support for optional sound ext routines
241  **
242  ** Revision 1.15  2000/09/27 12:26:03  matt
243  ** changed sound accumulators back to floats
244  **
245  ** Revision 1.14  2000/09/15 13:38:40  matt
246  ** changes for optimized apu core
247  **
248  ** Revision 1.13  2000/09/15 04:58:07  matt
249  ** simplifying and optimizing APU core
250  **
251  ** Revision 1.12  2000/07/30 04:32:59  matt
252  ** no more apu_getcyclerate hack
253  **
254  ** Revision 1.11  2000/07/17 01:52:31  matt
255  ** made sure last line of all source files is a newline
256  **
257  ** Revision 1.10  2000/07/06 11:42:41  matt
258  ** forgot to remove FDS register range
259  **
260  ** Revision 1.9  2000/07/04 04:51:41  matt
261  ** cleanups
262  **
263  ** Revision 1.8  2000/07/03 02:18:53  matt
264  ** much better external module exporting
265  **
266  ** Revision 1.7  2000/06/20 04:06:16  matt
267  ** migrated external sound definition to apu module
268  **
269  ** Revision 1.6  2000/06/20 00:08:58  matt
270  ** changed to driver based API
271  **
272  ** Revision 1.5  2000/06/09 16:49:02  matt
273  ** removed all floating point from sound generation
274  **
275  ** Revision 1.4  2000/06/09 15:12:28  matt
276  ** initial revision
277  **
278  */