test

Dependencies:   SDFileSystem mbed-dev

Fork of Nucleo_Ex06_EMU by woodstock .

Revision:
4:53ef91c87d74
Parent:
0:3dac1f1bc9e0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pNesX_Apu.cpp	Sat May 27 02:17:37 2017 +0000
@@ -0,0 +1,674 @@
+/*===================================================================*/
+/*                                                                   */
+/*  pNesX_Apu.cpp : NES APU emulation (for Nucleo)                   */
+/*                                                                   */
+/*  2016/1/20  Racoon                                                */
+/*                                                                   */
+/*===================================================================*/
+
+#include "mbed.h"
+#include "SDFileSystem.h"
+
+#include "pNesX.h"
+#include "pNesX_System.h"
+
+//====================
+// DAC
+//====================
+AnalogOut DAC_Out(PA_4);
+WORD TrDac, P1Dac, P2Dac, NsDac, MixDac;
+#define DACFreq 24000
+// Pulse clock(8 steps) = 1789773 / 2 / DACFreq * 1000
+#define PuClock 37287
+// Triangle clock(32 steps) = 1789773 / DACFreq * 1000
+#define TrClock 74574
+
+//====================
+// DAC Timer
+//====================
+#define TIMx                TIM4
+#define TIMx_CLK_ENABLE     __HAL_RCC_TIM4_CLK_ENABLE
+#define TIMx_IRQn           TIM4_IRQn
+#define TIMx_IRQHandler     TIM4_IRQHandler
+TIM_HandleTypeDef TimHandle;
+
+//====================
+// Audio channel variables
+//====================
+
+const BYTE LengthTable[] = {10, 254, 20,  2, 40,  4, 80,  6, 160,  8, 60, 10, 14, 12, 26, 14,
+                            12, 16, 24, 18, 48, 20, 96, 22, 192, 24, 72, 26, 16, 28, 32, 30};
+
+// Minimum Volume
+#define MV 4096
+const WORD Pulse_Table[4][8] = { 0,MV, 0, 0, 0, 0, 0, 0,
+                                 0,MV,MV, 0, 0, 0, 0, 0,
+                                 0,MV,MV,MV,MV, 0, 0, 0,
+                                MV, 0, 0,MV,MV,MV,MV,MV};
+// Pulse1
+bool P1Enable;
+int P1Timer;
+int P1WaveLength;
+int P1WavePeriod;
+int P1Lapse;
+int P1Duty;
+int P1Vol;
+int P1LengthCounter;
+int P1EnvSeq;
+int P1EnvCnt;
+int P1Sweep;
+int P1SwCnt;
+int P1SwLevel;
+bool P1SwReloadFlag;
+bool P1SwOverflow;
+
+// Pulse2
+bool P2Enable;
+int P2Timer;
+int P2WaveLength;
+int P2WavePeriod;
+int P2Lapse;
+int P2Duty;
+int P2Vol;
+int P2LengthCounter;
+int P2EnvSeq;
+int P2EnvCnt;
+int P2Sweep;
+int P2SwCnt;
+int P2SwLevel;
+bool P2SwReloadFlag;
+bool P2SwOverflow;
+    
+// Triangle                   
+bool TrEnable;
+int TrTimer;
+const WORD Triangle_Table[] = {61440,57344,53248,49152,45056,40960,36864,32768,28672,24576,20480,16384,12288,8192,4096,0,
+                               0,4096,8192,12288,16384,20480,24576,28672,32768,36864,40960,45056,49152,53248,57344,61440};
+bool LinearCounterReloadFlag;
+int LinearCounter;
+int TrLengthCounter;
+int TrWaveLength;
+int TrWavePeriod;
+int TrLapse;
+
+// Noise
+bool NsEnable;
+const WORD Noise_Freq[] = {0,0,0,0,0,0,12000,11186,8860,7046,4710,3523,2349,1762,880,440};
+int NsFreq;
+int NsSum;
+int NsVol;
+int NsLengthCounter;
+int NsEnvSeq;
+int NsEnvCnt;
+
+// DMC
+int DmOutLevel;
+
+/*-------------------------------------------------------------------*/
+/*  DAC Timer callback                                               */
+/*-------------------------------------------------------------------*/
+extern "C" {
+void TIMx_IRQHandler(void)
+{
+  HAL_TIM_IRQHandler(&TimHandle);
+}
+
+void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
+{
+  TIMx_CLK_ENABLE();
+  
+  HAL_NVIC_SetPriority(TIMx_IRQn, 4, 0);
+  HAL_NVIC_EnableIRQ(TIMx_IRQn);
+}
+
+static void Error_Handler(void)
+{
+  while(1);
+}
+} // extern "C"
+
+/*-------------------------------------------------------------------*/
+/*  Randomize value (Noise channel)                                  */
+/*-------------------------------------------------------------------*/
+WORD ShiftReg = 1;
+
+WORD Rand96()
+{
+    ShiftReg |= ((ShiftReg ^ (ShiftReg >> 6)) & 1) << 15;
+    ShiftReg >>= 1;
+    return (ShiftReg & 1) ? MV : 0;    
+}
+
+WORD Rand32k()
+{
+    ShiftReg |= ((ShiftReg ^ (ShiftReg >> 1)) & 1) << 15;
+    ShiftReg >>= 1;
+    return (ShiftReg & 1) ? MV : 0;    
+}
+
+/*-------------------------------------------------------------------*/
+/*  DAC Timer Interrup                                               */
+/*-------------------------------------------------------------------*/
+void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
+{
+    // Pulse1
+    if (P1Enable && P1LengthCounter > 0 && !P1SwOverflow && P1Timer > 8)
+    {
+        P1Lapse += PuClock; 
+        P1Lapse %= P1WavePeriod;
+        P1Dac = Pulse_Table[P1Duty][P1Lapse / P1WaveLength] * P1Vol;           
+    }
+
+    // Pulse2
+    if (P2Enable && P2LengthCounter > 0 && !P2SwOverflow && P2Timer > 8)
+    {
+        P2Lapse += PuClock; 
+        P2Lapse %= P2WavePeriod;
+        P2Dac = Pulse_Table[P2Duty][P2Lapse / P2WaveLength] * P2Vol;   
+    }
+    
+    // Triangle
+    if (TrEnable && LinearCounter > 0 && TrLengthCounter > 0)
+    {
+        TrLapse += TrClock; 
+        TrLapse %= TrWavePeriod;
+        TrDac = Triangle_Table[TrLapse / TrWaveLength];        
+        TrDac -= (TrDac * DmOutLevel / 295);
+    }
+    
+    // Noise
+    if (NsEnable && NsLengthCounter > 0)
+    {
+        NsSum += NsFreq;
+        if (NsSum >= DACFreq)
+        {
+            NsSum %= DACFreq;
+
+            NsDac = ((APU_Reg[0xe] & 0x80) ? Rand96() : Rand32k()) * NsVol;
+            NsDac -= (NsDac * DmOutLevel / 295);            
+        }        
+    }
+        
+    // Mixing
+    MixDac = (P1Dac + P2Dac + TrDac + NsDac) / 4;
+        
+    // Output to DAC
+    DAC_Out.write_u16(MixDac);   
+}
+ 
+/*-------------------------------------------------------------------*/
+/*  Apu Initialize Function                                          */
+/*-------------------------------------------------------------------*/
+void ApuInit()
+{
+    // Setup DAC Timer  
+    TimHandle.Instance = TIMx;   
+    TimHandle.Init.Prescaler = (uint32_t) ((SystemCoreClock / 2) / 240000) - 1; // 240000Hz
+    TimHandle.Init.Period = 10 - 1; // 240000/10=24000Hz 
+    TimHandle.Init.ClockDivision = 0;
+    TimHandle.Init.CounterMode = TIM_COUNTERMODE_UP;
+    
+    if(HAL_TIM_Base_Init(&TimHandle) != HAL_OK)
+        Error_Handler();
+    
+    if(HAL_TIM_Base_Start_IT(&TimHandle) != HAL_OK)
+        Error_Handler();
+}
+
+/*-------------------------------------------------------------------*/
+/*  Apu Mute                                                         */
+/*-------------------------------------------------------------------*/
+void ApuMute(bool mute)
+{
+    if (mute)
+    {
+        if(HAL_TIM_Base_Stop_IT(&TimHandle) != HAL_OK)
+            Error_Handler();
+    }
+    else
+    {
+        if(HAL_TIM_Base_Start_IT(&TimHandle) != HAL_OK)
+            Error_Handler();
+    }    
+}
+      
+/*-------------------------------------------------------------------*/
+/*  Apu Write Function                                               */
+/*-------------------------------------------------------------------*/
+void ApuWrite( WORD wAddr, BYTE byData )
+{
+  APU_Reg[ wAddr ] = byData;
+
+  switch( wAddr )
+  {
+    //====================
+    // Pulse1
+    //====================
+    case 0:
+      P1Duty = (byData & 0xc0) >> 6;
+    
+      if (byData & 0x10)
+      {
+        // constant volume
+        P1Vol = byData & 0xf;
+      }
+      else
+      {
+        // envelope on
+        P1Vol = 15;
+        P1EnvCnt = (byData & 0xf) + 1;
+      }
+      break;
+
+    case 1:
+      P1SwReloadFlag = true;
+      P1SwLevel = (byData & 7);
+      break;
+  
+    case 2:
+    case 3:
+      P1Timer = APU_Reg[3] & 7;
+      P1Timer = (P1Timer << 8) | APU_Reg[2];
+      P1WaveLength = (P1Timer+1) * 1000;
+      P1WavePeriod = P1WaveLength * 8;               
+
+      if (wAddr == 3)
+      {
+        P1LengthCounter = LengthTable[(byData & 0xf8) >> 3];
+    
+        // Reset sequencer
+        P1Lapse = 0;
+        P1EnvSeq = P1EnvCnt;
+        P1Sweep = 0;
+        P1SwCnt = 0;
+        P1SwOverflow = false;
+      }
+      break;
+      
+    //====================
+    // Pulse2
+    //====================
+    case 4:
+      P2Duty = (byData & 0xc0) >> 6;
+    
+      if (byData & 0x10)
+      {
+        // constant volume
+        P2Vol = byData & 0xf;
+      }
+      else
+      {
+        // envelope on
+        P2Vol = 15;
+        P2EnvCnt = (byData & 0xf) + 1;
+      }
+      break;
+      
+    case 5:
+      P2SwReloadFlag = true;
+      P2SwLevel = (byData & 7);
+      break;
+  
+    case 6:
+    case 7:
+      P2Timer = APU_Reg[7] & 7;
+      P2Timer = (P2Timer << 8) | APU_Reg[6];
+      P2WaveLength = (P2Timer+1) * 1000;
+      P2WavePeriod = P2WaveLength * 8;         
+      if (wAddr == 7)
+      {
+        P2LengthCounter = LengthTable[(byData & 0xf8) >> 3];
+    
+        // Reset sequencer
+        P2Lapse = 0;
+        P2EnvSeq = P2EnvCnt;
+        P2Sweep = 0;
+        P2SwCnt = 0;
+        P2SwOverflow = false;
+      }
+      break;
+      
+    //====================
+    // Triangle
+    //====================
+    case 0x8:
+      LinearCounterReloadFlag = true;
+      break;
+      
+    case 0xA:
+    case 0xB:
+      TrTimer = APU_Reg[0xB] & 7;
+      TrTimer = (TrTimer << 8) | APU_Reg[0xA];
+      TrWaveLength = TrTimer * 1000;
+      TrWavePeriod = TrWaveLength * 32;          
+      
+      if (wAddr == 0xB)
+      {
+        TrLengthCounter = LengthTable[(byData & 0xf8) >> 3];
+        LinearCounterReloadFlag = true;
+      }
+      break;
+      
+    //====================
+    // Noise
+    //====================
+    case 0xC:
+      if (byData & 0x10)
+      {
+        // constant volume
+        NsVol = byData & 0xf;
+      }
+      else
+      {
+        // envelope on
+        NsVol = 15;
+        NsEnvCnt = (byData & 0xf) + 1;
+      }
+      break;
+        
+    case 0xF:
+      NsLengthCounter = LengthTable[(byData & 0xf8) >> 3];
+    
+      // Reset sequencer
+      NsEnvSeq = NsEnvCnt;
+      break;
+
+    case 0xE:
+      NsFreq = Noise_Freq[byData & 0xf];
+      break;
+  
+    //====================
+    // DMC(PCM)
+    //====================
+    case 0x11:
+      DmOutLevel = byData & 0x7f;
+      break;
+      
+    //====================
+    // Control
+    //====================
+    case 0x15:
+      if (byData & 1)
+      {
+        P1Enable = true;
+      }
+      else
+      {
+        P1Enable = false;
+        P1LengthCounter = 0;
+      }
+              
+      if (byData & 2)
+      {
+        P2Enable = true;
+      }
+      else
+      {
+        P2Enable = false;
+        P2LengthCounter = 0;
+      }
+      
+      if (byData & 4)
+      {
+        TrEnable = true;
+      }
+      else
+      {
+        TrEnable = false;
+        TrLengthCounter = 0;
+      }
+
+      if (byData & 8)
+      {
+        NsEnable = true;
+      }
+      else
+      {
+        NsEnable = false;
+        NsLengthCounter = 0;
+      }
+      break;
+  } // Switch( wAddr ) 
+}
+
+/*-------------------------------------------------------------------*/
+/*  240Hz Clock                                                      */
+/*-------------------------------------------------------------------*/
+void pNesX_ApuClk_240Hz()
+{
+  //====================
+  // Pulse1 Envelope
+  //====================
+  if (!(APU_Reg[0] & 0x10) && P1EnvSeq > 0)
+  {
+    P1EnvSeq--;
+    if (P1EnvSeq == 0)
+    {
+      if (P1Vol > 0)
+      {
+        --P1Vol;
+        P1EnvSeq = P1EnvCnt;
+      }
+      else
+      { 
+        if (APU_Reg[0] & 0x20)
+        {
+          P1Vol = 15;
+          P1EnvSeq = P1EnvCnt;
+        }
+      }
+    }    
+  }
+
+  //====================
+  // Pulse2 Envelope
+  //====================
+  if (!(APU_Reg[4] & 0x10) && P2EnvSeq > 0)
+  {
+    P2EnvSeq--;
+    if (P2EnvSeq == 0)
+    {
+      if (P2Vol > 0)
+      {
+        --P2Vol;
+        P2EnvSeq = P2EnvCnt;
+      }
+      else
+      { 
+        if (APU_Reg[4] & 0x20)
+        {
+          P2Vol = 15;
+          P2EnvSeq = P2EnvCnt;
+        }
+      }
+    }    
+  }
+    
+  //====================
+  // Triangle Linear Counter
+  //====================
+  if (LinearCounterReloadFlag)
+  {
+    LinearCounter = APU_Reg[0x8] & 0x7f;
+  }
+  else if (LinearCounter > 0)
+  {
+    LinearCounter--;
+  }
+  
+  if (!(APU_Reg[0x8] & 0x80))
+  {
+    LinearCounterReloadFlag = false;
+  }
+  
+  //====================
+  // Noise Envelope
+  //====================
+  if (!(APU_Reg[0xc] & 0x10) && NsEnvSeq > 0)
+  {
+    NsEnvSeq--;
+    if (NsEnvSeq == 0)
+    {
+      if (NsVol > 0)
+      {
+        --NsVol;
+        NsEnvSeq = NsEnvCnt;
+      }
+      else
+      { 
+        if (APU_Reg[0xc] & 0x20)
+        {
+          NsVol = 15;
+          NsEnvSeq = NsEnvCnt;
+        }
+      }
+    }    
+  }  
+}
+
+/*-------------------------------------------------------------------*/
+/*  120Hz Clock                                                      */
+/*-------------------------------------------------------------------*/
+void pNesX_ApuClk_120Hz()
+{
+  //====================
+  // Pulse1 Sweep
+  //====================
+  if (P1SwReloadFlag)
+  {
+    P1Sweep = ((APU_Reg[1] & 0x70) >> 4) + 1;
+    P1SwCnt = 0;
+    P1SwReloadFlag = false;    
+  }
+  else if (APU_Reg[1] & 0x80 && !P1SwOverflow)
+  {
+    P1SwCnt++;
+    if (P1SwCnt == P1Sweep)
+    {
+      P1SwCnt = 0;
+      
+      int sweep = P1Timer >> P1SwLevel;
+      int value = P1Timer;
+      if (APU_Reg[1] & 8)
+      {
+        value = value - sweep - 1;
+        if (value < 8)
+        {
+          P1SwOverflow = true;
+          return;
+        }
+      }
+      else
+      {
+        value = value + sweep;
+        if (value > 0x7ff)
+        {
+          P1SwOverflow = true;
+          return;
+        }
+      } 
+      P1Timer = value;
+      
+      APU_Reg[3] &= 7;
+      APU_Reg[3] |= value >> 8;
+      APU_Reg[2] = value & 0xff;
+       
+      P1WaveLength = (value + 1) * 1000;
+      P1WavePeriod = P1WaveLength * 8;         
+    }
+  }
+
+  //====================
+  // Pulse1 Sweep
+  //====================  
+  if (P2SwReloadFlag)
+  {
+    P2Sweep = ((APU_Reg[5] & 0x70) >> 4) + 1;
+    P2SwCnt = 0;
+    P2SwReloadFlag = false;    
+  }
+  else if (APU_Reg[5] & 0x80 && !P2SwOverflow)
+  {
+    P2SwCnt++;
+    if (P2SwCnt == P2Sweep)
+    {
+      P2SwCnt = 0;
+      
+      int sweep = P2Timer >> P2SwLevel;
+      int value = P2Timer;
+      if (APU_Reg[5] & 8)
+      {
+        value = value - sweep;
+        if (value < 8)
+        {
+          P2SwOverflow = true;
+          return;
+        }
+      }
+      else
+      {
+        value = value + sweep;
+        if (value > 0x7ff)
+        {
+          P2SwOverflow = true;
+          return;
+        }
+      } 
+      
+      P2Timer = value;
+      
+      APU_Reg[7] &= 7;
+      APU_Reg[7] |= value >> 8;
+      APU_Reg[6] = value & 0xff;
+       
+      P2WaveLength = (value + 1) * 1000;
+      P2WavePeriod = P2WaveLength * 8;         
+      
+    }
+  }
+}
+
+/*-------------------------------------------------------------------*/
+/*  60Hz Clock                                                       */
+/*-------------------------------------------------------------------*/
+void pNesX_ApuClk_60Hz()
+{
+  // Pulse1
+  if (!(APU_Reg[0] & 0x20) && P1LengthCounter > 0)
+  {
+    P1LengthCounter--;
+  }
+
+  // Pulse2
+  if (!(APU_Reg[4] & 0x20) && P2LengthCounter > 0)
+  {
+    P2LengthCounter--;
+  }
+
+  // Triangle
+  if (!(APU_Reg[0x8] & 0x80) && TrLengthCounter > 0)
+  {
+    TrLengthCounter--;
+  }
+  
+  // Noise
+  if (!(APU_Reg[0xc] & 0x20) && NsLengthCounter > 0)
+  {
+    NsLengthCounter--;
+  }
+  
+}
+
+/*-------------------------------------------------------------------*/
+/*  Status                                                           */
+/*-------------------------------------------------------------------*/
+BYTE ApuRead( WORD wAddr )
+{
+  if (wAddr == 0x15)
+  {
+    return (P1LengthCounter > 0) | (P2LengthCounter > 0) << 1 | (TrLengthCounter > 0) << 2 | (NsLengthCounter > 0) << 3;
+  }
+  
+  return APU_Reg[ wAddr ];  
+}
+