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

📄 nes_apu.c

📁 一个最快NFC的模拟器
💻 C
📖 第 1 页 / 共 4 页
字号:
		  noise->regs[1] = value;
		  noise->freq = APU_TO_FIXED(noise_freq[value & 0x0F]);
		  
#ifdef REALTIME_NOISE
		  noise->xor_tap = (value & 0x80) ? 0x40: 0x02;
#else /* !REALTIME_NOISE */
		  /* detect transition from long->short sample */
		  if ((value & 0x80) && FALSE == noise->short_sample)
		  {
			  /* recalculate short noise buffer */
			  shift_register15(noise_short_lut, APU_NOISE_93);
			  noise->cur_pos = 0;
		  }
		  noise->short_sample = (value & 0x80) ? TRUE : FALSE;
#endif /* !REALTIME_NOISE */
	  }
#endif /* if !0 */
      break;

   case APU_WRD3:
#if 0
      apu->apus.noise.regs[2] = value;

      apu->apus.noise.vbl_length = vbl_lut[value >> 3];
      apu->apus.noise.env_vol = 0; /* reset envelope */
      if ( apu->enable_reg & 0x08) apu->apus.noise.enabled = TRUE;
#else
	  {
		  noise_t * noise = &(apu->apus.noise);
		  noise->regs[2] = value;
		  
		  noise->vbl_length = vbl_lut[value >> 3];
		  noise->env_vol = 0; /* reset envelope */
		  if (apu->enable_reg & 0x08)
			  noise->enabled = TRUE;
	  }
#endif
      break;

   /* DMC */
   case APU_WRE0:
#if 0
	  apu->apus.dmc.regs[0] = value;

      apu->apus.dmc.freq = APU_TO_FIXED(dmc_clocks[value & 0x0F]);
      apu->apus.dmc.looping = (value & 0x40) ? TRUE : FALSE;

      if (value & 0x80)
         apu->apus.dmc.irq_gen = TRUE;
      else
      {
         apu->apus.dmc.irq_gen = FALSE;
         apu->apus.dmc.irq_occurred = FALSE;
      }
#else
	  {
		  dmc_t * dmc = &(apu->apus.dmc);
		  
		  dmc->regs[0] = value;
		   
		  dmc->freq = APU_TO_FIXED(dmc_clocks[value & 0x0F]);
		  dmc->looping = (value & 0x40) ? TRUE : FALSE;
		   
		  if (value & 0x80)
			  dmc->irq_gen = TRUE;
		  else
		  {
			  dmc->irq_gen = FALSE;
			  dmc->irq_occurred = FALSE;
		  }
	  }
#endif
      break;

   case APU_WRE1: /* 7-bit DAC */
#if 0
      /* add the _delta_ between written value and
      ** current output level of the volume reg
      */
      value &= 0x7F; /* bit 7 ignored */
#ifndef APU_YANO
      apu->apus.dmc.output_vol += ((value - apu->apus.dmc.regs[1]) << 8);
#endif /* !APU_YANO */
      apu->apus.dmc.regs[1] = value;
#else
	  {
		  dmc_t * dmc = &(apu->apus.dmc);
		  value &= 0x7F; /* bit 7 ignored */
#ifndef APU_YANO
		  dmc->output_vol += ((value - dmc->regs[1]) << 8);
#endif
		  dmc->regs[1] = value;
	  }
      break;
#endif
      break;

   case APU_WRE2:
#if 0
      apu->apus.dmc.regs[2] = value;
      apu->apus.dmc.cached_addr = 0xC000 + (uint16) (value << 6);
#else
	  {
		  dmc_t * dmc = &(apu->apus.dmc);
		  dmc->regs[2] = value;
		  dmc->cached_addr = 0xC000 + (uint16) (value << 6);
	  }
#endif
      break;

   case APU_WRE3:
#if 0
      apu->apus.dmc.regs[3] = value;
      apu->apus.dmc.cached_dmalength = ((value << 4) + 1) << 3;
#else
	  {
		  dmc_t * dmc = &(apu->apus.dmc);
		  dmc->regs[3] = value;
		  dmc->cached_dmalength = ((value << 4) + 1) << 3;
	  }
#endif
      break;

   case APU_SMASK:
      /* bodge for timestamp queue */
#if 0
      apu->apus.dmc.enabled = (value & 0x10) ? TRUE : FALSE;

      apu->enable_reg = value;

      for (chan = 0; chan < 2; chan++)
      {
         if (0 == (value & (1 << chan)) )
         {
            apu->apus.rectangle[chan].enabled = FALSE;
            apu->apus.rectangle[chan].vbl_length = 0;
         }
      }

      if (0 == (value & 0x04))
      {
         apu->apus.triangle.enabled = FALSE;
         apu->apus.triangle.vbl_length = 0;
         apu->apus.triangle.linear_length = 0;
         apu->apus.triangle.counter_started = FALSE;
         apu->apus.triangle.write_latency = 0;
      }

      if (0 == (value & 0x08))
      {
         apu->apus.noise.enabled = FALSE;
         apu->apus.noise.vbl_length = 0;
      }

      if (value & 0x10)
      {
         if (0 == apu->apus.dmc.dma_length)
            apu_dmcreload(&apu->apus.dmc);
      }
      else
      {
         apu->apus.dmc.dma_length = 0;
         apu->apus.dmc.irq_occurred = FALSE;
      }
#else
	  {
		  //int chan;
		  dmc_t * dmc = &(apu->apus.dmc);

		  apu->enable_reg = value;
		  
		  if (0 == (value & 1))
		  {
			  rectangle_t * rect = &apu->apus.rectangle[0];
			  rect->enabled = FALSE;
			  rect->vbl_length = 0;
		  }
		  if (0 == (value & 2))
		  {
			  rectangle_t * rect = &apu->apus.rectangle[1];
			  rect->enabled = FALSE;
			  rect->vbl_length = 0;
		  }
		  
		  if (0 == (value & 0x04))
		  {
			  triangle_t * tri = &(apu->apus.triangle);

			  tri->enabled = FALSE;
			  tri->vbl_length = 0;
			  tri->linear_length = 0;
			  tri->counter_started = FALSE;
			  tri->write_latency = 0;
		  }
		  
		  if (0 == (value & 0x08))
		  {
			  noise_t * noise = &(apu->apus.noise);

			  noise->enabled = FALSE;
			  noise->vbl_length = 0;
		  }
		  
		  if (value & 0x10)
		  {
			  dmc->enabled = TRUE;
			  if (0 == dmc->dma_length)
				  apu_dmcreload(dmc);
		  }
		  else
		  {
			  dmc->enabled = FALSE;
			  dmc->dma_length = 0;
			  dmc->irq_occurred = FALSE;
		  }
	  }
#endif
      break;

      /* unused, but they get hit in some mem-clear loops */
   case 0x4009:
   case 0x400D:
      break;

   case 0x4017:
      if (value & 0x80)
         apu_cnt_rate = 4;
      else
         apu_cnt_rate = 5;
      break;

   default:
      break;
   }
}

/* Read from $4000-$4017 */
uint8 apu_read(uint32 address)
{
   uint8 value;

   ASSERT(apu);

   switch (address)
   {
   case APU_SMASK:
      value = 0;
      /* Return 1 in 0-5 bit pos if a channel is playing */
      if (apu->apus.rectangle[0].enabled_cur && apu->apus.rectangle[0].vbl_length_cur>0)
         value |= 0x01;
      if (apu->apus.rectangle[1].enabled_cur && apu->apus.rectangle[1].vbl_length_cur>0)
         value |= 0x02;
      if (apu->apus.triangle.enabled_cur && apu->apus.triangle.vbl_length_cur>0)
         value |= 0x04;
      if (apu->apus.noise.enabled_cur && apu->apus.noise.vbl_length_cur>0)
         value |= 0x08;
      /* bodge for timestamp queue */
      if (apu->apus.dmc.enabled_cur)
         value |= 0x10;
      if (apu->apus.dmc.irq_occurred_cur)
         value |= 0x80;
      break;

   default:
      value = (address >> 8); /* heavy capacitance on data bus */
      break;
   }

   return value;
}

uint8 ex_read(uint32 address)
{
   if(apu->ex_chip & 4)
   {
      return FDSSoundRead (address);
   }
   else if(apu->ex_chip & 16)
   {
      //return N106SoundReadData (address);
      apudata_t d;
      d.timestamp = nes6502_getcycles(FALSE);
      d.address = address + 0x10000;
      //ex_enqueue(&d);
	  apu_enqueue(&apu->ex_queue, &d);
      return 0x00;
   }
   else
   {
      return 0x00;
   }
}

void apu_write(uint32 address, uint8 value)
{
   apudata_t d;

   switch (address)
   {
   case 0x4015:
      /* bodge for timestamp queue */
      apu->apus.dmc.enabled = (value & 0x10) ? TRUE : FALSE;

   case 0x4000: case 0x4001: case 0x4002: case 0x4003:
   case 0x4004: case 0x4005: case 0x4006: case 0x4007:
   case 0x4008: case 0x4009: case 0x400A: case 0x400B:
   case 0x400C: case 0x400D: case 0x400E: case 0x400F:
   case 0x4010: case 0x4011: case 0x4012: case 0x4013:
   case 0x4017:
      d.timestamp = nes6502_getcycles(FALSE);
      d.address = address;
      d.value = value;
      //apu_enqueue(&d);
	  apu_enqueue(&apu->queue, &d);
      break;

   default:
      break;
   }
}

void apu_write_cur(uint32 address, uint8 value)
{
   /* for sync read $4015 */
   int chan;
   switch (address)
   {
   case APU_WRA0:
   case APU_WRB0:
      chan = (address & 4) ? 1 : 0;
      apu->apus.rectangle[chan].holdnote_cur = (value & 0x20) ? TRUE : FALSE;
      break;
   case APU_WRA3:
   case APU_WRB3:
      chan = (address & 4) ? 1 : 0;
      apu->apus.rectangle[chan].vbl_length_cur = vbl_length[value >> 3] * 5;
      if (apu->enable_reg_cur & (1<<chan))
         apu->apus.rectangle[chan].enabled_cur = TRUE;
      break;
   case APU_WRC0:
      apu->apus.triangle.holdnote_cur = (value & 0x80) ? TRUE : FALSE;
      break;
   case APU_WRC3:
      apu->apus.triangle.vbl_length_cur = vbl_length[value >> 3] * 5;
      if (apu->enable_reg_cur & 0x04)
         apu->apus.triangle.enabled_cur = TRUE;
      apu->apus.triangle.counter_started_cur = TRUE;
      break;
   case APU_WRD0:
      apu->apus.noise.holdnote_cur = (value & 0x20) ? TRUE : FALSE;
      break;
   case APU_WRD3:
      apu->apus.noise.vbl_length_cur = vbl_length[value >> 3] * 5;
      if (apu->enable_reg_cur & 0x08)
         apu->apus.noise.enabled_cur = TRUE;
      break;
   case APU_WRE0:
      apu->apus.dmc.freq_cur = dmc_clocks[value & 0x0F];
      apu->apus.dmc.phaseacc_cur = 0;
      apu->apus.dmc.looping_cur = (value & 0x40) ? TRUE : FALSE;
      if (value & 0x80)
         apu->apus.dmc.irq_gen_cur = TRUE;
      else
      {
         apu->apus.dmc.irq_gen_cur = FALSE;
         apu->apus.dmc.irq_occurred_cur = FALSE;
      }
      break;
   case APU_WRE3:
      apu->apus.dmc.cached_dmalength_cur = (value << 4) + 1;
      break;
   case APU_SMASK:
      apu->enable_reg_cur = value;
      for (chan = 0; chan < 2; chan++)
      {
         if (0 == (value & (1 << chan)))
         {
            apu->apus.rectangle[chan].enabled_cur = FALSE;
            apu->apus.rectangle[chan].vbl_length_cur = 0;
         }
      }
      if (0 == (value & 0x04))
      {
         apu->apus.triangle.enabled_cur = FALSE;
         apu->apus.triangle.vbl_length_cur = 0;
         apu->apus.triangle.counter_started_cur = FALSE;
      }
      if (0 == (value & 0x08))
      {
         apu->apus.noise.enabled_cur = FALSE;
         apu->apus.noise.vbl_length_cur = 0;
      }
      if (value & 0x10)
      {
         if(0 == apu->apus.dmc.dma_length_cur)
         {
           apu->apus.dmc.dma_length_cur = apu->apus.dmc.cached_dmalength_cur;
         }
         apu->apus.dmc.enabled_cur = TRUE;
      }
      else
      {
         apu->apus.dmc.dma_length_cur = 0;
         apu->apus.dmc.enabled_cur = FALSE;
         apu->apus.dmc.irq_occurred_cur = FALSE;
      }
      break;
   }
}

void ex_write(uint32 address, uint8 value)
{
   apudata_t d;
   d.timestamp = nes6502_getcycles(FALSE);
   d.address = address;
   d.value = value;
   //ex_enqueue(&d);
   apu_enqueue(&apu->ex_queue, &d);

   if(apu->ex_chip & 4)
   {
      FDSSoundWriteCurrent(address, value);
   }
}

void apu_getpcmdata(void **data, int *num_samples, int *sample_bits)
{
   ASSERT(apu);
   *data = apu->buffer;
   *num_samples = apu->num_samples;
   *sample_bits = apu->sample_bits;
}


void apu_process(void *buffer, int num_samples)
{
   apudata_t *d;
   uint32 elapsed_cycles;
   static int32 prev_sample = 0;
   int32 next_sample, accum;

   ASSERT(apu);

   /* grab it, keep it local for speed */
   elapsed_cycles = (uint32) apu->elapsed_cycles;

   if (NULL == buffer)
   {
      /* just go through the motions... */
      while (num_samples--)
      {
         apuqueue_t *q = &apu->queue;
		 apuqueue_t *ex_q = &apu->ex_queue;
         /*while ((FALSE == APU_QEMPTY()) && (apu->queue[apu->q_tail].timestamp <= elapsed_cycles))
         {
            d = apu_dequeue();
            apu_regwrite(d->address, d->value);
         }*/
		 while ((FALSE == APU_QEMPTY(q)) && (q->queue[q->q_tail].timestamp <= elapsed_cycles))
         {
			d = &q->queue[q->q_tail];
			q->q_tail = (q->q_tail + 1) & APUQUEUE_MASK;

            apu_regwrite(d->address, d->value);
         }

         //while((FALSE == EX_QEMPTY()) && apu->ex_queue[apu->ex_q_tail].timestamp <= elapsed_cycles)
		 while((FALSE == APU_QEMPTY(ex_q)) && ex_q->queue[ex_q->q_tail].timestamp <= elapsed_cycles)
         {
            //d = ex_dequeue();
			d = &ex_q->queue[ex_q->q_tail];
			ex_q->q_tail = (ex_q->q_tail + 1) & APUQUEUE_MASK;

            if(apu->ex_chip & 1)
            {
               VRC6SoundWrite(d->address, d->value);
            }
            else if(apu->ex_chip & 2)
            {
               OPLLSoundWrite(d->address, d->value);
            }
            else if(apu->ex_chip & 4)
            {
               FDSSoundWrite(d->address, d->value);
            }
            else if(apu->ex_chip & 8)
            {
               MMC5SoundWrite(d->address, d->value);
            }
            else if(apu->ex_chip & 16)
            {
               if(d->address & 0x10000)
               {
                  uint8 dummy = N106SoundRead(d->address & 0xFFFF);
               }
               else
               {
                  N106SoundWrite(d->address, d->value);
               }
            }
            else if(apu->ex_chip & 32)
            {
               PSGSoundWrite(d->address, d->value);
            }
         }

         elapsed_cycles += APU_FROM_FIXED(apu->cycle_rate);
      }
   }
   else
   {
      /* bleh */
      apu->buffer = buffer; 

      while (num_samples--)
      {
		 apuqueue_t *q = &apu->queue;
		 apuqueue_t *ex_q = &apu->ex_queue;

		 /*
         while ((FALSE == APU_QEMPTY()) && (apu->queue[apu->q_tail].timestamp <= elapsed_cycles))
         {
            d = apu_dequeue();
            apu_regwrite(d->address, d->value);
         }
		 */
		 while ((FALSE == APU_QEMPTY(q)) && (q->queue[q->q_tail].timestamp <= elapsed_cycles))
         {
            //d = apu_dequeue(q);
			d = &q->queue[q->q_tail];
			q->q_tail = (q->q_tail + 1) & APUQUEUE_MASK;

            apu_regwrite(d->address, d->value);
         }

         //while((FALSE == EX_QEMPTY()) && apu->ex_queue[apu->ex_q_tail].timestamp <= elapsed_cycles)
		 while((FALSE == APU_QEMPTY(ex_q)) && ex_q->queue[ex_q->q_tail].timestamp <= elapsed_cycles)
         {
            //d = ex_dequeue();
			d = &ex_q->queue[ex_q->q_tail];
			ex_q->q_tail = (ex_q->q_tail + 1) & APUQUEUE_MASK;

            if(apu->ex_chip & 1)
            {
               VRC6SoundWrite(d->address, d->value);
            }
            else if(apu->ex_chip & 2)
            {
               OPLLSoundWrite(d->address, d->value);
            }
            else if(apu->ex_chip & 4)
            {
               FDSSoundWrite(d->address, d->value);
            }
            else if(apu->ex_chip & 8)
            {
               MMC5SoundWrite(d->address, d->value);
            }
            else if(apu->ex_chip & 16)
            {
               if(d->address & 0x10000)

⌨️ 快捷键说明

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