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

📄 nes_apu.c

📁 一个最快NFC的模拟器
💻 C
📖 第 1 页 / 共 4 页
字号:
/*
** Nofrendo (c) 1998-2000 Matthew Conte (matt@conte.com)
**
**
** This program 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 program 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.
**
**
** nes_apu.c
**
** NES APU emulation
** $Id: nes_apu.c,v 1.5 2003/06/22 08:30:13 Rick Exp $
*/

/*
** Changes for nester:
** changes are marked with DCR
**
** removed bad #includes
** nullified ASSERT and log_printf
** added custom log_printf()
** #included stlib.h for malloc()
**
** - Darren Ranalli
*/

/*
** Changes for nester:
** support ExSounds
**
** - TAKEDA, toshiya
*/

#include <string.h>
#include <stdlib.h> //DCR
#include "types.h"
//DCR#include "log.h"
#include "nes_apu.h"
#include "nes6502.h"

/* included for nes_irq() and nes_clearframeirq() */
#ifndef NSF_PLAYER
//DCR#include "nes.h"
#endif /* !NSF_PLAYER */

// DCR
#define ASSERT(CONDITION)
#define log_printf(STR)

#ifndef  APU_YANO
#define  APU_OVERSAMPLE
#define  APU_VOLUME_DECAY(x)  ((x) -= ((x) >> 7))
#endif /* !APU_YANO */


/* pointer to active APU */
static apu_t *apu;

/* look up table madness */
static int32 decay_lut[16];
static int vbl_lut[32];
static int trilength_lut[128];

/* noise lookups for both modes */
#ifndef REALTIME_NOISE
static int8 noise_long_lut[APU_NOISE_32K];
static int8 noise_short_lut[APU_NOISE_93];
#endif /* !REALTIME_NOISE */


/* vblank length table used for rectangles, triangle, noise */
static const uint8 vbl_length[32] =
{
    5, 127,
   10,   1,
   19,   2,
   40,   3,
   80,   4,
   30,   5,
    7,   6,
   13,   7,
    6,   8,
   12,   9,
   24,  10,
   48,  11,
   96,  12,
   36,  13,
    8,  14,
   16,  15
};

/* frequency limit of rectangle channels */
static const int freq_limit[8] =
{
   0x3FF, 0x555, 0x666, 0x71C, 0x787, 0x7C1, 0x7E0, 0x7F0
};

/* noise frequency lookup table */
static const int noise_freq[16] =
{
     4,    8,   16,   32,   64,   96,  128,  160,
   202,  254,  380,  508,  762, 1016, 2034, 4068
};

/* DMC transfer freqs */
const int dmc_clocks[16] =
{
   428, 380, 340, 320, 286, 254, 226, 214,
   190, 160, 142, 128, 106,  85,  72,  54
};

/* ratios of pos/neg pulse for rectangle waves */
static const int duty_lut[4] = { 2, 4, 8, 12 };

// for $4017:bit7 by T.Yano
static int apu_cnt_rate = 5;


void apu_setcontext(apu_t *src_apu)
{
   apu = src_apu;
}

apu_t *apu_getcontext(void)
{
   return apu;
}

/*
** Simple queue routines
*/
//#define  APU_QEMPTY()   (apu->q_head == apu->q_tail)
//#define  EX_QEMPTY()   (apu->ex_q_head == apu->ex_q_tail)
#define  APU_QEMPTY(q)   ((q)->q_head == (q)->q_tail)

#include "nes_exsound.cpp"

/*
static void apu_enqueue(apudata_t *d)
{
   ASSERT(apu);
   apu->queue[apu->q_head] = *d;

   apu->q_head = (apu->q_head + 1) & APUQUEUE_MASK;

   if (APU_QEMPTY())
      log_printf("apu: queue overflow\n");      
}

static apudata_t *apu_dequeue(void)
{
   int loc;

   ASSERT(apu);

   if (APU_QEMPTY())
      log_printf("apu: queue empty\n");

   loc = apu->q_tail;
   apu->q_tail = (apu->q_tail + 1) & APUQUEUE_MASK;

   return &apu->queue[loc];
}
*/
INLINE void apu_enqueue(apuqueue_t *q, apudata_t *d)
{
   apudata_t *save = &q->queue[q->q_head];
   //q->queue[q->q_head] = *d;
   // M$ compiler generates better code this way
   save->address = d->address;
   save->timestamp = d->timestamp;
   save->value = d->value;

   q->q_head = (q->q_head + 1) & APUQUEUE_MASK;

   //if (APU_QEMPTY(q))
   //   log_printf("apu: queue overflow\n");      
}

INLINE apudata_t *apu_dequeue(apuqueue_t *q)
{
   int loc;

   //if (APU_QEMPTY(q))
   //   log_printf("apu: queue empty\n");

   loc = q->q_tail;
   q->q_tail = (q->q_tail + 1) & APUQUEUE_MASK;

   return &q->queue[loc];
}

/*
static void ex_enqueue(apudata_t *d)
{
   ASSERT(apu);
   apu->ex_queue[apu->ex_q_head] = *d;

   apu->ex_q_head = (apu->ex_q_head + 1) & APUQUEUE_MASK;

   if (EX_QEMPTY())
      log_printf("ex_apu: queue overflow\n");      
}

static apudata_t *ex_dequeue(void)
{
   int loc;

   ASSERT(apu);

   if (EX_QEMPTY())
      log_printf("ex_apu: queue empty\n");

   loc = apu->ex_q_tail;
   apu->ex_q_tail = (apu->ex_q_tail + 1) & APUQUEUE_MASK;

   return &apu->ex_queue[loc];
}
*/

void apu_setchan(int chan, boolean enabled)
{
   ASSERT(apu);
   apu->mix_enable[chan] = enabled;
}

/* emulation of the 15-bit shift register the
** NES uses to generate pseudo-random series
** for the white noise channel
*/
#ifdef REALTIME_NOISE
INLINE int8 shift_register15(uint8 xor_tap)
{
   static int sreg = 0x4000;
   int bit0, tap, bit14;

   bit0 = sreg & 1;
   tap = (sreg & xor_tap) ? 1 : 0;
   bit14 = (bit0 ^ tap);
   sreg >>= 1;
   sreg |= (bit14 << 14);
   return (bit0 ^ 1);
}
#else /* !REALTIME_NOISE */
static void shift_register15(int8 *buf, int count)
{
   static int sreg = 0x4000;
   int bit0, bit1, bit6, bit14;

   if (count == APU_NOISE_93)
   {
      while (count--)
      {
         bit0 = sreg & 1;
         bit6 = (sreg & 0x40) >> 6;
         bit14 = (bit0 ^ bit6);
         sreg >>= 1;
         sreg |= (bit14 << 14);
         *buf++ = bit0 ^ 1;
      }
   }
   else /* 32K noise */
   {
      while (count--)
      {
         bit0 = sreg & 1;
         bit1 = (sreg & 2) >> 1;
         bit14 = (bit0 ^ bit1);
         sreg >>= 1;
         sreg |= (bit14 << 14);
         *buf++ = bit0 ^ 1;
      }
   }
}
#endif /* !REALTIME_NOISE */

/* RECTANGLE WAVE
** ==============
** reg0: 0-3=volume, 4=envelope, 5=hold, 6-7=duty cycle
** reg1: 0-2=sweep shifts, 3=sweep inc/dec, 4-6=sweep length, 7=sweep on
** reg2: 8 bits of freq
** reg3: 0-2=high freq, 7-4=vbl length counter
*/
#define  APU_RECTANGLE_OUTPUT (chan->output_vol)
static int32 apu_rectangle(rectangle_t *chan)
{
   int32 output;

#ifdef APU_YANO
   double total;
   double sample_weight;
#else /* !APU_YANO */
#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_RECTANGLE_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
   // speed hacks. Rick.
   {
	   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

   /* TODO: using a table of max frequencies is not technically
   ** clean, but it is fast and (or should be) accurate 
   */
   if (chan->freq < 8 || (FALSE == chan->sweep_inc && chan->freq > chan->freq_limit))
      return APU_RECTANGLE_OUTPUT;

   /* frequency sweeping at a rate of (sweep_delay + 1) / 120 secs */
   if (chan->sweep_on && chan->sweep_shifts)
   {
      chan->sweep_phase -= 2 * apu_cnt_rate; /* 120/60 */
      while (chan->sweep_phase < 0)
      {
         chan->sweep_phase += chan->sweep_delay;

         if (chan->sweep_inc) /* ramp up */
         {
            if (TRUE == chan->sweep_complement)
               chan->freq += ~(chan->freq >> chan->sweep_shifts);
            else
               chan->freq -= (chan->freq >> chan->sweep_shifts);
         }
         else /* ramp down */
         {
            chan->freq += (chan->freq >> chan->sweep_shifts);
         }
      }
   }

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

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

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

      sample_weight = APU_TO_FIXED(chan->freq + 1);
      if (chan->phaseacc > 0) {
         sample_weight -= chan->phaseacc;
      }
      total += (chan->adder < chan->duty_flip)?sample_weight:-sample_weight;
   }
   chan->output_vol = (int)floor( output*total/apu->cycle_rate + 0.5);
#else /* !APU_YANO */
   chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
   if (chan->phaseacc >= 0)
      return APU_RECTANGLE_OUTPUT;

#ifdef APU_OVERSAMPLE
   num_times = total = 0;

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

   while (chan->phaseacc < 0)
   {
      chan->phaseacc += APU_TO_FIXED(chan->freq + 1);
      chan->adder = (chan->adder + 1) & 0x0F;

#ifdef APU_OVERSAMPLE
      if (chan->adder < chan->duty_flip)
         total += output;
      else
         total -= output;

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

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

   if (0 == chan->adder)
      chan->output_vol = output;
   else if (chan->adder == chan->duty_flip)
      chan->output_vol = -output;
#endif /* !APU_OVERSAMPLE */
#endif /* !APU_YANO */

   return APU_RECTANGLE_OUTPUT;
}

/* TRIANGLE WAVE
** =============
** reg0: 7=holdnote, 6-0=linear length counter
** reg2: low 8 bits of frequency
** reg3: 7-3=length counter, 2-0=high 3 bits of frequency
*/
//#define  APU_TRIANGLE_OUTPUT  (chan->output_vol + (chan->output_vol >> 1))
#define  APU_TRIANGLE_OUTPUT  ((chan->output_vol*21)>>4)
static int32 apu_triangle(triangle_t *chan)
{
#ifdef APU_YANO
   static double val;
   double sample_weight, total, prev_val;
#else /* !APU_YANO */
   APU_VOLUME_DECAY(chan->output_vol);
#endif /* !APU_YANO */

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

   if (chan->counter_started)
   {
      if (chan->linear_length > 0)
         chan->linear_length -= 4 * apu_cnt_rate;  /* 240/60 */
      if (chan->vbl_length > 0 && FALSE == chan->holdnote)
         chan->vbl_length -= apu_cnt_rate;
   }
   else if (FALSE == chan->holdnote && chan->write_latency)
   {
      if (--chan->write_latency == 0)
         chan->counter_started = TRUE;
   }

   if (chan->linear_length <= 0 || chan->freq < APU_TO_FIXED(4)) /* inaudible */
      return APU_TRIANGLE_OUTPUT;

#ifdef APU_YANO
   if ( chan->ideal_triangle) {
      total = 0;
      sample_weight = 0;
      prev_val = val;
      if ( chan->adder) {
         val -= (double)apu->cycle_rate/chan->freq;
      } else {
         val += (double)apu->cycle_rate/chan->freq;
      }
      while ( (val < -8) || (val >= 8)) {
         if (val <-8) {
            total += (prev_val + (-8))*(prev_val-(-8));
            sample_weight += prev_val-(-8);
            prev_val = -8;
            val = -16-val;
            chan->adder = 0;
         }
         if (val >= 8) {
            total += (prev_val + 8)*(8-prev_val);
            sample_weight += 8-prev_val;
            prev_val = 8;
            val = 16-val;
            chan->adder = 1;
         }
      }
      if ( chan->adder) {
         total += (prev_val + val)*(prev_val-val);
         sample_weight += prev_val-val;
      } else {
         total += (prev_val + val)*(val-prev_val);
         sample_weight += val-prev_val;
      }
      total /= sample_weight;
      chan->output_vol = (int)floor(total*256+0.5);
   } else { /* !ideal_triangle */
      sample_weight = chan->phaseacc;
      if ( sample_weight > apu->cycle_rate) {
         sample_weight = apu->cycle_rate;
      }
      total = (((chan->adder & 0x10)?0x1f:0)^chan->adder)*sample_weight;

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

         sample_weight = chan->freq;
         if (chan->phaseacc > 0) {
            sample_weight -= chan->phaseacc;
         }
         total += (((chan->adder & 0x10)?0x1f:0)^chan->adder)*sample_weight;
      }
      chan->output_vol = (int)floor( total*512/apu->cycle_rate + 0.5);
   }
#else /* !APU_YANO */
   chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */
   while (chan->phaseacc < 0)
   {
      chan->phaseacc += chan->freq;
      chan->adder = (chan->adder + 1) & 0x1F;

      if (chan->adder & 0x10)
         chan->output_vol -= (2 << 8);
      else
         chan->output_vol += (2 << 8);
   }
#endif /* !APU_YANO */

   return APU_TRIANGLE_OUTPUT;
}

⌨️ 快捷键说明

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