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 */