Example #1
0
        // Register description for APU http://wiki.nesdev.com/w/index.php/APU
        public void write(ushort addr, byte val)
        {
            // Write to a pulse settings register
            if (addr >= 0x4000 && addr <= 0x4007)
            {
                Pulse pulse;
                if (addr < 0x4004)
                {
                    pulse = PULSE_ONE;
                }
                else
                {
                    pulse = PULSE_TWO;
                }
                int normalizedAddress = addr & 0x3;
                switch (normalizedAddress)
                {
                case 0:     // 0x4000 or 0x4004
                    pulse.DUTY = (byte)((val >> 6) & 0x3);
                    pulse.LENGTH_COUNTER_HALT = ((val >> 5) & 0x1) == 1;
                    pulse.CONSTANT_VOLUME     = ((val >> 4) & 0x1) == 1;
                    pulse.ENVELOPE_DIVIDER_PERIOD_OR_VOLUME = (byte)(val & 0xF);
                    pulse.envelope_volume  = 15;
                    pulse.envelope_counter = pulse.ENVELOPE_DIVIDER_PERIOD_OR_VOLUME;
                    break;

                case 1:     // 0x4001 or 0x4005
                    pulse.SWEEP_ENABLED        = ((val >> 7) & 0x1) == 1;
                    pulse.SWEEP_PERIOD         = (byte)((val >> 4) & 0x7);
                    pulse.sweep_period_counter = pulse.SWEEP_PERIOD;
                    pulse.SWEEP_NEGATE         = ((val >> 3) & 0x1) == 1;
                    pulse.SWEEP_SHIFT          = (byte)((val >> 4) & 0x7);
                    break;

                case 2:     // 0x4002 or 0x4006
                    pulse.TIMER = (ushort)((pulse.TIMER & 0xFF00) | val);
                    break;

                case 3:     // 0x4003 or 0x4007
                    pulse.LENGTH_COUNTER_LOAD    = (byte)((val >> 3) & 0x1F);
                    pulse.current_length_counter = lengthCounterLookupTable[pulse.LENGTH_COUNTER_LOAD];
                    pulse.TIMER = (ushort)((pulse.TIMER & 0x00FF) | ((val & 0x7) << 8));
                    break;

                default:
                    break;
                }
            }
            else
            {
                // All other register writes
                switch (addr)
                {
                case 0x4008:
                    TRIANGLE.LENGTH_COUNTER_HALT    = (((val >> 7) & 0x1) == 1);
                    TRIANGLE.LINEAR_COUNTER_LOAD    = (byte)(val & 0x7F);
                    TRIANGLE.current_linear_counter = TRIANGLE.LINEAR_COUNTER_LOAD;
                    break;

                case 0x4009:     // Unused register
                    break;

                case 0x400A:
                    TRIANGLE.TIMER = (ushort)((TRIANGLE.TIMER & 0xFF00) | val);
                    break;

                case 0x400B:
                    TRIANGLE.LENGTH_COUNTER_LOAD    = (byte)((val >> 3) & 0x1F);
                    TRIANGLE.current_length_counter = lengthCounterLookupTable[TRIANGLE.LENGTH_COUNTER_LOAD];
                    TRIANGLE.current_linear_counter = TRIANGLE.LINEAR_COUNTER_LOAD;
                    TRIANGLE.TIMER = (ushort)((TRIANGLE.TIMER & 0x00FF) | ((val & 0x7) << 8));
                    break;

                case 0x400C:
                    NOISE.ENVELOPE_LOOP   = (((val >> 5) & 0x1) == 1);
                    NOISE.CONSTANT_VOLUME = (((val >> 4) & 0x1) == 1);
                    NOISE.VOLUME_ENVELOP  = (byte)(val & 0xF);
                    break;

                case 0x400D:     // Unused register
                    break;

                case 0x400E:
                    NOISE.LOOP_NOISE   = (((val >> 7) & 0x1) == 1);
                    NOISE.NOISE_PERIOD = (byte)(val & 0xF);
                    break;

                case 0x400F:
                    NOISE.LENGTH_COUNTER_LOAD = (byte)((val >> 3) & 0xF);
                    break;

                case 0x4010:
                    DMC.IRQ_ENABLE = (((val >> 7) & 0x1) == 1);
                    DMC.LOOP       = (((val >> 6) & 0x1) == 1);
                    DMC.FREQUENCY  = (byte)(val & 0xF);
                    break;

                case 0x4011:
                    DMC.LOAD_COUNTER = (byte)(val & 0x7F);
                    break;

                case 0x4012:
                    DMC.SAMPLE_ADDRESS = val;
                    break;

                case 0x4013:
                    DMC.SAMPLE_LENGTH = val;
                    break;

                case 0x4015:
                    DMC.ENABLED       = (((val >> 4) & 0x1) == 1);
                    NOISE.ENABLED     = (((val >> 3) & 0x1) == 1);
                    TRIANGLE.ENABLED  = (((val >> 2) & 0x1) == 1);
                    PULSE_TWO.ENABLED = (((val >> 1) & 0x1) == 1);
                    PULSE_ONE.ENABLED = (((val >> 0) & 0x1) == 1);
                    break;

                case 0x4017:
                    FRAME_COUNTER_MODE = (((val >> 7) & 0x1) == 1) ? FrameCounterMode.FIVE_STEP : FrameCounterMode.FOUR_STEP;
                    IRQ_INHIBIT        = (((val >> 6) & 0x1) == 1);
                    break;

                default:
                    log.error("Attempting to write to unknown address {0:X4}", addr);
                    break;
                }
            }
        }
Example #2
0
 // Register description for APU http://wiki.nesdev.com/w/index.php/APU
 public void write(ushort addr, byte val)
 {
     // Write to a pulse settings register
     if (addr >= 0x4000 && addr <= 0x4007)
     {
         Pulse pulse;
         if (addr < 0x4004)
         {
             pulse = PULSE_ONE;
         }
         else
         {
             pulse = PULSE_TWO;
         }
         int normalizedAddress = addr & 0x3;
         switch (normalizedAddress)
         {
             case 0: // 0x4000 or 0x4004
                 pulse.DUTY = (byte)((val >> 6) & 0x3);
                 pulse.LENGTH_COUNTER_HALT = ((val >> 5) & 0x1) == 1;
                 pulse.CONSTANT_VOLUME = ((val >> 4) & 0x1) == 1;
                 pulse.ENVELOPE_DIVIDER_PERIOD_OR_VOLUME = (byte)(val & 0xF);
                 pulse.envelope_volume = 15;
                 pulse.envelope_counter = pulse.ENVELOPE_DIVIDER_PERIOD_OR_VOLUME;
                 break;
             case 1: // 0x4001 or 0x4005
                 pulse.SWEEP_ENABLED = ((val >> 7) & 0x1) == 1;
                 pulse.SWEEP_PERIOD = (byte)((val >> 4) & 0x7);
                 pulse.sweep_period_counter = pulse.SWEEP_PERIOD;
                 pulse.SWEEP_NEGATE = ((val >> 3) & 0x1) == 1;
                 pulse.SWEEP_SHIFT = (byte)((val >> 4) & 0x7);
                 break;
             case 2: // 0x4002 or 0x4006
                 pulse.TIMER = (ushort)((pulse.TIMER & 0xFF00) | val);
                 break;
             case 3: // 0x4003 or 0x4007
                 pulse.LENGTH_COUNTER_LOAD = (byte)((val >> 3) & 0x1F);
                 pulse.current_length_counter = lengthCounterLookupTable[pulse.LENGTH_COUNTER_LOAD];
                 pulse.TIMER = (ushort)((pulse.TIMER & 0x00FF) | ((val & 0x7) << 8));
                 break;
             default:
                 break;
         }
     }
     else
     {
         // All other register writes
         switch (addr)
         {
             case 0x4008:
                 TRIANGLE.LENGTH_COUNTER_HALT = (((val >> 7) & 0x1) == 1);
                 TRIANGLE.LINEAR_COUNTER_LOAD = (byte)(val & 0x7F);
                 TRIANGLE.current_linear_counter = TRIANGLE.LINEAR_COUNTER_LOAD;
                 break;
             case 0x4009: // Unused register
                 break;
             case 0x400A:
                 TRIANGLE.TIMER = (ushort)((TRIANGLE.TIMER & 0xFF00) | val);
                 break;
             case 0x400B:
                 TRIANGLE.LENGTH_COUNTER_LOAD = (byte)((val >> 3) & 0x1F);
                 TRIANGLE.current_length_counter = lengthCounterLookupTable[TRIANGLE.LENGTH_COUNTER_LOAD];
                 TRIANGLE.current_linear_counter = TRIANGLE.LINEAR_COUNTER_LOAD;
                 TRIANGLE.TIMER = (ushort)((TRIANGLE.TIMER & 0x00FF) | ((val & 0x7) << 8));
                 break;
             case 0x400C:
                 NOISE.ENVELOPE_LOOP = (((val >> 5) & 0x1) == 1);
                 NOISE.CONSTANT_VOLUME = (((val >> 4) & 0x1) == 1);
                 NOISE.VOLUME_ENVELOP = (byte)(val & 0xF);
                 break;
             case 0x400D: // Unused register
                 break;
             case 0x400E:
                 NOISE.LOOP_NOISE = (((val >> 7) & 0x1) == 1);
                 NOISE.NOISE_PERIOD = (byte)(val & 0xF);
                 break;
             case 0x400F:
                 NOISE.LENGTH_COUNTER_LOAD = (byte)((val >> 3) & 0xF);
                 break;
             case 0x4010:
                 DMC.IRQ_ENABLE = (((val >> 7) & 0x1) == 1);
                 DMC.LOOP = (((val >> 6) & 0x1) == 1);
                 DMC.FREQUENCY = (byte)(val & 0xF);
                 break;
             case 0x4011:
                 DMC.LOAD_COUNTER = (byte)(val & 0x7F);
                 break;
             case 0x4012:
                 DMC.SAMPLE_ADDRESS = val;
                 break;
             case 0x4013:
                 DMC.SAMPLE_LENGTH = val;
                 break;
             case 0x4015:
                 DMC.ENABLED = (((val >> 4) & 0x1) == 1);
                 NOISE.ENABLED = (((val >> 3) & 0x1) == 1);
                 TRIANGLE.ENABLED = (((val >> 2) & 0x1) == 1);
                 PULSE_TWO.ENABLED = (((val >> 1) & 0x1) == 1);
                 PULSE_ONE.ENABLED = (((val >> 0) & 0x1) == 1);
                 break;
             case 0x4017:
                 FRAME_COUNTER_MODE = (((val >> 7) & 0x1) == 1) ? FrameCounterMode.FIVE_STEP : FrameCounterMode.FOUR_STEP;
                 IRQ_INHIBIT = (((val >> 6) & 0x1) == 1);
                 break;
             default:
                 log.error("Attempting to write to unknown address {0:X4}", addr);
                 break;
         }
     }
 }