src/apu/apu.cc
/*
 * Writes the given value to a memory mapped APU register.
 * Writes to invalid addresses are ignored.
 */
Writes the given value to a memory mapped APU register.
Writes to invalid addresses are ignored.
Writes the given value to a memory mapped APU register . Writes to invalid addresses are ignored .
void Apu::Write(DoubleWord reg_addr, DataWord val) {
  ApuPulse *pulse;
  switch(reg_addr) {
    case PULSE_A_CONTROL_ADDR:
    case PULSE_B_CONTROL_ADDR:
      pulse = (reg_addr == PULSE_A_CONTROL_ADDR) ? pulse_a_ : pulse_b_;
      pulse->control = val;
      break;
    case PULSE_A_SWEEP_ADDR:
    case PULSE_B_SWEEP_ADDR:
      pulse = (reg_addr == PULSE_A_SWEEP_ADDR) ? pulse_a_ : pulse_b_;
      pulse->sweep = val;
      pulse->sweep_reload = true;
      break;
    case PULSE_A_TIMERL_ADDR:
    case PULSE_B_TIMERL_ADDR:
      pulse = (reg_addr == PULSE_A_TIMERL_ADDR) ? pulse_a_ : pulse_b_;
      pulse->timer = (pulse->timer & ~TIMER_LOW_MASK) | val;
      break;
    case PULSE_A_LENGTH_ADDR:
    case PULSE_B_LENGTH_ADDR:
      pulse = (reg_addr == PULSE_A_LENGTH_ADDR) ? pulse_a_ : pulse_b_;
      if (((channel_status_ & FLAG_PULSE_A_ACTIVE) && (pulse == pulse_a_))
         || ((channel_status_ & FLAG_PULSE_B_ACTIVE) && (pulse == pulse_b_))) {
        pulse->length = length_table[(val & LENGTH_MASK) >> LENGTH_SHIFT];
      }
      pulse->timer = (pulse->timer & TIMER_LOW_MASK)
                   | (((static_cast<DoubleWord>(val)) & TIMER_HIGH_MASK)
                   << TIMER_HIGH_SHIFT);
      pulse->pos = 0;
      pulse->env_reset = true;
      break;
    case TRI_CONTROL_ADDR:
      triangle_->control = val;
      break;
    case TRI_TIMERL_ADDR:
      triangle_->timer = (triangle_->timer & ~TIMER_LOW_MASK) | val;
      break;
    case TRI_LENGTH_ADDR:
      if (channel_status_ & FLAG_TRI_ACTIVE) {
        triangle_->length = length_table[(val & LENGTH_MASK) >> LENGTH_SHIFT];
      }
      triangle_->timer = (triangle_->timer & TIMER_LOW_MASK)
                       | (((static_cast<DoubleWord>(val)) & TIMER_HIGH_MASK)
                       << TIMER_HIGH_SHIFT);
      triangle_->linear_reload = true;
      break;
    case NOISE_CONTROL_ADDR:
      noise_->control = val;
      break;
    case NOISE_PERIOD_ADDR:
      noise_->period = val;
      noise_->timer = noise_periods[noise_->period & NOISE_PERIOD_MASK];
      break;
    case NOISE_LENGTH_ADDR:
      if (channel_status_ & FLAG_NOISE_ACTIVE) {
        noise_->length = length_table[(val & LENGTH_MASK) >> LENGTH_SHIFT];
      }
      noise_->env_reset = true;
      break;
    case DMC_CONTROL_ADDR:
      dmc_->control = val & DMC_CONTROL_MASK;
      dmc_->rate = val & DMC_RATE_MASK;
      dmc_->clock = 0;
      break;
    case DMC_COUNTER_ADDR:
      dmc_->level = val & DMC_LEVEL_MASK;
      break;
    case DMC_ADDRESS_ADDR:
      dmc_->addr = ((static_cast<DoubleWord>(val)) << DMC_ADDR_SHIFT)
                 | DMC_ADDR_BASE;
      dmc_->current_addr = dmc_->addr;
      break;
    case DMC_LENGTH_ADDR:
      dmc_->length = ((static_cast<DoubleWord>(val)) << DMC_LENGTH_SHIFT)
                   | DMC_LENGTH_BASE;
      dmc_->bytes_remaining = dmc_->length;
      break;
    case APU_STATUS_ADDR:
      StatusWrite(val);
      break;
    case FRAME_COUNTER_ADDR:
      frame_control_ = val;
      if ((frame_control_ & FLAG_IRQ_DISABLE) && frame_irq_) {
        frame_irq_ = false;
        (*irq_line_)--;
      }
      frame_clock_ = 0;
      if (frame_control_ & FLAG_MODE) {
        frame_step_ = 1;
        RunFrameStep();
      }
      frame_step_ = 0;
      break;
    default:
      break;
  }
  return;
}
void Apu :: Write ( DoubleWord reg_addr , DataWord val ) { ApuPulse * pulse ; switch ( reg_addr ) { case PULSE_A_CONTROL_ADDR : case PULSE_B_CONTROL_ADDR : pulse = ( reg_addr == PULSE_A_CONTROL_ADDR ) ? pulse_a_ : pulse_b_ ; pulse -> control = val ; break ; case PULSE_A_SWEEP_ADDR : case PULSE_B_SWEEP_ADDR : pulse = ( reg_addr == PULSE_A_SWEEP_ADDR ) ? pulse_a_ : pulse_b_ ; pulse -> sweep = val ; pulse -> sweep_reload = true ; break ; case PULSE_A_TIMERL_ADDR : case PULSE_B_TIMERL_ADDR : pulse = ( reg_addr == PULSE_A_TIMERL_ADDR ) ? pulse_a_ : pulse_b_ ; pulse -> timer = ( pulse -> timer & ~ TIMER_LOW_MASK ) | val ; break ; case PULSE_A_LENGTH_ADDR : case PULSE_B_LENGTH_ADDR : pulse = ( reg_addr == PULSE_A_LENGTH_ADDR ) ? pulse_a_ : pulse_b_ ; if ( ( ( channel_status_ & FLAG_PULSE_A_ACTIVE ) && ( pulse == pulse_a_ ) ) || ( ( channel_status_ & FLAG_PULSE_B_ACTIVE ) && ( pulse == pulse_b_ ) ) ) { pulse -> length = length_table [ ( val & LENGTH_MASK ) >> LENGTH_SHIFT ] ; } pulse -> timer = ( pulse -> timer & TIMER_LOW_MASK ) | ( ( ( static_cast < DoubleWord > ( val ) ) & TIMER_HIGH_MASK ) << TIMER_HIGH_SHIFT ) ; pulse -> pos = 0 ; pulse -> env_reset = true ; break ; case TRI_CONTROL_ADDR : triangle_ -> control = val ; break ; case TRI_TIMERL_ADDR : triangle_ -> timer = ( triangle_ -> timer & ~ TIMER_LOW_MASK ) | val ; break ; case TRI_LENGTH_ADDR : if ( channel_status_ & FLAG_TRI_ACTIVE ) { triangle_ -> length = length_table [ ( val & LENGTH_MASK ) >> LENGTH_SHIFT ] ; } triangle_ -> timer = ( triangle_ -> timer & TIMER_LOW_MASK ) | ( ( ( static_cast < DoubleWord > ( val ) ) & TIMER_HIGH_MASK ) << TIMER_HIGH_SHIFT ) ; triangle_ -> linear_reload = true ; break ; case NOISE_CONTROL_ADDR : noise_ -> control = val ; break ; case NOISE_PERIOD_ADDR : noise_ -> period = val ; noise_ -> timer = noise_periods [ noise_ -> period & NOISE_PERIOD_MASK ] ; break ; case NOISE_LENGTH_ADDR : if ( channel_status_ & FLAG_NOISE_ACTIVE ) { noise_ -> length = length_table [ ( val & LENGTH_MASK ) >> LENGTH_SHIFT ] ; } noise_ -> env_reset = true ; break ; case DMC_CONTROL_ADDR : dmc_ -> control = val & DMC_CONTROL_MASK ; dmc_ -> rate = val & DMC_RATE_MASK ; dmc_ -> clock = 0 ; break ; case DMC_COUNTER_ADDR : dmc_ -> level = val & DMC_LEVEL_MASK ; break ; case DMC_ADDRESS_ADDR : dmc_ -> addr = ( ( static_cast < DoubleWord > ( val ) ) << DMC_ADDR_SHIFT ) | DMC_ADDR_BASE ; dmc_ -> current_addr = dmc_ -> addr ; break ; case DMC_LENGTH_ADDR : dmc_ -> length = ( ( static_cast < DoubleWord > ( val ) ) << DMC_LENGTH_SHIFT ) | DMC_LENGTH_BASE ; dmc_ -> bytes_remaining = dmc_ -> length ; break ; case APU_STATUS_ADDR : StatusWrite ( val ) ; break ; case FRAME_COUNTER_ADDR : frame_control_ = val ; if ( ( frame_control_ & FLAG_IRQ_DISABLE ) && frame_irq_ ) { frame_irq_ = false ; ( * irq_line_ ) -- ; } frame_clock_ = 0 ; if ( frame_control_ & FLAG_MODE ) { frame_step_ = 1 ; RunFrameStep ( ) ; } frame_step_ = 0 ; break ; default : break ; } return ; }
Writes the given value to a memory mapped APU register.
Writes the given value to a memory mapped APU register .
// Determine which register is being accessed. // Update the control and envelope register. // Update the sweep unit control register. // Update the low byte of the timer. // Update the pulse length and timer high. // Reset the sequencer position and envelope. // Update the linear control register. // Update the low byte of the triangle period. // Update the triangle length and timer high. // Set the linear counter reload flag, which can be cleared by the // control bit. // Update the noise envelope and control register. // Update the period register and timer. // Update the noise length counter. // Update the control bits and rate of the DMC channel. // Update the PCM output level of the DMC channel. // Update the base sample address of the DMC channel. // Update the sample length of the DMC channel. // Clear the frame IRQ if the disable flag was set by the write. // Reset the frame timer. // TODO: Add delay. // If the address is invalid, nothing happens.