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

📄 nes_apu.c

📁 linux下的MPEG1
💻 C
📖 第 1 页 / 共 2 页
字号:
/*** 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.4 2005/05/07 09:11:39 valtri Exp $*/#include <string.h>#include "types.h"#include "log.h"#include "nes_apu.h"#include "nes6502.h"#ifdef NSF_PLAYER#include "nsf.h"#else#include "nes.h"#include "nes_ppu.h"#include "nes_mmc.h"#include "nesinput.h"#endif /* !NSF_PLAYER */#define  APU_OVERSAMPLE#define  APU_VOLUME_DECAY(x)  ((x) -= ((x) >> 7))/* 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_NOISEstatic 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 };#if 0 /* unused */static void apu_setcontext(apu_t *src_apu){   apu = src_apu;}#endif/*** Simple queue routines*/#define  APU_QEMPTY()   (apu->q_head == apu->q_tail)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];}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_NOISEINLINE 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);}#elsestatic 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/* 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_volstatic int32 apu_rectangle(rectangle_t *chan){   int32 output;#ifdef APU_OVERSAMPLE   int num_times;   int32 total;#endif   APU_VOLUME_DECAY(chan->output_vol);   if (FALSE == chan->enabled || 0 == chan->vbl_length)      return APU_RECTANGLE_OUTPUT;   /* vbl length counter */   if (FALSE == chan->holdnote)      chan->vbl_length--;   /* envelope decay at a rate of (env_delay + 1) / 240 secs */   chan->env_phase -= 4; /* 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++;   }   if ((FALSE == chan->sweep_inc && chan->freq > chan->freq_limit)       || chan->freq < APU_TO_FIXED(4))      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; /* 120/60 */      while (chan->sweep_phase < 0)      {         chan->sweep_phase += chan->sweep_delay;         if (chan->sweep_inc) /* ramp up */            chan->freq -= chan->freq >> (chan->sweep_shifts);         else /* ramp down */            chan->freq += chan->freq >> (chan->sweep_shifts);      }   }   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   while (chan->phaseacc < 0)   {      chan->phaseacc += chan->freq;      chan->adder = (chan->adder + 1) & 0x0F;#ifdef APU_OVERSAMPLE      if (chan->adder < chan->duty_flip)         total += output;      else         total -= output;      num_times++;#endif   }#ifdef APU_OVERSAMPLE   chan->output_vol = total / num_times;#else   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   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 >> 2))static int32 apu_triangle(triangle_t *chan){   APU_VOLUME_DECAY(chan->output_vol);   if (FALSE == chan->enabled || 0 == chan->vbl_length)      return APU_TRIANGLE_OUTPUT;   if (chan->counter_started)   {      if (chan->linear_length > 0)         chan->linear_length--;      if (chan->vbl_length && FALSE == chan->holdnote)         chan->vbl_length--;   }   else if (FALSE == chan->holdnote && chan->write_latency)   {      if (--chan->write_latency == 0)         chan->counter_started = TRUE;   }/*   if (chan->countmode == COUNTMODE_COUNT)   {      if (chan->linear_length > 0)         chan->linear_length--;      if (chan->vbl_length)         chan->vbl_length--;   }*/   if (0 == chan->linear_length || chan->freq < APU_TO_FIXED(4)) /* inaudible */      return APU_TRIANGLE_OUTPUT;   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);   }   return APU_TRIANGLE_OUTPUT;}/* 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 + chan->output_vol) >> 2)static int32 apu_noise(noise_t *chan){   int32 outvol;#if defined(APU_OVERSAMPLE) && defined(REALTIME_NOISE)#else   int32 noise_bit;#endif#ifdef APU_OVERSAMPLE   int num_times;   int32 total;#endif   APU_VOLUME_DECAY(chan->output_vol);   if (FALSE == chan->enabled || 0 == chan->vbl_length)      return APU_NOISE_OUTPUT;   /* vbl length counter */   if (FALSE == chan->holdnote)      chan->vbl_length--;   /* envelope decay at a rate of (env_delay + 1) / 240 secs */   chan->env_phase -= 4; /* 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++;   }   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   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      noise_bit = shift_register15(chan->xor_tap);#endif#else      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#endif /* REALTIME_NOISE */   }#ifdef APU_OVERSAMPLE   chan->output_vol = total / num_times;#else   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   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 + chan->output_vol) >> 2)static int32 apu_dmc(dmc_t *chan){   int delta_bit;   APU_VOLUME_DECAY(chan->output_vol);   /* only process when channel is alive */   if (chan->dma_length)   {      chan->phaseacc -= apu->cycle_rate; /* # of cycles per sample */            while (chan->phaseacc < 0)      {         chan->phaseacc += chan->freq;                  delta_bit = (chan->dma_length & 7) ^ 7;                  if (7 == delta_bit)         {            chan->cur_byte = nes6502_getbyte(chan->address);                        /* steal a cycle from CPU*/            nes6502_setdma(1);            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;                  nes6502_irq();               }               /* bodge for timestamp queue */               chan->enabled = FALSE;               break;            }         }         /* positive delta */         if (chan->cur_byte & (1 << delta_bit))         {            if (chan->regs[1] < 0x7D)            {               chan->regs[1] += 2;               chan->output_vol += (2 << 8);            }/*            if (chan->regs[1] < 0x3F)               chan->regs[1]++;            chan->output_vol &= ~(0x7E << 8);            chan->output_vol |= ((chan->regs[1] << 1) << 8);*/         }         /* negative delta */         else                     {            if (chan->regs[1] > 1)            {               chan->regs[1] -= 2;               chan->output_vol -= (2 << 8);            }/*            if (chan->regs[1] > 0)               chan->regs[1]--;            chan->output_vol &= ~(0x7E << 8);            chan->output_vol |= ((chan->regs[1] << 1) << 8);*/         }      }   }   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:      chan = (address & 4) ? 1 : 0;      apu->rectangle[chan].regs[0] = value;      apu->rectangle[chan].volume = value & 0x0F;      apu->rectangle[chan].env_delay = decay_lut[value & 0x0F];      apu->rectangle[chan].holdnote = (value & 0x20) ? TRUE : FALSE;      apu->rectangle[chan].fixed_envelope = (value & 0x10) ? TRUE : FALSE;      apu->rectangle[chan].duty_flip = duty_lut[value >> 6];      break;   case APU_WRA1:   case APU_WRB1:

⌨️ 快捷键说明

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