⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nes_apu.c

📁 一个最快NFC的模拟器
💻 C
📖 第 1 页 / 共 4 页
字号:

/* WHITE NOISE CHANNEL
** ===================
** reg0: 0-3=volume, 4=envelope, 5=hold
** reg2: 7=small(93 byte) sample,3-0=freq lookup
** reg3: 7-4=vbl length counter
*/
//#define  APU_NOISE_OUTPUT  (chan->output_vol - (chan->output_vol >> 2))
#define  APU_NOISE_OUTPUT  ((chan->output_vol*13)>>4)

static int32 apu_noise(noise_t *chan)
{
   int32 outvol;

#ifdef APU_YANO
   static int32 noise_bit;
   double total;
   double sample_weight;
#else /* !APU_YANO */
#if defined(APU_OVERSAMPLE) && defined(REALTIME_NOISE)
#else /* !(APU_OVERSAMPLE && REALTIME_NOISE) */
   int32 noise_bit;
#endif /* !(APU_OVERSAMPLE && REALTIME_NOISE) */
#ifdef APU_OVERSAMPLE
   int num_times;
   int32 total;
#endif /* APU_OVERSAMPLE */

   APU_VOLUME_DECAY(chan->output_vol);
#endif /* !APU_YANO */

   if (FALSE == chan->enabled || chan->vbl_length <= 0)
      return APU_NOISE_OUTPUT;

   /* vbl length counter */
   if (FALSE == chan->holdnote)
      chan->vbl_length -= apu_cnt_rate;

   /* envelope decay at a rate of (env_delay + 1) / 240 secs */
#if 0
   chan->env_phase -= 4 * apu_cnt_rate; /* 240/60 */
   while (chan->env_phase < 0)
   {
      chan->env_phase += chan->env_delay;

      if (chan->holdnote)
         chan->env_vol = (chan->env_vol + 1) & 0x0F;
      else if (chan->env_vol < 0x0F)
         chan->env_vol++;
   }
#else
   {
	   int env_phase = chan->env_phase;
	   int env_delay = chan->env_delay;
	   int holdnote = chan->holdnote;
	   int env_vol = chan->env_vol;
	   env_phase -= 4 * apu_cnt_rate; /* 240/60 */
	   while (env_phase < 0)
	   {
		   env_phase += env_delay;
		   
		   if (holdnote)
			   env_vol = (env_vol + 1) & 0x0F;
		   else if (env_vol < 0x0F)
			   env_vol++;
	   }
	   chan->env_phase = env_phase;
	   chan->env_delay = env_delay;
	   chan->holdnote = holdnote;
	   chan->env_vol = env_vol;
   }
#endif


#ifdef APU_YANO
   if (chan->fixed_envelope)
      outvol = chan->volume << 8; /* fixed volume */
   else
      outvol = (chan->env_vol ^ 0x0F) << 8;

   sample_weight = chan->phaseacc;
   if ( sample_weight > apu->cycle_rate) {
      sample_weight = apu->cycle_rate;
   }
   total = noise_bit ? sample_weight:-sample_weight;

   chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
   while ( chan->phaseacc < 0) {
      chan->phaseacc += chan->freq;

#ifdef REALTIME_NOISE
      noise_bit = shift_register15(chan->xor_tap);
#else /* !REALTIME_NOISE */
      chan->cur_pos++;

      if (chan->short_sample)
      {
         if (APU_NOISE_93 == chan->cur_pos)
            chan->cur_pos = 0;
      }
      else
      {
         if (APU_NOISE_32K == chan->cur_pos)
            chan->cur_pos = 0;
      }

      if (chan->short_sample)
         noise_bit = noise_short_lut[chan->cur_pos];
      else
         noise_bit = noise_long_lut[chan->cur_pos];
#endif /* !REALTIME_NOISE */

      sample_weight = chan->freq;
      if (chan->phaseacc > 0) {
         sample_weight -= chan->phaseacc;
      }
      total += noise_bit ? sample_weight:-sample_weight;
   }
   chan->output_vol = (int)floor( outvol*total/apu->cycle_rate + 0.5);
#else /* !APU_YANO */
   chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
   if (chan->phaseacc >= 0)
      return APU_NOISE_OUTPUT;
   
#ifdef APU_OVERSAMPLE
   num_times = total = 0;

   if (chan->fixed_envelope)
      outvol = chan->volume << 8; /* fixed volume */
   else
      outvol = (chan->env_vol ^ 0x0F) << 8;
#endif /* APU_OVERSAMPLE */

   while (chan->phaseacc < 0)
   {
      chan->phaseacc += chan->freq;

#ifdef REALTIME_NOISE

#ifdef APU_OVERSAMPLE
      if (shift_register15(chan->xor_tap))
         total += outvol;
      else
         total -= outvol;

      num_times++;
#else /* !APU_OVERSAMPLE */
      noise_bit = shift_register15(chan->xor_tap);
#endif /* !APU_OVERSAMPLE */

#else /* !REALTIME_NOISE */
      chan->cur_pos++;

      if (chan->short_sample)
      {
         if (APU_NOISE_93 == chan->cur_pos)
            chan->cur_pos = 0;
      }
      else
      {
         if (APU_NOISE_32K == chan->cur_pos)
            chan->cur_pos = 0;
      }

#ifdef APU_OVERSAMPLE
      if (chan->short_sample)
         noise_bit = noise_short_lut[chan->cur_pos];
      else
         noise_bit = noise_long_lut[chan->cur_pos];

      if (noise_bit)
         total += outvol;
      else
         total -= outvol;

      num_times++;
#endif /* APU_OVERSAMPLE */
#endif /* !REALTIME_NOISE */
   }

#ifdef APU_OVERSAMPLE
   chan->output_vol = total / num_times;
#else /* !APU_OVERSAMPLE */
   if (chan->fixed_envelope)
      outvol = chan->volume << 8; /* fixed volume */
   else
      outvol = (chan->env_vol ^ 0x0F) << 8;

#ifndef REALTIME_NOISE
   if (chan->short_sample)
      noise_bit = noise_short_lut[chan->cur_pos];
   else
      noise_bit = noise_long_lut[chan->cur_pos];
#endif /* !REALTIME_NOISE */

   if (noise_bit)
      chan->output_vol = outvol;
   else
      chan->output_vol = -outvol;
#endif /* !APU_OVERSAMPLE */
#endif /* !APU_YANO */

   return APU_NOISE_OUTPUT;
}


INLINE void apu_dmcreload(dmc_t *chan)
{
   chan->address = chan->cached_addr;
   chan->dma_length = chan->cached_dmalength;
   chan->irq_occurred = FALSE;
}

/* DELTA MODULATION CHANNEL
** =========================
** reg0: 7=irq gen, 6=looping, 3-0=pointer to clock table
** reg1: output dc level, 6 bits unsigned
** reg2: 8 bits of 64-byte aligned address offset : $C000 + (value * 64)
** reg3: length, (value * 16) + 1
*/
//#define  APU_DMC_OUTPUT (chan->output_vol - (chan->output_vol >> 3))
#define  APU_DMC_OUTPUT ((chan->output_vol*13)>>4)
static int32 apu_dmc(dmc_t *chan)
{
#ifdef APU_YANO
   double total;
   double sample_weight;
#endif /* APU_YANO */
   int delta_bit;

#ifndef APU_YANO
   APU_VOLUME_DECAY(chan->output_vol);
#endif /* !APU_YANO */

   /* only process when channel is alive */
   if (chan->dma_length)
   {
#ifdef APU_YANO
      sample_weight = chan->phaseacc;
      if ( sample_weight > apu->cycle_rate) {
         sample_weight = apu->cycle_rate;
      }
      total = (chan->regs[1]<<8)*sample_weight;
#endif /* APU_YANO */
      chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
      
      while (chan->phaseacc < 0)
      {
         chan->phaseacc += chan->freq;
         
         if (0 == (chan->dma_length & 7))
         {
            chan->cur_byte = nes6502_getbyte(chan->address);
            
            /* steal a cycle from CPU*/
            nes6502_burn(1); // for New nes6502.c v1.23 by matt
//            nes6502_setdma(1); // for nes6502.c v1.4 by matt

            if (0xFFFF == chan->address)
               chan->address = 0x8000;
            else
               chan->address++;
         }

         if (--chan->dma_length == 0)
         {
            /* if loop bit set, we're cool to retrigger sample */
            if (chan->looping)
               apu_dmcreload(chan);
            else
            {
               /* check to see if we should generate an irq */
               if (chan->irq_gen)
               {
                  chan->irq_occurred = TRUE;
#ifndef NSF_PLAYER
//DCR                  nes_irq();
#endif /* !NSF_PLAYER */
               }

               /* bodge for timestamp queue */
#ifdef APU_YANO
               sample_weight = chan->freq - chan->phaseacc;
               total += (chan->regs[1]<<8)*sample_weight;
               while ( chan->phaseacc < 0) chan->phaseacc += chan->freq;
#endif /* !APU_YANO */
               chan->enabled = FALSE;
               break;
            }
         }
         delta_bit = (chan->dma_length & 7) ^ 7;

         /* positive delta */
         if (chan->cur_byte & (1 << delta_bit))
         {
            if (chan->regs[1] < 0x7D)
            {
               chan->regs[1] += 2;
#ifndef APU_YANO
               chan->output_vol += (2 << 8);
#endif /* !APU_YANO */
            }
         }
         /* negative delta */
         else            
         {
            if (chan->regs[1] > 1)
            {
               chan->regs[1] -= 2;
#ifndef APU_YANO
               chan->output_vol -= (2 << 8);
#endif /* !APU_YANO */
            }
         }
#ifdef APU_YANO
         sample_weight = chan->freq;
         if (chan->phaseacc > 0) {
            sample_weight -= chan->phaseacc;
         }
         total += (chan->regs[1]<<8)*sample_weight;
#endif /* !APU_YANO */
      }
#ifdef APU_YANO
      chan->output_vol = (int)floor( total/apu->cycle_rate + 0.5);
#endif /* !APU_YANO */
   }
#ifdef APU_YANO
   else
   {
      chan->output_vol = chan->regs[1] << 8;
   }
#endif /* !APU_YANO */

   return APU_DMC_OUTPUT;
}


static void apu_regwrite(uint32 address, uint8 value)
{  
   //int chan;

   ASSERT(apu);

   switch (address)
   {
   /* rectangles */
   case APU_WRA0:
   case APU_WRB0:
#if 0
      chan = (address & 4) ? 1 : 0;
      apu->apus.rectangle[chan].regs[0] = value;

      apu->apus.rectangle[chan].volume = value & 0x0F;
      apu->apus.rectangle[chan].env_delay = decay_lut[value & 0x0F];
      apu->apus.rectangle[chan].holdnote = (value & 0x20) ? TRUE : FALSE;
      apu->apus.rectangle[chan].fixed_envelope = (value & 0x10) ? TRUE : FALSE;
      apu->apus.rectangle[chan].duty_flip = duty_lut[value >> 6];
#else
	  {
      rectangle_t * rect = (address & 4) ? &(apu->apus.rectangle[1]) : &(apu->apus.rectangle[0]);

      rect->regs[0] = value;
	  rect->volume = value & 0x0F;
      rect->env_delay = decay_lut[value & 0x0F];
      rect->holdnote = (value & 0x20) ? TRUE : FALSE;
      rect->fixed_envelope = (value & 0x10) ? TRUE : FALSE;
      rect->duty_flip = duty_lut[value >> 6];
	  }
#endif

      break;

   case APU_WRA1:
   case APU_WRB1:
#if 0
      chan = (address & 4) ? 1 : 0;
      apu->apus.rectangle[chan].regs[1] = value;
      apu->apus.rectangle[chan].sweep_on = (value & 0x80) ? TRUE : FALSE;
      apu->apus.rectangle[chan].sweep_shifts = value & 7;
      apu->apus.rectangle[chan].sweep_delay = decay_lut[(value >> 4) & 7];
      apu->apus.rectangle[chan].sweep_inc = (value & 0x08) ? TRUE : FALSE;
      apu->apus.rectangle[chan].freq_limit = freq_limit[value & 7];
#else
	  {
      rectangle_t * rect = (address & 4) ? &(apu->apus.rectangle[1]) : &(apu->apus.rectangle[0]);

      rect->regs[1] = value;
      rect->sweep_on = (value & 0x80) ? TRUE : FALSE;
      rect->sweep_shifts = value & 7;
      rect->freq_limit = freq_limit[value & 7];
      rect->sweep_delay = decay_lut[(value >> 4) & 7];
      rect->sweep_inc = (value & 0x08) ? TRUE : FALSE;
	  }
#endif
      break;

   case APU_WRA2:
   case APU_WRB2:
#if 0
      chan = (address & 4) ? 1 : 0;
      apu->apus.rectangle[chan].regs[2] = value;
      apu->apus.rectangle[chan].freq = (apu->apus.rectangle[chan].freq & ~0xFF) | value;
#else
	  {
      rectangle_t * rect = (address & 4) ? &(apu->apus.rectangle[1]) : &(apu->apus.rectangle[0]);
      rect->regs[2] = value;
      rect->freq = (rect->freq & ~0xFF) | value;
	  }
#endif
      break;

   case APU_WRA3:
   case APU_WRB3:
#if 0
      chan = (address & 4) ? 1 : 0;
      apu->apus.rectangle[chan].regs[3] = value;

      apu->apus.rectangle[chan].vbl_length = vbl_lut[value >> 3];
      apu->apus.rectangle[chan].env_vol = 0;
      apu->apus.rectangle[chan].freq = ((value & 7) << 8) | (apu->apus.rectangle[chan].freq & 0xFF);
      apu->apus.rectangle[chan].adder = 0;
      if ( apu->enable_reg & (1<<chan))
         apu->apus.rectangle[chan].enabled = TRUE;
#else
  	  {
      rectangle_t * rect;
	  int enable_mask;
	  if (address & 4) {
		  rect = &(apu->apus.rectangle[1]);
		  enable_mask = 2;
	  } else {
		  rect = &(apu->apus.rectangle[0]);
		  enable_mask = 1;
	  }

      rect->regs[3] = value;

      rect->vbl_length = vbl_lut[value >> 3];
      rect->env_vol = 0;
      rect->freq = ((value & 7) << 8) | (rect->freq & 0xFF);
      rect->adder = 0;
	  if (apu->enable_reg & enable_mask)
		  rect->enabled = TRUE;
	  }
#endif
      break;

   /* triangle */
   case APU_WRC0:
#if 0
      apu->apus.triangle.regs[0] = value;
      apu->apus.triangle.holdnote = (value & 0x80) ? TRUE : FALSE;

      if (FALSE == apu->apus.triangle.counter_started && apu->apus.triangle.vbl_length > 0)
         apu->apus.triangle.linear_length = trilength_lut[value & 0x7F];
#else
	   {
		   triangle_t * tri = &(apu->apus.triangle);
		   tri->regs[0] = value;
		   tri->holdnote = (value & 0x80) ? TRUE : FALSE;
		   
		   if (FALSE == tri->counter_started && tri->vbl_length > 0)
			   tri->linear_length = trilength_lut[value & 0x7F];
	   }
#endif
	   break;

   case APU_WRC2:
#if 0
      apu->apus.triangle.regs[1] = value;
      apu->apus.triangle.freq = APU_TO_FIXED((((apu->apus.triangle.regs[2] & 7) << 8) + value) + 1);
#else
	  {
	  triangle_t * tri = &(apu->apus.triangle);
	  tri->regs[1] = value;
      tri->freq = APU_TO_FIXED((((tri->regs[2] & 7) << 8) + value) + 1);
	  }
#endif
      break;

   case APU_WRC3:
#if 0
      apu->apus.triangle.regs[2] = value;
  
      /* this is somewhat of a hack.  there appears to be some latency on 
      ** the Real Thing between when trireg0 is written to and when the 
      ** linear length counter actually begins its countdown.  we want to 
      ** prevent the case where the program writes to the freq regs first, 
      ** then to reg 0, and the counter accidentally starts running because 
      ** of the sound queue's timestamp processing.
      **
      ** set latency to a couple hundred cycles -- should be plenty of time 
      ** for the 6502 code to do a couple of table dereferences and load up 
      ** the other triregs
      */

      /* 06/13/00 MPC -- seems to work OK */
      apu->apus.triangle.write_latency = (int) (228 / APU_FROM_FIXED(apu->cycle_rate));

      apu->apus.triangle.freq = APU_TO_FIXED((((value & 7) << 8) + apu->apus.triangle.regs[1]) + 1);
      apu->apus.triangle.vbl_length = vbl_lut[value >> 3];
      apu->apus.triangle.counter_started = FALSE;
      apu->apus.triangle.linear_length = trilength_lut[apu->apus.triangle.regs[0] & 0x7F];
      if ( apu->enable_reg & 0x04) apu->apus.triangle.enabled = TRUE;
#else
	  {
		  triangle_t * tri = &(apu->apus.triangle);
		  tri->regs[2] = value;
		  tri->write_latency = (int) (228 / APU_FROM_FIXED(apu->cycle_rate));

		  tri->freq = APU_TO_FIXED((((value & 7) << 8) + tri->regs[1]) + 1);
		  tri->vbl_length = vbl_lut[value >> 3];
		  tri->counter_started = FALSE;
		  tri->linear_length = trilength_lut[tri->regs[0] & 0x7F];
		  if (apu->enable_reg & 0x04)
			  tri->enabled = TRUE;
	  }
#endif
      break;

   /* noise */
   case APU_WRD0:
#if 0
      apu->apus.noise.regs[0] = value;
      apu->apus.noise.env_delay = decay_lut[value & 0x0F];
      apu->apus.noise.holdnote = (value & 0x20) ? TRUE : FALSE;
      apu->apus.noise.fixed_envelope = (value & 0x10) ? TRUE : FALSE;
      apu->apus.noise.volume = value & 0x0F;
#else
	  {
		  noise_t * noise = &(apu->apus.noise);
		  noise->regs[0] = value;
		  noise->volume = value & 0x0F;
		  noise->env_delay = decay_lut[value & 0x0F];
		  noise->holdnote = (value & 0x20) ? TRUE : FALSE;
		  noise->fixed_envelope = (value & 0x10) ? TRUE : FALSE;
	  }
#endif
      break;

   case APU_WRD2:
#if 0
      apu->apus.noise.regs[1] = value;
      apu->apus.noise.freq = APU_TO_FIXED(noise_freq[value & 0x0F]);

#ifdef REALTIME_NOISE
      apu->apus.noise.xor_tap = (value & 0x80) ? 0x40: 0x02;
#else /* !REALTIME_NOISE */
      /* detect transition from long->short sample */
      if ((value & 0x80) && FALSE == apu->apus.noise.short_sample)
      {
         /* recalculate short noise buffer */
         shift_register15(noise_short_lut, APU_NOISE_93);
         apu->apus.noise.cur_pos = 0;
      }
      apu->apus.noise.short_sample = (value & 0x80) ? TRUE : FALSE;
#endif /* !REALTIME_NOISE */

#else  /* if 0 */
	  {
		  noise_t * noise = &(apu->apus.noise);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -